Index: /gsdl/trunk/perllib/cpan/Image/ExifTool.pm
===================================================================
--- /gsdl/trunk/perllib/cpan/Image/ExifTool.pm	(revision 16842)
+++ /gsdl/trunk/perllib/cpan/Image/ExifTool.pm	(revision 16842)
@@ -0,0 +1,4204 @@
+#------------------------------------------------------------------------------
+# File:         ExifTool.pm
+#
+# Description:  Read and write meta information
+#
+# URL:          http://owl.phy.queensu.ca/~phil/exiftool/
+#
+# Revisions:    Nov. 12/03 - P. Harvey Created
+#               (See html/history.html for revision history)
+#
+# Legal:        Copyright (c) 2003-2007, Phil Harvey (phil at owl.phy.queensu.ca)
+#               This library is free software; you can redistribute it and/or
+#               modify it under the same terms as Perl itself.
+#------------------------------------------------------------------------------
+
+package Image::ExifTool;
+
+use strict;
+require 5.004;  # require 5.004 for UNIVERSAL::isa (otherwise 5.002 would do)
+require Exporter;
+use File::RandomAccess;
+    
+use vars qw($VERSION $RELEASE @ISA %EXPORT_TAGS $AUTOLOAD @fileTypes %allTables
+            @tableOrder $exifAPP1hdr $xmpAPP1hdr $psAPP13hdr $psAPP13old
+            @loadAllTables %UserDefined $evalWarning);
+
+$VERSION = '7.00';
+$RELEASE = '';
+@ISA = qw(Exporter);
+%EXPORT_TAGS = (
+    # all public non-object-oriented functions
+    Public => [qw(
+        ImageInfo GetTagName GetShortcuts GetAllTags GetWritableTags
+        GetAllGroups GetDeleteGroups GetFileType CanWrite CanCreate
+    )],
+    DataAccess => [qw(
+        ReadValue GetByteOrder SetByteOrder ToggleByteOrder Get8u Get8s Get16u
+        Get16s Get32u Get32s GetFloat GetDouble GetFixed32s Write WriteValue
+        Tell Set8u Set8s Set16u Set32u
+    )],
+    Utils => [qw(
+        GetTagTable TagTableKeys GetTagInfoList GenerateTagIDs SetFileType
+        HtmlDump
+    )],
+    Vars => [qw(
+        %allTables @tableOrder @fileTypes
+    )],
+);
+# set all of our EXPORT_TAGS in EXPORT_OK
+Exporter::export_ok_tags(keys %EXPORT_TAGS);
+
+# test for problems that can arise if encoding.pm is used
+{ my $t = "\xff"; die "Incompatible encoding!\n" if ord($t) != 0xff; }
+
+# The following functions defined in Image::ExifTool::Writer are declared
+# here so their prototypes will be available.  The Writer routines will be
+# autoloaded when any of these are called.
+sub SetNewValue($;$$%);
+sub SetNewValuesFromFile($$;@);
+sub GetNewValues($;$$);
+sub CountNewValues($);
+sub SaveNewValues($);
+sub RestoreNewValues($);
+sub WriteInfo($$;$$);
+sub SetFileModifyDate($$;$);
+sub SetFileName($$;$);
+sub GetAllTags(;$);
+sub GetWritableTags(;$);
+sub GetAllGroups($);
+sub GetNewGroups($);
+sub GetDeleteGroups();
+# non-public routines below
+sub InsertTagValues($$$;$);
+sub IsWritable($);
+sub GetNewFileName($$);
+sub LoadAllTables();
+sub GetNewTagInfoList($;$);
+sub GetNewTagInfoHash($@);
+sub GetLangInfo($$);
+sub Get64s($$);
+sub Get64u($$);
+sub GetExtended($$);
+sub DecodeBits($$;$);
+sub EncodeBits($$;$$);
+sub HexDump($;$%);
+sub DumpTrailer($$);
+sub DumpUnknownTrailer($$);
+sub VerboseInfo($$$%);
+sub VerboseDir($$;$$);
+sub VPrint($$@);
+sub Rationalize($;$);
+sub Write($@);
+sub ProcessTrailers($$);
+sub WriteTrailerBuffer($$$);
+sub AddNewTrailers($;@);
+sub Tell($);
+sub WriteValue($$;$$$$);
+sub WriteDirectory($$$;$);
+sub WriteBinaryData($$$);
+sub CheckBinaryData($$$);
+sub WriteTIFF($$$);
+sub Charset2Unicode($$;$);
+sub Latin2Unicode($$);
+sub UTF82Unicode($$;$);
+sub Unicode2Charset($$;$);
+sub Unicode2Latin($$;$);
+sub Unicode2UTF8($$);
+sub PackUTF8(@);
+sub UnpackUTF8($);
+
+# list of main tag tables to load in LoadAllTables() (sub-tables are recursed
+# automatically).  Note: They will appear in this order in the documentation
+# (unless tweaked in BuildTagLookup::GetTableOrder()), so put Exif first.
+@loadAllTables = qw(
+    PhotoMechanic Exif GeoTiff CanonRaw KyoceraRaw MinoltaRaw SigmaRaw JPEG
+    Jpeg2000 BMP BMP PICT PNG MNG MIFF PDF PostScript Photoshop::Header
+    FujiFilm::RAF Panasonic::Raw Sony::SR2SubIFD ID3 Vorbis FLAC APE
+    APE::NewHeader APE::OldHeader MPC MPEG::Audio MPEG::Video QuickTime
+    QuickTime::ImageFile Flash Flash::FLV Real::Media Real::Audio
+    Real::Metafile RIFF AIFF ASF DICOM MIE HTML
+);
+
+# recognized file types, in the order we test unknown files
+# Notes: 1) There is no need to test for like types separately here
+# 2) Put types with no file signature at end of list to avoid false matches
+@fileTypes = qw(JPEG CRW TIFF GIF MRW RAF X3F JP2 PNG MIE MIFF PS PDF PSD XMP
+                BMP PPM RIFF AIFF ASF MOV MPEG Real SWF FLV OGG FLAC APE MPC
+                ICC HTML VRD QTIF FPX PICT MP3 DICM RAW);
+
+# file types that we can write (edit)
+my @writeTypes = qw(JPEG TIFF GIF CRW MRW ORF PNG MIE PSD XMP PPM EPS PS ICC
+                    VRD JP2);
+
+# file types that we can create from scratch
+my @createTypes = qw(XMP ICC MIE VRD);
+
+# file type lookup for all recognized file extensions
+my %fileTypeLookup = (
+    ACR  => ['DICM', 'American College of Radiology ACR-NEMA'],
+    AI   => [['PDF','PS'], 'Adobe Illustrator (PDF-like or PS-like)'],
+    AIF  => ['AIFF', 'Audio Interchange File Format'],
+    AIFC => ['AIFF', 'Audio Interchange File Format Compressed'],
+    AIFF => ['AIFF', 'Audio Interchange File Format'],
+    APE  => ['APE',  "Monkey's Audio format"],
+    ARW  => ['TIFF', 'Sony Alpha RAW format (TIFF-like)'],
+    ASF  => ['ASF',  'Microsoft Advanced Systems Format'],
+    AVI  => ['RIFF', 'Audio Video Interleaved (RIFF-based)'],
+    BMP  => ['BMP',  'Windows BitMaP'],
+    BTF  => ['BTF',  'Big Tagged Image File Format'],
+    CIFF => ['CRW',  'Camera Image File Format (same as CRW)'],
+    CR2  => ['TIFF', 'Canon RAW 2 format (TIFF-like)'],
+    CRW  => ['CRW',  'Canon RAW format'],
+    CS1  => ['PSD',  'Sinar CaptureShop 1-Shot RAW (PSD-like)'],
+    DC3  => ['DICM', 'DICOM image file'],
+    DCM  => ['DICM', 'DICOM image file'],
+    DIB  => ['BMP',  'Device Independent Bitmap (aka. BMP)'],
+    DIC  => ['DICM', 'DICOM image file'],
+    DICM => ['DICM', 'DICOM image file'],
+    DNG  => ['TIFF', 'Digital Negative (TIFF-like)'],
+    DCR  => ['TIFF', 'Kodak Digital Camera RAW (TIFF-like)'],
+    DOC  => ['FPX',  'Microsoft Word Document (FPX-like)'],
+    EPS  => ['EPS',  'Encapsulated PostScript Format'],
+    EPSF => ['EPS',  'Encapsulated PostScript Format'],
+    ERF  => ['TIFF', 'Epson Raw Format (TIFF-like)'],
+    FLAC => ['FLAC', 'Free Lossless Audio Codec'],
+    FLV  => ['FLV',  'Flash Video'],
+    FPX  => ['FPX',  'FlashPix'],
+    GIF  => ['GIF',  'Compuserve Graphics Interchange Format'],
+    HTM  => ['HTML', 'HyperText Markup Language'],
+    HTML => ['HTML', 'HyperText Markup Language'],
+    ICC  => ['ICC',  'International Color Consortium'],
+    ICM  => ['ICC',  'International Color Consortium'],
+    JNG  => ['PNG',  'JPG Network Graphics (PNG-like)'],
+    JP2  => ['JP2',  'JPEG 2000 file'],
+    JPEG => ['JPEG', 'Joint Photographic Experts Group'],
+    JPG  => ['JPEG', 'Joint Photographic Experts Group'],
+    JPX  => ['JP2',  'JPEG 2000 file'],
+    K25  => ['TIFF', 'Kodak DC25 RAW (TIFF-like)'],
+    M4A  => ['MOV',  'MPG4 Audio (QuickTime-based)'],
+    MEF  => ['TIFF', 'Mamiya (RAW) Electronic Format (TIFF-like)'],
+    MIE  => ['MIE',  'Meta Information Encapsulation format'],
+    MIF  => ['MIFF', 'Magick Image File Format'],
+    MIFF => ['MIFF', 'Magick Image File Format'],
+    MNG  => ['PNG',  'Multiple-image Network Graphics (PNG-like)'],
+    MOS  => ['TIFF', 'Creo Leaf Mosaic (TIFF-like)'],
+    MOV  => ['MOV',  'Apple QuickTime movie'],
+    MP3  => ['MP3',  'MPEG Layer 3 audio (uses ID3 information)'],
+    MP4  => ['MOV',  'MPEG Layer 4 video (QuickTime-based)'],
+    MPC  => ['MPC',  'Musepack Audio'],
+    MPEG => ['MPEG', 'MPEG audio/video format 1'],
+    MPG  => ['MPEG', 'MPEG audio/video format 1'],
+    MRW  => ['MRW',  'Minolta RAW format'],
+    NEF  => ['TIFF', 'Nikon (RAW) Electronic Format (TIFF-like)'],
+    OGG  => ['OGG',  'Ogg Vorbis audio file'],
+    ORF  => ['ORF',  'Olympus RAW format'],
+    PBM  => ['PPM',  'Portable BitMap (PPM-like)'],
+    PCT  => ['PICT', 'Apple PICTure'],
+    PDF  => ['PDF',  'Adobe Portable Document Format'],
+    PEF  => ['TIFF', 'Pentax (RAW) Electronic Format (TIFF-like)'],
+    PGM  => ['PPM',  'Portable Gray Map (PPM-like)'],
+    PICT => ['PICT', 'Apple PICTure'],
+    PNG  => ['PNG',  'Portable Network Graphics'],
+    PPM  => ['PPM',  'Portable Pixel Map'],
+    PPT  => ['FPX',  'Microsoft PowerPoint presentation (FPX-like)'],
+    PS   => ['PS',   'PostScript'],
+    PSD  => ['PSD',  'PhotoShop Drawing'],
+    QIF  => ['QTIF', 'QuickTime Image File'],
+    QT   => ['MOV',  'QuickTime movie'],
+    QTI  => ['QTIF', 'QuickTime Image File'],
+    QTIF => ['QTIF', 'QuickTime Image File'],
+    RA   => ['Real', 'Real Audio'],
+    RAF  => ['RAF',  'FujiFilm RAW Format'],
+    RAM  => ['Real', 'Real Audio Metafile'],
+    RAW  => ['RAW',  'Kyocera Contax N Digital RAW or Panasonic RAW'],
+    RIF  => ['RIFF', 'Resource Interchange File Format'],
+    RIFF => ['RIFF', 'Resource Interchange File Format'],
+    RM   => ['Real', 'Real Media'],
+    RMVB => ['Real', 'Real Media Variable Bitrate'],
+    RPM  => ['Real', 'Real Media Plug-in Metafile'],
+    RV   => ['Real', 'Real Video'],
+    SR2  => ['TIFF', 'Sony RAW Format 2 (TIFF-like)'],
+    SRF  => ['TIFF', 'Sony RAW Format (TIFF-like)'],
+    SWF  => ['SWF',  'Shockwave Flash'],
+    THM  => ['JPEG', 'Canon Thumbnail (aka. JPG)'],
+    TIF  => ['TIFF', 'Tagged Image File Format'],
+    TIFF => ['TIFF', 'Tagged Image File Format'],
+    VRD  => ['VRD',  'Canon VRD Recipe Data (written by DPP)'],
+    WAV  => ['RIFF', 'WAVeform (Windows digital audio format)'],
+    WDP  => ['TIFF', 'Windows Media Photo (TIFF-based)'],
+    WMA  => ['ASF',  'Windows Media Audio (ASF-based)'],
+    WMV  => ['ASF',  'Windows Media Video (ASF-based)'],
+    X3F  => ['X3F',  'Sigma RAW format'],
+    XHTML=> ['HTML', 'Extensible HyperText Markup Language'],
+    XLS  => ['FPX',  'Microsoft Excel worksheet (FPX-like)'],
+    XMP  => ['XMP',  'Extensible Metadata Platform data file'],
+);
+
+# MIME types for applicable file types above
+# (missing entries default to 'application/unknown')
+my %mimeType = (
+    AIFF => 'audio/aiff',
+    APE  => 'audio/x-monkeys-audio',
+    ASF  => 'video/x-ms-asf',
+    ARW  => 'image/x-raw',
+    AVI  => 'video/avi',
+    BMP  => 'image/bmp',
+    BTF  => 'application/unknown', #TEMPORARY!
+    CR2  => 'image/x-raw',
+    CRW  => 'image/x-raw',
+    EPS  => 'application/postscript',
+    ERF  => 'image/x-raw',
+    DCR  => 'image/x-raw',
+    DICM => 'application/dicom',
+    DNG  => 'image/x-raw',
+    DOC  => 'application/msword',
+    FLAC => 'audio/flac',
+    FLV  => 'video/x-flv',
+    FPX  => 'image/vnd.fpx',
+    GIF  => 'image/gif',
+    HTML => 'text/html',
+    JNG  => 'image/jng',
+    JP2  => 'image/jpeg2000',
+    JPEG => 'image/jpeg',
+    K25  => 'image/x-raw',
+    M4A  => 'audio/mp4',
+    MEF  => 'image/x-raw',
+    MIE  => 'application/x-mie',
+    MIFF => 'application/x-magick-image',
+    MNG  => 'video/mng',
+    MOS  => 'image/x-raw',
+    MOV  => 'video/quicktime',
+    MP3  => 'audio/mpeg',
+    MP4  => 'video/mp4',
+    MPC  => 'audio/x-musepack',
+    MPEG => 'video/mpeg',
+    MRW  => 'image/x-raw',
+    NEF  => 'image/x-raw',
+    OGG  => 'audio/x-ogg',
+    ORF  => 'image/x-raw',
+    PBM  => 'image/x-portable-bitmap',
+    PDF  => 'application/pdf',
+    PEF  => 'image/x-raw',
+    PGM  => 'image/x-portable-graymap',
+    PICT => 'image/pict',
+    PNG  => 'image/png',
+    PPM  => 'image/x-portable-pixmap',
+    PPT  => 'application/vnd.ms-powerpoint',
+    PS   => 'application/postscript',
+    PSD  => 'application/photoshop',
+    QTIF => 'image/x-quicktime',
+    RA   => 'audio/x-pn-realaudio',
+    RAF  => 'image/x-raw',
+    RAM  => 'audio/x-pn-realaudio',
+    RAW  => 'image/x-raw',
+    RM   => 'application/vnd.rn-realmedia',
+    RMVB => 'application/vnd.rn-realmedia-vbr',
+    RPM  => 'audio/x-pn-realaudio-plugin',
+    RV   => 'video/vnd.rn-realvideo',
+    SR2  => 'image/x-raw',
+    SRF  => 'image/x-raw',
+    SWF  => 'application/x-shockwave-flash',
+    TIFF => 'image/tiff',
+    WAV  => 'audio/x-wav',
+    WDP  => 'image/vnd.ms-photo',
+    WMA  => 'audio/x-ms-wma',
+    WMV  => 'video/x-ms-wmv',
+    X3F  => 'image/x-raw',
+    XLS  => 'application/vnd.ms-excel',
+    XMP  => 'application/rdf+xml',
+);
+
+# module names for each file type
+# (missing entries have same module name as file type)
+my %moduleName = (
+    BTF  => 'BigTIFF',
+    CRW  => 'CanonRaw',
+    DICM => 'DICOM',
+    EPS  => 'PostScript',
+    ICC  => 'ICC_Profile',
+    FLV  => 'Flash',
+    FPX  => 'FlashPix',
+    JP2  => 'Jpeg2000',
+    JPEG => '',     # (in the current module)
+    MOV  => 'QuickTime',
+    MP3  => 'ID3',
+    MRW  => 'MinoltaRaw',
+    OGG  => 'Vorbis',
+    ORF  => 'Olympus',
+    PS   => 'PostScript',
+    PSD  => 'Photoshop',
+    QTIF => 'QuickTime',
+    RAF  => 'FujiFilm',
+    RAW  => 'KyoceraRaw',
+    SWF  => 'Flash',
+    TIFF => '',
+    VRD  => 'CanonVRD',
+    X3F  => 'SigmaRaw',
+);
+
+# default group priority for writing
+my @defaultWriteGroups = qw(EXIF IPTC XMP MakerNotes Photoshop ICC_Profile CanonVRD);
+
+# group hash for ExifTool-generated tags
+my %allGroupsExifTool = ( 0 => 'ExifTool', 1 => 'ExifTool', 2 => 'ExifTool' );
+
+# headers for various segment types
+$exifAPP1hdr = "Exif\0\0";
+$xmpAPP1hdr = "http://ns.adobe.com/xap/1.0/\0";
+$psAPP13hdr = "Photoshop 3.0\0";
+$psAPP13old = 'Adobe_Photoshop2.5:';
+
+sub DummyWriteProc { return 1; }
+
+# tag information for preview image -- this should be used for all
+# PreviewImage tags so they are handled properly when reading/writing
+%Image::ExifTool::previewImageTagInfo = (
+    Name => 'PreviewImage',
+    Writable => 'undef',
+    # a value of 'none' is ok...
+    WriteCheck => '$val eq "none" ? undef : $self->CheckImage(\$val)',
+    DataTag => 'PreviewImage',
+    # we allow preview image to be set to '', but we don't want a zero-length value
+    # in the IFD, so set it temorarily to 'none'.  Note that the length is <= 4,
+    # so this value will fit in the IFD so the preview fixup won't be generated.
+    ValueConv => '$self->ValidateImage(\$val,$tag)',
+    ValueConvInv => '$val eq "" and $val="none"; $val',
+);
+
+# extra tags that aren't truly EXIF tags, but are generated by the script
+# Note: any tag in this list with a name corresponding to a Group0 name is
+#       used to write the entire corresponding directory as a block.
+%Image::ExifTool::Extra = (
+    GROUPS => { 0 => 'File', 1 => 'File', 2 => 'Image' },
+    DID_TAG_ID => 1,   # tag ID's aren't meaningful for these tags
+    WRITE_PROC => \&DummyWriteProc,
+    Comment => {
+        Notes => 'comment embedded in JPEG, GIF89a or PPM/PGM/PBM image',
+        Writable => 1,
+        WriteGroup => 'Comment',
+        Priority => 0,  # to preserve order of JPEG COM segments
+    },
+    Directory => {
+        Writable => 1,
+        Protected => 1,
+        # translate backslashes in directory names and add trailing '/'
+        ValueConvInv => '$_=$val; tr/\\\\/\//; m{[^/]$} and $_ .= "/"; $_',
+    },
+    FileName => {
+        Writable => 1,
+        Protected => 1,
+        ValueConvInv => '$val=~tr/\\\\/\//; $val',
+    },
+    FileSize => {
+        PrintConv => sub {
+            my $val = shift;
+            $val < 2048 and return "$val bytes";
+            $val < 2097152 and return sprintf('%.0f kB', $val / 1024);
+            return sprintf('%.0f MB', $val / 1048576);
+        },
+    },
+    FileType    => { },
+    FileModifyDate => {
+        Description => 'File Modification Date/Time',
+        Notes => 'the filesystem modification time',
+        Groups => { 2 => 'Time' },
+        Writable => 1,
+        Shift => 'Time',
+        ValueConv => 'ConvertUnixTime($val,"local")',
+        ValueConvInv => 'GetUnixTime($val,"local")',
+        PrintConv => '$self->ConvertDateTime($val)',
+        PrintConvInv => '$val',
+    },
+    MIMEType    => { },
+    ImageWidth  => { },
+    ImageHeight => { },
+    XResolution => { },
+    YResolution => { },
+    MaxVal      => { }, # max pixel value in PPM or PGM image
+    EXIF => {
+        Notes => 'the full EXIF data block',
+        Groups => { 0 => 'EXIF' },
+        Binary => 1,
+    },
+    ICC_Profile => {
+        Notes => 'the full ICC_Profile data block',
+        Groups => { 0 => 'ICC_Profile' },
+        Flags => ['Writable' ,'Protected', 'Binary'],
+        WriteCheck => q{
+            require Image::ExifTool::ICC_Profile;
+            return Image::ExifTool::ICC_Profile::ValidateICC(\$val);
+        },
+    },
+    XMP => {
+        Notes => 'the full XMP data block',
+        Groups => { 0 => 'XMP' },
+        Flags => [ 'Writable', 'Binary' ],
+        WriteCheck => q{
+            require Image::ExifTool::XMP;
+            return Image::ExifTool::XMP::CheckXMP($self, $tagInfo, \$val);
+        },
+    },
+    CanonVRD => {
+        Notes => 'the full Canon DPP VRD trailer block',
+        Groups => { 0 => 'CanonVRD' },
+        Flags => ['Writable' ,'Protected', 'Binary'],
+        WriteCheck => q{
+            return undef if $val =~ /^CANON OPTIONAL DATA\0/;
+            return 'Invalid CanonVRD data';
+        },
+    },
+    Encryption  => { }, # PDF encryption filter
+    ExifByteOrder => {
+        Writable => 1,
+        Notes => 'only writable for newly created EXIF segments',
+        PrintConv => {
+            II => 'Little-endian (Intel)',
+            MM => 'Big-endian (Motorola)',
+        },
+    },
+    ExifToolVersion => {
+        Description => 'ExifTool Version Number',
+        Groups      => \%allGroupsExifTool
+    },
+    Error       => { Priority => 0, Groups => \%allGroupsExifTool },
+    Warning     => { Priority => 0, Groups => \%allGroupsExifTool },
+);
+
+# information decoded from JPEG SOF frame
+# (define this here to avoid loading JPEG.pm)
+# (ref http://www.w3.org/Graphics/JPEG/itu-t81.pdf)
+%Image::ExifTool::JPEG::SOF = (
+    GROUPS => { 0 => 'File', 1 => 'File', 2 => 'Image' },
+    NOTES => 'This information is extracted from the JPEG Start Of Frame segment.',
+    VARS => { NO_ID => 1 },
+	EncodingProcess => {
+	    PrintHex => 1,
+	    PrintConv => {
+	        0x0 => 'Baseline DCT, Huffman coding',
+            0x1 => 'Extended sequential DCT, Huffman coding',
+            0x2 => 'Progressive DCT, Huffman coding',
+            0x3 => 'Lossless, Huffman coding',
+            0x5 => 'Sequential DCT, differential Huffman coding',
+            0x6 => 'Progressive DCT, differential Huffman coding',
+            0x7 => 'Lossless, Differential Huffman coding',
+            0x9 => 'Extended sequential DCT, arithmetic coding',
+            0xa => 'Progressive DCT, arithmetic coding',
+            0xb => 'Lossless, arithmetic coding',
+            0xd => 'Sequential DCT, differential arithmetic coding',
+            0xe => 'Progressive DCT, differential arithmetic coding',
+            0xf => 'Lossless, differential arithmetic coding',
+        }
+    },
+    BitsPerSample    => { },
+    ImageHeight      => { },
+    ImageWidth       => { },
+    ColorComponents  => { },
+    YCbCrSubSampling => {
+        Notes => 'calculated from components table',
+        PrintConv => {
+            '1 1' => 'YCbCr4:4:4 (1 1)',
+            '2 1' => 'YCbCr4:2:2 (2 1)',
+            '2 2' => 'YCbCr4:2:0 (2 2)',
+            '4 1' => 'YCbCr4:1:1 (4 1)',
+            '4 2' => 'YCbCr4:1:0 (4 2)',
+            '1 2' => 'YCbCr4:4:0 (1 2)',
+        },
+    },
+);
+
+# static private ExifTool variables
+
+%allTables = ( );   # list of all tables loaded (except composite tags)
+@tableOrder = ( );  # order the tables were loaded
+
+my $didTagID;       # flag indicating we are accessing tag ID's
+
+# composite tags (accumulation of all Composite tag tables)
+%Image::ExifTool::Composite = (
+    GROUPS => { 0 => 'Composite', 1 => 'Composite' },
+    DID_TAG_ID => 1,    # want empty tagID's for composite tags
+    WRITE_PROC => \&DummyWriteProc,
+);
+
+# JFIF APP0 definitions
+%Image::ExifTool::JFIF::Main = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
+    CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
+    GROUPS => { 0 => 'JFIF', 1 => 'JFIF', 2 => 'Image' },
+    DATAMEMBER => [ 2, 3, 5 ],
+    0 => {
+        Name => 'JFIFVersion',
+        Format => 'int8u[2]',
+        PrintConv => '$val=~tr/ /./;$val',
+    },
+    2 => {
+        Name => 'ResolutionUnit',
+        Writable => 1,
+        RawConv => '$$self{JFIFResolutionUnit} = $val',
+        PrintConv => {
+            0 => 'None',
+            1 => 'inches',
+            2 => 'cm',
+        },
+        Priority => -1,
+    },
+    3 => {
+        Name => 'XResolution',
+        Format => 'int16u',
+        Writable => 1,
+        Priority => -1,
+        RawConv => '$$self{JFIFXResolution} = $val',
+    },
+    5 => {
+        Name => 'YResolution',
+        Format => 'int16u',
+        Writable => 1,
+        Priority => -1,
+        RawConv => '$$self{JFIFYResolution} = $val',
+    },
+);
+%Image::ExifTool::JFIF::Extension = (
+    GROUPS => { 0 => 'JFIF', 1 => 'JFIF', 2 => 'Image' },
+    0x10 => {
+        Name => 'ThumbnailImage',
+        ValueConv => '$self->ValidateImage(\$val,$tag)',
+    },
+);
+
+# special tag names (not used for tag info)
+my %specialTags = (
+    PROCESS_PROC=>1, WRITE_PROC=>1, CHECK_PROC=>1, GROUPS=>1, FORMAT=>1,
+    FIRST_ENTRY=>1, TAG_PREFIX=>1, PRINT_CONV=>1, DID_TAG_ID=>1, WRITABLE=>1,
+    NOTES=>1, IS_OFFSET=>1, EXTRACT_UNKNOWN=>1, NAMESPACE=>1, PREFERRED=>1,
+    PARENT=>1, PRIORITY=>1, WRITE_GROUP=>1, LANG_INFO=>1, VARS=>1,
+    DATAMEMBER=>1,
+);
+
+#------------------------------------------------------------------------------
+# Warning handler routines (warning string stored in $evalWarning)
+#
+# Set warning message
+# Inputs: 0) warning string (undef to reset warning)
+sub SetWarning($) { $evalWarning = $_[0]; }
+
+# Get warning message
+sub GetWarning()  { return $evalWarning; }
+
+# Clean unnecessary information (line number, LF) from warning
+# Inputs: 0) warning string or undef to use current warning
+# Returns: cleaned warning
+sub CleanWarning(;$)
+{
+    my $str = shift;
+    unless (defined $str) {
+        return undef unless defined $evalWarning;
+        $str = $evalWarning;
+    }
+    $str = $1 if $str =~ /(.*) at /s;
+    $str =~ s/\s+$//s;
+    return $str;
+}
+
+#==============================================================================
+# New - create new ExifTool object
+# Inputs: 0) reference to exiftool object or ExifTool class name
+sub new
+{
+    local $_;
+    my $that = shift;
+    my $class = ref($that) || $that || 'Image::ExifTool';
+    my $self = bless {}, $class;
+
+    # make sure our main Exif tag table has been loaded
+    GetTagTable("Image::ExifTool::Exif::Main");
+
+    $self->ClearOptions();      # create default options hash
+    $self->{VALUE} = { };       # must initialize this for warning messages
+    $self->{DEL_GROUP} = { };   # list of groups to delete when writing
+
+    # initialize our new groups for writing
+    $self->SetNewGroups(@defaultWriteGroups);
+
+    return $self;
+}
+
+#------------------------------------------------------------------------------
+# ImageInfo - return specified information from image file
+# Inputs: 0) [optional] ExifTool object reference
+#         1) filename, file reference, or scalar data reference
+#         2-N) list of tag names to find (or tag list reference or options reference)
+# Returns: reference to hash of tag/value pairs (with "Error" entry on error)
+# Notes:
+#   - if no tags names are specified, the values of all tags are returned
+#   - tags may be specified with leading '-' to exclude
+#   - can pass a reference to list of tags to find, in which case the list will
+#     be updated with the tags found in the proper case and in the specified order.
+#   - can pass reference to hash specifying options
+#   - returned tag values may be scalar references indicating binary data
+#   - see ClearOptions() below for a list of options and their default values
+# Examples:
+#   use Image::ExifTool 'ImageInfo';
+#   my $info = ImageInfo($file, 'DateTimeOriginal', 'ImageSize');
+#    - or -
+#   my $exifTool = new Image::ExifTool;
+#   my $info = $exifTool->ImageInfo($file, \@tagList, {Sort=>'Group0'} );
+sub ImageInfo($;@)
+{
+    local $_;
+    # get our ExifTool object ($self) or create one if necessary
+    my $self;
+    if (ref $_[0] and UNIVERSAL::isa($_[0],'Image::ExifTool')) {
+        $self = shift;
+    } else {
+        $self = new Image::ExifTool;
+    }
+    my %saveOptions = %{$self->{OPTIONS}};  # save original options
+
+    # initialize file information
+    $self->{FILENAME} = $self->{RAF} = undef;
+
+    $self->ParseArguments(@_);              # parse our function arguments
+    $self->ExtractInfo(undef);              # extract meta information from image
+    my $info = $self->GetInfo(undef);       # get requested information
+
+    $self->{OPTIONS} = \%saveOptions;       # restore original options
+
+    return $info;   # return requested information
+}
+
+#------------------------------------------------------------------------------
+# Get/set ExifTool options
+# Inputs: 0) ExifTool object reference,
+#         1) Parameter name, 2) Value to set the option
+#         3-N) More parameter/value pairs
+# Returns: original value of last option specified
+sub Options($$;@)
+{
+    local $_;
+    my $self = shift;
+    my $options = $$self{OPTIONS};
+    my $oldVal;
+
+    while (@_) {
+        my $param = shift;
+        $oldVal = $options->{$param};
+        last unless @_;
+        $options->{$param} = shift;
+        # clone Exclude list and expand shortcuts
+        if ($param eq 'Exclude' and defined $options->{$param}) {
+            my @exclude;
+            my $val = $options->{$param};
+            if (ref $val eq 'ARRAY') {
+                @exclude = @$val;
+            } else {
+                @exclude = ($val);
+            }
+            ExpandShortcuts(\@exclude);
+            $options->{$param} = \@exclude;
+        }
+    }
+    return $oldVal;
+}
+
+#------------------------------------------------------------------------------
+# ClearOptions - set options to default values
+# Inputs: 0) ExifTool object reference
+sub ClearOptions($)
+{
+    local $_;
+    my $self = shift;
+
+    # create options hash with default values
+    # (commented out options don't need initializing)
+    $self->{OPTIONS} = {
+    #   Binary      => undef,   # flag to extract binary values even if tag not specified
+    #   ByteOrder   => undef,   # default byte order when creating EXIF information
+        Charset     => 'UTF8',  # character set for converting Unicode characters
+    #   Compact     => undef,   # compact XMP and IPTC data
+        Composite   => 1,       # flag to calculate Composite tags
+    #   Compress    => undef,   # flag to write new values as compressed if possible
+    #   CoordFormat => undef,   # GPS lat/long coordinate format
+    #   DateFormat  => undef,   # format for date/time
+        Duplicates  => 1,       # flag to save duplicate tag values
+    #   Exclude     => undef,   # tags to exclude
+    #   FastScan    => undef,   # flag to avoid scanning for trailer
+    #   FixBase     => undef,   # fix maker notes base offsets
+    #   Group#      => undef,   # return tags for specified groups in family #
+        HtmlDump    => 0,       # HTML dump (0-3, higher # = bigger limit)
+    #   HtmlDumpBase => undef,  # base address for HTML dump
+    #   IgnoreMinorErrors => undef, # ignore minor errors when reading/writing
+    #   List        => undef,   # extract lists of PrintConv values into arrays
+    #   MakerNotes  => undef,   # extract maker notes as a block
+    #   MissingTagValue =>undef,# value for missing tags when expanded in expressions
+        PrintConv   => 1,       # flag to enable print conversion
+    #   ScanForXMP  => undef,   # flag to scan for XMP information in all files
+        Sort        => 'Input', # order to sort found tags (Input, File, Alpha, Group#)
+    #   StrictDate  => undef,   # flag to return undef for invalid date conversions
+        TextOut     => \*STDOUT,# file for Verbose/HtmlDump output
+        Unknown     => 0,       # flag to get values of unknown tags (0-2)
+        Verbose     => 0,       # print verbose messages (0-4, higher # = more verbose)
+    };
+}
+
+#------------------------------------------------------------------------------
+# Extract meta information from image
+# Inputs: 0) ExifTool object reference
+#         1-N) Same as ImageInfo()
+# Returns: 1 if this was a valid image, 0 otherwise
+# Notes: pass an undefined value to avoid parsing arguments
+sub ExtractInfo($;@)
+{
+    local $_;
+    my $self = shift;
+    my $options = $self->{OPTIONS};     # pointer to current options
+    my %saveOptions;
+
+    if (defined $_[0] or $options->{HtmlDump}) {
+        %saveOptions = %$options;       # save original options
+        
+        # require duplicates for html dump
+        $self->Options(Duplicates => 1) if $options->{HtmlDump};
+
+        if (defined $_[0]) {
+            # only initialize filename if called with arguments
+            $self->{FILENAME} = undef;  # name of file (or '' if we didn't open it)
+            $self->{RAF} = undef;       # RandomAccess object reference
+    
+            $self->ParseArguments(@_);  # initialize from our arguments
+        }
+    }
+    # initialize ExifTool object members
+    $self->Init();
+
+    delete $self->{MAKER_NOTE_FIXUP};   # fixup information for extracted maker notes
+    delete $self->{MAKER_NOTE_BYTE_ORDER};
+    delete $self->{DONE_ID3};
+
+    my $filename = $self->{FILENAME};   # image file name ('' if already open)
+    my $raf = $self->{RAF};             # RandomAccess object
+
+    # return our version number
+    $self->FoundTag('ExifToolVersion', "$VERSION$RELEASE");
+
+    local *EXIFTOOL_FILE;   # avoid clashes with global namespace
+
+    unless ($raf) {
+        # save file name
+        if (defined $filename and $filename ne '') {
+            unless ($filename eq '-') {
+                my $name = $filename;
+                # extract file name from pipe if necessary
+                $name =~ /\|$/ and $name =~ s/.*?"(.*)".*/$1/;
+                my $dir;
+                if (eval 'require File::Basename') {
+                    $dir = File::Basename::dirname($name);
+                    $name = File::Basename::basename($name);
+                } else {
+                    $name =~ tr/\\/\//;
+                    if ($name =~ s/(.*)\///) {  # remove path
+                        $dir = length($1) ? $1 : '/';
+                    }
+                }
+                $self->FoundTag('FileName', $name);
+                $self->FoundTag('Directory', $dir) if defined $dir and length $dir;
+            }
+            # open the file
+            if (open(EXIFTOOL_FILE,$filename)) {
+                my $filePt = \*EXIFTOOL_FILE;
+                # create random access file object
+                $raf = new File::RandomAccess($filePt);
+                # patch to force pipe to be buffered because seek returns success
+                # in Windows cmd shell pipe even though it really failed
+                $raf->{TESTED} = -1 if $filename eq '-' or $filename =~ /\|$/;
+                $self->{RAF} = $raf;
+            } else {
+                $self->Error('Error opening file');
+            }
+        } else {
+            $self->Error('No file specified');
+        }
+    }
+
+    if ($raf) {
+        # get file size and last modified time if this is a plain file
+        if ($raf->{FILE_PT} and -f $raf->{FILE_PT}) {
+            my $fileSize = -s _;
+            my $fileTime = -M _;
+            $self->FoundTag('FileSize', $fileSize) if defined $fileSize;
+            $self->FoundTag('FileModifyDate', $^T - $fileTime*(24*3600)) if defined $fileTime;
+        }
+
+        # get list of file types to check
+        my $tiffType;
+        $self->{FILE_EXT} = GetFileExtension($filename);
+        my @fileTypeList = GetFileType($filename);
+        if (@fileTypeList) {
+            # add remaining types to end of list so we test them all
+            my $pat = join '|', @fileTypeList;
+            push @fileTypeList, grep(!/^($pat)$/, @fileTypes);
+            $tiffType = $self->{FILE_EXT};
+        } else {
+            # scan through all recognized file types
+            @fileTypeList = @fileTypes;
+            $tiffType = 'TIFF';
+        }
+        push @fileTypeList, ''; # end of list marker
+        # initialize the input file for seeking in binary data
+        $raf->BinMode();    # set binary mode before we start reading
+        my $pos = $raf->Tell(); # get file position so we can rewind
+        my %dirInfo = ( RAF => $raf, Base => $pos );
+        # loop through list of file types to test
+        my $type;
+        for (;;) {
+            $type = shift @fileTypeList;
+            unless ($type) {
+                last unless defined $type;
+                # last ditch effort to scan past unknown header for JPEG/TIFF
+                my $buff;
+                $raf->Read($buff, 1024);
+                next unless $buff =~ /(\xff\xd8\xff|MM\0\x2a|II\x2a\0)/g;
+                $type = ($1 eq "\xff\xd8\xff") ? 'JPEG' : 'TIFF';
+                my $skip = pos($buff) - length($1);
+                $dirInfo{Base} = $pos + $skip;
+                $raf->Seek($pos + $skip, 0);
+                $self->Warn("Skipped unknown $skip byte header");
+            }
+            # save file type in member variable
+            $self->{FILE_TYPE} = $type;
+            $dirInfo{Parent} = ($type eq 'TIFF') ? $tiffType : $type;
+            my $module = $moduleName{$type};
+            $module = $type unless defined $module;
+            my $func = "Process$type";
+
+            # load module if necessary
+            if ($module) {
+                require "Image/ExifTool/$module.pm";
+                $func = "Image::ExifTool::${module}::$func";
+            }
+            # process the file
+            no strict 'refs';
+            &$func($self, \%dirInfo) and last;
+            use strict 'refs';
+
+            # seek back to try again from the same position in the file
+            unless ($raf->Seek($pos, 0)) {
+                $self->Error('Error seeking in file');
+                last;
+            }
+        }
+        # scan for XMP if specified
+        if ($self->Options('ScanForXMP') and (not defined $type or
+            (not $self->Options('FastScan') and not $$self{FoundXMP})))
+        {
+            $raf->Seek($pos, 0);
+            require Image::ExifTool::XMP;
+            Image::ExifTool::XMP::ScanForXMP($self, $raf) and $type = '';
+        }
+        unless (defined $type) {
+            # if we were given a single image with a known type there
+            # must be a format error since we couldn't read it, otherwise
+            # it is likely we don't support images of this type
+            $self->Error(GetFileType($filename) ?
+                'File format error' : 'Unknown file type');
+        }
+        # extract binary EXIF data block only if requested
+        if (defined $self->{EXIF_DATA} and $self->{REQ_TAG_LOOKUP}->{exif}) {
+            $self->FoundTag('EXIF', $self->{EXIF_DATA});
+        }
+        # calculate composite tags
+        $self->BuildCompositeTags() if $options->{Composite};
+
+        # do our HTML dump if requested
+        if ($self->{HTML_DUMP}) {
+            $raf->Seek(0, 2);   # seek to end of file
+            $self->{HTML_DUMP}->FinishTiffDump($self, $raf->Tell());
+            my $pos = $options->{HtmlDumpBase};
+            $pos = ($self->{FIRST_EXIF_POS} || 0) unless defined $pos;
+            my $dataPt = defined $self->{EXIF_DATA} ? \$self->{EXIF_DATA} : undef;
+            undef $dataPt if defined $self->{EXIF_POS} and $pos != $self->{EXIF_POS};
+            $self->{HTML_DUMP}->Print($raf, $dataPt, $pos,
+                $options->{TextOut}, $options->{HtmlDump},
+                $self->{FILENAME} ? "HTML Dump ($self->{FILENAME})" : 'HTML Dump');
+        }
+
+        $raf->Close() if $filename;     # close the file if we opened it
+    }
+
+    # restore original options
+    %saveOptions and $self->{OPTIONS} = \%saveOptions;
+
+    return exists $self->{VALUE}->{Error} ? 0 : 1;
+}
+
+#------------------------------------------------------------------------------
+# Get hash of extracted meta information
+# Inputs: 0) ExifTool object reference
+#         1-N) options hash reference, tag list reference or tag names
+# Returns: Reference to information hash
+# Notes: - pass an undefined value to avoid parsing arguments
+#        - If groups are specified, first groups take precedence if duplicate
+#          tags found but Duplicates option not set.
+sub GetInfo($;@)
+{
+    local $_;
+    my $self = shift;
+    my %saveOptions;
+
+    unless (@_ and not defined $_[0]) {
+        %saveOptions = %{$self->{OPTIONS}}; # save original options
+        # must set FILENAME so it isn't parsed from the arguments
+        $self->{FILENAME} = '' unless defined $self->{FILENAME};
+        $self->ParseArguments(@_);
+    }
+
+    # get reference to list of tags for which we will return info
+    my $rtnTags = $self->SetFoundTags();
+
+    # build hash of tag information
+    my (%info, %ignored);
+    my $conv = $self->{OPTIONS}->{PrintConv} ? 'PrintConv' : 'ValueConv';
+    foreach (@$rtnTags) {
+        my $val = $self->GetValue($_, $conv);
+        defined $val or $ignored{$_} = 1, next;
+        $info{$_} = $val;
+    }
+
+    # remove ignored tags from the list
+    my $reqTags = $self->{REQUESTED_TAGS} || [ ];
+    if (%ignored and not @$reqTags) {
+        my @goodTags;
+        foreach (@$rtnTags) {
+            push @goodTags, $_ unless $ignored{$_};
+        }
+        $rtnTags = $self->{FOUND_TAGS} = \@goodTags;
+    }
+
+    # return sorted tag list if provided with a list reference
+    if ($self->{IO_TAG_LIST}) {
+        # use file order by default if no tags specified
+        # (no such thing as 'Input' order in this case)
+        my $sortOrder = $self->{OPTIONS}->{Sort};
+        unless (@$reqTags or ($sortOrder and $sortOrder ne 'Input')) {
+            $sortOrder = 'File';
+        }
+        # return tags in specified sort order
+        @{$self->{IO_TAG_LIST}} = $self->GetTagList($rtnTags, $sortOrder);
+    }
+
+    # restore original options
+    %saveOptions and $self->{OPTIONS} = \%saveOptions;
+
+    return \%info;
+}
+
+#------------------------------------------------------------------------------
+# Combine information from a list of info hashes
+# Unless Duplicates is enabled, first entry found takes priority
+# Inputs: 0) ExifTool object reference, 1-N) list of info hash references
+# Returns: Combined information hash reference
+sub CombineInfo($;@)
+{
+    local $_;
+    my $self = shift;
+    my (%combinedInfo, $info);
+
+    if ($self->{OPTIONS}->{Duplicates}) {
+        while ($info = shift) {
+            my $key;
+            foreach $key (keys %$info) {
+                $combinedInfo{$key} = $$info{$key};
+            }
+        }
+    } else {
+        my (%haveInfo, $tag);
+        while ($info = shift) {
+            foreach $tag (keys %$info) {
+                my $tagName = GetTagName($tag);
+                next if $haveInfo{$tagName};
+                $haveInfo{$tagName} = 1;
+                $combinedInfo{$tag} = $$info{$tag};
+            }
+        }
+    }
+    return \%combinedInfo;
+}
+
+#------------------------------------------------------------------------------
+# Inputs: 0) ExifTool object reference
+#         1) [optional] reference to info hash or tag list ref (default is found tags)
+#         2) [optional] sort order ('File', 'Input', ...)
+# Returns: List of tags in specified order
+sub GetTagList($;$$)
+{
+    local $_;
+    my ($self, $info, $sortOrder) = @_;
+
+    my $foundTags;
+    if (ref $info eq 'HASH') {
+        my @tags = keys %$info;
+        $foundTags = \@tags;
+    } elsif (ref $info eq 'ARRAY') {
+        $foundTags = $info;
+    }
+    my $fileOrder = $self->{FILE_ORDER};
+
+    if ($foundTags) {
+        # make sure a FILE_ORDER entry exists for all tags
+        # (note: already generated bogus entries for FOUND_TAGS case below)
+        foreach (@$foundTags) {
+            next if defined $$fileOrder{$_};
+            $$fileOrder{$_} = 999;
+        }
+    } else {
+        $sortOrder = $info if $info and not $sortOrder;
+        $foundTags = $self->{FOUND_TAGS} || $self->SetFoundTags() or return undef;
+    }
+    $sortOrder or $sortOrder = $self->{OPTIONS}->{Sort};
+
+    # return original list if no sort order specified
+    return @$foundTags unless $sortOrder and $sortOrder ne 'Input';
+
+    if ($sortOrder eq 'Alpha') {
+        return sort @$foundTags;
+    } elsif ($sortOrder =~ /^Group(\d*)/) {
+        my $family = $1 || 0;
+        # want to maintain a basic file order with the groups
+        # ordered in the way they appear in the file
+        my (%groupCount, %groupOrder);
+        my $numGroups = 0;
+        my $tag;
+        foreach $tag (sort { $$fileOrder{$a} <=> $$fileOrder{$b} } @$foundTags) {
+            my $group = $self->GetGroup($tag, $family);
+            my $num = $groupCount{$group};
+            $num or $num = $groupCount{$group} = ++$numGroups;
+            $groupOrder{$tag} = $num;
+        }
+        return sort { $groupOrder{$a} <=> $groupOrder{$b} or
+                      $$fileOrder{$a} <=> $$fileOrder{$b} } @$foundTags;
+    } else {
+        return sort { $$fileOrder{$a} <=> $$fileOrder{$b} } @$foundTags;
+    }
+}
+
+#------------------------------------------------------------------------------
+# Get list of found tags in specified sort order
+# Inputs: 0) ExifTool object reference, 1) sort order ('File', 'Input', ...)
+# Returns: List of tags in specified order
+# Notes: If not specified, sort order is taken from OPTIONS
+sub GetFoundTags($;$)
+{
+    local $_;
+    my ($self, $sortOrder) = @_;
+    my $foundTags = $self->{FOUND_TAGS} || $self->SetFoundTags() or return undef;
+    return $self->GetTagList($foundTags, $sortOrder);
+}
+
+#------------------------------------------------------------------------------
+# Get list of requested tags
+# Inputs: 0) ExifTool object reference
+# Returns: List of requested tags
+sub GetRequestedTags($)
+{
+    local $_;
+    return @{$_[0]->{REQUESTED_TAGS}};
+}
+
+#------------------------------------------------------------------------------
+# Get tag value
+# Inputs: 0) ExifTool object reference, 1) tag key
+#         2) [optional] Value type: PrintConv, ValueConv, Both or Raw, the default
+#            is PrintConv or ValueConv, depending on the PrintConv option setting
+# Returns: Scalar context: tag value or undefined
+#          List context: list of values or empty list
+sub GetValue($$;$)
+{
+    local $_;
+    my ($self, $tag, $type) = @_;
+
+    # start with the raw value
+    my $value = $self->{VALUE}->{$tag};
+    return wantarray ? () : undef unless defined $value;
+
+    # figure out what conversions to do
+    my (@convTypes, $tagInfo);
+    $type or $type = $self->{OPTIONS}->{PrintConv} ? 'PrintConv' : 'ValueConv';
+    unless ($type eq 'Raw') {
+        $tagInfo = $self->{TAG_INFO}->{$tag};
+        push @convTypes, 'ValueConv';
+        push @convTypes, 'PrintConv' unless $type eq 'ValueConv';
+    }
+
+    # do the conversions
+    my (@val, @prt, @raw, $convType, $valueConv);
+    foreach $convType (@convTypes) {
+        last if ref $value eq 'SCALAR'; # don't convert a scalar reference
+        my $conv = $$tagInfo{$convType};
+        unless (defined $conv) {
+            if ($convType eq 'ValueConv') {
+                next unless $$tagInfo{Binary};
+                $conv = '\$val';  # return scalar reference for binary values
+            } else {
+                # use PRINT_CONV from tag table if PrintConv not defined
+                next unless defined($conv = $tagInfo->{Table}->{PRINT_CONV});
+            }
+        }
+        # save old ValueConv value if we want Both
+        $valueConv = $value if $type eq 'Both' and $convType eq 'PrintConv';
+        my ($i, $val, $vals, @values, $convList);
+        # split into list if conversion is an array
+        if (ref $conv eq 'ARRAY') {
+            $convList = $conv;
+            $conv = $$convList[0];
+            my @valList = split ' ', $value;
+            $value = \@valList;
+        }
+        # initialize array so we can iterate over values in list
+        if (ref $value eq 'ARRAY') {
+            $i = 0;
+            $vals = $value;
+            $val = $$vals[0];
+        } else {
+            $val = $value;
+        }
+        # loop through all values in list
+        for (;;) {
+            if (defined $conv) {
+                # get values of required tags if this is a composite tag
+                if (ref $val eq 'HASH' and not @val) {
+                    foreach (keys %$val) {
+                        $raw[$_] = $self->{VALUE}->{$$val{$_}};
+                        ($val[$_], $prt[$_]) = $self->GetValue($$val{$_}, 'Both');
+                        next if defined $val[$_] or not $tagInfo->{Require}->{$_};
+                        return wantarray ? () : undef;
+                    }
+                    # set $val to $val[0], or \@val for a CODE ref conversion
+                    $val = ref $conv eq 'CODE' ? \@val : $val[0];
+                }
+                if (ref $conv eq 'HASH') {
+                    # look up converted value in hash
+                    unless (defined($value = $$conv{$val})) {
+                        if ($$conv{BITMASK}) {
+                            $value = DecodeBits($val, $$conv{BITMASK});
+                        } else {
+                            if ($$tagInfo{PrintHex} and $val and IsInt($val) and
+                                $convType eq 'PrintConv')
+                            {
+                                $val = sprintf('0x%x',$val);
+                            }
+                            $value = "Unknown ($val)";
+                        }
+                    }
+                } else {
+                    # call subroutine or do eval to convert value
+                    local $SIG{'__WARN__'} = \&SetWarning;
+                    undef $evalWarning;
+                    if (ref $conv eq 'CODE') {
+                        $value = &$conv($val, $self);
+                    } else {
+                        #### eval ValueConv/PrintConv ($val, $self, @val, @prt, @raw)
+                        $value = eval $conv;
+                        $@ and $evalWarning = $@;
+                    }
+                    if ($evalWarning) {
+                        delete $SIG{'__WARN__'};
+                        warn "$convType $tag: " . CleanWarning() . "\n";
+                    }
+                }
+            } else {
+                $value = $val;
+            }
+            last unless $vals;
+            # save this converted value and step to next value in list
+            push @values, $value if defined $value;
+            if (++$i >= scalar(@$vals)) {
+                $value = \@values if @values;
+                last;
+            }
+            $val = $$vals[$i];
+            $conv = $$convList[$i] if $convList;
+        }
+        # return undefined now if no value
+        return wantarray ? () : undef unless defined $value;
+        # join back into single value if split for conversion list
+        if ($convList and ref $value eq 'ARRAY') {
+            $value = join($convType eq 'PrintConv' ? '; ' : ' ', @$value);
+        }
+    }
+    if ($type eq 'Both') {
+        # $valueConv is undefined if there was no print conversion done
+        $valueConv = $value unless defined $valueConv;
+        # return Both values as a list (ValueConv, PrintConv)
+        return ($valueConv, $value);
+    }
+    if (ref $value eq 'ARRAY') {
+        # return array if requested
+        return @$value if wantarray;
+        # return list reference for Raw, ValueConv or if List option set
+        return $value if @convTypes < 2 or $self->{OPTIONS}->{List};
+        # otherwise join in comma-separated string
+        $value = join ', ', @$value;
+    }
+    return $value;
+}
+
+#------------------------------------------------------------------------------
+# Get tag identification number
+# Inputs: 0) ExifTool object reference, 1) tag key
+# Returns: Tag ID if available, otherwise ''
+sub GetTagID($$)
+{
+    local $_;
+    my ($self, $tag) = @_;
+    my $tagInfo = $self->{TAG_INFO}->{$tag};
+
+    if ($tagInfo) {
+        GenerateAllTagIDs();    # make sure tag ID's are generated
+        defined $$tagInfo{TagID} and return $$tagInfo{TagID};
+    }
+    # no ID for this tag (shouldn't happen)
+    return '';
+}
+
+#------------------------------------------------------------------------------
+# Get description for specified tag
+# Inputs: 0) ExifTool object reference, 1) tag key
+# Returns: Tag description
+# Notes: Will always return a defined value, even if description isn't available
+sub GetDescription($$)
+{
+    local $_;
+    my ($self, $tag) = @_;
+    my $tagInfo = $self->{TAG_INFO}->{$tag};
+    # ($tagInfo should be defined for any extracted tag,
+    # but we might as well handle the case where it isn't)
+    my $desc;
+    $desc = $$tagInfo{Description} if $tagInfo;
+    # just make the tag more readable if description doesn't exist
+    unless ($desc) {
+        $desc = MakeDescription(GetTagName($tag));
+        # save description in tag information
+        $$tagInfo{Description} = $desc if $tagInfo;
+    }
+    return $desc;
+}
+
+#------------------------------------------------------------------------------
+# Get group name for specified tag
+# Inputs: 0) ExifTool object reference
+#         1) tag key (or reference to tagInfo hash, not part of the public API)
+#         2) [optional] group family number (-1 to get extended group list)
+# Returns: Scalar context: Group name (for family 0 if not otherwise specified)
+#          Array context: Group name if family specified, otherwise list of
+#          group names for each family.
+sub GetGroup($$;$)
+{
+    local $_;
+    my ($self, $tag, $family) = @_;
+    my ($tagInfo, @groups, $extra);
+    if (ref $tag eq 'HASH') {
+        $tagInfo = $tag;
+        $tag = $tagInfo->{Name};
+    } else {
+        $tagInfo = $self->{TAG_INFO}->{$tag} or return '';
+    }
+    my $groups = $$tagInfo{Groups};
+    # fill in default groups unless already done
+    unless ($$tagInfo{GotGroups}) {
+        my $tagTablePtr = $$tagInfo{Table};
+        if ($tagTablePtr) {
+            # construct our group list
+            $groups or $groups = $$tagInfo{Groups} = { };
+            # fill in default groups
+            foreach (keys %{$$tagTablePtr{GROUPS}}) {
+                $$groups{$_} or $$groups{$_} = $tagTablePtr->{GROUPS}->{$_};
+            }
+        }
+        # set flag indicating group list was built
+        $$tagInfo{GotGroups} = 1;
+    }
+    if (defined $family and $family >= 0) {
+        return $$groups{$family} || 'Other' unless $family == 1;
+        $groups[$family] = $$groups{$family};
+    } else {
+        return $$groups{0} unless wantarray;
+        foreach (0..2) { $groups[$_] = $$groups{$_}; }
+    }
+    # modify family 1 group name if necessary
+    if ($extra = $self->{GROUP1}->{$tag}) {
+        if ($extra =~ /^\+(.*)/) {
+            $groups[1] .= $1;
+        } else {
+            $groups[1] = $extra;
+        }
+    }
+    if ($family) {
+        return $groups[1] if $family == 1;
+        # add additional matching group names to list
+        # ie) for MIE-Doc, also add MIE1, MIE1-Doc, MIE-Doc1 and MIE1-Doc1
+        # and for MIE2-Doc3, also add MIE2, MIE-Doc3, MIE2-Doc and MIE-Doc
+        if ($groups[1] =~ /^MIE(\d*)-(.+?)(\d*)$/) {
+            push @groups, 'MIE' . ($1 || '1');
+            push @groups, 'MIE' . ($1 ? '' : '1') . "-$2$3";
+            push @groups, "MIE$1-$2" . ($3 ? '' : '1');
+            push @groups, 'MIE' . ($1 ? '' : '1') . "-$2" . ($3 ? '' : '1');
+        }
+    }
+    return @groups;
+}
+
+#------------------------------------------------------------------------------
+# Get group names for specified tags
+# Inputs: 0) ExifTool object reference
+#         1) [optional] information hash reference (default all extracted info)
+#         2) [optional] group family number (default 0)
+# Returns: List of group names in alphabetical order
+sub GetGroups($;$$)
+{
+    local $_;
+    my $self = shift;
+    my $info = shift;
+    my $family;
+
+    # figure out our arguments
+    if (ref $info ne 'HASH') {
+        $family = $info;
+        $info = $self->{VALUE};
+    } else {
+        $family = shift;
+    }
+    $family = 0 unless defined $family;
+
+    # get a list of all groups in specified information
+    my ($tag, %groups);
+    foreach $tag (keys %$info) {
+        $groups{ $self->GetGroup($tag, $family) } = 1;
+    }
+    return sort keys %groups;
+}
+
+#------------------------------------------------------------------------------
+# Set priority for group where new values are written
+# Inputs: 0) ExifTool object reference,
+#         1-N) group names (reset to default if no groups specified)
+sub SetNewGroups($;@)
+{
+    local $_;
+    my ($self, @groups) = @_;
+    @groups or @groups = @defaultWriteGroups;
+    my $count = @groups;
+    my %priority;
+    foreach (@groups) {
+        $priority{lc($_)} = $count--;
+    }
+    $priority{file} = 10;       # 'File' group is always written (Comment)
+    $priority{composite} = 10;  # 'Composite' group is always written
+    # set write priority (higher # is higher priority)
+    $self->{WRITE_PRIORITY} = \%priority;
+    $self->{WRITE_GROUPS} = \@groups;
+}
+
+#------------------------------------------------------------------------------
+# Build composite tags from required tags
+# Inputs: 0) ExifTool object reference
+# Note: Tag values are calculated in alphabetical order unless a tag Require's
+#       or Desire's another composite tag, in which case the calculation is
+#       deferred until after the other tag is calculated.
+sub BuildCompositeTags($)
+{
+    local $_;
+    my $self = shift;
+
+    # first, add user-defined composite tags if necessary
+    if (defined %UserDefined and $UserDefined{'Image::ExifTool::Composite'}) {
+        AddCompositeTags($UserDefined{'Image::ExifTool::Composite'},1);
+        delete $UserDefined{'Image::ExifTool::Composite'};
+    }
+    my @tagList = sort keys %Image::ExifTool::Composite;
+    my %tagsUsed;
+
+    my $rawValue = $self->{VALUE};
+    for (;;) {
+        my %notBuilt;
+        foreach (@tagList) {
+            $notBuilt{$_} = 1;
+        }
+        my @deferredTags;
+        my $tag;
+COMPOSITE_TAG:
+        foreach $tag (@tagList) {
+            next if $specialTags{$tag};
+            my $tagInfo = $self->GetTagInfo(\%Image::ExifTool::Composite, $tag);
+            next unless $tagInfo;
+            # put required tags into array and make sure they all exist
+            my (%tagKey, $type, $found);
+            foreach $type ('Require','Desire') {
+                my $req = $$tagInfo{$type} or next;
+                # save Require'd and Desire'd tag values in list
+                my $index;
+                foreach $index (keys %$req) {
+                    my $reqTag = $$req{$index};
+                    # allow tag group to be specified
+                    if ($reqTag =~ /(.+?):(.+)/) {
+                        my ($reqGroup, $name) = ($1, $2);
+                        my $family;
+                        $family = $1 if $reqGroup =~ s/^(\d+)//;
+                        my $i = 0;
+                        for (;;++$i) {
+                            $reqTag = $name;
+                            $reqTag .= " ($i)" if $i;
+                            last unless defined $$rawValue{$reqTag};
+                            my @groups = $self->GetGroup($reqTag, $family);
+                            last if grep { $reqGroup eq $_ } @groups;
+                        }
+                    } elsif ($notBuilt{$reqTag}) {
+                        # calculate this tag later if it relies on another
+                        # Composite tag which hasn't been calculated yet
+                        push @deferredTags, $tag;
+                        next COMPOSITE_TAG;
+                    }
+                    if (defined $$rawValue{$reqTag}) {
+                        $found = 1;
+                    } else {
+                        # don't continue if we require this tag
+                        $type eq 'Require' and next COMPOSITE_TAG;
+                    }
+                    $tagKey{$index} = $reqTag;
+                }
+            }
+            delete $notBuilt{$tag}; # this tag is OK to build now
+            next unless $found;     # can't build tag if no values found
+            # keep track of all require'd tag keys
+            foreach (keys %tagKey) {
+                # only tag keys with same name as a composite tag can be replaced
+                # (also eliminates keys with instance numbers which can't be replaced either)
+                next unless $Image::ExifTool::Composite{$tagKey{$_}};
+                my $keyRef = \$tagKey{$_};
+                $tagsUsed{$$keyRef} or $tagsUsed{$$keyRef} = [ ];
+                push @{$tagsUsed{$$keyRef}}, $keyRef;
+            }
+            # save reference to tag key lookup as value for composite tag
+            my $key = $self->FoundTag($tagInfo, \%tagKey);
+            # check to see if we just replaced one of the tag keys we require'd
+            next unless defined $key and $tagsUsed{$key};
+            foreach (@{$tagsUsed{$key}}) {
+                $$_ = $self->{MOVED_KEY};   # replace with new tag key
+            }
+            delete $tagsUsed{$key};         # can't be replaced again
+        }
+        last unless @deferredTags;
+        if (@deferredTags == @tagList) {
+            # everything was deferred in the last pass,
+            # must be a circular dependency
+            warn "Circular dependency in Composite tags\n";
+            last;
+        }
+        @tagList = @deferredTags; # calculate deferred tags now
+    }
+}
+
+#------------------------------------------------------------------------------
+# Get tag name (removes copy index)
+# Inputs: 0) Tag key
+# Returns: Tag name
+sub GetTagName($)
+{
+    local $_;
+    $_[0] =~ /^(\S+)/;
+    return $1;
+}
+
+#------------------------------------------------------------------------------
+# Get list of shortcuts
+# Returns: Shortcut list (sorted alphabetically)
+sub GetShortcuts()
+{
+    local $_;
+    require Image::ExifTool::Shortcuts;
+    return sort keys %Image::ExifTool::Shortcuts::Main;
+}
+
+#------------------------------------------------------------------------------
+# Get file type for specified extension
+# Inputs: 0) file name or extension (case is not significant)
+#         1) flag to return long description instead of type
+# Returns: File type (or desc) or undef if extension not supported.  In array
+#          context, may return more than one file type if the file may be
+#          different formats.  Returns list of all recognized extensions if no
+#          file specified
+sub GetFileType(;$$)
+{
+    local $_;
+    my ($file, $desc) = @_;
+    return sort keys %fileTypeLookup unless defined $file;
+    my $fileType;
+    my $fileExt = GetFileExtension($file);
+    $fileExt = uc($file) unless $fileExt;
+    $fileExt and $fileType = $fileTypeLookup{$fileExt}; # look up the file type
+    return $$fileType[1] if $desc;  # return description if specified
+    $fileType = $$fileType[0];      # get file type (or list of types)
+    if (wantarray) {
+        return () unless $fileType;
+        return @$fileType if ref $fileType eq 'ARRAY';
+    } elsif ($fileType) {
+        $fileType = $fileExt if ref $fileType eq 'ARRAY';
+    }
+    return $fileType;
+}
+
+#------------------------------------------------------------------------------
+# Return true if we can write the specified file type
+# Inputs: 0) file name or ext,
+# Returns: true if writable, 0 if not writable, undef if unrecognized
+# Note: This will return true for some TIFF-based RAW images which we shouldn't really write
+sub CanWrite($)
+{
+    local $_;
+    my $file = shift or return undef;
+    my $type = GetFileType($file) or return undef;
+    return scalar(grep /^$type$/, @writeTypes);
+}
+
+#------------------------------------------------------------------------------
+# Return true if we can create the specified file type
+# Inputs: 0) file name or ext,
+# Returns: true if creatable, 0 if not writable, undef if unrecognized
+sub CanCreate($)
+{
+    local $_;
+    my $file = shift or return undef;
+    my $type = GetFileType($file) or return undef;
+    return scalar(grep /^$type$/, @createTypes);
+}
+
+#==============================================================================
+# Functions below this are not part of the public API
+
+# Initialize member variables
+# Inputs: 0) ExifTool object reference
+sub Init($)
+{
+    local $_;
+    my $self = shift;
+    # delete all DataMember variables (lower-case names)
+    foreach (keys %$self) {
+        /[a-z]/ and delete $self->{$_};
+    }
+    delete $self->{FOUND_TAGS};     # list of found tags
+    delete $self->{EXIF_DATA};      # the EXIF data block
+    delete $self->{EXIF_POS};       # EXIF position in file
+    delete $self->{FIRST_EXIF_POS}; # position of first EXIF in file
+    delete $self->{EXIF_BYTE_ORDER};# the EXIF byte ordering
+    delete $self->{HTML_DUMP};      # html dump information
+    $self->{BASE}       = 0;        # base for offsets from start of file
+    $self->{FILE_ORDER} = { };      # hash of tag order in file
+    $self->{VALUE}      = { };      # hash of raw tag values
+    $self->{TAG_INFO}   = { };      # hash of tag information
+    $self->{GROUP1}     = { };      # hash of family 1 group names
+    $self->{PRIORITY}   = { };      # priority of current tags
+    $self->{PROCESSED}  = { };      # hash of processed directory start positions
+    $self->{DIR_COUNT}  = { };      # count various types of directories
+    $self->{NUM_FOUND}  = 0;        # total number of tags found (incl. duplicates)
+    $self->{CHANGED}    = 0;        # number of tags changed (writer only)
+    $self->{INDENT}     = '  ';     # initial indent for verbose messages
+    $self->{PRIORITY_DIR} = '';     # the priority directory name
+    $self->{TIFF_TYPE}  = '';       # type of TIFF data (APP1, TIFF, NEF, etc...)
+    $self->{CameraMake} = '';       # camera make
+    $self->{CameraModel}= '';       # camera model
+    $self->{CameraType} = '';       # Olympus camera type
+    if ($self->Options('HtmlDump')) {
+        require Image::ExifTool::HtmlDump;
+        $self->{HTML_DUMP} = new Image::ExifTool::HtmlDump;
+    }
+    # make sure our TextOut is a file reference
+    $self->{OPTIONS}->{TextOut} = \*STDOUT unless ref $self->{OPTIONS}->{TextOut};
+}
+
+#------------------------------------------------------------------------------
+# Parse function arguments and set member variables accordingly
+# Inputs: Same as ImageInfo()
+# - sets REQUESTED_TAGS, REQ_TAG_LOOKUP, IO_TAG_LIST, FILENAME, RAF, OPTIONS
+sub ParseArguments($;@)
+{
+    my $self = shift;
+    my $options = $self->{OPTIONS};
+    my @exclude;
+    my @oldGroupOpts = grep /^Group/, keys %{$self->{OPTIONS}};
+    my $wasExcludeOpt;
+
+    $self->{REQUESTED_TAGS} = [ ];
+    $self->{REQ_TAG_LOOKUP} = { };
+    $self->{IO_TAG_LIST} = undef;
+
+    # handle our input arguments
+    while (@_) {
+        my $arg = shift;
+        if (ref $arg) {
+            if (ref $arg eq 'ARRAY') {
+                $self->{IO_TAG_LIST} = $arg;
+                foreach (@$arg) {
+                    if (/^-(.*)/) {
+                        push @exclude, $1;
+                    } else {
+                        push @{$self->{REQUESTED_TAGS}}, $_;
+                    }
+                }
+            } elsif (ref $arg eq 'HASH') {
+                my $opt;
+                foreach $opt (keys %$arg) {
+                    # a single new group option overrides all old group options
+                    if (@oldGroupOpts and $opt =~ /^Group/) {
+                        foreach (@oldGroupOpts) {
+                            delete $options->{$_};
+                        }
+                        undef @oldGroupOpts;
+                    }
+                    $options->{$opt} = $$arg{$opt};
+                    $opt eq 'Exclude' and $wasExcludeOpt = 1;
+                }
+            } elsif (ref $arg eq 'SCALAR' or UNIVERSAL::isa($arg,'GLOB')) {
+                next if defined $self->{RAF};
+                # convert image data from UTF-8 to character stream if necessary
+                # (patches RHEL 3 UTF8 LANG problem)
+                if (ref $arg eq 'SCALAR' and eval 'require Encode; Encode::is_utf8($$arg)') {
+                    my $buff = pack('C*', unpack('U0U*', $$arg));
+                    $arg = \$buff;
+                }
+                $self->{RAF} = new File::RandomAccess($arg);
+                # set filename to empty string to indicate that
+                # we have a file but we didn't open it
+                $self->{FILENAME} = '';
+            } else {
+                warn "Don't understand ImageInfo argument $arg\n";
+            }
+        } elsif (defined $self->{FILENAME}) {
+            if ($arg =~ /^-(.*)/) {
+                push @exclude, $1;
+            } else {
+                push @{$self->{REQUESTED_TAGS}}, $arg;
+            }
+        } else {
+            $self->{FILENAME} = $arg;
+        }
+    }
+    # expand shortcuts in tag arguments if provided
+    if (@{$self->{REQUESTED_TAGS}}) {
+        ExpandShortcuts($self->{REQUESTED_TAGS});
+        # initialize lookup for requested tags
+        foreach (@{$self->{REQUESTED_TAGS}}) {
+            $self->{REQ_TAG_LOOKUP}->{lc(/.+?:(.+)/ ? $1 : $_)} = 1;
+        }
+    }
+
+    if (@exclude or $wasExcludeOpt) {
+        # must add existing excluded tags
+        if ($options->{Exclude}) {
+            if (ref $options->{Exclude} eq 'ARRAY') {
+                push @exclude, @{$options->{Exclude}};
+            } else {
+                push @exclude, $options->{Exclude};
+            }
+        }
+        $options->{Exclude} = \@exclude;
+        # expand shortcuts in new exclude list
+        ExpandShortcuts($options->{Exclude});
+    }
+}
+
+#------------------------------------------------------------------------------
+# Set list of found tags
+# Inputs: 0) ExifTool object reference
+# Returns: Reference to found tags list (in order of requested tags)
+sub SetFoundTags($)
+{
+    my $self = shift;
+    my $options = $self->{OPTIONS};
+    my $reqTags = $self->{REQUESTED_TAGS} || [ ];
+    my $duplicates = $options->{Duplicates};
+    my $exclude = $options->{Exclude};
+    my $fileOrder = $self->{FILE_ORDER};
+    my @groupOptions = sort grep /^Group/, keys %$options;
+    my $doDups = $duplicates || $exclude || @groupOptions;
+    my ($tag, $rtnTags);
+
+    # only return requested tags if specified
+    if (@$reqTags) {
+        $rtnTags or $rtnTags = [ ];
+        # scan through the requested tags and generate a list of tags we found
+        my $tagHash = $self->{VALUE};
+        my $reqTag;
+        foreach $reqTag (@$reqTags) {
+            my (@matches, $group, $family, $allGrp, $allTag);
+            if ($reqTag =~ /^(\d+)?(.+?):(.+)/) {
+                ($family, $group, $tag) = ($1, $2, $3);
+                $allGrp = 1 if $group =~ /^(\*|all)$/i;
+                $family = -1 unless defined $family;
+            } else {
+                $tag = $reqTag;
+                $family = -1;
+            }
+            if (defined $tagHash->{$reqTag} and not $doDups) {
+                $matches[0] = $tag;
+            } elsif ($tag =~ /^(\*|all)$/i) {
+                # tag name of '*' or 'all' matches all tags
+                if ($doDups or $allGrp) {
+                    @matches = keys %$tagHash;
+                } else {
+                    @matches = grep(!/ /, keys %$tagHash);
+                }
+                next unless @matches;   # don't want entry in list for '*' tag
+                $allTag = 1;
+            } elsif ($doDups or defined $group) {
+                # must also look for tags like "Tag (1)"
+                @matches = grep(/^$tag(\s|$)/i, keys %$tagHash);
+            } else {
+                # find first matching value
+                # (use in list context to return value instead of count)
+                ($matches[0]) = grep /^$tag$/i, keys %$tagHash;
+                defined $matches[0] or undef @matches;
+            }
+            if (defined $group and not $allGrp) {
+                # keep only specified group
+                my @grpMatches;
+                foreach (@matches) {
+                    my @groups = $self->GetGroup($_, $family);
+                    next unless grep /^$group$/i, @groups;
+                    push @grpMatches, $_;
+                }
+                @matches = @grpMatches;
+                next unless @matches or not $allTag;
+            }
+            if (@matches > 1) {
+                # maintain original file order for multiple tags
+                @matches = sort { $$fileOrder{$a} <=> $$fileOrder{$b} } @matches;
+                # return only the highest priority tag unless duplicates wanted
+                unless ($doDups or $allTag or $allGrp) {
+                    $tag = shift @matches;
+                    my $oldPriority = $self->{PRIORITY}->{$tag} || 1;
+                    foreach (@matches) {
+                        my $priority = $self->{PRIORITY}->{$_};
+                        $priority = 1 unless defined $priority;
+                        next unless $priority >= $oldPriority;
+                        $tag = $_;
+                        $oldPriority = $priority || 1;
+                    }
+                    @matches = ( $tag );
+                }
+            } elsif (not @matches) {
+                # put entry in return list even without value (value is undef)
+                $matches[0] = "$tag (0)";
+                # bogus file order entry to avoid warning if sorting in file order
+                $self->{FILE_ORDER}->{$matches[0]} = 999;
+            }
+            push @$rtnTags, @matches;
+        }
+    } else {
+        # no requested tags, so we want all tags
+        my @allTags;
+        if ($doDups) {
+            @allTags = keys %{$self->{VALUE}};
+        } else {
+            foreach (keys %{$self->{VALUE}}) {
+                # only include tag if it doesn't end in a copy number
+                push @allTags, $_ unless / /;
+            }
+        }
+        $rtnTags = \@allTags;
+    }
+
+    # filter excluded tags and group options
+    while (($exclude or @groupOptions) and @$rtnTags) {
+        if ($exclude) {
+            my @filteredTags;
+EX_TAG:     foreach $tag (@$rtnTags) {
+                my $tagName = GetTagName($tag);
+                my @matches = grep /(^|:)($tagName|\*|all)$/i, @$exclude;
+                foreach (@matches) {
+                    next EX_TAG unless /^(\d+)?(.+?):/;
+                    my ($family, $group) = ($1, $2);
+                    next EX_TAG if $group =~ /^(\*|all)$/i;
+                    $family = -1 unless defined $family;
+                    my @groups = $self->GetGroup($tag, $family);
+                    next EX_TAG if grep /^$group$/i, @groups;
+                }
+                push @filteredTags, $tag;
+            }
+            $rtnTags = \@filteredTags;      # use new filtered tag list
+            last if $duplicates and not @groupOptions;
+        }
+        # filter groups if requested, or to remove duplicates
+        my (%keepTags, %wantGroup, $family, $groupOpt);
+        my $allGroups = 1;
+        # build hash of requested/excluded group names for each group family
+        my $wantOrder = 0;
+        foreach $groupOpt (@groupOptions) {
+            $groupOpt =~ /^Group(\d*)/ or next;
+            $family = $1 || 0;
+            $wantGroup{$family} or $wantGroup{$family} = { };
+            my $groupList;
+            if (ref $options->{$groupOpt} eq 'ARRAY') {
+                $groupList = $options->{$groupOpt};
+            } else {
+                $groupList = [ $options->{$groupOpt} ];
+            }
+            foreach (@$groupList) {
+                # groups have priority in order they were specified
+                ++$wantOrder;
+                my ($groupName, $want);
+                if (/^-(.*)/) {
+                    # excluded group begins with '-'
+                    $groupName = $1;
+                    $want = 0;          # we don't want tags in this group
+                } else {
+                    $groupName = $_;
+                    $want = $wantOrder; # we want tags in this group
+                    $allGroups = 0;     # don't want all groups if we requested one
+                }
+                $wantGroup{$family}->{$groupName} = $want;
+            }
+        }
+        # loop through all tags and decide which ones we want
+        my (@tags, %bestTag);
+GR_TAG: foreach $tag (@$rtnTags) {
+            my $wantTag = $allGroups;   # want tag by default if want all groups
+            foreach $family (keys %wantGroup) {
+                my $group = $self->GetGroup($tag, $family);
+                my $wanted = $wantGroup{$family}->{$group};
+                next unless defined $wanted;
+                next GR_TAG unless $wanted;     # skip tag if group excluded
+                # take lowest non-zero want flag
+                next if $wantTag and $wantTag < $wanted;
+                $wantTag = $wanted;
+            }
+            next unless $wantTag;
+            if ($duplicates) {
+                push @tags, $tag;
+            } else {
+                my $tagName = GetTagName($tag);
+                my $bestTag = $bestTag{$tagName};
+                if (defined $bestTag) {
+                    next if $wantTag > $keepTags{$bestTag};
+                    if ($wantTag == $keepTags{$bestTag}) {
+                        # want two tags with the same name -- keep the latest one
+                        if ($tag =~ / \((\d+)\)$/) {
+                            my $tagNum = $1;
+                            next if $bestTag !~ / \((\d+)\)$/ or $1 > $tagNum;
+                        }
+                    }
+                    # this tag is better, so delete old best tag
+                    delete $keepTags{$bestTag};
+                }
+                $keepTags{$tag} = $wantTag;    # keep this tag (for now...)
+                $bestTag{$tagName} = $tag;      # this is our current best tag
+            }
+        }
+        unless ($duplicates) {
+            # construct new tag list with no duplicates, preserving order
+            foreach $tag (@$rtnTags) {
+                push @tags, $tag if $keepTags{$tag};
+            }
+        }
+        $rtnTags = \@tags;
+        last;
+    }
+
+    # save found tags and return reference
+    return $self->{FOUND_TAGS} = $rtnTags;
+}
+
+#------------------------------------------------------------------------------
+# Utility to load our write routines if required (called via AUTOLOAD)
+# Inputs: 0) autoload function, 1-N) function arguments
+# Returns: result of function or dies if function not available
+# To Do: Generalize this routine so it works on systems that don't use '/'
+#        as a path name separator.
+sub DoAutoLoad(@)
+{
+    my $autoload = shift;
+    my @callInfo = split(/::/, $autoload);
+    my $file = 'Image/ExifTool/Write';
+
+    return if $callInfo[$#callInfo] eq 'DESTROY';
+    if (@callInfo == 4) {
+        # load Image/ExifTool/WriteMODULE.pl
+        $file .= "$callInfo[2].pl";
+    } else {
+        # load Image/ExifTool/Writer.pl
+        $file .= 'r.pl';
+    }
+    # attempt to load the package
+    eval "require '$file'" or die "Error while attempting to call $autoload\n$@\n";
+    unless (defined &$autoload) {
+        my @caller = caller(0);
+        # reproduce Perl's standard 'undefined subroutine' message:
+        die "Undefined subroutine $autoload called at $caller[1] line $caller[2]\n";
+    }
+    no strict 'refs';
+    return &$autoload(@_);     # call the function
+}
+
+#------------------------------------------------------------------------------
+# AutoLoad our writer routines when necessary
+#
+sub AUTOLOAD
+{
+    return DoAutoLoad($AUTOLOAD, @_);
+}
+
+#------------------------------------------------------------------------------
+# Add warning tag
+# Inputs: 0) ExifTool object reference, 1) warning message, 2) true if minor
+# Returns: true if warning tag was added
+sub Warn($$;$)
+{
+    my ($self, $str, $ignorable) = @_;
+    if ($ignorable) {
+        return 0 if $self->{OPTIONS}->{IgnoreMinorErrors};
+        $str = "[minor] $str";
+    }
+    $self->FoundTag('Warning', $str);
+    return 1;
+}
+
+#------------------------------------------------------------------------------
+# Add error tag
+# Inputs: 0) ExifTool object reference, 1) error message, 2) true if minor
+# Returns: true if error tag was added, otherwise warning was added
+sub Error($$;$)
+{
+    my ($self, $str, $ignorable) = @_;
+    if ($ignorable) {
+        if ($self->{OPTIONS}->{IgnoreMinorErrors}) {
+            $self->Warn($str);
+            return 0;
+        }
+        $str = "[minor] $str";
+    }
+    $self->FoundTag('Error', $str);
+    return 1;
+}
+
+#------------------------------------------------------------------------------
+# Expand shortcuts
+# Inputs: 0) reference to list of tags
+# Notes: Handles leading '-' for excluded tags, group names, and redirected tags
+sub ExpandShortcuts($)
+{
+    my $tagList = shift || return;
+
+    require Image::ExifTool::Shortcuts;
+
+    # expand shortcuts
+    my @expandedTags;
+    my ($entry, $tag, $excl);
+    foreach $entry (@$tagList) {
+        # remove leading '-'
+        ($excl, $tag) = $entry =~ /^(-?)(.*)/s;
+        my ($post, @post);
+        # handle redirection
+        if ($tag =~ /(.+?)([-+]?[<>].+)/s and not $excl) {
+            ($tag, $post) = ($1, $2);
+            if ($post =~ /^[-+]?>/ or $post !~ /\$/) {
+                # expand shortcuts in postfix (rhs of redirection)
+                my ($op, $p2, $t2) = ($post =~ /([-+]?[<>])(.+?:)?(.+)/);
+                $p2 = '' unless defined $p2;
+                my ($match) = grep /^\Q$t2\E$/i, keys %Image::ExifTool::Shortcuts::Main;
+                if ($match) {
+                    foreach (@{$Image::ExifTool::Shortcuts::Main{$match}}) {
+                        /^-/ and next;  # ignore excluded tags
+                        if ($p2 and /(.+?:)(.+)/) {
+                            push @post, "$op$_";
+                        } else {
+                            push @post, "$op$p2$_";
+                        }
+                    }
+                    next unless @post;
+                    $post = shift @post;
+                }
+            }
+        } else {
+            $post = '';
+        }
+        # handle group names
+        my $pre;
+        if ($tag =~ /(.+?:)(.+)/) {
+            ($pre, $tag) = ($1, $2);
+        } else {
+            $pre = '';
+        }
+        # loop over all postfixes
+        for (;;) {
+            # expand the tag name
+            my ($match) = grep /^\Q$tag\E$/i, keys %Image::ExifTool::Shortcuts::Main;
+            if ($match) {
+                if ($excl) {
+                    # entry starts with '-', so exclude all tags in this shortcut
+                    foreach (@{$Image::ExifTool::Shortcuts::Main{$match}}) {
+                        /^-/ and next;  # ignore excluded exclude tags
+                        # group of expanded tag takes precedence
+                        if ($pre and /(.+?:)(.+)/) {
+                            push @expandedTags, "$excl$_";
+                        } else {
+                            push @expandedTags, "$excl$pre$_";
+                        }
+                    }
+                } elsif (length $pre or length $post) {
+                    foreach (@{$Image::ExifTool::Shortcuts::Main{$match}}) {
+                        /(-?)(.+?:)?(.+)/;
+                        if ($2) {
+                            # group from expanded tag takes precedence
+                            push @expandedTags, "$_$post";
+                        } else {
+                            push @expandedTags, "$1$pre$3$post";
+                        }
+                    }
+                } else {
+                    push @expandedTags, @{$Image::ExifTool::Shortcuts::Main{$match}};
+                }
+            } else {
+                push @expandedTags, "$excl$pre$tag$post";
+            }
+            last unless @post;
+            $post = shift @post;
+        }
+    }
+    @$tagList = @expandedTags;
+}
+
+#------------------------------------------------------------------------------
+# Add hash of composite tags to our composites
+# Inputs: 0) hash reference to table of composite tags to add or module name,
+#         1) overwrite existing tag
+sub AddCompositeTags($;$)
+{
+    local $_;
+    my ($add, $overwrite) = @_;
+    my $module;
+    unless (ref $add) {
+        $module = $add;
+        $add .= '::Composite';
+        no strict 'refs';
+        $add = \%$add;
+    }
+    my $defaultGroups = $$add{GROUPS};
+
+    # make sure default groups are defined in families 0 and 1
+    if ($defaultGroups) {
+        $defaultGroups->{0} or $defaultGroups->{0} = 'Composite';
+        $defaultGroups->{1} or $defaultGroups->{1} = 'Composite';
+        $defaultGroups->{2} or $defaultGroups->{2} = 'Other';
+    } else {
+        $defaultGroups = $$add{GROUPS} = { 0 => 'Composite', 1 => 'Composite', 2 => 'Other' };
+    }
+    SetupTagTable($add);
+    my $tagID;
+    foreach $tagID (keys %$add) {
+        next if $specialTags{$tagID};   # must skip special tags
+        my $tagInfo = $$add{$tagID};
+        # tagID's MUST be the exact tag name for logic in BuildCompositeTags()
+        my $tag = $$tagInfo{Name};
+        $$tagInfo{Module} = $module if $$tagInfo{Writable};
+        # allow composite tags with the same name
+        my ($t, $n, $type);
+        while ($Image::ExifTool::Composite{$tag} and not $overwrite) {
+            $n ? $n += 1 : $n = 2, $t = $tag;
+            $tag = "${t}_$n";
+        }
+        # convert scalar Require/Desire entries
+        foreach $type ('Require','Desire') {
+            my $req = $$tagInfo{$type} or next;
+            $$tagInfo{$type} = { 0 => $req } if ref($req) ne 'HASH';
+        }
+        # add this composite tag to our main composite table
+        $$tagInfo{Table} = \%Image::ExifTool::Composite;
+        $Image::ExifTool::Composite{$tag} = $tagInfo;
+        # set all default groups in tag
+        my $groups = $$tagInfo{Groups};
+        $groups or $groups = $$tagInfo{Groups} = { };
+        # fill in default groups
+        foreach (keys %$defaultGroups) {
+            $$groups{$_} or $$groups{$_} = $$defaultGroups{$_};
+        }
+        # set flag indicating group list was built
+        $$tagInfo{GotGroups} = 1;
+    }
+}
+
+#------------------------------------------------------------------------------
+# Expand tagInfo Flags
+# Inputs: 0) tagInfo hash ref
+# Notes: $$tagInfo{Flags} must be defined to call this routine
+sub ExpandFlags($)
+{
+    my $tagInfo = shift;
+    my $flags = $$tagInfo{Flags};
+    if (ref $flags eq 'ARRAY') {
+        foreach (@$flags) {
+            $$tagInfo{$_} = 1;
+        }
+    } elsif (ref $flags eq 'HASH') {
+        my $key;
+        foreach $key (keys %$flags) {
+            $$tagInfo{$key} = $$flags{$key};
+        }
+    } else {
+        $$tagInfo{$flags} = 1;
+    }
+}
+
+#------------------------------------------------------------------------------
+# Set up tag table (must be done once for each tag table used)
+# Inputs: 0) Reference to tag table
+# Notes: - generates 'Name' field from key if it doesn't exist
+#        - stores 'Table' pointer
+#        - expands 'Flags' for quick lookup
+sub SetupTagTable($)
+{
+    my $tagTablePtr = shift;
+    my $tagID;
+    foreach $tagID (TagTableKeys($tagTablePtr)) {
+        my @infoArray = GetTagInfoList($tagTablePtr,$tagID);
+        # process conditional tagInfo arrays
+        my $tagInfo;
+        foreach $tagInfo (@infoArray) {
+            $$tagInfo{Table} = $tagTablePtr;
+            my $tag = $$tagInfo{Name};
+            unless (defined $tag) {
+                # generate name equal to tag ID if 'Name' doesn't exist
+                $tag = $tagID;
+                $$tagInfo{Name} = ucfirst($tag); # make first char uppercase
+            }
+            $$tagInfo{Flags} and ExpandFlags($tagInfo);
+        }
+    }
+}
+
+#------------------------------------------------------------------------------
+# Utilities to check for numerical types
+# Inputs: 0) value;  Returns: true if value is a numerical type
+# Notes: May change commas to decimals in floats for use in other locales
+sub IsFloat($) {
+    return 1 if $_[0] =~ /^([+-]?)(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?$/;
+    # allow comma separators (for other locales)
+    return 0 unless $_[0] =~ /^([+-]?)(?=\d|,\d)\d*(,\d*)?([Ee]([+-]?\d+))?$/;
+    $_[0] =~ tr/,/./;   # but translate ',' to '.'
+    return 1;
+}
+sub IsInt($)   { return scalar($_[0] =~ /^[+-]?\d+$/); }
+sub IsHex($)   { return scalar($_[0] =~ /^(0x)?[0-9a-f]{1,8}$/i); }
+
+# round floating point value to specified number of significant digits
+# Inputs: 0) value, 1) number of sig digits;  Returns: rounded number
+sub RoundFloat($$)
+{
+    my ($val, $sig) = @_;
+    $val == 0 and return 0;
+    my $sign = $val < 0 ? ($val=-$val, -1) : 1;
+    my $log = log($val) / log(10);
+    my $exp = int($log) - $sig + ($log > 0 ? 1 : 0);
+    return $sign * int(10 ** ($log - $exp) + 0.5) * 10 ** $exp;
+}
+
+#------------------------------------------------------------------------------
+# Utility routines to for reading binary data values from file
+
+my $swapBytes;               # set if EXIF header is not native byte ordering
+my $swapWords;               # swap 32-bit words in doubles (ARM quirk)
+my $currentByteOrder = 'MM'; # current byte ordering ('II' or 'MM')
+my %unpackMotorola = ( S => 'n', L => 'N', C => 'C', c => 'c' );
+my %unpackIntel    = ( S => 'v', L => 'V', C => 'C', c => 'c' );
+my %unpackStd = %unpackMotorola;
+
+# Swap bytes in data if necessary
+# Inputs: 0) data, 1) number of bytes
+# Returns: swapped data
+sub SwapBytes($$)
+{
+    return $_[0] unless $swapBytes;
+    my ($val, $bytes) = @_;
+    my $newVal = '';
+    $newVal .= substr($val, $bytes, 1) while $bytes--;
+    return $newVal;
+}
+# Swap words.  Inputs: 8 bytes of data, Returns: swapped data
+sub SwapWords($)
+{
+    return $_[0] unless $swapWords and length($_[0]) == 8;
+    return substr($_[0],4,4) . substr($_[0],0,4)
+}
+
+# Unpack value, letting unpack() handle byte swapping
+# Inputs: 0) unpack template, 1) data reference, 2) offset
+# Returns: unpacked number
+# - uses value of %unpackStd to determine the unpack template
+# - can only be called for 'S' or 'L' templates since these are the only
+#   templates for which you can specify the byte ordering.
+sub DoUnpackStd(@)
+{
+    $_[2] and return unpack("x$_[2] $unpackStd{$_[0]}", ${$_[1]});
+    return unpack($unpackStd{$_[0]}, ${$_[1]});
+}
+# Pack value
+# Inputs: 0) template, 1) value, 2) data ref (or undef), 3) offset (if data ref)
+# Returns: packed value
+sub DoPackStd(@)
+{
+    my $val = pack($unpackStd{$_[0]}, $_[1]);
+    $_[2] and substr(${$_[2]}, $_[3], length($val)) = $val;
+    return $val;
+}
+
+# Unpack value, handling the byte swapping manually
+# Inputs: 0) # bytes, 1) unpack template, 2) data reference, 3) offset
+# Returns: unpacked number
+# - uses value of $swapBytes to determine byte ordering
+sub DoUnpack(@)
+{
+    my ($bytes, $template, $dataPt, $pos) = @_;
+    my $val;
+    if ($swapBytes) {
+        $val = '';
+        $val .= substr($$dataPt,$pos+$bytes,1) while $bytes--;
+    } else {
+        $val = substr($$dataPt,$pos,$bytes);
+    }
+    defined($val) or return undef;
+    return unpack($template,$val);
+}
+
+# Unpack double value
+# Inputs: 0) unpack template, 1) data reference, 2) offset
+# Returns: unpacked number
+sub DoUnpackDbl(@)
+{
+    my ($template, $dataPt, $pos) = @_;
+    my $val = substr($$dataPt,$pos,8);
+    defined($val) or return undef;
+    # swap bytes and 32-bit words (ARM quirk) if necessary, then unpack value
+    return unpack($template, SwapWords(SwapBytes($val, 8)));
+}
+
+# Inputs: 0) data reference, 1) offset into data
+sub Get8s($$)     { return DoUnpackStd('c', @_); }
+sub Get8u($$)     { return DoUnpackStd('C', @_); }
+sub Get16s($$)    { return DoUnpack(2, 's', @_); }
+sub Get16u($$)    { return DoUnpackStd('S', @_); }
+sub Get32s($$)    { return DoUnpack(4, 'l', @_); }
+sub Get32u($$)    { return DoUnpackStd('L', @_); }
+sub GetFloat($$)  { return DoUnpack(4, 'f', @_); }
+sub GetDouble($$) { return DoUnpackDbl('d', @_); }
+
+sub GetRational32s($$)
+{
+    my ($dataPt, $pos) = @_;
+    my $denom = Get16s($dataPt, $pos + 2) or return 'inf';
+    # round off to a reasonable number of significant figures
+    return RoundFloat(Get16s($dataPt,$pos) / $denom, 7);
+}
+sub GetRational32u($$)
+{
+    my ($dataPt, $pos) = @_;
+    my $denom = Get16u($dataPt, $pos + 2) or return 'inf';
+    return RoundFloat(Get16u($dataPt,$pos) / $denom, 7);
+}
+sub GetRational64s($$)
+{
+    my ($dataPt, $pos) = @_;
+    my $denom = Get32s($dataPt, $pos + 4) or return 'inf';
+    return RoundFloat(Get32s($dataPt,$pos) / $denom, 10);
+}
+sub GetRational64u($$)
+{
+    my ($dataPt, $pos) = @_;
+    my $denom = Get32u($dataPt, $pos + 4) or return 'inf';
+    return RoundFloat(Get32u($dataPt,$pos) / $denom, 10);
+}
+sub GetFixed16s($$)
+{
+    my ($dataPt, $pos) = @_;
+    my $val = Get16s($dataPt, $pos) / 0x100;
+    return int($val * 1000 + ($val<0 ? -0.5 : 0.5)) / 1000;
+}
+sub GetFixed16u($$)
+{
+    my ($dataPt, $pos) = @_;
+    return int((Get16u($dataPt, $pos) / 0x100) * 1000 + 0.5) / 1000;
+}
+sub GetFixed32s($$)
+{
+    my ($dataPt, $pos) = @_;
+    my $val = Get32s($dataPt, $pos) / 0x10000;
+    # remove insignificant digits
+    return int($val * 1e5 + ($val>0 ? 0.5 : -0.5)) / 1e5;
+}
+sub GetFixed32u($$)
+{
+    my ($dataPt, $pos) = @_;
+    # remove insignificant digits
+    return int((Get32u($dataPt, $pos) / 0x10000) * 1e5 + 0.5) / 1e5;
+}
+# Inputs: 0) value, 1) data ref, 2) offset
+sub Set8s(@)  { return DoPackStd('c', @_); }
+sub Set8u(@)  { return DoPackStd('C', @_); }
+sub Set16u(@) { return DoPackStd('S', @_); }
+sub Set32u(@) { return DoPackStd('L', @_); }
+
+#------------------------------------------------------------------------------
+# Get current byte order ('II' or 'MM')
+sub GetByteOrder() { return $currentByteOrder; }
+
+#------------------------------------------------------------------------------
+# Set byte ordering
+# Inputs: 0) 'II'=intel, 'MM'=motorola
+# Returns: 1 on success
+sub SetByteOrder($)
+{
+    my $order = shift;
+
+    if ($order eq 'MM') {       # big endian (Motorola)
+        %unpackStd = %unpackMotorola;
+    } elsif ($order eq 'II') {  # little endian (Intel)
+        %unpackStd = %unpackIntel;
+    } else {
+        return 0;
+    }
+    my $val = unpack('S','A ');
+    my $nativeOrder;
+    if ($val == 0x4120) {       # big endian
+        $nativeOrder = 'MM';
+    } elsif ($val == 0x2041) {  # little endian
+        $nativeOrder = 'II';
+    } else {
+        warn sprintf("Unknown native byte order! (pattern %x)\n",$val);
+        return 0;
+    }
+    $currentByteOrder = $order;  # save current byte order
+
+    # swap bytes if our native CPU byte ordering is not the same as the EXIF
+    $swapBytes = ($order ne $nativeOrder);
+
+    # little-endian ARM has big-endian words for doubles (thanks Riku Voipio)
+    # (Note: Riku's patch checked for '0ff3', but I think it should be 'f03f' since
+    # 1 is '000000000000f03f' on an x86 -- so check for both, but which is correct?)
+    my $pack1d = pack('d', 1);
+    $swapWords = ($pack1d eq "\0\0\x0f\xf3\0\0\0\0" or
+                  $pack1d eq "\0\0\xf0\x3f\0\0\0\0");
+    return 1;
+}
+
+#------------------------------------------------------------------------------
+# Change byte order
+sub ToggleByteOrder()
+{
+    SetByteOrder(GetByteOrder() eq 'II' ? 'MM' : 'II');
+}
+
+#------------------------------------------------------------------------------
+# hash lookups for reading values from data
+my %formatSize = (
+    int8s => 1,
+    int8u => 1,
+    int16s => 2,
+    int16u => 2,
+    int32s => 4,
+    int32u => 4,
+    int64s => 8,
+    int64u => 8,
+    rational32s => 4,
+    rational32u => 4,
+    rational64s => 8,
+    rational64u => 8,
+    fixed16s => 2,
+    fixed16u => 2,
+    fixed32s => 4,
+    fixed32u => 4,
+    float => 4,
+    double => 8,
+    extended => 10,
+    string => 1,
+    binary => 1,
+   'undef' => 1,
+    ifd => 4,
+    ifd8 => 8,
+);
+my %readValueProc = (
+    int8s => \&Get8s,
+    int8u => \&Get8u,
+    int16s => \&Get16s,
+    int16u => \&Get16u,
+    int32s => \&Get32s,
+    int32u => \&Get32u,
+    int64s => \&Get64s,
+    int64u => \&Get64u,
+    rational32s => \&GetRational32s,
+    rational32u => \&GetRational32u,
+    rational64s => \&GetRational64s,
+    rational64u => \&GetRational64u,
+    fixed16s => \&GetFixed16s,
+    fixed16u => \&GetFixed16u,
+    fixed32s => \&GetFixed32s,
+    fixed32u => \&GetFixed32u,
+    float => \&GetFloat,
+    double => \&GetDouble,
+    extended => \&GetExtended,
+    ifd => \&Get32u,
+    ifd8 => \&Get64u,
+);
+sub FormatSize($) { return $formatSize{$_[0]}; }
+
+#------------------------------------------------------------------------------
+# Read value from binary data (with current byte ordering)
+# Inputs: 0) data reference, 1) value offset, 2) format string,
+#         3) number of values (or undef to use all data)
+#         4) valid data length relative to offset
+# Returns: converted value, or undefined if data isn't there
+#          or list of values in list context
+sub ReadValue($$$$$)
+{
+    my ($dataPt, $offset, $format, $count, $size) = @_;
+
+    my $len = $formatSize{$format};
+    unless ($len) {
+        warn "Unknown format $format";
+        $len = 1;
+    }
+    unless ($count) {
+        return '' if defined $count or $size < $len;
+        $count = int($size / $len);
+    }
+    # make sure entry is inside data
+    if ($len * $count > $size) {
+        $count = int($size / $len);     # shorten count if necessary
+        $count < 1 and return undef;    # return undefined if no data
+    }
+    my @vals;
+    my $proc = $readValueProc{$format};
+    if ($proc) {
+        for (;;) {
+            push @vals, &$proc($dataPt, $offset);
+            last if --$count <= 0;
+            $offset += $len;
+        }
+    } else {
+        # handle undef/binary/string
+        $vals[0] = substr($$dataPt, $offset, $count);
+        # truncate string at null terminator if necessary
+        $vals[0] =~ s/\0.*//s if $format eq 'string';
+    }
+    if (wantarray) {
+        return @vals;
+    } elsif (@vals > 1) {
+        return join(' ', @vals);
+    } else {
+        return $vals[0];
+    }
+}
+
+#------------------------------------------------------------------------------
+# Convert UTF-8 to current character set
+# Inputs: 0) ExifTool ref, 1) UTF-8 string
+# Return: Converted string
+sub UTF82Charset($$)
+{
+    my ($self, $val) = @_;
+    if ($self->{OPTIONS}->{Charset} eq 'Latin' and $val =~ /[\x80-\xff]/) {
+        $val = Image::ExifTool::UTF82Unicode($val,'n',$self);
+        $val = Image::ExifTool::Unicode2Latin($val,'n',$self);
+    }
+    return $val;
+}
+
+#------------------------------------------------------------------------------
+# Convert Latin to current character set
+# Inputs: 0) ExifTool ref, 1) Latin string
+# Return: Converted string
+sub Latin2Charset($$)
+{
+    my ($self, $val) = @_;
+    if ($self->{OPTIONS}->{Charset} eq 'UTF8' and $val =~ /[\x80-\xff]/) {
+        $val = Image::ExifTool::Latin2Unicode($val,'n');
+        $val = Image::ExifTool::Unicode2UTF8($val,'n');
+    }
+    return $val;
+}
+
+#------------------------------------------------------------------------------
+# Decode bit mask
+# Inputs: 0) value to decode, 1) Reference to hash for decoding (or undef)
+#         2) optional bits per word (defaults to 32)
+sub DecodeBits($$;$)
+{
+    my ($vals, $lookup, $bits) = @_;
+    $bits or $bits = 32;
+    my ($val, $i, @bitList);
+    my $num = 0;
+    foreach $val (split ' ', $vals) {
+        for ($i=0; $i<$bits; ++$i) {
+            next unless $val & (1 << $i);
+            my $n = $i + $num;
+            if (not $lookup) {
+                push @bitList, $n;
+            } elsif ($$lookup{$n}) {
+                push @bitList, $$lookup{$n};
+            } else {
+                push @bitList, "[$n]";
+            }
+        }
+        $num += $bits;
+    }
+    return '(none)' unless @bitList;
+    return join($lookup ? ', ' : ',', @bitList);
+}
+
+#------------------------------------------------------------------------------
+# Validate an extracted image and repair if necessary
+# Inputs: 0) ExifTool object reference, 1) image reference, 2) tag name
+# Returns: image reference or undef if it wasn't valid
+sub ValidateImage($$$)
+{
+    my ($self, $imagePt, $tag) = @_;
+    return undef if $$imagePt eq 'none';
+    unless ($$imagePt =~ /^(Binary data|\xff\xd8\xff)/ or
+            # the first byte of the preview of some Minolta cameras is wrong,
+            # so check for this and set it back to 0xff if necessary
+            $$imagePt =~ s/^.(\xd8\xff\xdb)/\xff$1/ or
+            $self->Options('IgnoreMinorErrors'))
+    {
+        # issue warning only if the tag was specifically requested
+        if ($self->{REQ_TAG_LOOKUP}->{lc($tag)}) {
+            $self->Warn("$tag is not a valid JPEG image",1);
+            return undef;
+        }
+    }
+    return $imagePt;
+}
+
+#------------------------------------------------------------------------------
+# Make description from a tag name
+# Inputs: 0) tag name 1) optional tagID to add at end of description
+# Returns: description
+sub MakeDescription($;$)
+{
+    my ($tag, $tagID) = @_;
+    # start with the tag name and force first letter to be upper case
+    my $desc = ucfirst($tag);
+    $desc =~ tr/_/ /;       # translate underlines to spaces
+    # put a space between lower/UPPER case and lower/number combinations
+    $desc =~ s/([a-z])([A-Z\d])/$1 $2/g;
+    # put a space between acronyms and words
+    $desc =~ s/([A-Z])([A-Z][a-z])/$1 $2/g;
+    # put spaces after numbers (if more than one character following number)
+    $desc =~ s/(\d)([A-Z]\S)/$1 $2/g;
+    # remove space in hex number
+    $desc =~ s/ 0x ([\dA-Fa-f])/ 0x$1/g;
+    $desc .= ' ' . $tagID if defined $tagID;
+    return $desc;
+}
+
+#------------------------------------------------------------------------------
+# Return printable value
+# Inputs: 0) ExifTool object reference
+#         1) value to print, 2) true for unlimited line length
+sub Printable($;$)
+{
+    my ($self, $outStr, $unlimited) = @_;
+    return '(undef)' unless defined $outStr;
+    $outStr =~ tr/\x01-\x1f\x7f-\xff/./;
+    $outStr =~ s/\x00//g;
+    # limit length if verbose < 4
+    if (length($outStr) > 60 and not $unlimited and $self->{OPTIONS}->{Verbose} < 4) {
+        $outStr = substr($outStr,0,54) . '[snip]';
+    }
+    return $outStr;
+}
+
+#------------------------------------------------------------------------------
+# Convert date/time from Exif format
+# Inputs: 0) ExifTool object reference, 1) Date/time in EXIF format
+# Returns: Formatted date/time string
+sub ConvertDateTime($$)
+{
+    my ($self, $date) = @_;
+    my $dateFormat = $self->{OPTIONS}->{DateFormat};
+    # only convert date if a format was specified and the date is recognizable
+    if ($dateFormat) {
+        if ($date =~ /^(\d+):(\d+):(\d+)\s+(\d+):(\d+):(\d+)/ and eval 'require POSIX') {
+            $date = POSIX::strftime($dateFormat, $6, $5, $4, $3, $2-1, $1-1900);
+        } elsif ($self->{OPTIONS}->{StrictDate}) {
+            undef $date;
+        }
+    }
+    return $date;
+}
+
+#------------------------------------------------------------------------------
+# Convert Unix time to EXIF date/time string
+# Inputs: 0) Unix time value, 1) non-zero to use local instead of GMT time
+# Returns: EXIF date/time string
+sub ConvertUnixTime($;$)
+{
+    my $time = shift;
+    return '0000:00:00 00:00:00' if $time == 0;
+    my @tm = shift() ? localtime($time) : gmtime($time);
+    return sprintf("%4d:%.2d:%.2d %.2d:%.2d:%.2d", $tm[5]+1900, $tm[4]+1,
+                   $tm[3], $tm[2], $tm[1], $tm[0]);
+}
+
+#------------------------------------------------------------------------------
+# Get Unix time from EXIF-formatted date/time string
+# Inputs: 0) EXIF date/time string, 1) non-zero to use local instead of GMT time
+# Returns: Unix time or undefined on error
+sub GetUnixTime($;$)
+{
+    my $timeStr = shift;
+    return 0 if $timeStr eq '0000:00:00 00:00:00';
+    my @tm = ($timeStr =~ /^(\d+):(\d+):(\d+)\s+(\d+):(\d+):(\d+)/);
+    return undef unless @tm == 6;
+    return undef unless eval 'require Time::Local';
+    $tm[0] -= 1900;     # convert year
+    $tm[1] -= 1;        # convert month
+    @tm = reverse @tm;  # change to order required by timelocal()
+    return shift() ? Time::Local::timelocal(@tm) : Time::Local::timegm(@tm);
+}
+
+#------------------------------------------------------------------------------
+# Save information for HTML dump
+# Inputs: 0) ExifTool hash ref, 1) start offset, 2) data size
+#         3) comment string, 4) tool tip (or SAME), 5) flags
+sub HtmlDump($$$$;$$)
+{
+    my $self = shift;
+    my $pos = shift;
+    $pos += $$self{BASE} if $$self{BASE};
+    $$self{HTML_DUMP} and $self->{HTML_DUMP}->Add($pos, @_);
+}
+
+#------------------------------------------------------------------------------
+# JPEG constants
+my %jpegMarker = (
+    0x01 => 'TEM',
+    0xc0 => 'SOF0', # to SOF15, with a few exceptions below
+    0xc4 => 'DHT',
+    0xc8 => 'JPGA',
+    0xcc => 'DAC',
+    0xd0 => 'RST0',
+    0xd8 => 'SOI',
+    0xd9 => 'EOI',
+    0xda => 'SOS',
+    0xdb => 'DQT',
+    0xdc => 'DNL',
+    0xdd => 'DRI',
+    0xde => 'DHP',
+    0xdf => 'EXP',
+    0xe0 => 'APP0', # to APP15
+    0xf0 => 'JPG0',
+    0xfe => 'COM',
+);
+
+#------------------------------------------------------------------------------
+# Get JPEG marker name
+# Inputs: 0) Jpeg number
+# Returns: marker name
+sub JpegMarkerName($)
+{
+    my $marker = shift;
+    my $markerName = $jpegMarker{$marker};
+    unless ($markerName) {
+        $markerName = $jpegMarker{$marker & 0xf0};
+        if ($markerName and $markerName =~ /^([A-Z]+)\d+$/) {
+            $markerName = $1 . ($marker & 0x0f);
+        } else {
+            $markerName = sprintf("marker 0x%.2x", $marker);
+        }
+    }
+    return $markerName;
+}
+
+#------------------------------------------------------------------------------
+# Identify trailer ending at specified offset from end of file
+# Inputs: 0) RAF reference, 1) offset from end of file (0 by default)
+# Returns: Trailer info hash (with RAF and DirName set),
+#          or undef if no recognized trailer was found
+# Notes: leaves file position unchanged
+sub IdentifyTrailer($;$)
+{
+    my $raf = shift;
+    my $offset = shift || 0;
+    my $pos = $raf->Tell();
+    my ($buff, $type, $len);
+    while ($raf->Seek(-$offset, 2) and ($len = $raf->Tell()) > 0) {
+        # read up to 64 bytes before specified offset from end of file
+        $len = 64 if $len > 64;
+        $raf->Seek(-$len, 1) and $raf->Read($buff, $len) == $len or last;
+        if ($buff =~ /AXS(!|\*).{8}$/s) {
+            $type = 'AFCP';
+        } elsif ($buff =~ /\xa1\xb2\xc3\xd4$/) {
+            $type = 'FotoStation';
+        } elsif ($buff =~ /cbipcbbl$/) {
+            $type = 'PhotoMechanic';
+        } elsif ($buff =~ /^CANON OPTIONAL DATA\0/) {
+            $type = 'CanonVRD';
+        } elsif ($buff =~ /~\0\x04\0zmie~\0\0\x06.{4}[\x10\x18]\x04$/s or
+                 $buff =~ /~\0\x04\0zmie~\0\0\x0a.{8}[\x10\x18]\x08$/s)
+        {
+            $type = 'MIE';
+        }
+        last;
+    }
+    $raf->Seek($pos, 0);    # restore original file position
+    return $type ? { RAF => $raf, DirName => $type } : undef;
+}
+
+#------------------------------------------------------------------------------
+# Extract EXIF information from a jpg image
+# Inputs: 0) ExifTool object reference, 1) dirInfo ref with RAF set
+# Returns: 1 on success, 0 if this wasn't a valid JPEG file
+sub ProcessJPEG($$)
+{
+    my ($self, $dirInfo) = @_;
+    my ($ch,$s,$length);
+    my $verbose = $self->{OPTIONS}->{Verbose};
+    my $out = $self->{OPTIONS}->{TextOut};
+    my $raf = $$dirInfo{RAF};
+    my $htmlDump = $self->{HTML_DUMP};
+    my %dumpParms = ( Out => $out );
+    my ($success, $icc_profile, $wantPreview, $trailInfo);
+
+    # check to be sure this is a valid JPG file
+    return 0 unless $raf->Read($s, 2) == 2 and $s eq "\xff\xd8";
+    $dumpParms{MaxLen} = 128 if $verbose < 4;
+    $self->SetFileType();   # set FileType tag
+    if ($htmlDump) {
+        my $pos = $raf->Tell() - 2;
+        $self->HtmlDump(0, $pos, '[unknown header]') if $pos;
+        $self->HtmlDump($pos, 2, 'JPEG header', 'SOI Marker');
+    }
+
+    # set input record separator to 0xff (the JPEG marker) to make reading quicker
+    my $oldsep = $/;
+    $/ = "\xff";
+
+    my ($nextMarker, $nextSegDataPt, $nextSegPos, $combinedSegData, $dumpEnd);
+
+    # read file until we reach an end of image (EOI) or start of scan (SOS)
+    Marker: for (;;) {
+        # set marker and data pointer for current segment
+        my $marker = $nextMarker;
+        my $segDataPt = $nextSegDataPt;
+        my $segPos = $nextSegPos;
+        undef $nextMarker;
+        undef $nextSegDataPt;
+#
+# read ahead to the next segment unless we have reached EOI or SOS
+#
+        unless ($marker and ($marker==0xd9 or ($marker==0xda and not $wantPreview))) {
+            # read up to next marker (JPEG markers begin with 0xff)
+            my $buff;
+            $raf->ReadLine($buff) or last;
+            # JPEG markers can be padded with unlimited 0xff's
+            for (;;) {
+                $raf->Read($ch, 1) or last Marker;
+                $nextMarker = ord($ch);
+                last unless $nextMarker == 0xff;
+            }
+            # read data for all markers except 0xd9 (EOI) and stand-alone
+            # markers 0x00, 0x01 and 0xd0-0xd7 (NULL, TEM, RST0-RST7)
+            if ($nextMarker!=0xd9 and $nextMarker!=0x00 and $nextMarker!=0x01 and
+                ($nextMarker<0xd0 or $nextMarker>0xd7))
+            {
+                # read record length word
+                last unless $raf->Read($s, 2) == 2;
+                my $len = unpack('n',$s);   # get data length
+                last unless defined($len) and $len >= 2;
+                $nextSegPos = $raf->Tell();
+                $len -= 2;  # subtract size of length word
+                last unless $raf->Read($buff, $len) == $len;
+                $nextSegDataPt = \$buff;    # set pointer to our next data
+            }
+            # read second segment too if this was the first
+            next unless defined $marker;
+        }
+        # set some useful variables for the current segment
+        my $hdr = "\xff" . chr($marker);    # header for this segment
+        my $markerName = JpegMarkerName($marker);
+#
+# parse the current segment
+#
+        # handle SOF markers: SOF0-SOF15, except DHT(0xc4), JPGA(0xc8) and DAC(0xcc)
+        if (($marker & 0xf0) == 0xc0 and ($marker == 0xc0 or $marker & 0x03)) {
+            $length = length $$segDataPt;
+            if ($verbose) {
+                print $out "JPEG $markerName ($length bytes):\n";
+                HexDump($segDataPt, undef, %dumpParms, Addr=>$segPos) if $verbose>2;
+            }
+            next unless $length >= 6;
+            # extract some useful information
+            my ($p, $h, $w, $n) = unpack('Cn2C', $$segDataPt);
+            my $sof = GetTagTable('Image::ExifTool::JPEG::SOF');
+            $self->FoundTag($$sof{ImageWidth}, $w);
+            $self->FoundTag($$sof{ImageHeight}, $h);
+            $self->FoundTag($$sof{EncodingProcess}, $marker - 0xc0);
+            $self->FoundTag($$sof{BitsPerSample}, $p);
+            $self->FoundTag($$sof{ColorComponents}, $n);
+            next unless $n == 3 and $length >= 15;
+            my ($i, $hmin, $hmax, $vmin, $vmax);
+            # loop through all components to determine sampling frequency
+            for ($i=0; $i<$n; ++$i) {
+                my $sf = Get8u($segDataPt, 7 + 3 * $i);
+                # isolate horizontal and vertical components
+                my ($hf, $vf) = ($sf >> 4, $sf & 0x0f);
+                unless ($i) {
+                    $hmin = $hmax = $hf;
+                    $vmin = $vmax = $vf;
+                    next;
+                }
+                # determine min/max frequencies
+                $hmin = $hf if $hf < $hmin;
+                $hmax = $hf if $hf > $hmax;
+                $vmin = $vf if $vf < $vmin;
+                $vmax = $vf if $vf > $vmax;
+            }
+            if ($hmin and $vmin) {
+                my ($hs, $vs) = ($hmax / $hmin, $vmax / $vmin);
+                $self->FoundTag($$sof{YCbCrSubSampling}, "$hs $vs");
+            }
+            next;
+        } elsif ($marker == 0xd9) {         # EOI
+            $verbose and print $out "JPEG EOI\n";
+            my $pos = $raf->Tell();
+            if ($htmlDump and $dumpEnd) {
+                $self->HtmlDump($dumpEnd, $pos-2-$dumpEnd, '[JPEG Image Data]', undef, 0x08);
+                $self->HtmlDump($pos-2, 2, 'JPEG EOI', undef);
+                $dumpEnd = 0;
+            }
+            $success = 1;
+            # we are here because we are looking for trailer information
+            if ($wantPreview and $self->{VALUE}->{PreviewImageStart}) {
+                my $buff;
+                # most previews start right after the JPEG EOI, but the Olympus E-20
+                # preview is 508 bytes into the trailer, and the K-M Maxxum 7D preview
+                # is 979 bytes in, but Minolta previews can have a random first byte...
+                if ($raf->Read($buff, 1024) and ($buff =~ /\xff\xd8\xff./g or
+                    ($self->{CameraMake} =~ /Minolta/i and $buff =~ /.\xd8\xff\xdb/g)))
+                {
+                    # adjust PreviewImageStart to this location
+                    my $start = $self->{VALUE}->{PreviewImageStart};
+                    my $actual = $pos + pos($buff) - 4;
+                    if ($start ne $actual and $verbose > 1) {
+                        print $out "(Fixed PreviewImage location: $start -> $actual)\n";
+                    }
+                    $self->{VALUE}->{PreviewImageStart} = $actual;
+                }
+                $raf->Seek($pos, 0);
+            }
+            # process trailer now or finish processing trailers
+            # and scan for AFCP if necessary
+            my $fromEnd = 0;
+            if ($trailInfo) {
+                $$trailInfo{ScanForAFCP} = 1;   # scan now if necessary
+                $self->ProcessTrailers($trailInfo);
+                # save offset from end of file to start of first trailer
+                $fromEnd = $$trailInfo{Offset};
+                undef $trailInfo;
+            }
+            # finally, dump remaining information in JPEG trailer
+            if ($verbose or $htmlDump) {
+                $raf->Seek(0, 2);
+                my $endPos = $raf->Tell() - $fromEnd;
+                $self->DumpUnknownTrailer({
+                    RAF => $raf,
+                    DataPos => $pos,
+                    DirLen => $endPos - $pos
+                }) if $endPos > $pos;
+            }
+            last;       # all done parsing file
+        } elsif ($marker == 0xda) {         # SOS
+            # all done with meta information unless we have a trailer
+            $verbose and print $out "JPEG SOS\n";
+            unless ($self->Options('FastScan')) {
+                $trailInfo = IdentifyTrailer($raf);
+                # process trailer now unless we are doing verbose dump
+                if ($trailInfo and $verbose < 3 and not $htmlDump) {
+                    # process trailers (keep trailInfo to finish processing later
+                    # only if we can't finish without scanning from end of file)
+                    $self->ProcessTrailers($trailInfo) and undef $trailInfo;
+                }
+                if ($wantPreview) {
+                    # seek ahead and validate preview image
+                    my $buff;
+                    my $curPos = $raf->Tell();
+                    if ($raf->Seek($self->GetValue('PreviewImageStart'), 0) and
+                        $raf->Read($buff, 4) == 4 and
+                        $buff =~ /^.\xd8\xff[\xc4\xdb\xe0-\xef]/)
+                    {
+                        undef $wantPreview;
+                    }
+                    $raf->Seek($curPos, 0) or last;
+                }
+                next if $trailInfo or $wantPreview or $verbose > 2 or $htmlDump;
+            }
+            # nothing interesting to parse after start of scan (SOS)
+            $success = 1;
+            last;   # all done parsing file
+        } elsif ($marker==0x00 or $marker==0x01 or ($marker>=0xd0 and $marker<=0xd7)) {
+            # handle stand-alone markers 0x00, 0x01 and 0xd0-0xd7 (NULL, TEM, RST0-RST7)
+            $verbose and $marker and print $out "JPEG $markerName:\n";
+            next;
+        }
+        # handle all other markers
+        my $dumpType = '';
+        $length = length $$segDataPt;
+        if ($verbose) {
+            print $out "JPEG $markerName ($length bytes):\n";
+            if ($verbose > 2) {
+                my %extraParms = ( Addr => $segPos );
+                $extraParms{MaxLen} = 128 if $verbose == 4;
+                HexDump($segDataPt, undef, %dumpParms, %extraParms);
+            }
+        }
+        if ($marker == 0xe0) {              # APP0 (JFIF, CIFF)
+            if ($$segDataPt =~ /^JFIF\0/) {
+                $dumpType = 'JFIF';
+                my %dirInfo = (
+                    DataPt => $segDataPt,
+                    DataPos  => $segPos,
+                    DirStart => 5,
+                    DirLen => $length - 5,
+                );
+                SetByteOrder('MM');
+                my $tagTablePtr = GetTagTable('Image::ExifTool::JFIF::Main');
+                $self->ProcessDirectory(\%dirInfo, $tagTablePtr);
+            } elsif ($$segDataPt =~ /^JFXX\0\x10/) {
+                $dumpType = 'JFXX';
+                my $tagTablePtr = GetTagTable('Image::ExifTool::JFIF::Extension');
+                my $tagInfo = $self->GetTagInfo($tagTablePtr, 0x10);
+                $self->FoundTag($tagInfo, substr($$segDataPt, 6));
+            } elsif ($$segDataPt =~ /^(II|MM).{4}HEAPJPGM/s) {
+                $dumpType = 'CIFF';
+                my %dirInfo = (
+                    RAF => new File::RandomAccess($segDataPt),
+                );
+                $self->{SET_GROUP1} = 'CIFF';
+                require Image::ExifTool::CanonRaw;
+                Image::ExifTool::CanonRaw::ProcessCRW($self, \%dirInfo);
+                delete $self->{SET_GROUP1};
+            }
+        } elsif ($marker == 0xe1) {         # APP1 (EXIF, XMP)
+            if ($$segDataPt =~ /^Exif\0/) { # (some Kodak cameras don't put a second \0)
+                undef $dumpType;    # (will be dumped here)
+                # this is EXIF data --
+                # get the data block (into a common variable)
+                my $hdrLen = length($exifAPP1hdr);
+                my %dirInfo = (
+                    Parent => $markerName,
+                    DataPt => $segDataPt,
+                    DataPos => $segPos,
+                    DirStart => $hdrLen,
+                    Base => $segPos + $hdrLen,
+                );
+                if ($htmlDump) {
+                    $self->HtmlDump($segPos-4, 4, 'APP1 header',
+                             "Data size: $length bytes");
+                    $self->HtmlDump($segPos, $hdrLen, 'Exif header',
+                             'APP1 data type: Exif');
+                    $dumpEnd = $segPos + $length;
+                }
+                # extract the EXIF information (it is in standard TIFF format)
+                $self->ProcessTIFF(\%dirInfo);
+                # avoid looking for preview unless necessary because it really slows
+                # us down -- only look for it if we found pointer, and preview is
+                # outside EXIF, and PreviewImage is specifically requested
+                my $start = $self->GetValue('PreviewImageStart');
+                my $length = $self->GetValue('PreviewImageLength');
+                if ($start and $length and
+                    $start + $length > $self->{EXIF_POS} + length($self->{EXIF_DATA}) and
+                    $self->{REQ_TAG_LOOKUP}->{previewimage})
+                {
+                    $wantPreview = 1;
+                }
+            } else {
+                # Hmmm.  Could be XMP, let's see
+                my $processed;
+                if ($$segDataPt =~ /^http/ or $$segDataPt =~ /<exif:/) {
+                    $dumpType = 'XMP';
+                    my $start = ($$segDataPt =~ /^$xmpAPP1hdr/) ? length($xmpAPP1hdr) : 0;
+                    my $tagTablePtr = GetTagTable('Image::ExifTool::XMP::Main');
+                    my %dirInfo = (
+                        Base     => 0,
+                        DataPt   => $segDataPt,
+                        DataPos  => $segPos,
+                        DataLen  => $length,
+                        DirStart => $start,
+                        DirLen   => $length - $start,
+                        Parent   => $markerName,
+                    );
+                    $processed = $self->ProcessDirectory(\%dirInfo, $tagTablePtr);
+                }
+                if ($verbose and not $processed) {
+                    $self->Warn("Ignored EXIF block length $length (bad header)");
+                }
+            }
+        } elsif ($marker == 0xe2) {         # APP2 (ICC Profile, FPXR)
+            if ($$segDataPt =~ /^ICC_PROFILE\0/) {
+                $dumpType = 'ICC_Profile';
+                # must concatenate blocks of profile
+                my $block_num = Get8u($segDataPt, 12);
+                my $blocks_tot = Get8u($segDataPt, 13);
+                $icc_profile = '' if $block_num == 1;
+                if (defined $icc_profile) {
+                    $icc_profile .= substr($$segDataPt, 14);
+                    if ($block_num == $blocks_tot) {
+                        my $tagTablePtr = GetTagTable('Image::ExifTool::ICC_Profile::Main');
+                        my %dirInfo = (
+                            DataPt   => \$icc_profile,
+                            DataPos  => $segPos + 14,
+                            DataLen  => length($icc_profile),
+                            DirStart => 0,
+                            DirLen   => length($icc_profile),
+                            Parent   => $markerName,
+                        );
+                        $self->ProcessDirectory(\%dirInfo, $tagTablePtr);
+                        undef $icc_profile;
+                    }
+                }
+            } elsif ($$segDataPt =~ /^FPXR\0/) {
+                $dumpType = 'FPXR';
+                my $tagTablePtr = GetTagTable('Image::ExifTool::FlashPix::Main');
+                my %dirInfo = (
+                    DataPt   => $segDataPt,
+                    DataPos  => $segPos,
+                    DataLen  => $length,
+                    DirStart => 0,
+                    DirLen   => $length,
+                    Parent   => $markerName,
+                    # set flag if this is the last FPXR segment
+                    LastFPXR => not ($nextMarker==$marker and $$nextSegDataPt=~/^FPXR\0/),
+                );
+                $self->ProcessDirectory(\%dirInfo, $tagTablePtr);
+            }
+        } elsif ($marker == 0xe3) {         # APP3 (Kodak "Meta")
+            if ($$segDataPt =~ /^(Meta|META|Exif)\0\0/) {
+                undef $dumpType;    # (will be dumped here)
+                my %dirInfo = (
+                    Parent => $markerName,
+                    DataPt => $segDataPt,
+                    DataPos => $segPos,
+                    DirStart => 6,
+                    Base => $segPos + 6,
+                );
+                if ($htmlDump) {
+                    $self->HtmlDump($segPos-4, 10, 'APP3 Meta header');
+                    $dumpEnd = $segPos + $length;
+                }
+                my $tagTablePtr = GetTagTable('Image::ExifTool::Kodak::Meta');
+                $self->ProcessTIFF(\%dirInfo, $tagTablePtr);
+            }
+        } elsif ($marker == 0xe5) {         # APP5 (Ricoh "RMETA")
+            if ($$segDataPt =~ /^RMETA\0/) {
+                $dumpType = 'Ricoh RMETA';
+                my %dirInfo = (
+                    Parent => $markerName,
+                    DataPt => $segDataPt,
+                    DataPos => $segPos,
+                    DirStart => 6,
+                    Base => $segPos + 6,
+                );
+                my $tagTablePtr = GetTagTable('Image::ExifTool::Ricoh::RMETA');
+                $self->ProcessDirectory(\%dirInfo, $tagTablePtr);
+            }
+        } elsif ($marker == 0xe6) {         # APP6 (Toshiba EPPIM)
+            if ($$segDataPt =~ /^EPPIM\0/) {
+                undef $dumpType;    # (will be dumped here)
+                my %dirInfo = (
+                    Parent => $markerName,
+                    DataPt => $segDataPt,
+                    DataPos => $segPos,
+                    DirStart => 6,
+                    Base => $segPos + 6,
+                );
+                if ($htmlDump) {
+                    $self->HtmlDump($segPos-4, 10, 'APP6 EPPIM header');
+                    $dumpEnd = $segPos + $length;
+                }
+                my $tagTablePtr = GetTagTable('Image::ExifTool::JPEG::EPPIM');
+                $self->ProcessTIFF(\%dirInfo, $tagTablePtr);
+            }
+        } elsif ($marker == 0xe8) {         # APP8 (SPIFF)
+            # my sample SPIFF has 32 bytes of data, but spec states 30
+            if ($$segDataPt =~ /^SPIFF\0/ and $length == 32) {
+                $dumpType = 'SPIFF';
+                my %dirInfo = (
+                    DataPt => $segDataPt,
+                    DataPos  => $segPos,
+                    DirStart => 6,
+                    DirLen => $length - 6,
+                );
+                my $tagTablePtr = GetTagTable('Image::ExifTool::JPEG::SPIFF');
+                $self->ProcessDirectory(\%dirInfo, $tagTablePtr);
+            }
+        } elsif ($marker == 0xea) {         # APP10 (PhotoStudio Unicode comments)
+            if ($$segDataPt =~ /^UNICODE\0/) {
+                $dumpType = 'PhotoStudio';
+                my $comment = $self->Unicode2Charset(substr($$segDataPt,8), 'MM');
+                $self->FoundTag('Comment', $comment);
+            }
+        } elsif ($marker == 0xec) {         # APP12 (Ducky, Picture Info)
+            if ($$segDataPt =~ /^Ducky/) {
+                $dumpType = 'Ducky';
+                my %dirInfo = (
+                    DataPt => $segDataPt,
+                    DataPos => $segPos,
+                    DirStart => 5,
+                    DirLen => $length - 5,
+                );
+                my $tagTablePtr = GetTagTable('Image::ExifTool::APP12::Ducky');
+                $self->ProcessDirectory(\%dirInfo, $tagTablePtr);
+            } else {
+                my %dirInfo = ( DataPt => $segDataPt );
+                my $tagTablePtr = GetTagTable('Image::ExifTool::APP12::PictureInfo');
+                $self->ProcessDirectory(\%dirInfo, $tagTablePtr) and $dumpType = 'Picture Info';
+            }
+        } elsif ($marker == 0xed) {         # APP13 (Photoshop, Adobe_CM)
+            my $isOld;
+            if ($$segDataPt =~ /^$psAPP13hdr/ or ($$segDataPt =~ /^$psAPP13old/ and $isOld=1)) {
+                $dumpType = 'Photoshop';
+                # add this data to the combined data if it exists
+                if (defined $combinedSegData) {
+                    $combinedSegData .= substr($$segDataPt,length($psAPP13hdr));
+                    $segDataPt = \$combinedSegData;
+                    $length = length $combinedSegData;  # update length
+                }
+                # peek ahead to see if the next segment is photoshop data too
+                if ($nextMarker == $marker and $$nextSegDataPt =~ /^$psAPP13hdr/) {
+                    # initialize combined data if necessary
+                    $combinedSegData = $$segDataPt unless defined $combinedSegData;
+                    next;   # will handle the combined data the next time around
+                }
+                my $hdrlen = $isOld ? 27 : 14;
+                # process APP13 Photoshop record
+                my $tagTablePtr = GetTagTable('Image::ExifTool::Photoshop::Main');
+                my %dirInfo = (
+                    DataPt   => $segDataPt,
+                    DataPos  => $segPos,
+                    DataLen  => $length,
+                    DirStart => $hdrlen,    # directory starts after identifier
+                    DirLen   => $length - $hdrlen,
+                    Parent   => $markerName,
+                );
+                $self->ProcessDirectory(\%dirInfo, $tagTablePtr);
+                undef $combinedSegData;
+            } elsif ($$segDataPt =~ /^Adobe_CM/) {
+                $dumpType = 'Adobe_CM';
+                SetByteOrder('MM');
+                my $tagTablePtr = GetTagTable('Image::ExifTool::JPEG::AdobeCM');
+                my %dirInfo = (
+                    DataPt   => $segDataPt,
+                    DataPos  => $segPos,
+                    DirStart => 8,
+                    DirLen   => $length - 8,
+                );
+                $self->ProcessDirectory(\%dirInfo, $tagTablePtr);
+            }
+        } elsif ($marker == 0xee) {         # APP14 (Adobe)
+            if ($$segDataPt =~ /^Adobe/) {
+                $dumpType = 'Adobe';
+                SetByteOrder('MM');
+                my $tagTablePtr = GetTagTable('Image::ExifTool::JPEG::Adobe');
+                my %dirInfo = (
+                    DataPt   => $segDataPt,
+                    DataPos  => $segPos,
+                    DirStart => 5,
+                    DirLen   => $length - 5,
+                );
+                $self->ProcessDirectory(\%dirInfo, $tagTablePtr);
+            }
+        } elsif ($marker == 0xef) {         # APP15 (GraphicConverter)
+            if ($$segDataPt =~ /^Q\s*(\d+)/ and $length == 4) {
+                $dumpType = 'GraphicConverter';
+                my $tagTablePtr = GetTagTable('Image::ExifTool::JPEG::GraphConv');
+                $self->HandleTag($tagTablePtr, 'Q', $1);
+            }
+        } elsif ($marker == 0xfe) {         # COM (JPEG comment)
+            $dumpType = 'Comment';
+            $self->FoundTag('Comment', $$segDataPt);
+        } elsif (($marker & 0xf0) != 0xe0) {
+            undef $dumpType;    # only dump unknown APP segments
+        }
+        if (defined $dumpType) {
+            if (not $dumpType and $self->{OPTIONS}->{Unknown}) {
+                $self->Warn("Unknown $markerName segment", 1);
+            }
+            if ($htmlDump) {
+                my $desc = $markerName . ($dumpType ? " $dumpType" : '') . ' segment';
+                $self->HtmlDump($segPos-4, $length+4, $desc, undef, 0x08);
+                $dumpEnd = $segPos + $length;
+            }
+        }
+        undef $$segDataPt;
+    }
+    $/ = $oldsep;     # restore separator to original value
+    $success or $self->Warn('JPEG format error');
+    return 1;
+}
+
+#------------------------------------------------------------------------------
+# Process TIFF data
+# Inputs: 0) ExifTool object reference, 1) directory information reference
+#         2) optional tag table reference
+# Returns: 1 if this looked like a valid EXIF block, 0 otherwise, or -1 on write error
+sub ProcessTIFF($$;$)
+{
+    my ($self, $dirInfo, $tagTablePtr) = @_;
+    my $dataPt = $$dirInfo{DataPt};
+    my $fileType = $$dirInfo{Parent} || '';
+    my $raf = $$dirInfo{RAF};
+    my $base = $$dirInfo{Base} || 0;
+    my $outfile = $$dirInfo{OutFile};
+    my ($length, $err, $canonSig);
+
+    # read the image file header and offset to 0th IFD if necessary
+    if ($raf) {
+        if ($outfile) {
+            $raf->Seek(0, 0) or return 0;
+            if ($base) {
+                $raf->Read($$dataPt, $base) == $base or return 0;
+                Write($outfile, $$dataPt) or $err = 1;
+            }
+        } else {
+            $raf->Seek($base, 0) or return 0;
+        }
+        $raf->Read($self->{EXIF_DATA}, 8) == 8 or return 0;
+    } elsif ($dataPt) {
+        # save a copy of the EXIF data
+        my $dirStart = $$dirInfo{DirStart} || 0;
+        $self->{EXIF_DATA} = substr(${$$dirInfo{DataPt}}, $dirStart);
+    } elsif ($outfile) {
+        # create TIFF information from scratch
+        $self->{EXIF_DATA} = "MM\0\x2a\0\0\0\x08";
+    } else {
+        $self->{EXIF_DATA} = '';
+    }
+    $$self{FIRST_EXIF_POS} = $base + $$self{BASE} unless defined $$self{FIRST_EXIF_POS};
+    $$self{EXIF_POS} = $base;
+    $dataPt = \$self->{EXIF_DATA};
+
+    # set byte ordering
+    SetByteOrder(substr($$dataPt,0,2)) or return 0;
+    # save EXIF byte ordering
+    $self->{EXIF_BYTE_ORDER} = GetByteOrder();
+
+    # verify the byte ordering
+    my $identifier = Get16u($dataPt, 2);
+    # identifier is 0x2a for TIFF (but 0x4f52, 0x5352 or ?? for ORF)
+  # no longer do this because ORF files use different values
+  #  return 0 unless $identifier == 0x2a;
+
+    # get offset to IFD0
+    my $offset = Get32u($dataPt, 4);
+    $offset >= 8 or return 0;
+
+    if ($raf) {
+        # Canon CR2 images usually have an offset of 16, but it may be
+        # greater if edited by PhotoMechanic, so check the 4-byte signature
+        if ($identifier == 0x2a and $offset >= 16) {
+            $raf->Read($canonSig, 8) == 8 or return 0;
+            $$dataPt .= $canonSig;
+            if ($canonSig =~ /^CR\x02\0/) {
+                $fileType = 'CR2';
+                $self->HtmlDump($base+8, 8, '[CR2 header]') if $self->{HTML_DUMP};
+            } else {
+                undef $canonSig;
+            }
+        } elsif ($identifier == 0x55 and $fileType =~ /^(RAW|TIFF)$/) {
+            $fileType = 'RAW';  # Panasonic RAW file
+            $tagTablePtr = GetTagTable('Image::ExifTool::Panasonic::Raw');
+        } elsif ($identifier == 0x2b and $fileType eq 'TIFF') {
+            # this looks like a BigTIFF image
+            $raf->Seek(0);
+            require Image::ExifTool::BigTIFF;
+            return 1 if Image::ExifTool::BigTIFF::ProcessBTF($self, $dirInfo);
+        } elsif (Get8u($dataPt, 2) == 0xbc and $fileType eq 'TIFF') {
+            $fileType = 'WDP';  # Windows Media Photo file
+        }
+        # we have a valid TIFF (or whatever) file
+        if ($fileType and not $self->{VALUE}->{FileType}) {
+            $self->SetFileType($fileType);
+        }
+    }
+    $self->FoundTag('ExifByteOrder', GetByteOrder());
+    if ($self->{HTML_DUMP}) {
+        my $o = (GetByteOrder() eq 'II') ? 'Little' : 'Big';
+        $self->HtmlDump($base, 4, "TIFF header", "Byte order: $o endian", 0);
+        $self->HtmlDump($base+4, 4, "IFD0 pointer", sprintf("Offset: 0x%.4x",$offset), 0);
+    }
+    # remember where we found the TIFF data (APP1, APP3, TIFF, NEF, etc...)
+    $self->{TIFF_TYPE} = $fileType;
+
+    # get reference to the main EXIF table
+    $tagTablePtr or $tagTablePtr = GetTagTable('Image::ExifTool::Exif::Main');
+
+    # build directory information hash
+    my %dirInfo = (
+        Base     => $base,
+        DataPt   => $dataPt,
+        DataLen  => length $$dataPt,
+        DataPos  => 0,
+        DirStart => $offset,
+        DirLen   => length $$dataPt,
+        RAF      => $raf,
+        DirName  => 'IFD0',
+        Parent   => $fileType,
+        ImageData=> 1, # set flag to get information to copy image data later
+    );
+
+    # extract information from the image
+    unless ($outfile) {
+        # process the directory
+        $self->ProcessDirectory(\%dirInfo, $tagTablePtr);
+        # process GeoTiff information if available
+        if ($self->{VALUE}->{GeoTiffDirectory}) {
+            require Image::ExifTool::GeoTiff;
+            Image::ExifTool::GeoTiff::ProcessGeoTiff($self);
+        }
+        # process information in recognized trailers
+        if ($raf) {
+            my $trailInfo = IdentifyTrailer($raf);
+            if ($trailInfo) {
+                $$trailInfo{ScanForAFCP} = 1;   # scan to find AFCP if necessary
+                $self->ProcessTrailers($trailInfo);
+            }
+        }
+        return 1;
+    }
+#
+# rewrite the image
+#
+    if ($$dirInfo{NoTiffEnd}) {
+        delete $self->{TIFF_END};
+    } else {
+        # initialize TIFF_END so it will be updated by WriteExif()
+        $self->{TIFF_END} = 0;
+    }
+    if ($canonSig) {
+        # write Canon CR2 specially because it has a header we want to preserve,
+        # and possibly trailers added by the Canon utilities and/or PhotoMechanic
+        $dirInfo{OutFile} = $outfile;
+        require Image::ExifTool::CanonRaw;
+        Image::ExifTool::CanonRaw::WriteCR2($self, \%dirInfo, $tagTablePtr) or $err = 1;
+    } else {
+        # write TIFF header (8 bytes to be immediately followed by IFD)
+        $dirInfo{NewDataPos} = 8;
+        # preserve padding between image data blocks in ORF images
+        # (otherwise dcraw has problems because it assumes fixed block spacing)
+        $dirInfo{PreserveImagePadding} = 1 if $fileType eq 'ORF' or $identifier != 0x2a;
+        my $newData = $self->WriteDirectory(\%dirInfo, $tagTablePtr);
+        if (not defined $newData) {
+            $err = 1;
+        } elsif (length($newData)) {
+            my $offset = 8;
+            my $header = substr($$dataPt, 0, 4) . Set32u($offset);
+            Write($outfile, $header, $newData) or $err = 1;
+            undef $newData; # free memory
+        }
+        # copy over image data now if necessary
+        if (ref $dirInfo{ImageData} and not $err) {
+            $self->CopyImageData($dirInfo{ImageData}, $outfile) or $err = 1;
+            delete $dirInfo{ImageData};
+        }
+    }
+    # rewrite trailers if they exist
+    if ($raf and $self->{TIFF_END} and not $err) {
+        my ($buf, $trailInfo);
+        $raf->Seek(0, 2) or $err = 1;
+        my $extra = $raf->Tell() - $self->{TIFF_END};
+        # check for trailer and process if possible
+        for (;;) {
+            last unless $extra > 12;
+            $raf->Seek($self->{TIFF_END});  # seek back to end of image
+            $trailInfo = IdentifyTrailer($raf);
+            last unless $trailInfo;
+            my $tbuf = '';
+            $$trailInfo{OutFile} = \$tbuf;  # rewrite trailer(s)
+            $$trailInfo{ScanForAFCP} = 1;   # scan for AFCP if necessary
+            # rewrite all trailers to buffer
+            unless ($self->ProcessTrailers($trailInfo)) {
+                undef $trailInfo;
+                $err = 1;
+                last;
+            }
+            # calculate unused bytes before trailer
+            $extra = $$trailInfo{DataPos} - $self->{TIFF_END};
+            last; # yes, the 'for' loop was just a cheap 'goto'
+        }
+        # ignore a single zero byte if used for padding
+        # (note that Photoshop CS adds a trailer with 2 zero bytes
+        #  for some reason, and these will be preserved)
+        if ($extra > 0 and $self->{TIFF_END} & 0x01) {
+            $raf->Seek($self->{TIFF_END}, 0) or $err = 1;
+            $raf->Read($buf, 1) or $err = 1;
+            $buf eq "\0" and --$extra, ++$self->{TIFF_END};
+        }
+        if ($extra > 0) {
+            if ($self->{DEL_GROUP}->{Trailer}) {
+                $self->VPrint(0, "  Deleting unknown trailer ($extra bytes)\n");
+                ++$self->{CHANGED};
+            } else {
+                $self->VPrint(0, "  Preserving unknown trailer ($extra bytes)\n");
+                $raf->Seek($self->{TIFF_END}, 0) or $err = 1;
+                while ($extra) {
+                    my $n = $extra < 65536 ? $extra : 65536;
+                    $raf->Read($buf, $n) == $n or $err = 1, last;
+                    Write($outfile, $buf) or $err = 1, last;
+                    $extra -= $n;
+                }
+            }
+        }
+        # write trailer buffer if necessary
+        $self->WriteTrailerBuffer($trailInfo, $outfile) or $err = 1 if $trailInfo;
+        # add any new trailers we are creating
+        my $trailPt = $self->AddNewTrailers();
+        Write($outfile, $$trailPt) or $err = 1 if $trailPt;
+    }
+    delete $self->{TIFF_END};
+    return $err ? -1 : 1;
+}
+
+#------------------------------------------------------------------------------
+# Return list of tag table keys (ignoring special keys)
+# Inputs: 0) reference to tag table
+# Returns: List of table keys (unsorted)
+sub TagTableKeys($)
+{
+    local $_;
+    my $tagTablePtr = shift;
+    my @keyList;
+    foreach (keys %$tagTablePtr) {
+        push(@keyList, $_) unless $specialTags{$_};
+    }
+    return @keyList;
+}
+
+#------------------------------------------------------------------------------
+# GetTagTable
+# Inputs: 0) table name
+# Returns: tag table reference, or undefined if not found
+# Notes: Always use this function instead of requiring module and using table
+# directly since this function also does the following the first time the table
+# is loaded:
+# - requires new module if necessary
+# - generates default GROUPS hash and Group 0 name from module name
+# - registers Composite tags if Composite table found
+# - saves descriptions for tags in specified table
+# - generates default TAG_PREFIX to be used for unknown tags
+sub GetTagTable($)
+{
+    my $tableName = shift or return undef;
+
+    my $table = $allTables{$tableName};
+
+    unless ($table) {
+        no strict 'refs';
+        unless (defined %$tableName) {
+            # try to load module for this table
+            if ($tableName =~ /(.*)::/) {
+                my $module = $1;
+                unless (eval "require $module") {
+                    $@ and warn $@;
+                }
+            }
+            unless (defined %$tableName) {
+                warn "Can't find table $tableName\n";
+                return undef;
+            }
+        }
+        no strict 'refs';
+        $table = \%$tableName;
+        use strict 'refs';
+        # set default group 0 and 1 from module name unless already specified
+        my $defaultGroups = $$table{GROUPS};
+        $defaultGroups or $defaultGroups = $$table{GROUPS} = { };
+        unless ($$defaultGroups{0} and $$defaultGroups{1}) {
+            if ($tableName =~ /Image::.*?::([^:]*)/) {
+                $$defaultGroups{0} = $1 unless $$defaultGroups{0};
+                $$defaultGroups{1} = $1 unless $$defaultGroups{1};
+            } else {
+                $$defaultGroups{0} = $tableName unless $$defaultGroups{0};
+                $$defaultGroups{1} = $tableName unless $$defaultGroups{1};
+            }
+        }
+        $$defaultGroups{2} = 'Other' unless $$defaultGroups{2};
+        # generate a tag prefix for unknown tags if necessary
+        unless ($$table{TAG_PREFIX}) {
+            my $tagPrefix;
+            if ($tableName =~ /Image::.*?::(.*)::Main/ || $tableName =~ /Image::.*?::(.*)/) {
+                ($tagPrefix = $1) =~ s/::/_/g;
+            } else {
+                $tagPrefix = $tableName;
+            }
+            $$table{TAG_PREFIX} = $tagPrefix;
+        }
+        # set up the new table
+        SetupTagTable($table);
+        # add any user-defined tags
+        if (defined %UserDefined and $UserDefined{$tableName}) {
+            my $tagID;
+            foreach $tagID (TagTableKeys($UserDefined{$tableName})) {
+                my $tagInfo = $UserDefined{$tableName}->{$tagID};
+                if (ref $tagInfo eq 'HASH') {
+                    $$tagInfo{Name} or $$tagInfo{Name} = ucfirst($tagID);
+                } else {
+                    $tagInfo = { Name => $tagInfo };
+                }
+                if ($$table{WRITABLE} and not defined $$tagInfo{Writable} and
+                    not $$tagInfo{SubDirectory})
+                {
+                    $$tagInfo{Writable} = $$table{WRITABLE};
+                }
+                delete $$table{$tagID}; # replace any existing entry
+                AddTagToTable($table, $tagID, $tagInfo);
+            }
+        }
+        # generate tag ID's if necessary
+        GenerateTagIDs($table) if $didTagID;
+        # remember order we loaded the tables in
+        push @tableOrder, $tableName;
+        # insert newly loaded table into list
+        $allTables{$tableName} = $table;
+    }
+    return $table;
+}
+
+#------------------------------------------------------------------------------
+# Process an image directory
+# Inputs: 0) ExifTool object reference, 1) directory information reference
+#         2) tag table reference, 3) optional reference to processing procedure
+# Returns: Result from processing (1=success)
+sub ProcessDirectory($$$;$)
+{
+    my ($self, $dirInfo, $tagTablePtr, $processProc) = @_;
+
+    return 0 unless $tagTablePtr and $dirInfo;
+    # use default proc from tag table if no proc specified
+    $processProc or $processProc = $$tagTablePtr{PROCESS_PROC};
+    # set directory name from default group0 name if not done already
+    $$dirInfo{DirName} or $$dirInfo{DirName} = $tagTablePtr->{GROUPS}->{0};
+    # guard against cyclical recursion into the same directory
+    if (defined $$dirInfo{DirStart} and defined $$dirInfo{DataPos}) {
+        my $addr = $$dirInfo{DirStart} + $$dirInfo{DataPos} + ($$dirInfo{Base}||0);
+        if ($self->{PROCESSED}->{$addr}) {
+            $self->Warn("$$dirInfo{DirName} pointer references previous $self->{PROCESSED}->{$addr} directory");
+            return 0;
+        }
+        $self->{PROCESSED}->{$addr} = $$dirInfo{DirName};
+    }
+    # otherwise process as an EXIF directory
+    $processProc or $processProc = \&Image::ExifTool::Exif::ProcessExif;
+    my $oldOrder = GetByteOrder();
+    my $oldIndent = $self->{INDENT};
+    my $oldDir = $self->{DIR_NAME};
+    $self->{INDENT} .= '| ';
+    $self->{DIR_NAME} = $$dirInfo{DirName};
+    my $rtnVal = &$processProc($self, $dirInfo, $tagTablePtr);
+    $self->{INDENT} = $oldIndent;
+    $self->{DIR_NAME} = $oldDir;
+    SetByteOrder($oldOrder);
+    return $rtnVal;
+}
+
+#------------------------------------------------------------------------------
+# Get standardized file extension
+# Inputs: 0) file name
+# Returns: standardized extension (all uppercase)
+sub GetFileExtension($)
+{
+    my $filename = shift;
+    my $fileExt;
+    if ($filename and $filename =~ /.*\.(.+)$/) {
+        $fileExt = uc($1);   # change extension to upper case
+        # convert TIF extension to TIFF because we use the
+        # extension for the file type tag of TIFF images
+        $fileExt eq 'TIF' and $fileExt = 'TIFF';
+    }
+    return $fileExt;
+}
+
+#------------------------------------------------------------------------------
+# Get list of tag information hashes for given tag ID
+# Inputs: 0) Tag table reference, 1) tag ID
+# Returns: Array of tag information references
+# Notes: Generates tagInfo hash if necessary
+sub GetTagInfoList($$)
+{
+    my ($tagTablePtr, $tagID) = @_;
+    my $tagInfo = $$tagTablePtr{$tagID};
+
+    if (ref $tagInfo eq 'HASH') {
+        return ($tagInfo);
+    } elsif (ref $tagInfo eq 'ARRAY') {
+        return @$tagInfo;
+    } elsif ($tagInfo) {
+        # create hash with name
+        $tagInfo = $$tagTablePtr{$tagID} = { Name => $tagInfo };
+        return ($tagInfo);
+    }
+    return ();
+}
+
+#------------------------------------------------------------------------------
+# Find tag information, processing conditional tags
+# Inputs: 0) ExifTool object reference, 1) tagTable pointer, 2) tag ID
+#         3) optional value reference, 4) optional format type, 5) optional value count
+# Returns: pointer to tagInfo hash, undefined if none found, or '' if $valPt needed
+# Notes: You should always call this routine to find a tag in a table because
+# this routine will evaluate conditional tags.
+# Arguments 3-5 are only required if the information type allows $valPt, $format and/or
+# $count in a Condition, and if not given when needed this routine returns ''.
+sub GetTagInfo($$$;$$$)
+{
+    my ($self, $tagTablePtr, $tagID) = @_;
+    my ($valPt, $format, $count);
+
+    my @infoArray = GetTagInfoList($tagTablePtr, $tagID);
+    # evaluate condition
+    my $tagInfo;
+    foreach $tagInfo (@infoArray) {
+        my $condition = $$tagInfo{Condition};
+        if ($condition) {
+            ($valPt, $format, $count) = splice(@_, 3) if @_ > 3;
+            return '' if $condition =~ /\$(valPt|format|count)\b/ and not defined $valPt;
+            # set old value for use in condition if needed
+            my $oldVal = $self->{VALUE}->{$$tagInfo{Name}};
+            #### eval Condition ($self, $oldVal, [$valPt, $format, $count])
+            unless (eval $condition) {
+                $@ and warn "Condition $$tagInfo{Name}: $@";
+                next;
+            }
+        }
+        if ($$tagInfo{Unknown} and not $self->{OPTIONS}->{Unknown}) {
+            # don't return Unknown tags unless that option is set
+            return undef;
+        }
+        # return the tag information we found
+        return $tagInfo;
+    }
+    # generate information for unknown tags (numerical only) if required
+    if (not $tagInfo and $self->{OPTIONS}->{Unknown} and $tagID =~ /^\d+$/ and
+        not $$self{NO_UNKNOWN})
+    {
+        my $printConv;
+        if (defined $$tagTablePtr{PRINT_CONV}) {
+            $printConv = $$tagTablePtr{PRINT_CONV};
+        } else {
+            # limit length of printout (can be very long)
+            $printConv = 'length($val) > 60 ? substr($val,0,55) . "[...]" : $val';
+        }
+        my $hex = sprintf("0x%.4x", $tagID);
+        my $prefix = $$tagTablePtr{TAG_PREFIX};
+        $tagInfo = {
+            Name => "${prefix}_$hex",
+            Description => MakeDescription($prefix, $hex),
+            Unknown => 1,
+            Writable => 0,  # can't write unknown tags
+            PrintConv => $printConv,
+        };
+        # add tag information to table
+        AddTagToTable($tagTablePtr, $tagID, $tagInfo);
+    } else {
+        undef $tagInfo;
+    }
+    return $tagInfo;
+}
+
+#------------------------------------------------------------------------------
+# Add new tag to table (must use this routine to add new tags to a table)
+# Inputs: 0) reference to tag table, 1) tag ID
+#         2) reference to tag information hash
+# Notes: - will not overwrite existing entry in table
+# - info need contain no entries when this routine is called
+sub AddTagToTable($$$)
+{
+    my ($tagTablePtr, $tagID, $tagInfo) = @_;
+
+    # define necessary entries in information hash
+    if ($$tagInfo{Groups}) {
+        # fill in default groups from table GROUPS
+        foreach (keys %{$$tagTablePtr{GROUPS}}) {
+            next if $tagInfo->{Groups}->{$_};
+            $tagInfo->{Groups}->{$_} = $tagTablePtr->{GROUPS}->{$_};
+        }
+    } else {
+        $$tagInfo{Groups} = $$tagTablePtr{GROUPS};
+    }
+    $$tagInfo{Flags} and ExpandFlags($tagInfo);
+    $$tagInfo{GotGroups} = 1,
+    $$tagInfo{Table} = $tagTablePtr;
+    $$tagInfo{TagID} = $tagID;
+
+    unless ($$tagInfo{Name}) {
+        my $prefix = $$tagTablePtr{TAG_PREFIX};
+        $$tagInfo{Name} = "${prefix}_$tagID";
+        # make description to prevent tagID from getting mangled by MakeDescription()
+        $$tagInfo{Description} = MakeDescription($prefix, $tagID);
+    }
+    # add tag to table, but never overwrite existing entries (could potentially happen
+    # if someone thinks there isn't any tagInfo because a condition wasn't satisfied)
+    $$tagTablePtr{$tagID} = $tagInfo unless defined $$tagTablePtr{$tagID};
+}
+
+#------------------------------------------------------------------------------
+# Handle simple extraction of new tag information
+# Inputs: 0) ExifTool object ref, 1) tag table reference, 2) tagID, 3) value,
+#         4-N) parameters hash: Index, DataPt, DataPos, Start, Size, Parent,
+#              TagInfo, ProcessProc
+# Returns: tag key or undef if tag not found
+sub HandleTag($$$$;%)
+{
+    my ($self, $tagTablePtr, $tag, $val, %parms) = @_;
+    my $verbose = $self->{OPTIONS}->{Verbose};
+    my $tagInfo = $parms{TagInfo} || $self->GetTagInfo($tagTablePtr, $tag);
+    my $dataPt = $parms{DataPt};
+    my $subdir;
+
+    if ($tagInfo) {
+        $subdir = $$tagInfo{SubDirectory}
+    } else {
+        return undef unless $verbose;
+    }
+    # read value if not done already (not necessary for subdir)
+    unless (defined $val or $subdir) {
+        my $start = $parms{Start} || 0;
+        my $size = $parms{Size} || 0;
+        # read from data in memory if possible
+        if ($dataPt and $start >= 0 and $start + $size <= length($$dataPt)) {
+            $val = substr($$dataPt, $start, $size);
+        } else {
+            my $name = $tagInfo ? $$tagInfo{Name} : "tag $tag";
+            $self->Warn("Error extracting value for $name");
+            return undef;
+        }
+    }
+    # do verbose print if necessary
+    if ($verbose) {
+        $parms{Value} = $val;
+        $parms{Table} = $tagTablePtr;
+        $self->VerboseInfo($tag, $tagInfo, %parms);
+    }
+    if ($tagInfo) {
+        if ($subdir) {
+            $dataPt or $dataPt = \$val;
+            # process subdirectory information
+            my %dirInfo = (
+                DirName  => $$tagInfo{Name},
+                DataPt   => $dataPt,
+                DataLen  => length $$dataPt,
+                DataPos  => $parms{DataPos},
+                DirStart => $parms{Start},
+                DirLen   => $parms{Size},
+                Parent   => $parms{Parent},
+            );
+            my $subTablePtr = GetTagTable($$subdir{TagTable}) || $tagTablePtr;
+            $self->ProcessDirectory(\%dirInfo, $subTablePtr, $$subdir{ProcessProc});
+        } else {
+            return $self->FoundTag($tagInfo, $val);
+        }
+    }
+    return undef;
+}
+
+#------------------------------------------------------------------------------
+# Add tag to hash of extracted information
+# Inputs: 0) reference to ExifTool object
+#         1) reference to tagInfo hash or tag name
+#         2) data value (or reference to require hash if composite)
+# Returns: tag key or undef if no value
+sub FoundTag($$$)
+{
+    local $_;
+    my ($self, $tagInfo, $value) = @_;
+    my $tag;
+
+    if (ref $tagInfo eq 'HASH') {
+        $tag = $$tagInfo{Name} or warn("No tag name\n"), return undef;
+    } else {
+        $tag = $tagInfo;
+        # look for tag in Extra
+        $tagInfo = $self->GetTagInfo(GetTagTable('Image::ExifTool::Extra'), $tag);
+        # make temporary hash if tag doesn't exist in Extra
+        # (not advised to do this since the tag won't show in list)
+        $tagInfo or $tagInfo = { Name => $tag, Groups => \%allGroupsExifTool };
+        $self->{OPTIONS}->{Verbose} and $self->VerboseInfo(undef, $tagInfo, Value => $value);
+    }
+    my $rawValueHash = $self->{VALUE};
+    if ($$tagInfo{RawConv}) {
+        my $conv = $$tagInfo{RawConv};
+        my $val = $value;   # must do this in case eval references $val
+        # initialize @val for use in Composite RawConv expressions
+        my @val;
+        if (ref $val eq 'HASH') {
+            foreach (keys %$val) { $val[$_] = $$rawValueHash{$$val{$_}}; }
+        }
+        if (ref $conv eq 'CODE') {
+            $value = &$conv($val, $self);
+        } else {
+            #### eval RawConv ($self, $val)
+            $value = eval $conv;
+            $@ and warn "RawConv: $@\n";
+        }
+        return undef unless defined $value;
+    }
+    # get tag priority
+    my $priority = $$tagInfo{Priority};
+    defined $priority or $priority = $tagInfo->{Table}->{PRIORITY};
+    # handle duplicate tag names
+    if (defined $rawValueHash->{$tag}) {
+        if ($$tagInfo{List} and $tagInfo eq $self->{TAG_INFO}->{$tag} and
+            not $self->{NO_LIST})
+        {
+            # use a list reference for multiple values
+            if (ref $rawValueHash->{$tag} ne 'ARRAY') {
+                $rawValueHash->{$tag} = [ $rawValueHash->{$tag} ];
+            }
+            push @{$rawValueHash->{$tag}}, $value;
+            return $tag;    # return without creating a new entry
+        }
+        # get next available tag key
+        my $nextTag = NextTagKey($rawValueHash, $tag);
+#
+# take tag with highest priority
+#
+        # promote existing 0-priority tag so it takes precedence over a new 0-tag
+        my $oldPriority = $self->{PRIORITY}->{$tag} || 1;
+        # set priority for this tag (default is 1)
+        $priority = 1 if not defined $priority or
+            # increase 0-priority tags if this is the priority directory
+            ($priority == 0 and $self->{DIR_NAME} and $self->{PRIORITY_DIR} and
+            $self->{DIR_NAME} eq $self->{PRIORITY_DIR});
+        if ($priority >= $oldPriority) {
+            $self->{MOVED_KEY} = $nextTag;  # used in BuildCompositeTags()
+            $self->{PRIORITY}->{$nextTag} = $self->{PRIORITY}->{$tag};
+            $rawValueHash->{$nextTag} = $rawValueHash->{$tag};
+            $self->{FILE_ORDER}->{$nextTag} = $self->{FILE_ORDER}->{$tag};
+            $self->{TAG_INFO}->{$nextTag} = $self->{TAG_INFO}->{$tag};
+            if ($self->{GROUP1}->{$tag}) {
+                $self->{GROUP1}->{$nextTag} = $self->{GROUP1}->{$tag};
+                delete $self->{GROUP1}->{$tag};
+            }
+        } else {
+            $tag = $nextTag;        # don't override the existing tag
+        }
+        $self->{PRIORITY}->{$tag} = $priority;
+    } elsif ($priority) {
+        # set tag priority (only if exists and non-zero)
+        $self->{PRIORITY}->{$tag} = $priority;
+    }
+
+    # save the raw value, file order, tagInfo ref and group1 name if necessary
+    $rawValueHash->{$tag} = $value;
+    $self->{FILE_ORDER}->{$tag} = ++$self->{NUM_FOUND};
+    $self->{TAG_INFO}->{$tag} = $tagInfo;
+    $self->{GROUP1}->{$tag} = $self->{SET_GROUP1} if $self->{SET_GROUP1};
+
+    return $tag;
+}
+
+#------------------------------------------------------------------------------
+# Get next available tag key
+# Inputs: 0) hash reference (keys are tag keys), 1) tag name
+# Returns: next available tag key
+sub NextTagKey($$)
+{
+    my ($info, $tag) = @_;
+    if (exists $$info{$tag}) {
+        my $name = $tag;
+        my $i;
+        for ($i=1; ; ++$i) {
+            $tag = "$name ($i)";
+            last unless exists $$info{$tag};
+        }
+    }
+    return $tag;
+}
+
+#------------------------------------------------------------------------------
+# Make current directory the priority directory if not set already
+# Inputs: 0) reference to ExifTool object
+sub SetPriorityDir($)
+{
+    my $self = shift;
+    $self->{PRIORITY_DIR} = $self->{DIR_NAME} unless $self->{PRIORITY_DIR};
+}
+
+#------------------------------------------------------------------------------
+# Set family 1 group name specific to this tag instance
+# Inputs: 0) reference to ExifTool object, 1) tag key, 2) group name
+sub SetGroup1($$$)
+{
+    my ($self, $tagKey, $extra) = @_;
+    $self->{GROUP1}->{$tagKey} = $extra;
+}
+
+#------------------------------------------------------------------------------
+# Set ID's for all tags in specified table
+# Inputs: 0) tag table reference
+sub GenerateTagIDs($)
+{
+    my $table = shift;
+
+    unless ($$table{DID_TAG_ID}) {
+        $$table{DID_TAG_ID} = 1;    # set flag so we won't do this table again
+        my ($tagID, $tagInfo);
+        foreach $tagID (keys %$table) {
+            next if $specialTags{$tagID};
+            # define tag ID in each element of conditional array
+            my @infoArray = GetTagInfoList($table,$tagID);
+            foreach $tagInfo (@infoArray) {
+                # define tag ID's in info hash
+                $$tagInfo{TagID} = $tagID;
+            }
+        }
+    }
+}
+
+#------------------------------------------------------------------------------
+# Generate TagID's for all loaded tables
+# Inputs: None
+# Notes: Causes subsequently loaded tables to automatically generate TagID's too
+sub GenerateAllTagIDs()
+{
+    unless ($didTagID) {
+        my $tableName;
+        foreach $tableName (keys %allTables) {
+            # generate tag ID's for all tags in this table
+            GenerateTagIDs($allTables{$tableName});
+        }
+        $didTagID = 1;
+    }
+}
+
+#------------------------------------------------------------------------------
+# Delete specified tag
+# Inputs: 0) reference to ExifTool object
+#         1) tag key
+sub DeleteTag($$)
+{
+    my ($self, $tag) = @_;
+    delete $self->{VALUE}->{$tag};
+    delete $self->{FILE_ORDER}->{$tag};
+    delete $self->{TAG_INFO}->{$tag};
+    delete $self->{GROUP1}->{$tag};
+}
+
+#------------------------------------------------------------------------------
+# Set the FileType and MIMEType tags
+# Inputs: 0) ExifTool object reference
+#         1) Optional file type (uses FILE_TYPE if not specified)
+sub SetFileType($;$)
+{
+    my $self = shift;
+    my $baseType = $self->{FILE_TYPE};
+    my $fileType = shift || $baseType;
+    my $mimeType = $mimeType{$fileType};
+    # use base file type if necessary (except if 'TIFF', which is a special case)
+    $mimeType = $mimeType{$baseType} unless $mimeType or $baseType eq 'TIFF';
+    $self->FoundTag('FileType', $fileType);
+    $self->FoundTag('MIMEType', $mimeType || 'application/unknown');
+}
+
+#------------------------------------------------------------------------------
+# Modify the value of the MIMEType tag
+# Inputs: 0) ExifTool object reference, 1) file or MIME type
+# Notes: combines existing type with new type: ie) a/b + c/d => c/b-d
+sub ModifyMimeType($;$)
+{
+    my ($self, $mime) = @_;
+    $mime =~ m{/} or $mime = $mimeType{$mime} or return;
+    my $old = $self->{VALUE}->{MIMEType};
+    if (defined $old) {
+        my ($a, $b) = split '/', $old;
+        my ($c, $d) = split '/', $mime;
+        $d =~ s/^x-//;
+        $self->{VALUE}->{MIMEType} = "$c/$b-$d";
+        $self->VPrint(0, "  Modified MIMEType = $c/$b-$d\n");
+    } else {
+        $self->FoundTag('MIMEType', $mime);
+    }
+}
+
+#------------------------------------------------------------------------------
+# Print verbose output
+# Inputs: 0) ExifTool ref, 1) verbose level (prints if level > this), 2-N) print args
+sub VPrint($$@)
+{
+    my $self = shift;
+    my $level = shift;
+    if ($self->{OPTIONS}->{Verbose} and $self->{OPTIONS}->{Verbose} > $level) {
+        my $out = $self->{OPTIONS}->{TextOut};
+        print $out @_;
+    }
+}
+
+#------------------------------------------------------------------------------
+# Verbose dump
+# Inputs: 0) ExifTool ref, 1) data ref, 2-N) HexDump options
+sub VerboseDump($$;%)
+{
+    my $self = shift;
+    my $dataPt = shift;
+    if ($self->{OPTIONS}->{Verbose} and $self->{OPTIONS}->{Verbose} > 2) {
+        HexDump($dataPt, undef,
+            Out => $self->{OPTIONS}->{TextOut},
+            MaxLen => $self->{OPTIONS}->{Verbose} < 4 ? 96 : undef,
+            @_
+        );
+    }
+}
+
+#------------------------------------------------------------------------------
+# Extract binary data from file
+# 0) ExifTool object reference, 1) offset, 2) length, 3) tag name if conditional
+# Returns: binary data, or undef on error
+# Notes: Returns "Binary data #### bytes" instead of data unless tag is
+#        specifically requested or the Binary option is set
+sub ExtractBinary($$$;$)
+{
+    my ($self, $offset, $length, $tag) = @_;
+
+    if ($tag and not $self->{OPTIONS}->{Binary} and
+        not $self->{REQ_TAG_LOOKUP}->{lc($tag)})
+    {
+        return "Binary data $length bytes";
+    }
+    my $buff;
+    unless ($self->{RAF}->Seek($offset,0)
+        and $self->{RAF}->Read($buff, $length) == $length)
+    {
+        $tag or $tag = 'binary data';
+        $self->Warn("Error reading $tag from file");
+        return undef;
+    }
+    return $buff;
+}
+
+#------------------------------------------------------------------------------
+# Process binary data
+# Inputs: 0) ExifTool object reference, 1) directory information reference
+#         2) tag table reference
+# Returns: 1 on success
+sub ProcessBinaryData($$$)
+{
+    my ($self, $dirInfo, $tagTablePtr) = @_;
+    my $dataPt = $$dirInfo{DataPt};
+    my $offset = $$dirInfo{DirStart} || 0;
+    my $size = $$dirInfo{DirLen} || (length($$dataPt) - $offset);
+    my $base = $$dirInfo{Base} || 0;
+    my $verbose = $self->{OPTIONS}->{Verbose};
+    my $unknown = $self->{OPTIONS}->{Unknown};
+    my $dataPos;
+
+    # get default format ('int8u' unless specified)
+    my $defaultFormat = $$tagTablePtr{FORMAT} || 'int8u';
+    my $increment = $formatSize{$defaultFormat};
+    unless ($increment) {
+        warn "Unknown format $defaultFormat\n";
+        $defaultFormat = 'int8u';
+        $increment = $formatSize{$defaultFormat};
+    }
+    # prepare list of tag numbers to extract
+    my @tags;
+    if ($unknown > 1 and defined $$tagTablePtr{FIRST_ENTRY}) {
+        # scan through entire binary table
+        @tags = ($$tagTablePtr{FIRST_ENTRY}..(int($size/$increment) - 1));
+    } elsif ($$dirInfo{DataMember}) {
+        @tags = @{$$dirInfo{DataMember}};
+        $verbose = 0;   # no verbose output of extracted values when writing
+    } else {
+        # extract known tags in numerical order
+        @tags = sort { $a <=> $b } TagTableKeys($tagTablePtr);
+    }
+    if ($verbose) {
+        $self->VerboseDir('BinaryData', undef, $size);
+        $dataPos = $$dirInfo{DataPos} || 0;
+    }
+    my $index;
+    my $nextIndex = 0;
+    my %val;
+    foreach $index (@tags) {
+        my $tagInfo;
+        if ($$tagTablePtr{$index}) {
+            $tagInfo = $self->GetTagInfo($tagTablePtr, $index) or next;
+            next if $$tagInfo{Unknown} and
+                   ($$tagInfo{Unknown} > $unknown or $index < $nextIndex);
+        } else {
+            # don't generate unknown tags in binary tables unless Unknown > 1
+            next unless $unknown > 1;
+            next if $index < $nextIndex;    # skip if data already used
+            $tagInfo = $self->GetTagInfo($tagTablePtr, $index) or next;
+            $$tagInfo{Unknown} = 2;    # set unknown to 2 for binary unknowns
+        }
+        my $count = 1;
+        my $format = $$tagInfo{Format};
+        my $entry = $index * $increment;        # relative offset of this entry
+        if ($format) {
+            if ($format =~ /(.*)\[(.*)\]/) {
+                $format = $1;
+                $count = $2;
+                # evaluate count to allow count to be based on previous values
+                #### eval Format (%val, $size)
+                $count = eval $count;
+                $@ and warn("Format $$tagInfo{Name}: $@"), next;
+                next if $count < 0;
+            } elsif ($format eq 'string') {
+                # allow string with no specified count to run to end of block
+                $count = ($size > $entry) ? $size - $entry : 0;
+            }
+        } else {
+            $format = $defaultFormat;
+        }
+        if ($unknown > 1) {
+            # calculate next valid index for unknown tag
+            my $ni = $index + ($formatSize{$format} * $count) / $increment;
+            $nextIndex = $ni unless $nextIndex > $ni;
+        }
+        my $val = ReadValue($dataPt, $entry+$offset, $format, $count, $size-$entry);
+        next unless defined $val;
+        if ($verbose) {
+            my $len = $count * ($formatSize{$format} || 1);
+            $len > $size - $entry and $len = $size - $entry;
+            $self->VerboseInfo($index, $tagInfo,
+                Table  => $tagTablePtr,
+                Value  => $val,
+                DataPt => $dataPt,
+                Size   => $len,
+                Start  => $entry+$offset,
+                Addr   => $entry+$offset+$base+$dataPos,
+                Format => $format,
+                Count  => $count,
+            );
+        }
+        $val += $base + $$self{BASE} if $$tagInfo{IsOffset};
+        $val{$index} = $val;
+        $self->FoundTag($tagInfo,$val);
+    }
+    return 1;
+}
+
+#..............................................................................
+# Load .ExifTool_config file from user's home directory (unless 'noConfig' set)
+unless ($Image::ExifTool::noConfig) {
+    my $config = '.ExifTool_config';
+    # get our home directory (HOMEDRIVE and HOMEPATH are used in Windows cmd shell)
+    my $home = $ENV{EXIFTOOL_HOME} || $ENV{HOME} ||
+               ($ENV{HOMEDRIVE} || '') . ($ENV{HOMEPATH} || '') || '.';
+    # look for the config file in 1) the home directory, 2) the program dir
+    my $file = "$home/$config";
+    -r $file or $file = ($0 =~ /(.*[\\\/])/ ? $1 : './') . $config;
+    if (-r $file) {
+        eval "require '$file'"; # load the config file
+        # print warning (minus "Compilation failed" part)
+        $@ and $_=$@, s/Compilation failed.*//s, warn $_;
+    }
+}
+
+#------------------------------------------------------------------------------
+1;  # end
Index: /gsdl/trunk/perllib/cpan/Image/ExifTool.pod
===================================================================
--- /gsdl/trunk/perllib/cpan/Image/ExifTool.pod	(revision 16842)
+++ /gsdl/trunk/perllib/cpan/Image/ExifTool.pod	(revision 16842)
@@ -0,0 +1,1607 @@
+#------------------------------------------------------------------------------
+# File:         ExifTool.pod - Documentation for File::ExifTool
+#
+# Description:  Read and write meta information
+#
+# URL:          http://owl.phy.queensu.ca/~phil/exiftool/
+#
+# Legal:        Copyright (c) 2003-2007, Phil Harvey (phil at owl.phy.queensu.ca)
+#               This library is free software; you can redistribute it and/or
+#               modify it under the same terms as Perl itself.
+#------------------------------------------------------------------------------
+
+=head1 NAME
+
+Image::ExifTool - Read and write meta information
+
+=head1 SYNOPSIS
+
+  use Image::ExifTool qw(:Public);
+
+  # ---- Simple procedural usage ----
+
+  # Get hash of meta information tag names/values from an image
+  $info = ImageInfo('a.jpg');
+
+  # ---- Object-oriented usage ----
+
+  # Create a new Image::ExifTool object
+  $exifTool = new Image::ExifTool;
+
+  # Extract meta information from an image
+  $exifTool->ExtractInfo($file, \%options);
+
+  # Get list of tags in the order they were found in the file
+  @tagList = $exifTool->GetFoundTags('File');
+
+  # Get the value of a specified tag
+  $value = $exifTool->GetValue($tag, $type);
+
+  # Get a tag description
+  $description = $exifTool->GetDescription($tag);
+
+  # Get the group name associated with this tag
+  $group = $exifTool->GetGroup($tag, $family);
+
+  # Set a new value for a tag
+  $exifTool->SetNewValue($tag, $newValue);
+
+  # Write new meta information to a file
+  $success = $exifTool->WriteInfo($srcfile, $dstfile);
+
+  # ...plus a host of other useful methods...
+
+=head1 DESCRIPTION
+
+ExifTool provides an extensible set of perl modules to read and write meta
+information in image, audio and video files, including the maker note
+information of many digital cameras by various manufacturers such as Canon,
+Casio, FujiFilm, HP, JVC/Victor, Kodak, Leaf, Minolta/Konica-Minolta, Nikon,
+Olympus/Epson, Panasonic/Leica, Pentax/Asahi, Ricoh, Sanyo, Sigma/Foveon and
+Sony.
+
+Below is a list of file types and meta information formats currently
+supported by ExifTool (r = read, w = write, c = create):
+
+                File Types                 |    Meta Information
+  ---------------------------------------  |  --------------------
+  ACR   r       JP2   r/w     PPT   r      |  EXIF           r/w/c
+  AI    r       JPEG  r/w     PS    r/w    |  GPS            r/w/c
+  AIFF  r       K25   r       PSD   r/w    |  IPTC           r/w/c
+  APE   r       M4A   r       QTIF  r      |  XMP            r/w/c
+  ARW   r       MEF   r/w     RA    r      |  MakerNotes     r/w/c
+  ASF   r       MIE   r/w/c   RAF   r      |  Photoshop IRB  r/w/c
+  AVI   r       MIFF  r       RAM   r      |  ICC Profile    r/w/c
+  BMP   r       MNG   r/w     RAW   r/w    |  MIE            r/w/c
+  BTF   r       MOS   r/w     RIFF  r      |  JFIF           r/w/c
+  CR2   r/w     MOV   r       RM    r      |  Ducky APP12    r/w/c
+  CRW   r/w     MP3   r       SR2   r      |  CIFF           r/w
+  CS1   r/w     MP4   r       SRF   r      |  AFCP           r/w
+  DCM   r       MPC   r       SWF   r      |  DICOM          r
+  DCR   r       MPG   r       THM   r/w    |  Flash          r
+  DNG   r/w     MRW   r/w     TIFF  r/w    |  FlashPix       r
+  DOC   r       NEF   r/w     VRD   r/w/c  |  GeoTIFF        r
+  EPS   r/w     OGG   r       WAV   r      |  PrintIM        r
+  ERF   r/w     ORF   r/w     WDP   r/w    |  ID3            r
+  FLAC  r       PBM   r/w     WMA   r      |  Kodak Meta     r
+  FLV   r       PDF   r       WMV   r      |  Ricoh RMETA    r
+  FPX   r       PEF   r/w     X3F   r      |  Picture Info   r
+  GIF   r/w     PGM   r/w     XLS   r      |  Adobe APP14    r
+  HTML  r       PICT  r       XMP   r/w/c  |  APE            r
+  ICC   r/w/c   PNG   r/w                  |  Vorbis         r
+  JNG   r/w     PPM   r/w                  |  (and more)
+
+=head1 CONFIGURATION
+
+User-defined tags can be added via the ExifTool configuration file.  See
+"ExifTool_config" in the ExifTool distribution for more details.  If
+necessary, the configuration feature can be disabled by setting the ExifTool
+C<noConfig> flag before loading Image::ExifTool.  For example:
+
+    BEGIN { $Image::ExifTool::noConfig = 1 }
+    use Image::ExifTool;
+
+=head1 METHODS
+
+=head2 new
+
+Creates a new ExifTool object.
+
+    $exifTool = new Image::ExifTool;
+
+Note that ExifTool uses AUTOLOAD to load non-member methods, so any class
+using Image::ExifTool as a base class must define an AUTOLOAD which calls
+Image::ExifTool::DoAutoLoad().  ie)
+
+    sub AUTOLOAD
+    {
+        Image::ExifTool::DoAutoLoad($AUTOLOAD, @_);
+    }
+
+=head2 ImageInfo
+
+Obtain meta information from image.  This is the one step function for
+obtaining meta information from an image.  Internally, L</ImageInfo> calls
+L</ExtractInfo> to extract the information, L</GetInfo> to generate the
+information hash, and L</GetTagList> for the returned tag list.
+
+    # Return meta information for 2 tags only (procedural)
+    $info = ImageInfo($filename, $tag1, $tag2);
+
+    # Return information about an open image file (object-oriented)
+    $info = $exifTool->ImageInfo(\*FILE);
+
+    # Return information from image data in memory for specified tags
+    %options = (PrintConv => 0);
+    @tagList = qw(filename imagesize xmp:creator exif:* -ifd1:*);
+    $info = ImageInfo(\$imageData, \@tagList, \%options);
+
+    # Extract information from an embedded thumbnail image
+    $info = ImageInfo('image.jpg', 'thumbnailimage');
+    $thumbInfo = ImageInfo($$info{ThumbnailImage});
+
+=over 4
+
+=item Inputs:
+
+L</ImageInfo> is very flexible about the input arguments, and interprets
+them based on their type.  It may be called with one or more arguments.
+The one required argument is either a SCALAR (the image file name), a file
+reference (a reference to the image file) or a SCALAR reference (a
+reference to the image in memory).  Other arguments are optional.  The
+order of the arguments is not significant, except that the first SCALAR is
+taken to be the file name unless a file reference or scalar reference comes
+earlier in the argument list.
+
+Below is an explanation of how the L</ImageInfo> function arguments are
+interpreted:
+
+=over 4
+
+=item ExifTool ref
+
+L</ImageInfo> may be called with an ExifTool object if desired.  The
+advantage of using the object-oriented form is that the options may be set
+before calling L</ImageInfo>, and the object may be used afterward to access
+member functions.  Must be the first argument if used.
+
+=item SCALAR
+
+The first scalar argument is taken to be the file name unless an earlier
+argument specified the image data via a file reference (file ref) or data
+reference (SCALAR ref).  The remaining scalar arguments are names of tags
+for requested information.  If no tags are specified, all possible
+information is extracted.
+
+Tag names are case-insensitive and may be prefixed by an optional group name
+followed by a colon.  The group name may begin with a family number
+(ie. '1IPTC:Keywords'), to restrict matches to a specific family.  A tag
+name of '*' may be used, thus allowing 'GROUP:*' to represent all tags in a
+specific group, or a group name of '*' may be used, in which case all
+available instances of the specified tag are returned regardless of the
+L</Duplicates> setting (ie. '*:WhiteBalance').  And finally, a leading '-'
+indicates tags to be excluded (ie. '-IFD1:*').
+
+Note that keys in the returned information hash and elements of the returned
+tag list are not necessarily the same as these tag names -- group names are
+removed, the case may be changed, and an instance number may be added.  For
+this reason it is best to use either the keys of the returned hash or the
+elements of the tag array when accessing the tag values.
+
+See L<Image::ExifTool::TagNames|Image::ExifTool::TagNames> for a complete
+list of ExifTool tag names.
+
+=item File ref
+
+A reference to an open image file.  If you use this method (or a SCALAR
+reference) to access information in an image, the FileName and Directory
+tags will not be returned.  (Also, the FileSize and FileModifyDate tags will
+not be returned unless it is a plain file.)  Image processing begins at the
+current file position, and on return the file position is unspecified.
+
+=item SCALAR ref
+
+A reference to image data in memory.
+
+=item ARRAY ref
+
+Reference to a list of tag names.  On entry, any elements in the list are
+added to the list of requested tags.  Tags with names beginning with '-' are
+excluded.  On return, this list is updated to contain an ordered list of tag
+keys for all extracted tags.
+
+=item HASH ref
+
+Reference to a hash containing the options settings.  See L</Options>
+documentation below for a list of available options.  Options specified
+as arguments to L</ImageInfo> take precedence over L</Options> settings.
+
+=back
+
+=item Return Values:
+
+L</ImageInfo> returns a reference to a hash of tag key/value pairs.  The tag
+keys are identifiers, which are similar to the tag names but may have an
+embedded instance number if multiple tags with the same name were extracted
+from the image.  Many of the ExifTool functions require a tag key as an
+argument. Use L</GetTagName> to get the tag name for a given tag key.  Note
+that the case of the tag names may not be the same as requested.  Here is a
+simple example to print out the information returned by L</ImageInfo>:
+
+    foreach (sort keys %$info) {
+        print "$_ => $$info{$_}\n";
+    }
+
+Values of the returned hash are usually simple scalars, but a scalar
+reference is used to indicate binary data and an array reference may be used
+to indicate a list.  Lists of values are joined by commas into a single
+string if and only if the PrintConv option is enabled and the List option is
+disabled (which are the defaults).  Note that binary values are not
+necessarily extracted unless specifically requested or the Binary option is
+set.  If not extracted the value is a reference to a string of the form
+"Binary data ##### bytes".
+
+The code below gives an example of how to handle these return values, as
+well as illustrating the use of other ExifTool functions:
+
+    use Image::ExifTool;
+    my $exifTool = new Image::ExifTool;
+    $exifTool->Options(Unknown => 1);
+    my $info = $exifTool->ImageInfo('a.jpg');
+    my $group = '';
+    my $tag;
+    foreach $tag ($exifTool->GetFoundTags('Group0')) {
+        if ($group ne $exifTool->GetGroup($tag)) {
+            $group = $exifTool->GetGroup($tag);
+            print "---- $group ----\n";
+        }
+        my $val = $info->{$tag};
+        if (ref $val eq 'SCALAR') {
+            if ($$val =~ /^Binary data/) {
+                $val = "($$val)";
+            } else {
+                my $len = length($$val);
+                $val = "(Binary data $len bytes)";
+            }
+        }
+        printf("%-32s : %s\n", $exifTool->GetDescription($tag), $val);
+    }
+
+As well as tags representing information extracted from the image,
+the following tags generated by ExifTool may be returned:
+
+    ExifToolVersion - The ExifTool version number.
+
+    Error - An error message if the image could not be read.
+
+    Warning - A warning message if problems were encountered
+              while extracting information from the image.
+
+=back
+
+=head2 Options
+
+Get/set ExifTool options.  This function can be called to set the default
+options for an ExifTool object.  Options set this way are in effect for
+all function calls but may be overridden by options passed as arguments
+to a specific function.
+
+    # Exclude the 'OwnerName' tag from returned information
+    $exifTool->Options(Exclude => 'OwnerName');
+
+    # Only get information in EXIF or MakerNotes groups
+    $exifTool->Options(Group0 => ['EXIF', 'MakerNotes']);
+
+    # Ignore information from IFD1
+    $exifTool->Options(Group1 => '-IFD1');
+
+    # Sort by groups in family 2, and extract unknown tags
+    $exifTool->Options(Sort => 'Group2', Unknown => 1);
+
+    # Do not extract duplicate tag names
+    $oldSetting = $exifTool->Options(Duplicates => 0);
+
+    # Get current setting
+    $isVerbose = $exifTool->Options('Verbose');
+
+=over 4
+
+=item Inputs:
+
+0) ExifTool object reference.
+
+1) Option parameter name.
+
+2) [optional] Option parameter value (may be undef to clear option).
+
+3-N) [optional] Additional parameter/value pairs.
+
+=item Option Parameters:
+
+=over 4
+
+=item Binary
+
+Flag to extract the value data for all binary tags.  Tag values representing
+large binary data blocks (ie. ThumbnailImage) are not necessarily extracted
+unless this option is set or the tag is specifically requested by name.
+Default is 0.
+
+=item ByteOrder
+
+The byte order for newly created EXIF segments when writing.  Note that if
+EXIF information already exists, the existing order is maintained.  Valid
+values are 'MM', 'II' and undef.  If ByteOrder is not defined (the default),
+then the maker note byte order is used (if they are being copied), otherwise
+big-endian ('MM') order is assumed.  This can also be set via the
+ExifByteOrder tag, but the ByteOrder option takes precedence if both are
+set.
+
+=item Charset
+
+Character set for converting special characters.  Valid values are:
+
+  UTF8  - UTF-8 characters (the default)
+  Latin - Windows Latin1 (cp1252)
+
+Note that this option affects some types of information when reading/writing
+the file and other types when getting/setting tag values, so it must be
+defined for both types of access.
+
+=item Compact
+
+Flag to write compact output.  Default is 0.  The XMP specification suggests
+that the data be padded with blanks to allow in-place editing.  By setting
+this flag, 2kB is saved for files with XMP data.
+
+=item Composite
+
+Flag to calculate Composite tags automatically.  Default is 1.
+
+=item Compress
+
+Flag to write new values in compressed format if possible.  Has no effect
+unless Compress::Zlib is installed.  Default is 0.
+
+=item CoordFormat
+
+Format for printing GPS coordinates.  This is a printf format string with
+specifiers for degrees, minutes and seconds in that order, however minutes
+and seconds may be omitted.  For example, the following table gives the
+output for the same coordinate using various formats:
+
+        CoordFormat          Example Output
+    -------------------    ------------------
+    q{%d deg %d' %.2f"}    54 deg 59' 22.80"   (the default)
+    q{%d deg %.4f min}     54 deg 59.3800 min
+    q{%.6f degrees}        54.989667 degrees
+
+=item DateFormat
+
+Format for printing EXIF date/time.  See C<strftime> in the L<POSIX> package
+for details about the format string.  The default format is equivalent to
+"%Y:%m:%d %H:%M:%S".  If date can not be converted, value is left unchanged
+unless the StrictDate option is set.
+
+=item Duplicates
+
+Flag to preserve values of duplicate tags (instead of overwriting existing
+value).  Default is 1.
+
+=item Exclude
+
+Exclude specified tags from tags extracted from an image.  The option value
+is either a tag name or reference to a list of tag names to exclude.  The
+case of tag names is not significant.  This option is ignored for
+specifically requested tags.  Tags may also be excluded by preceeding their
+name with a '-' in the arguments to L</ImageInfo>.
+
+=item FastScan
+
+Flag to increase speed of extracting information from JPEG images.  With
+this option set, ExifTool will not scan to the end of a JPEG image to check
+for an AFCP, CanonVRD, FotoStation, PhotoMechanic, MIE or PreviewImage
+trailer.  Also, when combined with the ScanForXMP option, prevents scanning
+for XMP in recognized file types.  Default 0.
+
+=item FixBase
+
+Fix maker notes base offset.  A common problem with image editing software
+is that offsets in the maker notes are not adjusted properly when the file
+is modified.  This may cause the wrong values to be extracted for some maker
+note entries when reading the edited file.  FixBase specifies an integer
+value to be added to the maker notes base offset.  It may also be set to the
+empty string ('') for ExifTool will take its best guess at the correct base,
+or undef (the default) for no base adjustment.
+
+=item Group#
+
+Extract tags only for specified groups in family # (Group0 assumed if #
+not given).  The option value may be a single group name or a reference
+to a list of groups.  Case is significant in group names.  Specify a
+group to be excluded by preceding group name with a '-'.  See
+L</GetAllGroups> for a list of available groups.
+
+=item HtmlDump
+
+Dump information in hex to dynamic HTML web page.  The value may be 0-3 for
+increasingly larger limits on the maximum block size.  Default is 0.  Output
+goes to the file specified by the TextOut option (\*STDOUT by default).
+
+=item HtmlDumpBase
+
+Specifies base for HTML dump offsets.  If not defined, the EXIF/TIFF base
+offset is used.  Set to 0 for absolute offsets.  Default is undef.
+
+=item IgnoreMinorErrors
+
+Flag to ignore minor errors.  Causes minor errors to be downgraded to
+warnings, and minor warnings to be ignored.  This option is provided mainly
+to allow writing of files when minor errors occur, but also allows thumbnail
+and preview images to be extracted even if they don't have a recognizable
+header.  Minor errors/warnings are denoted by '[minor]' at the start of the
+message.
+
+=item List
+
+Flag to extract lists of PrintConv values into arrays instead of
+concatenating them into comma-separated strings.  Default is 0.
+
+=item MakerNotes
+
+Option to extract MakerNotes and other writable subdirectories (such as
+PrintIM) as a data block.  Normally when the MakerNotes are extracted they
+are rebuilt to include data outside the boundaries of the original maker
+note data block, but a value of 2 disables this feature.  Possible values
+are:
+
+  0 - Do not extract writable subdirectories [default]
+  1 - Extract and rebuild maker notes into self-contained block
+  2 - Extract without rebuilding maker notes
+
+=item MissingTagValue
+
+Value for missing tags in expressions evaluated by L</SetNewValuesFromFile>.
+If not set, a minor error is issued for missing values, or the value is set
+to '' if L</IgnoreMinorErrors> is set.  Default is undef.
+
+=item PrintConv
+
+Flag to enable automatic print conversion.  Also enables inverse
+print conversion for writing.  Default is 1.
+
+=item ScanForXMP
+
+Flag for scan all files (even unrecognized formats) for XMP information
+unless XMP was already found in the file.  When combined with the FastScan
+option, only unrecognized file types are scanned for XMP.  Default is 0.
+
+=item Sort
+
+Specifies order to sort tags in returned list:
+
+  Alpha  - Sort alphabetically
+  File   - Sort in order that tags were found in the file
+  Group# - Sort by tag group, where # is the group family
+           number.  If # is not specified, Group0 is assumed.
+           See GetGroup for a list of groups.
+  Input  - Sort in same order as input tag arguments (default)
+
+=item StrictDate
+
+Flag to return undefined value for any date which can't be converted when
+the DateFormat option is used.  Default is 0.
+
+=item TextOut
+
+Output file reference for Verbose and HtmlDump options.  Default is
+\*STDOUT.
+
+=item Unknown
+
+Flag to get the values of unknown tags.  If set to 1, unknown tags are
+extracted from EXIF (or other tagged-format) directories.  If set to 2,
+unknown tags are also extracted from binary data blocks.  Default is 0.
+
+=item Verbose
+
+Print verbose messages to file specified by TextOut option.  Value may be
+from 0 to 5 for increasingly verbose messages.  Default is 0.  With the
+verbose option set, messages are printed to the console as the file is
+parsed.  Level 1 prints the tag names and raw values.  Level 2 adds more
+details about the tags.  Level 3 adds a hex dump of the tag data, but with
+limits on the number of bytes dumped.  Levels 4 and 5 remove the dump limit
+on tag values and JPEG segment data respectively.
+
+=back
+
+=item Return Values:
+
+The original value of the last specified parameter.
+
+=back
+
+=head2 ClearOptions
+
+Reset all options to their default values.
+
+    $exifTool->ClearOptions();
+
+=over 4
+
+=item Inputs:
+
+0) ExifTool object reference
+
+=item Return Values:
+
+(none)
+
+=back
+
+=head2 ExtractInfo
+
+Extract all meta information from an image.
+
+    $success = $exifTool->ExtractInfo('image.jpg', \%options);
+
+=over 4
+
+=item Inputs:
+
+L</ExtractInfo> takes exactly the same arguments as L</ImageInfo>.  The only
+difference is that a list of tag keys is not returned if an ARRAY reference
+is given.  The following options are effective in the call to
+L</ExtractInfo>:
+
+Binary, Composite, DateFormat, Unknown and Verbose.
+
+=item Return Value:
+
+1 if image was valid, 0 otherwise (and 'Error' tag set).
+
+=back
+
+=head2 GetInfo
+
+L</GetInfo> is called to return meta information after it has been extracted
+from the image by a previous call to L</ExtractInfo> or L</ImageInfo>. This
+function may be called repeatedly after a single call to L</ExtractInfo> or
+L</ImageInfo>.
+
+    # Get image width and hieght only
+    $info = $exifTool->GetInfo('ImageWidth', 'ImageHeight');
+
+    # Get information for all tags in list (list updated with tags found)
+    $info = $exifTool->GetInfo(\@ioTagList);
+
+    # Get all information in Author or Location groups
+    $info = $exifTool->GetInfo({Group2 => ['Author', 'Location']});
+
+=over 4
+
+=item Inputs:
+
+Inputs are the same as L</ExtractInfo> and L</ImageInfo> except that an
+image can not be specified.  Options in effect are:
+
+Duplicates, Exclude, Group#, PrintConv (and Sort if tag list reference is
+given).
+
+=item Return Value:
+
+Reference to information hash, the same as with L</ImageInfo>.
+
+=back
+
+=head2 WriteInfo
+
+Write meta information to a file.  The specified source file is rewritten to
+the same-type destination file with new information as specified by previous
+calls to L</SetNewValue>.  The necessary segments and/or directories are
+created in the destination file as required to store the specified
+information.  May be called repeatedly to write the same information to
+additional files without the need to call L</SetNewValue> again.
+
+    # add information to a source file, writing output to new file
+    $exifTool->WriteInfo($srcfile, $dstfile);
+
+    # create XMP data file from scratch
+    $exifTool->WriteInfo(undef, $dstfile, 'XMP');
+
+    # edit file in place (you do have backups, right?)
+    $exifTool->WriteInfo($srcfile);
+
+=over 4
+
+=item Inputs:
+
+0) ExifTool object reference
+
+1) Source file name, file reference, scalar reference, or undefined to
+create a file from scratch
+
+2) [optional] Destination file name, file or scalar reference
+
+3) [optional] Destination file type
+
+=item Return Value:
+
+1 if file was written OK, 2 if file was written but no changes made, 0 on
+file write error.
+
+If an error code is returned, an Error tag is set and GetValue('Error') can
+be called to obtain the error description.  A Warning tag may be set even if
+this routine is successful.
+
+    $errorMessage = $exifTool->GetValue('Error');
+    $warningMessage = $exifTool->GetValue('Warning');
+
+=item Notes:
+
+The source file name may be undefined to create a file from scratch
+(currently only XMP, ICC and MIE files can be created in this way).  If
+undefined, the destination file type is required unless the type can be
+determined from the destination file name.
+
+The destination file name may be undefined to edit a file in place (make
+sure you have backups!).  If a destination file name is given, the specified
+file must not exist because existing destination files will not be
+overwritten.
+
+The destination file type is only used if the source file is undefined.
+
+=back
+
+=head2 CombineInfo
+
+Combine information from more than one information hash into a single hash.
+
+    $info = $exifTool->CombineInfo($info1, $info2, $info3);
+
+=over 4
+
+=item Inputs:
+
+0) ExifTool object reference
+
+1-N) Information hash references
+
+=back
+
+If the Duplicates option is disabled and duplicate tags exist, the order of
+the hashes is significant.  In this case, the value used is the first value
+found as the hashes are scanned in order of input.  The Duplicates option
+is the only option that is in effect for this function.
+
+=head2 GetTagList
+
+Get a sorted list of tags from the specified information hash or tag list.
+
+    @tags = $exifTool->GetTagList($info, 'Group0');
+
+=over 4
+
+=item Inputs:
+
+0) ExifTool object reference,
+
+1) [optional] Information hash reference or tag list reference,
+
+2) [optional] Sort order ('File', 'Input', 'Alpha' or 'Group#').
+
+If the information hash or tag list reference is not provided, then the list
+of found tags from the last call to L</ImageInfo>, L</ExtractInfo> or
+L</GetInfo> is used instead, and the result is the same as if
+L</GetFoundTags> was called.  If sort order is not specified, the sort order
+is taken from the current options settings.
+
+=item Return Values:
+
+A list of tags in the specified order.
+
+=back
+
+=head2 GetFoundTags
+
+Get list of found tags in specified sort order.  The found tags are the tags
+for the information obtained from the most recent call to L</ImageInfo>,
+L</ExtractInfo> or L</GetInfo> for this object.
+
+    @tags = $exifTool->GetFoundTags('File');
+
+=over 4
+
+=item Inputs:
+
+0) ExifTool object reference
+
+1) [optional] Sort order ('File', 'Input', 'Alpha' or 'Group#')
+
+If sort order is not specified, the sort order from the ExifTool options is
+used.
+
+=item Return Values:
+
+A list of tags in the specified order.
+
+=back
+
+=head2 GetRequestedTags
+
+Get list of requested tags.  These are the tags that were specified in the
+arguments of the most recent call to L</ImageInfo>, L</ExtractInfo> or
+L</GetInfo>, including tags specified via a tag list reference. Shortcut
+tags are expanded in the list.
+
+    @tags = $exifTool->GetRequestedTags();
+
+=over 4
+
+=item Inputs:
+
+(none)
+
+=item Return Values:
+
+List of requested tags in the same order that they were specified.
+Note that this list will be empty if tags were not specifically requested
+(ie. If extracting all tags).
+
+=back
+
+=head2 GetValue
+
+Get the value of a specified tag.  The returned value is either the
+human-readable (PrintConv) value, the converted machine-readable (ValueConv)
+value, or the original raw (Raw) value.  If the value type is not specified,
+the PrintConv value is returned if the PrintConv option is set, otherwise
+the ValueConv value is returned.  The PrintConv values are same as the
+values returned by L</ImageInfo> and L</GetInfo> in the tag/value hash
+unless the PrintConv option is disabled.
+
+Tags which represent lists of multiple values (as may happen with 'Keywords'
+for example) are handled specially.  In scalar context, the returned
+PrintConv value for these tags is either a comma-separated string of values
+or a list reference (depending on the List option setting), and the
+ValueConv value is always a list reference.  But in list context,
+L</GetValue> always returns the list itself.
+
+    # PrintConv example
+    my $val = $exifTool->GetValue($tag);
+    if (ref $val eq 'SCALAR') {
+        print "$tag = (unprintable value)\n";
+    } else {
+        print "$tag = $val\n";
+    }
+
+    # ValueConv examples
+    my $val = $exifTool->GetValue($tag, 'ValueConv');
+    if (ref $val eq 'ARRAY') {
+        print "$tag is a list of values\n";
+    } elsif (ref $val eq 'SCALAR') {
+        print "$tag represents binary data\n";
+    } else {
+        print "$tag is a simple scalar\n";
+    }
+
+    my @keywords = $exifTool->GetValue('Keywords', 'ValueConv');
+
+=over 4
+
+=item Inputs:
+
+0) ExifTool object reference
+
+1) Tag key
+
+2) [optional] Value type, 'PrintConv', 'ValueConv', 'Both' or 'Raw'
+
+The default value type is 'PrintConv' if the PrintConv option is set,
+otherwise the default is 'ValueConv'.  A value type of 'Both' returns both
+ValueConv and PrintConv values as a list.
+
+=item Return Values:
+
+The value of the specified tag.  If the tag represents a list of values and
+the List option is disabled then PrintConv returns a comma separated string
+of values, otherwise a reference to the list is returned in scalar context.
+The list itself is returned in list context.  Values may also be scalar
+references to binary data.
+
+Note: It is possible for L</GetValue> to return an undefined ValueConv or
+PrintConv value (or an empty list in list context) even if the tag exists,
+since it is possible for these conversions to yield undefined values.
+
+=back
+
+=head2 SetNewValue
+
+Set the new value for a tag.  The routine may be called multiple times to
+set the values of many tags before using L</WriteInfo> to write the new
+values to an image.
+
+For list-type tags (like Keywords), either call repeatedly with the same tag
+name for each value, or call with a reference to the list of values.
+
+    # set a new value for a tag (errors go to STDERR)
+    $success = $exifTool->SetNewValue($tag, $value);
+
+    # set a new value and capture any error message
+    ($success, $errStr) = $exifTool->SetNewValue($tag, $value);
+
+    # delete information for specified tag if it exists in image
+    # (also resets AddValue and DelValue options for this tag)
+    $exifTool->SetNewValue($tag);
+
+    # reset all values from previous calls to SetNewValue()
+    $exifTool->SetNewValue();
+
+    # delete a specific keyword
+    $exifTool->SetNewValue('Keywords', $word, DelValue => 1);
+
+    # write a list of keywords
+    $exifTool->SetNewValue('Keywords', ['word1','word2']);
+
+    # add a keyword without replacing existing keywords
+    $exifTool->SetNewValue(Keywords => $word, AddValue => 1);
+
+    # set a tag in a specific group
+    $exifTool->SetNewValue(Headline => $val, Group => 'XMP');
+    $exifTool->SetNewValue('XMP:Headline' => $val);  # equivalent
+
+    # shift original date/time back by 1 hour
+    $exifTool->SetNewValue(DateTimeOriginal => '1:00', Shift => -1);
+
+=over 4
+
+=item Inputs:
+
+0) ExifTool object reference
+
+1) [optional] Tag key or tag name, or undefined to clear all new values.  A
+tag name of '*' can be used when deleting tags to delete all tags, or all
+tags in a specified group.  The tag name may be prefixed by group name,
+separated by a colon (ie. 'GROUP:TAG'), which is equivalent to using a
+'Group' option argument.
+
+2) [optional] New value for tag.  Undefined to delete tag from file.  May be
+a scalar, scalar reference, or list reference to set a list of values.
+
+3-N) [optional] SetNewValue options hash entries (see below)
+
+=item SetNewValue Options:
+
+=over 4
+
+=item Type
+
+The type of value being set.  Valid values are PrintConv, ValueConv or Raw.
+Default is PrintConv if the L</PrintConv> Option is set, otherwise
+ValueConv.
+
+=item AddValue
+
+Specifies that the value be added to an existing list rather than replacing
+the list.  Valid values are 0 or 1.  Default is 0.
+
+=item DelValue
+
+Delete the existing tag if it has the specified value.  Valid values are
+0 or 1.  Default is 0.
+
+=item Group
+
+Specifies group name where tag should be written.  If not specified, tag is
+written to highest priority group as specified by L</SetNewGroups>.  Any
+family 0 or 1 group name may be used.  Case is not significant.
+
+=item NoShortcut
+
+Disables default behaviour of looking up tag in shortcuts if not found
+otherwise.
+
+=item Protected
+
+Bit mask for tag protection levels to write.  Bit 0x01 allows writing of
+'unsafe' tags (ie. tags not copied automatically via
+L</SetNewValuesFromFile>).  Bit 0x02 allows writing of 'protected' tags, and
+should only be used internally by ExifTool.  See
+L<Image::ExifTool::TagNames|Image::ExifTool::TagNames>, for a list of tag
+names indicating 'unsafe' and 'protected' tags.  Default is 0.
+
+=item Replace
+
+Flag to replace the previous new value for this tag (ie. replace the value
+set in a previous call to L</SetNewValue>).  Valid values are 0 (don't
+replace), 1 (replace with specified new value) or 2 (reset previous new
+value only).
+
+=item Shift
+
+Shift the tag by the specified value.  Currently only date/time tags can be
+shifted.  Undefined for no shift, 1 for a positive shift, or -1 for a
+negative shift.  If 0, the shift is applied only if the tag is shiftable.
+In this case, the direction of the shift is positive if AddValue is set, or
+negative if DelValue is set.  Default is undef.  See
+L<Image::ExifTool::Shift.pl|Image::ExifTool::Shift.pl> for more information.
+
+=back
+
+=item Return Values:
+
+In scalar context, returns the number of tags set and error messages are
+printed to STDERR.  In list context, returns the number of tags set and the
+error string.
+
+=back
+
+=head2 SetNewValuesFromFile
+
+A very powerful routine that sets new values for tags from information found
+in a specified file.
+
+    # set new values from all information in a file...
+    my $info = $exifTool->SetNewValuesFromFile($srcFile);
+    # ...then write these values to another image
+    my $result = $exifTool->WriteInfo($file2, $outFile);
+
+    # set all new values, preserving original groups
+    $exifTool->SetNewValuesFromFile($srcFile, '*:*');
+
+    # set specific information
+    $exifTool->SetNewValuesFromFile($srcFile, @tags);
+
+    # set new value from a different tag in specific group
+    $exifTool->SetNewValuesFromFile($fp, 'IPTC:Keywords>XMP-dc:Subject');
+
+    # add all IPTC keywords to XMP subject list
+    $exifTool->SetNewValuesFromFile($fp, 'IPTC:Keywords+>XMP-dc:Subject');
+
+    # set new value from an expression involving other tags
+    $exifTool->SetNewValuesFromFile($file,
+        'Comment<ISO=$ISO Aperture=$aperture Exposure=$shutterSpeed');
+
+=over 4
+
+=item Inputs:
+
+0) ExifTool object reference
+
+1) File name, file reference, or scalar reference
+
+2-N) [optional] List of tag names to set.  All writable tags are set if none
+are specified.  The tag names are not case sensitive, and may be prefixed by
+an optional family 0 or 1 group name, separated by a colon (ie. 'exif:iso').
+A leading '-' indicates tags to be excluded (ie. '-comment').  An asterisk
+('*') may be used for the tag name, and is useful when a group is specified
+to set all tags from a group (ie. 'XMP:*').  A special feature allows tag
+names of the form 'SRCTAG>DSTTAG' (or 'DSTTAGE<lt>SRCTAG') to be specified
+to copy information to a tag with a different name or a specified group.
+Both 'SRCTAG' and 'DSTTAG' may use '*' and/or be prefixed by a group name
+(ie. 'modifyDate>fileModifyDate' or '*>xmp:*').  Copied tags may also be
+added or deleted from a list with arguments of the form 'SRCTAG+>DSTTAG' or
+'SRCTAG->DSTTAG'.  Tags are evaluated in order, so exclusions apply only to
+tags included earlier in the list.  An extension of this feature allows the
+tag value to be set from an expression containing tag names with leading '$'
+symbols (ie. 'CommentE<lt>Filename: $filename').  Braces '{}' may be used
+around the tag name to separate it from subsequent text, and a '$$' is used
+to to represent a '$' symbol.  (The behaviour for missing tags in
+expressions is defined by the L</MissingTagValue> option.)
+
+By default, this routine will commute information between same-named tags in
+different groups, allowing information to be translated between images with
+different formats.  This behaviour may be modified by specifying a group
+name for extracted tags (even if '*' is used as a group name), in which case
+the information is written to the original group, unless redirected to a
+different group.  (For example, a tag name of '*:*' may be specified to copy
+all information while preserving the original groups.)
+
+=item Return Values:
+
+A hash of information that was set successfully.  May include Warning or
+Error entries if there were problems reading the input file.
+
+=item Notes:
+
+The PrintConv option applies to this routine, but it normally should be left
+on to provide more reliable transfer of information between groups.
+
+If a preview image exists, it is not copied.  The preview image must be
+transferred separately if desired.
+
+When simply copying all information between files of the same type, it is
+usually desirable to preserve the original groups by specifying '*:*' for
+the tags to set.
+
+=back
+
+=head2 GetNewValues
+
+Get list of new Raw values for the specified tag.  These are the values
+that will be written to file.  Only tags which support a 'List' may return
+more than one value.
+
+    $rawVal = $exifTool->GetNewValues($tag);
+
+    @rawVals = $exifTool->GetNewValues($tag);
+
+=over 4
+
+=item Inputs:
+
+0) ExifTool object reference
+
+1) Tag key or tag name
+
+=item Return Values:
+
+List of new Raw tag values.  The list may be empty if the tag is being
+deleted (ie. if SetNewValue was called without a value).
+
+=back
+
+=head2 CountNewValues
+
+Return the total number of new values set.
+
+    $numSet = $exifTool->CountNewValues();
+    ($numSet, $numPseudo) = $exifTool->CountNewValues();
+
+=over 4
+
+=item Inputs:
+
+0) ExifTool object reference
+
+=item Return Values:
+
+In scalar context, returns the total number of tags with new values set.  In
+list context, also returns the number of "pseudo" tag values which have been
+set.  "Pseudo" tags are tags like FileName and FileModifyDate which are not
+contained within the file and can be changed without rewriting the file.
+
+=back
+
+=head2 SaveNewValues
+
+Save state of new values to be later restored by L</RestoreNewValues>.
+
+    $exifTool->SaveNewValues();         # save state of new values
+    $exifTool->SetNewValue(ISO => 100); # set new value for ISO
+    $exifTool->WriteInfo($src, $dst1);  # write ISO + previous new values
+    $exifTool->RestoreNewValues();      # restore previous new values
+    $exifTool->WriteInfo($src, $dst2);  # write previous new values only
+
+=over 4
+
+=item Inputs:
+
+0) ExifTool object reference
+
+=item Return Value:
+
+None.
+
+=back
+
+=head2 RestoreNewValues
+
+Restore new values to the settings that existed when L</SaveNewValues> was
+last called.  May be called repeatedly after a single call to
+L</SaveNewValues>.  See L</SaveNewValues> above for an example.
+
+=over 4
+
+=item Inputs:
+
+0) ExifTool object reference
+
+=item Return Value:
+
+None.
+
+=back
+
+=head2 SetFileModifyDate
+
+Set the file modification time from the new value of the FileModifyDate tag.
+
+    $result = $exifTool->SetFileModifyDate($file);
+
+=over 4
+
+=item Inputs:
+
+0) ExifTool object reference
+
+1) File name
+
+2) [optional] Base time if applying shift (days before $^T)
+
+=item Return Value:
+
+1 if the time was changed, 0 if nothing was done, or -1 if there was an
+error setting the time.
+
+=back
+
+=head2 SetFileName
+
+Set the file name and directory.  If not specified, the new file name is
+derived from the new values of the FileName and Directory tags.  If the
+FileName tag contains a '/', then the file is renamed into a new directory.
+If FileName ends with '/', then it is taken as a directory name and the file
+is moved into the new directory.  The new value for the Directory tag takes
+precedence over any directory specified in FileName.
+
+    $result = $exifTool->SetFileName($file);
+    $result = $exifTool->SetFileName($file, $newName);
+
+=over 4
+
+=item Inputs:
+
+0) ExifTool object reference
+
+1) Current file name
+
+2) [optional] New file name
+
+=item Return Value:
+
+1 if the file name or directory was changed, 0 if nothing was done, or -1 if
+there was an error renaming the file.
+
+=item Notes:
+
+Will not overwrite existing files. New directories are created as necessary.
+
+=back
+
+=head2 SetNewGroups
+
+Set the order of the preferred groups when adding new information.  In
+subsequent calls to L</SetNewValue>, new information will be created in the
+first valid group of this list.  This has an impact only if the group is not
+specified when calling L</SetNewValue> and if the tag name exists in more
+than one group.  The default order is EXIF, IPTC, XMP then MakerNotes.  Any
+family 0 group name may be used.  Case is not significant.
+
+    $exifTool->SetNewGroups('XMP','EXIF','IPTC');
+
+=over 4
+
+=item Inputs:
+
+0) ExifTool object reference
+
+1-N) Groups in order of priority.  If no groups are specified, the priorities
+are reset to the defaults.
+
+=item Return Value:
+
+None.
+
+=back
+
+=head2 GetNewGroups
+
+Get current group priority list.
+
+    @groups = $exifTool->GetNewGroups();
+
+=over 4
+
+=item Inputs:
+
+0) ExifTool object reference
+
+=item Return Values:
+
+List of group names in order of write priority.  Highest priority first.
+
+=back
+
+=head2 GetTagID
+
+Get the ID for the specified tag.  The ID is the IFD tag number in EXIF
+information, the property name in XMP information, or the data offset in a
+binary data block. For some tags, such as Composite tags where there is no ID,
+an empty string is returned.
+
+    $id = $exifTool->GetTagID($tag);
+
+=over 4
+
+=item Inputs:
+
+0) ExifTool object reference
+
+1) Tag key
+
+=item Return Values:
+
+Tag ID or '' of there is no ID for this tag.
+
+=back
+
+=head2 GetDescription
+
+Get description for specified tag.  This function will always return a
+defined value.  In the case where the description doesn't exist, the tag
+name is returned.
+
+=over 4
+
+=item Inputs:
+
+0) ExifTool object reference
+
+1) Tag key
+
+=item Return Values:
+
+A description for the specified tag.
+
+=back
+
+=head2 GetGroup
+
+Get group name for specified tag.
+
+    $group = $exifTool->GetGroup($tag, 0);
+
+=over 4
+
+=item Inputs:
+
+0) ExifTool object reference
+
+1) Tag key
+
+2) [optional] Group family number
+
+=item Return Values:
+
+Group name (or 'Other' if tag has no group).  If no group family is
+specified, L</GetGroup> returns the name of the group in family 0 when
+called in scalar context, or the names of groups for all families in list
+context. See L</GetAllGroups> for a list of groups in each famly.
+
+=back
+
+=head2 GetGroups
+
+Get list of group names for specified information.
+
+    @groups = $exifTool->GetGroups($info, 2);
+
+=over 4
+
+=item Inputs:
+
+0) ExifTool object reference
+
+1) [optional] Info hash ref (default is all extracted info)
+
+2) [optional] Group family number (default 0)
+
+=item Return Values:
+
+List of group names in alphabetical order. If information hash is not
+specified, the group names are returned for all extracted information.
+
+=back
+
+=head2 BuildCompositeTags
+
+Builds composite tags from required tags.  The composite tags are
+convenience tags which are derived from the values of other tags.  This
+routine is called automatically by L</ImageInfo> and L</ExtractInfo> if the
+Composite option is set.
+
+=over 4
+
+=item Inputs:
+
+0) ExifTool object reference
+
+=item Return Values:
+
+(none)
+
+=item Notes:
+
+Tag values are calculated in alphabetical order unless a tag Require's or
+Desire's another composite tag, in which case the calculation is deferred
+until after the other tag is calculated. Composite tags may need to read
+data from the image for their value to be determined, so for these
+L</BuildCompositeTags> must be called while the image is available.  This is
+only a problem if L</ImageInfo> is called with a filename (as opposed to a
+file reference or scalar reference) since in this case the file is closed
+before L</ImageInfo> returns.  However if you enable the Composite option,
+L</BuildCompositeTags> is called from within L</ImageInfo> before the file
+is closed.
+
+=back
+
+=head2 GetTagName [static]
+
+Get name of tag from tag key.  This is a convenience function that
+strips the embedded instance number, if it exists, from the tag key.
+
+Note: "static" in the heading above indicates that the function does not
+require an ExifTool object reference as the first argument.  All functions
+documented below are also static.
+
+    $tagName = Image::ExifTool::GetTagName($tag);
+
+=over 4
+
+=item Inputs:
+
+0) Tag key
+
+=item Return Value:
+
+Tag name.  This is the same as the tag key but has the instance number
+removed.
+
+=back
+
+=head2 GetShortcuts [static]
+
+Get a list of shortcut tags.
+
+=over 4
+
+=item Inputs:
+
+(none)
+
+=item Return Values:
+
+List of shortcut tags (as defined in Image::ExifTool::Shortcuts).
+
+=back
+
+=head2 GetAllTags [static]
+
+Get list of all available tag names.
+
+    @tagList = Image::ExifTool::GetAllTags($group);
+
+=over 4
+
+=item Inputs:
+
+0) [optional] Group name
+
+=item Return Values:
+
+A list of all available tags in alphabetical order, or all tags in specified
+group.  The group name is case insensitive, and any group in any family may
+be used except for EXIF family 1 groups (ie. the specific IFD).
+
+=back
+
+=head2 GetWritableTags [static]
+
+Get list of all writable tag names.
+
+    @tagList = Image::ExifTool::GetWritableTags($group);
+
+=over 4
+
+=item Inputs:
+
+0) [optional] Group name
+
+=item Return Values:
+
+A list of all writable tags in alphabetical order.  These are the tags for
+which values may be set through L</SetNewValue>.  If a group name is given,
+returns only writable tags in specified group.  The group name is case
+insensitive, and any group in any family may be used except for EXIF family
+1 groups (ie. the specific IFD).
+
+=back
+
+=head2 GetAllGroups [static]
+
+Get list of all group names in specified family.
+
+    @groupList = Image::ExifTool::GetAllGroups($family);
+
+=over 4
+
+=item Inputs:
+
+0) Group family number (0-2)
+
+=item Return Values:
+
+A list of all groups in the specified family in alphabetical order.
+
+=back
+
+Three families of groups are currently defined: 0, 1 and 2. Families 0 and 1
+are based on the file structure, and family 2 classifies information based
+on the logical category to which the information refers.
+
+Families 0 and 1 are similar except that family 1 is more specific, and
+sub-divides the EXIF, MakerNotes, XMP and ICC_Profile groups to give more
+detail about the specific location where the information was found.  The
+EXIF group is split up based on the specific IFD (Image File Directory), the
+MakerNotes group is divided into groups for each manufacturer, and the XMP
+group is separated based on the XMP namespace prefix.  Note that only common
+XMP namespaces are listed below but additional namespaces may be present in
+some XMP data.  Also note that the 'XMP-xmp...' group names may appear in
+the older form 'XMP-xap...' since these names evolved as the XMP standard
+was developed.  The ICC_Profile group is broken down to give information
+about the specific ICC_Profile tag from which multiple values were
+extracted.  As well, information extracted from the ICC_Profile header is
+separated into the ICC-header group.
+
+Here is a complete list of groups for each family:
+
+=over 4
+
+=item Family 0 (Information Type):
+
+AFCP, AIFF, APE, APP12, APP13, APP14, APP15, APP5, APP6, APP8, ASF, BMP,
+CanonVRD, Composite, DICOM, DNG, Ducky, EXIF, ExifTool, FLAC, File, Flash,
+FlashPix, FotoStation, GeoTiff, HTML, ICC_Profile, ID3, IPTC, JFIF, JPEG,
+Jpeg2000, Leaf, MIE, MIFF, MNG, MPC, MPEG, MakerNotes, Meta, PDF, PICT, PNG,
+PhotoMechanic, Photoshop, PostScript, PrintIM, QuickTime, RAF, RIFF, Real,
+SigmaRaw, Vorbis, XMP
+
+=item Family 1 (Specific Location):
+
+AFCP, AIFF, APE, ASF, Adobe, AdobeCM, BMP, Canon, CanonCustom, CanonRaw,
+CanonVRD, Casio, Composite, DICOM, DNG, Ducky, EPPIM, ExifIFD, ExifTool,
+FLAC, File, Flash, FlashPix, FotoStation, FujiFilm, GPS, GeoTiff,
+GlobParamIFD, GraphConv, HP, HTML, HTML-dc, HTML-ncc, HTML-prod, HTML-vw96,
+HTTP-equiv, ICC-chrm, ICC-clrt, ICC-header, ICC-meas, ICC-view, ICC_Profile,
+ID3, ID3v1, ID3v2_2, ID3v2_3, ID3v2_4, IFD0, IFD1, IPTC, InteropIFD, JFIF,
+JPEG, JVC, Jpeg2000, Kodak, KodakBordersIFD, KodakEffectsIFD, KyoceraRaw,
+Leaf, LeafSubIFD, MAC, MIE-Audio, MIE-Camera, MIE-Doc, MIE-Extender,
+MIE-Flash, MIE-GPS, MIE-Geo, MIE-Image, MIE-Lens, MIE-Main, MIE-MakerNotes,
+MIE-Meta, MIE-Orient, MIE-Preview, MIE-Thumbnail, MIE-UTM, MIE-Unknown,
+MIE-Video, MIFF, MNG, MPC, MPEG, MakerNotes, MakerUnknown, MetaIFD, Minolta,
+MinoltaRaw, Nikon, NikonCapture, NikonPreview, NikonScan, Olympus, PDF,
+PICT, PNG, Panasonic, Pentax, PhotoMechanic, Photoshop, PictureInfo,
+PostScript, PrintIM, QuickTime, RAF, RAF2, RIFF, RMETA, Real, Real-CONT,
+Real-MDPR, Real-PROP, Real-RA3, Real-RA4, Real-RA5, Real-RJMD, Ricoh, SPIFF,
+SR2, SRF#, Sanyo, Sigma, SigmaRaw, Sony, SubIFD, Track#, Vorbis, XMP,
+XMP-DICOM, XMP-PixelLive, XMP-aux, XMP-cc, XMP-crs, XMP-dc, XMP-dex,
+XMP-exif, XMP-iptcCore, XMP-lr, XMP-mediapro, XMP-microsoft, XMP-pdf,
+XMP-photomech, XMP-photoshop, XMP-tiff, XMP-xmp, XMP-xmpBJ, XMP-xmpDM,
+XMP-xmpMM, XMP-xmpPLUS, XMP-xmpRights, XMP-xmpTPg
+
+=item Family 2 (Category):
+
+Audio, Author, Camera, Document, ExifTool, Image, Location, Other, Printing,
+Time, Unknown, Video
+
+=back
+
+=head2 GetDeleteGroups [static]
+
+Get list of all deletable group names.
+
+    @delGroups = Image::ExifTool::GetDeleteGroups();
+
+=over 4
+
+=item Inputs:
+
+None.
+
+=item Return Values:
+
+A list of deletable group names in alphabetical order.  The current list of
+deletable group names is:
+
+AFCP, CIFF, CanonVRD, EXIF, ExifIFD, Ducky, File, FlashPix, FotoStation,
+GlobParamIFD, GPS, IFD0, IFD1, InteropIFD, ICC_Profile, IPTC, JFIF,
+MakerNotes, Meta, MetaIFD, MIE, PhotoMechanic, Photoshop, PNG, PrintIM,
+RMETA, SubIFD, Trailer, XMP
+
+All names in this list are either family 0 or family 1 group names, with the
+exception of 'Trailer' which allows all trailers in JPEG and TIFF-format
+images to be deleted at once, including unknown trailers.  To schedule a
+group for deletion, call L</SetNewValue> with an undefined value and a tag
+name like 'Trailer:*'.
+
+=back
+
+=head2 GetFileType [static]
+
+Get type of file given file name.
+
+    my $type = Image::ExifTool::GetFileType($filename);
+    my $desc = Image::ExifTool::GetFileType($filename, 1);
+
+=over 4
+
+=item Inputs:
+
+0) [optional] File name (or just an extension)
+
+1) [optional]  Flag to return a description instead of a type
+
+=item Return Value:
+
+A string, based on the file extension, which represents the type of file.
+Returns undefined value if file type is not supported by ExifTool.  In array
+context, may return more than one file type if the file may be different
+formats.  Returns a list of extensions for all recognized file types if no
+input extension is specified.
+
+=back
+
+=head2 CanWrite [static]
+
+Can the specified file or file type be written?
+
+    my $writable = Image::ExifTool::CanWrite($filename);
+
+=over 4
+
+=item Inputs:
+
+0) File name, file extension, or file type
+
+=item Return Value:
+
+True if the specified file type can be written (edited).
+
+=back
+
+=head2 CanCreate [static]
+
+Can the specified file or file type be created?
+
+    my $creatable = Image::ExifTool::CanCreate($filename);
+
+=over 4
+
+=item Inputs:
+
+0) File name, file extension, or file type
+
+=item Return Value:
+
+True if the specified file type can be created from scratch.  Currently,
+this can only be done with XMP files.
+
+=back
+
+=head1 AUTHOR
+
+Copyright 2003-2007, Phil Harvey
+
+This library is free software; you can redistribute it and/or modify it
+under the same terms as Perl itself.
+
+=head1 CREDITS
+
+Many people have helped in the development of ExifTool through their bug
+reports, comments and suggestions, and/or additions to the code.  See
+html/index.html in the Image::ExifTool distribution package for a list of
+people who have contributed to this project.
+
+=head1 SEE ALSO
+
+L<exiftool(1)|exiftool>,
+L<Image::ExifTool::TagNames(3pm)|Image::ExifTool::TagNames>,
+L<Image::ExifTool::Shortcuts(3pm)|Image::ExifTool::Shortcuts>,
+L<Image::ExifTool::Shift.pl|Image::ExifTool::Shift.pl>,
+L<Image::Info(3pm)|Image::Info>,
+L<Image::MetaData::JPEG(3pm)|Image::MetaData::JPEG>
+
+=cut
+
+# end
Index: /gsdl/trunk/perllib/cpan/Image/ExifTool/AFCP.pm
===================================================================
--- /gsdl/trunk/perllib/cpan/Image/ExifTool/AFCP.pm	(revision 16842)
+++ /gsdl/trunk/perllib/cpan/Image/ExifTool/AFCP.pm	(revision 16842)
@@ -0,0 +1,273 @@
+#------------------------------------------------------------------------------
+# File:         AFCP.pm
+#
+# Description:  Read/write AFCP trailer
+#
+# Revisions:    12/26/2005 - P. Harvey Created
+#
+# References:   1) http://www.tocarte.com/media/axs_afcp_spec.pdf
+#------------------------------------------------------------------------------
+
+package Image::ExifTool::AFCP;
+
+use strict;
+use vars qw($VERSION);
+use Image::ExifTool qw(:DataAccess :Utils);
+
+$VERSION = '1.02';
+
+sub ProcessAFCP($$);
+
+%Image::ExifTool::AFCP::Main = (
+    PROCESS_PROC => \&ProcessAFCP,
+    NOTES => q{
+AFCP stands for AXS File Concatenation Protocol, and is a poorly designed
+protocol for appending information to the end of files.  This can be used as
+an auxiliary technique to store IPTC information in images, but is
+incompatible with some file formats.
+
+ExifTool will read and write (but not create) AFCP IPTC information in JPEG
+images, but not other image formats.
+    },
+    IPTC => { SubDirectory => { TagTable => 'Image::ExifTool::IPTC::Main' } },
+    TEXT => 'Text',
+    Nail => {
+        Name => 'ThumbnailImage',
+        ValueConv => q{
+            my $img = substr($val, 18);
+            return $self->ValidateImage(\$img,$tag);
+        },
+    },
+    PrVw => {
+        Name => 'PreviewImage',
+        ValueConv => q{
+            my $img = substr($val, 18);
+            return $self->ValidateImage(\$img,$tag);
+        },
+    },
+);
+
+#------------------------------------------------------------------------------
+# Read/write AFCP information in a file
+# Inputs: 0) ExifTool object reference, 1) dirInfo reference
+# (Set 'ScanForAFCP' member in dirInfo to scan from current position for AFCP)
+# Returns: 1 on success, 0 if this file didn't contain AFCP information
+#          -1 on write error or if the offsets were incorrect on reading
+# - updates DataPos to point to actual AFCP start if ScanForAFCP is set
+# - updates DirLen to trailer length
+# - returns Fixup reference in dirInfo hash when writing
+sub ProcessAFCP($$)
+{
+    my ($exifTool, $dirInfo) = @_;
+    my $raf = $$dirInfo{RAF};
+    my $curPos = $raf->Tell();
+    my $offset = $$dirInfo{Offset} || 0;    # offset from end of file
+    my $rtnVal = 0;
+
+NoAFCP: for (;;) {
+        my ($buff, $fix, $dirBuff, $valBuff, $fixup, $vers);
+        # look for AXS trailer
+        last unless $raf->Seek(-12-$offset, 2) and
+                    $raf->Read($buff, 12) == 12 and
+                    $buff =~ /^(AXS(!|\*))/;
+        my $endPos = $raf->Tell();
+        my $hdr = $1;
+        SetByteOrder($2 eq '!' ? 'MM' : 'II');
+        my $startPos = Get32u(\$buff, 4);
+        if ($raf->Seek($startPos, 0) and $raf->Read($buff, 12) == 12 and $buff =~ /^$hdr/) {
+            $fix = 0;
+        } else {
+            $rtnVal = -1;
+            # look for start of AXS trailer if 'ScanForAFCP'
+            last unless $$dirInfo{ScanForAFCP} and $raf->Seek($curPos, 0);
+            my $actualPos = $curPos;
+            # first look for header right at current position
+            for (;;) {
+                last if $raf->Read($buff, 12) == 12 and $buff =~ /^$hdr/;
+                last NoAFCP if $actualPos != $curPos;
+                # scan for AXS header (could be after preview image)
+                for (;;) {
+                    my $buf2;
+                    $raf->Read($buf2, 65536) or last NoAFCP;
+                    $buff .= $buf2;
+                    if ($buff =~ /$hdr/g) {
+                        $actualPos += pos($buff) - length($hdr);
+                        last;   # ok, now go back and re-read header
+                    }
+                    $buf2 = substr($buf2, -3);  # only need last 3 bytes for next test
+                    $actualPos += length($buff) - length($buf2);
+                    $buff = $buf2;
+                }
+                last unless $raf->Seek($actualPos, 0);  # seek to start of AFCP
+            }
+            # calculate shift for fixing AFCP offsets
+            $fix = $actualPos - $startPos;
+        }
+        # set variables returned in dirInfo hash
+        $$dirInfo{DataPos} = $startPos + $fix;  # actual start position
+        $$dirInfo{DirLen} = $endPos - ($startPos + $fix);
+
+        $rtnVal = 1;
+        my $verbose = $exifTool->Options('Verbose');
+        my $out = $exifTool->Options('TextOut');
+        my $outfile = $$dirInfo{OutFile};
+        if ($outfile) {
+            # allow all AFCP information to be deleted
+            if ($exifTool->{DEL_GROUP}->{AFCP}) {
+                $verbose and print $out "  Deleting AFCP\n";
+                ++$exifTool->{CHANGED};
+                last;
+            }
+            $dirBuff = $valBuff = '';
+            require Image::ExifTool::Fixup;
+            $fixup = $$dirInfo{Fixup};
+            $fixup or $fixup = $$dirInfo{Fixup} = new Image::ExifTool::Fixup;
+            $vers = substr($buff, 4, 2); # get version number
+        } else {
+            $exifTool->DumpTrailer($dirInfo) if $verbose or $exifTool->{HTML_DUMP};
+        }
+        # read AFCP directory data
+        my $numEntries = Get16u(\$buff, 6);
+        my $dir;
+        unless ($raf->Read($dir, 12 * $numEntries) == 12 * $numEntries) {
+            $exifTool->Error('Error reading AFCP directory', 1);
+            last;
+        }
+        if ($verbose > 2 and not $outfile) {
+            my $dat = $buff . $dir;
+            print $out "  AFCP Directory:\n";
+            Image::ExifTool::HexDump(\$dat, undef,
+                Addr   => $$dirInfo{DataPos},
+                Width  => 12,
+                Prefix => $exifTool->{INDENT},
+                Out => $out,
+            );
+        }
+        $fix and $exifTool->Warn("Adjusted AFCP offsets by $fix", 1);
+#
+# process AFCP directory
+#
+        my $tagTablePtr = GetTagTable('Image::ExifTool::AFCP::Main');
+        my ($index, $entry);
+        for ($index=0; $index<$numEntries; ++$index) {
+            my $entry = 12 * $index;
+            my $tag = substr($dir, $entry, 4);
+            my $size = Get32u(\$dir, $entry + 4);
+            my $offset = Get32u(\$dir, $entry + 8);
+            if ($size < 0x80000000 and
+                $raf->Seek($offset+$fix, 0) and
+                $raf->Read($buff, $size) == $size)
+            {
+                if ($outfile) {
+                    # rewrite this information
+                    my $tagInfo = $exifTool->GetTagInfo($tagTablePtr, $tag);
+                    if ($tagInfo and $$tagInfo{SubDirectory}) {
+                        my %subdirInfo = (
+                            DataPt => \$buff,
+                            DirStart => 0,
+                            DirLen => $size,
+                            DataPos => $offset + $fix,
+                            Parent => 'AFCP',
+                        );
+                        my $subTable = GetTagTable($tagInfo->{SubDirectory}->{TagTable});
+                        my $newDir = $exifTool->WriteDirectory(\%subdirInfo, $subTable);
+                        if (defined $newDir) {
+                            $size = length $newDir;
+                            $buff = $newDir;
+                        }
+                    }
+                    $fixup->AddFixup(length($dirBuff) + 8);
+                    $dirBuff .= $tag . Set32u($size) . Set32u(length $valBuff);
+                    $valBuff .= $buff;
+                } else {
+                    # extract information
+                    $exifTool->HandleTag($tagTablePtr, $tag, $buff,
+                        DataPt => \$buff,
+                        Size => $size,
+                        Index => $index,
+                        DataPos => $offset + $fix,
+                    );
+                }
+            } else {
+                $exifTool->Warn("Bad AFCP directory");
+                $rtnVal = -1 if $outfile;
+                last;
+            }
+        }
+        if ($outfile and length($dirBuff)) {
+            my $outPos = Tell($outfile);    # get current outfile position
+            # apply fixup to directory pointers
+            my $valPos = $outPos + 12;      # start of value data
+            $fixup->{Shift} += $valPos + length($dirBuff);
+            $fixup->ApplyFixup(\$dirBuff);
+            # write the AFCP header, directory, value data and EOF record (with zero checksums)
+            Write($outfile, $hdr, $vers, Set16u(length($dirBuff)/12), Set32u(0),
+                  $dirBuff, $valBuff, $hdr, Set32u($outPos), Set32u(0)) or $rtnVal = -1;
+            # complete fixup so the calling routine can apply further shifts
+            $fixup->AddFixup(length($dirBuff) + length($valBuff) + 4);
+            $fixup->{Start} += $valPos;
+            $fixup->{Shift} -= $valPos;
+        }
+        last;
+    }
+    return $rtnVal;
+}
+
+1;  # end
+
+__END__
+
+=head1 NAME
+
+Image::ExifTool::AFCP - Read/write AFCP trailer
+
+=head1 SYNOPSIS
+
+This module is used by Image::ExifTool
+
+=head1 DESCRIPTION
+
+This module contains definitions required by Image::ExifTool to extract
+information from the AFCP trailer.  Although the AFCP specification is
+compatible with various file formats, ExifTool currently only processes AFCP
+in JPEG images.
+
+=head1 NOTES
+
+AFCP is a specification which allows meta information (including IPTC) to be
+appended to the end of a file.
+
+It is a poorly designed protocol because (like TIFF) it uses absolute
+offsets to specify data locations.  This is a huge blunder because it makes
+the AFCP information dependent on the file length, so it is easily
+invalidated by image editing software which doesn't recognize the AFCP
+trailer to fix up these offsets when the file length changes.  ExifTool will
+attempt to fix these invalid offsets if possible.
+
+Scanning for AFCP information may be time consuming, especially when reading
+from a sequential device, since the information is at the end of the file.
+In these instances, the ExifTool FastScan option may be used to disable
+scanning for AFCP information.
+
+=head1 AUTHOR
+
+Copyright 2003-2007, Phil Harvey (phil at owl.phy.queensu.ca)
+
+This library is free software; you can redistribute it and/or modify it
+under the same terms as Perl itself.
+
+=head1 REFERENCES
+
+=over 4
+
+=item L<http://www.tocarte.com/media/axs_afcp_spec.pdf>
+
+=back
+
+=head1 SEE ALSO
+
+L<Image::ExifTool::TagNames/AFCP Tags>,
+L<Image::ExifTool(3pm)|Image::ExifTool>
+
+=cut
+
Index: /gsdl/trunk/perllib/cpan/Image/ExifTool/AIFF.pm
===================================================================
--- /gsdl/trunk/perllib/cpan/Image/ExifTool/AIFF.pm	(revision 16842)
+++ /gsdl/trunk/perllib/cpan/Image/ExifTool/AIFF.pm	(revision 16842)
@@ -0,0 +1,219 @@
+#------------------------------------------------------------------------------
+# File:         AIFF.pm
+#
+# Description:  Read AIFF meta information
+#
+# Revisions:    01/06/2006 - P. Harvey Created
+#
+# References:   1) http://developer.apple.com/documentation/QuickTime/INMAC/SOUND/imsoundmgr.30.htm#pgfId=3190
+#               2) http://astronomy.swin.edu.au/~pbourke/dataformats/aiff/
+#               3) http://www.mactech.com/articles/mactech/Vol.06/06.01/SANENormalized/
+#------------------------------------------------------------------------------
+
+package Image::ExifTool::AIFF;
+
+use strict;
+use vars qw($VERSION);
+use Image::ExifTool qw(:DataAccess :Utils);
+use Image::ExifTool::ID3;
+
+$VERSION = '1.01';
+
+# information for time/date-based tags (time zero is Jan 1, 1904)
+my %timeInfo = (
+    Groups => { 2 => 'Time' },
+    ValueConv => 'ConvertUnixTime($val - ((66 * 365 + 17) * 24 * 3600))',
+    PrintConv => '$self->ConvertDateTime($val)',
+);
+
+# AIFF info
+%Image::ExifTool::AIFF::Main = (
+    PROCESS_PROC => \&Image::ExifTool::AIFF::ProcessSubChunks,
+    GROUPS => { 2 => 'Audio' },
+    NOTES => 'Only the tags decoded by ExifTool are listed in this table.',
+#    FORM => 'Format',
+    FVER => {
+        Name => 'FormatVersion',
+        SubDirectory => { TagTable => 'Image::ExifTool::AIFF::FormatVers' },
+    },
+    COMM => {
+        Name => 'Common',
+        SubDirectory => { TagTable => 'Image::ExifTool::AIFF::Common' },
+    },
+    COMT => {
+        Name => 'Comment',
+        SubDirectory => { TagTable => 'Image::ExifTool::AIFF::Comment' },
+    },
+    NAME => 'Name',
+    AUTH => { Name => 'Author', Groups => { 2 => 'Author' } },
+   '(c) ' => { Name => 'Copyright', Groups => { 2 => 'Author' } },
+    ANNO => 'Annotation',
+   'ID3 ' => {
+        Name => 'ID3',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::ID3::Main',
+            ProcessProc => \&Image::ExifTool::ID3::ProcessID3,
+        },
+    },
+#    SSND => 'SoundData',
+#    MARK => 'Marker',
+#    INST => 'Instrument',
+#    MIDI => 'MidiData',
+#    AESD => 'AudioRecording',
+#    APPL => 'ApplicationSpecific',
+);
+
+%Image::ExifTool::AIFF::Common = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    GROUPS => { 2 => 'Audio' },
+    FORMAT => 'int16u',
+    0 => 'NumChannels',
+    1 => { Name => 'NumSampleFrames', Format => 'int32u' },
+    3 => 'SampleSize',
+    4 => { Name => 'SampleRate', Format => 'extended' }, #3
+    9 => {
+        Name => 'CompressionType',
+        Format => 'string[4]',
+        PrintConv => {
+            NONE => 'None',
+            ACE2 => 'ACE 2-to-1',
+            ACE8 => 'ACE 8-to-3',
+            MAC3 => 'MAC 3-to-1',
+            MAC6 => 'MAC 6-to-1',
+            sowt => 'Little-endian, no compression',
+        },
+    },
+);
+
+%Image::ExifTool::AIFF::FormatVers = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    FORMAT => 'int32u',
+    0 => { Name => 'FormatVersionTime', %timeInfo },
+);
+
+%Image::ExifTool::AIFF::Comment = (
+    PROCESS_PROC => \&Image::ExifTool::AIFF::ProcessComment,
+    GROUPS => { 2 => 'Audio' },
+    0 => { Name => 'CommentTime', %timeInfo },
+    1 => 'MarkerID',
+    2 => 'Comment',
+);
+
+#------------------------------------------------------------------------------
+# Process AIFF Comment chunk
+# Inputs: 0) ExifTool object reference, 1) DirInfo reference, 2) tag table ref
+# Returns: 1 on success
+sub ProcessComment($$$)
+{
+    my ($exifTool, $dirInfo, $tagTablePtr) = @_;
+    my $dataPt = $$dirInfo{DataPt};
+    my $dirLen = $$dirInfo{DirLen};
+    my $verbose = $exifTool->Options('Verbose');
+    return 0 unless $dirLen > 2;
+    my $numComments = unpack('n',$$dataPt);
+    my $pos = 2;
+    my $i;
+    $verbose and $exifTool->VerboseDir('Comment', $numComments);
+    for ($i=0; $i<$numComments; ++$i) {
+        last if $pos + 8 > $dirLen;
+        my ($time, $markerID, $size) = unpack("x${pos}Nnn", $$dataPt);
+        $exifTool->HandleTag($tagTablePtr, 0, $time);
+        $exifTool->HandleTag($tagTablePtr, 1, $markerID) if $markerID;
+        $pos += 8;
+        last if $pos + $size > $dirLen;
+        my $val = substr($$dataPt, $pos, $size);
+        $exifTool->HandleTag($tagTablePtr, 2, $val);
+        ++$size if $size & 0x01;    # account for padding byte if necessary
+        $pos += $size;
+    }
+}
+
+#------------------------------------------------------------------------------
+# Extract information from a AIFF file
+# Inputs: 0) ExifTool object reference, 1) DirInfo reference
+# Returns: 1 on success, 0 if this wasn't a valid AIFF file
+sub ProcessAIFF($$)
+{
+    my ($exifTool, $dirInfo) = @_;
+    my $raf = $$dirInfo{RAF};
+    my $verbose = $exifTool->Options('Verbose');
+    my ($buff, $err);
+
+    # verify this is a valid AIFF file
+    return 0 unless $raf->Read($buff, 12) == 12;
+    return 0 unless $buff =~ /^FORM....(AIF(F|C))/s;
+    $exifTool->SetFileType($1);
+    SetByteOrder('MM');
+    my $tagTablePtr = GetTagTable('Image::ExifTool::AIFF::Main');
+    my $pos = 12;
+#
+# Read chunks in AIFF image
+#
+    for (;;) {
+        $raf->Read($buff, 8) == 8 or last;
+        $pos += 8;
+        my ($tag, $len) = unpack('a4N', $buff);
+        my $tagInfo = $exifTool->GetTagInfo($tagTablePtr, $tag);
+        $exifTool->VPrint(0, "AIFF '$tag' chunk ($len bytes of data):\n");
+        # AIFF chunks are padded to an even number of bytes
+        my $len2 = $len + ($len & 0x01);
+        if ($tagInfo) {
+            $raf->Read($buff, $len2) == $len2 or $err=1, last;
+            $exifTool->HandleTag($tagTablePtr, $tag, $buff,
+                DataPt => \$buff,
+                DataPos => $pos,
+                Start => 0,
+                Size => $len,
+            );
+        } else {
+            $raf->Seek($len2, 1) or $err=1, last;
+        }
+        $pos += $len2;
+    }
+    $err and $exifTool->Warn('Error reading AIFF file (corrupted?)');
+    return 1;
+}
+
+1;  # end
+
+__END__
+
+=head1 NAME
+
+Image::ExifTool::AIFF - Read AIFF meta information
+
+=head1 SYNOPSIS
+
+This module is used by Image::ExifTool
+
+=head1 DESCRIPTION
+
+This module contains routines required by Image::ExifTool to extract
+information from AIFF (Audio Interchange File Format) audio files.
+
+=head1 AUTHOR
+
+Copyright 2003-2007, Phil Harvey (phil at owl.phy.queensu.ca)
+
+This library is free software; you can redistribute it and/or modify it
+under the same terms as Perl itself.
+
+=head1 REFERENCES
+
+=over 4
+
+=item L<http://developer.apple.com/documentation/QuickTime/INMAC/SOUND/imsoundmgr.30.htm#pgfId=3190>
+
+=item L<http://astronomy.swin.edu.au/~pbourke/dataformats/aiff/>
+
+=item L<http://www.mactech.com/articles/mactech/Vol.06/06.01/SANENormalized/>
+
+=back
+
+=head1 SEE ALSO
+
+L<Image::ExifTool::TagNames/AIFF Tags>,
+L<Image::ExifTool(3pm)|Image::ExifTool>
+
+=cut
+
Index: /gsdl/trunk/perllib/cpan/Image/ExifTool/APE.pm
===================================================================
--- /gsdl/trunk/perllib/cpan/Image/ExifTool/APE.pm	(revision 16842)
+++ /gsdl/trunk/perllib/cpan/Image/ExifTool/APE.pm	(revision 16842)
@@ -0,0 +1,254 @@
+#------------------------------------------------------------------------------
+# File:         APE.pm
+#
+# Description:  Read Monkey's Audio meta information
+#
+# Revisions:    11/13/2006 - P. Harvey Created
+#
+# References:   1) http://www.monkeysaudio.com/
+#               2) http://www.personal.uni-jena.de/~pfk/mpp/sv8/apetag.html
+#------------------------------------------------------------------------------
+
+package Image::ExifTool::APE;
+
+use strict;
+use vars qw($VERSION);
+use Image::ExifTool qw(:DataAccess :Utils);
+
+$VERSION = '1.01';
+
+# APE metadata blocks
+%Image::ExifTool::APE::Main = (
+    GROUPS => { 2 => 'Audio' },
+    NOTES => q{
+        Tags found in Monkey's Audio (APE) information.  Only a few common tags are
+        listed below, but ExifTool will extract any tag found.  ExifTool supports
+        APEv1 and APEv2 tags, as well as ID3 information in APE files.
+    },
+    Album   => { },
+    Artist  => { },
+    Genre   => { },
+    Title   => { },
+    Track   => { },
+    Year    => { },
+    'Tool Version' => { Name => 'ToolVersion' },
+    'Tool Name'    => { Name => 'ToolName' },
+);
+
+# APE MAC header version 3.97 or earlier
+%Image::ExifTool::APE::OldHeader = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    GROUPS => { 1 => 'MAC', 2 => 'Audio' },
+    FORMAT => 'int16u',
+    NOTES => 'APE MAC audio header for version 3.97 or earlier.',
+    0 => {
+        Name => 'APEVersion',
+        ValueConv => '$val / 1000',
+    },
+    1 => 'CompressionLevel',
+  # 2 => 'FormatFlags',
+    3 => 'Channels',
+    4 => { Name => 'SampleRate', Format => 'int32u' },
+  # 6 => { Name => 'HeaderBytes', Format => 'int32u' }, # WAV header bytes
+  # 8 => { Name => 'TerminatingBytes', Format => 'int32u' },
+    10 => { Name => 'TotalFrames', Format => 'int32u' },
+    12 => { Name => 'FinalFrameBlocks', Format => 'int32u' },
+);
+
+# APE MAC header version 3.98 or later
+%Image::ExifTool::APE::NewHeader = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    GROUPS => { 1 => 'MAC', 2 => 'Audio' },
+    FORMAT => 'int16u',
+    NOTES => 'APE MAC audio header for version 3.98 or later.',
+    0 => 'CompressionLevel',
+  # 1 => 'FormatFlags',
+    2 => { Name => 'BlocksPerFrame',   Format => 'int32u' },
+    4 => { Name => 'FinalFrameBlocks', Format => 'int32u' },
+    6 => { Name => 'TotalFrames',      Format => 'int32u' },
+    8 => 'BitsPerSample',
+    9 => 'Channels',
+    10 => { Name => 'SampleRate',      Format => 'int32u' },
+);
+
+#------------------------------------------------------------------------------
+# Make tag info hash for specified tag
+# Inputs: 0) tag name, 1) tag table ref
+# - must only call if tag doesn't exist
+sub MakeTag($$)
+{
+    my ($tag, $tagTablePtr) = @_;
+    my $name = ucfirst(lc($tag));
+    # remove invalid characters in tag name and capitalize following letters
+    $name =~ s/[^\w-]+(.?)/\U$1/sg;
+    $name =~ s/([a-z0-9])_([a-z])/$1\U$2/g;
+    Image::ExifTool::AddTagToTable($tagTablePtr, $tag, { Name => $name });
+}
+
+#------------------------------------------------------------------------------
+# Extract information from an APE file
+# Inputs: 0) ExifTool object reference, 1) dirInfo reference
+# - Just looks for APE trailer if FileType is already set
+# Returns: 1 on success, 0 if this wasn't a valid APE file
+sub ProcessAPE($$)
+{
+    my ($exifTool, $dirInfo) = @_;
+
+    # must first check for leading/trailing ID3 information
+    unless ($exifTool->{DONE_ID3}) {
+        require Image::ExifTool::ID3;
+        Image::ExifTool::ID3::ProcessID3($exifTool, $dirInfo) and return 1;
+    }
+    my $raf = $$dirInfo{RAF};
+    my $verbose = $exifTool->Options('Verbose');
+    my ($buff, $i, $header, $tagTablePtr, $dataPos);
+
+    # check APE signature and process audio information
+    # unless this is some other type of file
+    unless ($exifTool->{VALUE}->{FileType}) {
+        $raf->Read($buff, 32) == 32 or return 0;
+        $buff =~ /^(MAC |APETAGEX)/ or return 0;
+        $exifTool->SetFileType();
+        SetByteOrder('II');
+
+        if ($buff =~ /^APETAGEX/) {
+            # we already read the APE header
+            $header = 1;
+        } else {
+            # process the MAC header
+            my $vers = Get16u(\$buff, 4);
+            my $table;
+            if ($vers <= 3970) {
+                $buff = substr($buff, 4);
+                $table = GetTagTable('Image::ExifTool::APE::OldHeader');
+            } else {
+                my $dlen = Get32u(\$buff, 8);
+                my $hlen = Get32u(\$buff, 12);
+                unless ($dlen & 0x80000000 or $hlen & 0x80000000) {
+                    if ($raf->Seek($dlen, 0) and $raf->Read($buff, $hlen) == $hlen) {
+                        $table = GetTagTable('Image::ExifTool::APE::NewHeader');
+                    }
+                }
+            }
+            $exifTool->ProcessDirectory( { DataPt => \$buff }, $table) if $table;
+        }
+    }
+    # look for APE trailer unless we already found an APE header
+    unless ($header) {
+        # look for the APE trailer footer...
+        my $footPos = -32;
+        # (...but before the ID3v1 trailer if it exists)
+        $footPos -= 128 if $exifTool->{DONE_ID3} == 2;
+        $raf->Seek($footPos, 2)     or return 1;
+        $raf->Read($buff, 32) == 32 or return 1;
+        $buff =~ /^APETAGEX/        or return 1;
+        SetByteOrder('II');
+    }
+#
+# Read the APE data (we have just read the APE header or footer into $buff)
+#
+    my ($version, $size, $count, $flags) = unpack('x8V4', $buff);
+    $version /= 1000;
+    $size -= 32;    # get size of data only
+    if (($size & 0x80000000) == 0 and
+        ($header or $raf->Seek(-$size-32, 1)) and
+        $raf->Read($buff, $size) == $size)
+    {
+        if ($verbose) {
+            $exifTool->VerboseDir("APEv$version", $count, $size);
+            $exifTool->VerboseDump(\$buff, DataPos => $raf->Tell() - $size);
+        }
+        $tagTablePtr = GetTagTable('Image::ExifTool::APE::Main');
+        $dataPos = $raf->Tell() - $size;
+    } else {
+        $count = -1;
+    }
+#
+# Process the APE tags
+#
+    my $pos = 0;
+    for ($i=0; $i<$count; ++$i) {
+        # read next APE tag
+        last if $pos + 8 > $size;
+        my $len = Get32u(\$buff, $pos);
+        my $flags = Get32u(\$buff, $pos + 4);
+        pos($buff) = $pos + 8;
+        last unless $buff =~ /\G(.*?)\0/sg;
+        my $tag = $1;
+        $pos = pos($buff);
+        last if $pos + $len > $size;
+        my $val = substr($buff, $pos, $len);
+        MakeTag($tag, $tagTablePtr) unless $$tagTablePtr{$tag};
+        # handle binary-value tags
+        if (($flags & 0x06) == 0x02) {
+            my $buf2 = $val;
+            $val = \$buf2;
+            # extract cover art description separately (hackitty hack)
+            if ($tag =~ /^Cover Art/) {
+                $buf2 =~ s/^([\x20-\x7f]*)\0//;
+                if ($1) {
+                    my $t = "$tag Desc";
+                    my $v = $1;
+                    MakeTag($t, $tagTablePtr) unless $$tagTablePtr{$t};
+                    $exifTool->HandleTag($tagTablePtr, $t, $v);
+                }
+            }
+        }
+        $exifTool->HandleTag($tagTablePtr, $tag, $val,
+            Index => $i,
+            DataPt => \$buff,
+            DataPos => $dataPos,
+            Start => $pos,
+            Size => $len,
+        );
+        $pos += $len;
+    }
+    $i == $count or $exifTool->Warn('Bad APE trailer');
+    return 1;
+}
+
+1;  # end
+
+__END__
+
+=head1 NAME
+
+Image::ExifTool::APE - Read Monkey's Audio meta information
+
+=head1 SYNOPSIS
+
+This module is used by Image::ExifTool
+
+=head1 DESCRIPTION
+
+This module contains definitions required by Image::ExifTool to extract meta
+information from Monkey's Audio (APE) audio files.
+
+=head1 BUGS
+
+Currently doesn't parse MAC header unless it is at the start of the file.
+
+=head1 AUTHOR
+
+Copyright 2003-2007, Phil Harvey (phil at owl.phy.queensu.ca)
+
+This library is free software; you can redistribute it and/or modify it
+under the same terms as Perl itself.
+
+=head1 REFERENCES
+
+=over 4
+
+=item L<http://www.monkeysaudio.com/>
+
+=item L<http://www.personal.uni-jena.de/~pfk/mpp/sv8/apetag.html>
+
+=back
+
+=head1 SEE ALSO
+
+L<Image::ExifTool::TagNames/APE Tags>,
+L<Image::ExifTool(3pm)|Image::ExifTool>
+
+=cut
+
Index: /gsdl/trunk/perllib/cpan/Image/ExifTool/APP12.pm
===================================================================
--- /gsdl/trunk/perllib/cpan/Image/ExifTool/APP12.pm	(revision 16842)
+++ /gsdl/trunk/perllib/cpan/Image/ExifTool/APP12.pm	(revision 16842)
@@ -0,0 +1,329 @@
+#------------------------------------------------------------------------------
+# File:         APP12.pm
+#
+# Description:  Read APP12 meta information
+#
+# Revisions:    10/18/2005 - P. Harvey Created
+#
+# References:   1) Heinrich Giesen private communication
+#------------------------------------------------------------------------------
+
+package Image::ExifTool::APP12;
+
+use strict;
+use vars qw($VERSION);
+use Image::ExifTool qw(:DataAccess);
+
+$VERSION = '1.06';
+
+sub ProcessAPP12($$$);
+sub ProcessDucky($$$);
+sub WriteDucky($$$);
+
+# APP12 tags (ref PH)
+%Image::ExifTool::APP12::PictureInfo = (
+    PROCESS_PROC => \&ProcessAPP12,
+    GROUPS => { 0 => 'APP12', 1 => 'PictureInfo', 2 => 'Image' },
+    NOTES => q{
+        The JPEG APP12 "Picture Info" segment was used by some older cameras, and
+        contains ASCII-based meta information.  Below are some tags which have been
+        observed Agfa and Polaroid images, however ExifTool will extract information
+        from any tags found in this segment.
+    },
+    FNumber => {
+        ValueConv => '$val=~s/^[A-Za-z ]*//;$val',  # Agfa leads with an 'F'
+        PrintConv => 'sprintf("%.1f",$val)',
+    },
+    Aperture => {
+        PrintConv => 'sprintf("%.1f",$val)',
+    },
+    TimeDate => {
+        Name => 'DateTimeOriginal',
+        Description => 'Date/Time Original',
+        Groups => { 2 => 'Time' },
+        ValueConv => '$val=~/^\d+$/ ? ConvertUnixTime($val) : $val',
+        PrintConv => '$self->ConvertDateTime($val)',
+    },
+    Shutter => {
+        Name => 'ExposureTime',
+        ValueConv => '$val * 1e-6',
+        PrintConv => 'Image::ExifTool::Exif::PrintExposureTime($val)',
+    },
+    shtr => {
+        Name => 'ExposureTime',
+        ValueConv => '$val * 1e-6',
+        PrintConv => 'Image::ExifTool::Exif::PrintExposureTime($val)',
+    },
+   'Serial#'    => {
+        Name => 'SerialNumber',
+        Groups => { 2 => 'Camera' },
+    },
+    Flash       => { PrintConv => { 0 => 'Off', 1 => 'On' } },
+    Macro       => { PrintConv => { 0 => 'Off', 1 => 'On' } },
+    StrobeTime  => { },
+    Ytarget     => { Name => 'YTarget' },
+    ylevel      => { Name => 'YLevel' },
+    FocusPos    => { },
+    FocusMode   => { },
+    Quality     => { },
+    ExpBias     => 'ExposureCompensation',
+    FWare       => 'FirmwareVersion',
+    StrobeTime  => { },
+    Resolution  => { },
+    Protect     => { },
+    ConTake     => { },
+    ImageSize   => { PrintConv => '$val=~tr/-/x/;$val' },
+    ColorMode   => { },
+    Zoom        => { },
+    ZoomPos     => { },
+    LightS      => { },
+    Type        => {
+        Name => 'CameraType',
+        Groups => { 2 => 'Camera' },
+        DataMember => 'CameraType',
+        RawConv => '$self->{CameraType} = $val',
+    },
+    Version     => { Groups => { 2 => 'Camera' } },
+    ID          => { Groups => { 2 => 'Camera' } },
+);
+
+# APP12 segment written in Photoshop "Save For Web" images
+# (from tests with Photoshop 7 files - PH/1)
+%Image::ExifTool::APP12::Ducky = (
+    PROCESS_PROC => \&ProcessDucky,
+    WRITE_PROC => \&WriteDucky,
+    GROUPS => { 0 => 'Ducky', 1 => 'Ducky', 2 => 'Image' },
+    WRITABLE => 'string',
+    NOTES => q{
+        Photoshop uses the JPEG APP12 "Ducky" segment to store some information in
+        "Save for Web" images.
+    },
+    1 => { #PH
+        Name => 'Quality',
+        Priority => 0,
+        Avoid => 1,
+        Writable => 'int32u',
+        ValueConv => 'unpack("N",$val)',    # 4-byte integer
+        ValueConvInv => 'pack("N",$val)',
+        PrintConv => '"$val%"',
+        PrintConvInv => '$val=~/(\d+)/ ? $1 : undef',
+    },
+    2 => { #1
+        Name => 'Comment',
+        Priority => 0,
+        Avoid => 1,
+        # (ignore 4-byte character count at start of value)
+        ValueConv => '$self->Unicode2Charset(substr($val,4),"MM")',
+        ValueConvInv => 'pack("N",length $val) . $self->Charset2Unicode($val,"MM")',
+    },
+    3 => { #PH
+        Name => 'Copyright',
+        Priority => 0,
+        Avoid => 1,
+        Groups => { 2 => 'Author' },
+        # (ignore 4-byte character count at start of value)
+        ValueConv => '$self->Unicode2Charset(substr($val,4),"MM")',
+        ValueConvInv => 'pack("N",length $val) . $self->Charset2Unicode($val,"MM")',
+    },
+);
+
+#------------------------------------------------------------------------------
+# Write APP12 Ducky segment
+# Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
+# Returns: New directory data or undefined on error
+sub WriteDucky($$$)
+{
+    my ($exifTool, $dirInfo, $tagTablePtr) = @_;
+    $exifTool or return 1;    # allow dummy access to autoload this package
+    my $dataPt = $$dirInfo{DataPt};
+    my $pos = $$dirInfo{DirStart};
+    my $newTags = $exifTool->GetNewTagInfoHash($tagTablePtr);
+    my @addTags = sort { $a <=> $b } keys(%$newTags);
+    my $verbose = $exifTool->Options('Verbose');
+    my $out = $exifTool->Options('TextOut');
+    my ($dirEnd, %doneTags);
+    if ($dataPt) {
+        $dirEnd = $pos + $$dirInfo{DirLen};
+    } else {
+        my $tmp = '';
+        $dataPt = \$tmp;
+        $pos = $dirEnd = 0;
+    }
+    my $newData = '';
+    SetByteOrder('MM');
+    # process all data blocks in Ducky segment
+    for (;;) {
+        my ($tag, $len, $val);
+        if ($pos + 4 <= $dirEnd) {
+            $tag = Get16u($dataPt, $pos);
+            $len = Get16u($dataPt, $pos + 2);
+            $pos += 4;
+            if ($pos + $len > $dirEnd) {
+                $exifTool->Warn('Invalid Ducky block length');
+                return undef;
+            }
+            $val = substr($$dataPt, $pos, $len);
+            $pos += $len;
+        } else {
+            last unless @addTags;
+            $tag = pop @addTags;
+            next if $doneTags{$tag};
+        }
+        $doneTags{$tag} = 1;
+        my $tagInfo = $$newTags{$tag};
+        if ($tagInfo) {
+            my $newValueHash = $exifTool->GetNewValueHash($tagInfo);
+            my $isNew;
+            if (defined $val) {
+                if (Image::ExifTool::IsOverwriting($newValueHash, $val)) {
+                    if ($verbose > 1) {
+                        my $pval = $exifTool->Printable($val);
+                        print $out "    - Ducky:$$tagInfo{Name} = '$pval'\n";
+                    }
+                    $isNew = 1;
+                }
+            } else {
+                next unless Image::ExifTool::IsCreating($newValueHash);
+                $isNew = 1;
+            }
+            if ($isNew) {
+                $val = Image::ExifTool::GetNewValues($newValueHash);
+                ++$exifTool->{CHANGED};
+                next unless defined $val;   # next if tag is being deleted
+                if ($verbose > 1) {
+                    my $pval = $exifTool->Printable($val);
+                    print $out "    + Ducky:$$tagInfo{Name} = '$pval'\n";
+                }
+            }
+        }
+        $newData .= pack('nn', $tag, length $val) . $val;
+    }
+    $newData .= "\0\0" if length $newData;
+    return $newData;
+}
+
+#------------------------------------------------------------------------------
+# Process APP12 Ducky segment (ref PH)
+# Inputs: 0) ExifTool object reference, 1) Directory information ref, 2) tag table ref
+# Returns: 1 on success, 0 if this wasn't a recognized Ducky segment
+# Notes: This segment has the following format:
+#   1) 5 bytes: "Ducky"
+#   2) multiple data blocks (all integers are big endian):
+#      a) 2 bytes: block type (0=end, 1=Quality, 2=Comment, 3=Copyright)
+#      b) 2 bytes: block length (N)
+#      c) N bytes: block data
+sub ProcessDucky($$$)
+{
+    my ($exifTool, $dirInfo, $tagTablePtr) = @_;
+    my $dataPt = $$dirInfo{DataPt};
+    my $pos = $$dirInfo{DirStart};
+    my $dirEnd = $pos + $$dirInfo{DirLen};
+    SetByteOrder('MM');
+    # process all data blocks in Ducky segment
+    for (;;) {
+        last if $pos + 4 > $dirEnd;
+        my $tag = Get16u($dataPt, $pos);
+        my $len = Get16u($dataPt, $pos + 2);
+        $pos += 4;
+        if ($pos + $len > $dirEnd) {
+            $exifTool->Warn('Invalid Ducky block length');
+            last;
+        }
+        my $val = substr($$dataPt, $pos, $len);
+        $exifTool->HandleTag($tagTablePtr, $tag, $val,
+            DataPt => $dataPt,
+            DataPos => $$dirInfo{DataPos},
+            Start => $pos,
+            Size => $len,
+        );
+        $pos += $len;
+    }
+    return 1;
+}
+
+#------------------------------------------------------------------------------
+# Process APP12 Picture Info segment (ref PH)
+# Inputs: 0) ExifTool object reference, 1) Directory information ref, 2) tag table ref
+# Returns: 1 on success, 0 if this wasn't a recognized APP12
+sub ProcessAPP12($$$)
+{
+    my ($exifTool, $dirInfo, $tagTablePtr) = @_;
+    my $dataPt = $$dirInfo{DataPt};
+    my $dirStart = $$dirInfo{DirStart} || 0;
+    my $dirLen = $$dirInfo{DirLen} || (length($$dataPt) - $dirStart);
+    if ($dirLen != $dirStart + length($$dataPt)) {
+        my $buff = substr($$dataPt, $dirStart, $dirLen);
+        $dataPt = \$buff;
+    } else {
+        pos($$dataPt) = $$dirInfo{DirStart};
+    }
+    my $verbose = $exifTool->Options('Verbose');
+    my $success = 0;
+    my $section = '';
+    pos($$dataPt) = 0;
+
+    # this regular expression is a bit complex, but basically we are looking for
+    # section headers (ie. "[Camera Info]") and tag/value pairs (ie. "tag=value",
+    # where "value" may contain white space), separated by spaces or CR/LF.
+    # (APP12 uses CR/LF, but Olympus TextualInfo is similar and uses spaces)
+    while ($$dataPt =~ /(\[.*?\]|[\w#-]+=[\x20-\x7e]+?(?=\s*([\n\r\0]|[\w#-]+=|\[|$)))/g) {
+        my $token = $1;
+        # was this a section name?
+        if ($token =~ /^\[(.*)\]/) {
+            $exifTool->VerboseDir($1) if $verbose;
+            $section = ($token =~ /\[(\S+) ?Info\]/i) ? $1 : '';
+            $success = 1;
+            next;
+        }
+        $exifTool->VerboseDir($$dirInfo{DirName}) if $verbose and not $success;
+        $success = 1;
+        my ($tag, $val) = ($token =~ /(\S+)=(.+)/);
+        my $tagInfo = $exifTool->GetTagInfo($tagTablePtr, $tag);
+        $verbose and $exifTool->VerboseInfo($tag, $tagInfo, Value => $val);
+        unless ($tagInfo) {
+            # add new tag to table
+            $tagInfo = { Name => $tag };
+            # put in Camera group if information in "Camera" section
+            $$tagInfo{Groups} = { 2 => 'Camera' } if $section =~ /camera/i;
+            Image::ExifTool::AddTagToTable($tagTablePtr, $tag, $tagInfo);
+        }
+        $exifTool->FoundTag($tagInfo, $val);
+    }
+    return $success;
+}
+
+
+1;  #end
+
+__END__
+
+=head1 NAME
+
+Image::ExifTool::APP12 - Read APP12 meta information
+
+=head1 SYNOPSIS
+
+This module is loaded automatically by Image::ExifTool when required.
+
+=head1 DESCRIPTION
+
+This module contains definitions required by Image::ExifTool to interpret
+APP12 meta information.
+
+=head1 AUTHOR
+
+Copyright 2003-2007, Phil Harvey (phil at owl.phy.queensu.ca)
+
+This library is free software; you can redistribute it and/or modify it
+under the same terms as Perl itself.
+
+=head1 ACKNOWLEDGEMENTS
+
+Thanks to Heinrich Giesen for his help decoding APP12 "Ducky" information.
+
+=head1 SEE ALSO
+
+L<Image::ExifTool::TagNames/APP12 Tags>,
+L<Image::ExifTool(3pm)|Image::ExifTool>
+
+=cut
Index: /gsdl/trunk/perllib/cpan/Image/ExifTool/ASF.pm
===================================================================
--- /gsdl/trunk/perllib/cpan/Image/ExifTool/ASF.pm	(revision 16842)
+++ /gsdl/trunk/perllib/cpan/Image/ExifTool/ASF.pm	(revision 16842)
@@ -0,0 +1,981 @@
+#------------------------------------------------------------------------------
+# File:         ASF.pm
+#
+# Description:  Read ASF/WMA/WMV meta information
+#
+# Revisions:    12/23/2005 - P. Harvey Created
+#
+# References:   1) http://www.microsoft.com/windows/windowsmedia/format/asfspec.aspx
+#------------------------------------------------------------------------------
+
+package Image::ExifTool::ASF;
+
+use strict;
+use vars qw($VERSION);
+use Image::ExifTool qw(:DataAccess :Utils);
+use Image::ExifTool::Exif;
+
+$VERSION = '1.05';
+
+sub ProcessMetadata($$$);
+sub ProcessContentDescription($$$);
+sub ProcessPreview($$$);
+sub ProcessCodecList($$$);
+
+# GUID definitions
+my %errorCorrection = (
+    '20FB5700-5B55-11CF-A8FD-00805F5C442B' => 'No Error Correction',
+    'BFC3CD50-618F-11CF-8BB2-00AA00B4E220' => 'Audio Spread',
+);
+
+my %streamType = (
+    'F8699E40-5B4D-11CF-A8FD-00805F5C442B' => 'Audio',
+    'BC19EFC0-5B4D-11CF-A8FD-00805F5C442B' => 'Video',
+    '59DACFC0-59E6-11D0-A3AC-00A0C90348F6' => 'Command',
+    'B61BE100-5B4E-11CF-A8FD-00805F5C442B' => 'JFIF',
+    '35907DE0-E415-11CF-A917-00805F5C442B' => 'Degradable JPEG',
+    '91BD222C-F21C-497A-8B6D-5AA86BFC0185' => 'File Transfer',
+    '3AFB65E2-47EF-40F2-AC2C-70A90D71D343' => 'Binary',
+);
+
+my %mutex = (
+    'D6E22A00-35DA-11D1-9034-00A0C90349BE' => 'MutexLanguage',
+    'D6E22A01-35DA-11D1-9034-00A0C90349BE' => 'MutexBitrate',
+    'D6E22A02-35DA-11D1-9034-00A0C90349BE' => 'MutexUnknown',
+);
+
+my %bandwidthSharing = (
+    'AF6060AA-5197-11D2-B6AF-00C04FD908E9' => 'SharingExclusive',
+    'AF6060AB-5197-11D2-B6AF-00C04FD908E9' => 'SharingPartial',
+);
+
+my %typeSpecific = (
+    '776257D4-C627-41CB-8F81-7AC7FF1C40CC' => 'WebStreamMediaSubtype',
+    'DA1E6B13-8359-4050-B398-388E965BF00C' => 'WebStreamFormat',
+);
+
+my %advancedContentEncryption = (
+    '7A079BB6-DAA4-4e12-A5CA-91D38DC11A8D' => 'DRMNetworkDevices',
+);
+
+# ASF top level objects
+%Image::ExifTool::ASF::Main = (
+    PROCESS_PROC => \&Image::ExifTool::ASF::ProcessASF,
+    NOTES => q{
+        ASF format is used by Windows WMA and WMV files.  Tag ID's aren't listed
+        because they are huge 128-bit GUID's that would ruin the formatting of this
+        table.
+    },
+    '75B22630-668E-11CF-A6D9-00AA0062CE6C' => {
+        Name => 'Header',
+        SubDirectory => { TagTable => 'Image::ExifTool::ASF::Header', Size => 6 },
+    },
+    '75B22636-668E-11CF-A6D9-00AA0062CE6C' => 'Data',
+    '33000890-E5B1-11CF-89F4-00A0C90349CB' => 'SimpleIndex',
+    'D6E229D3-35DA-11D1-9034-00A0C90349BE' => 'Index',
+    'FEB103F8-12AD-4C64-840F-2A1D2F7AD48C' => 'MediaIndex',
+    '3CB73FD0-0C4A-4803-953D-EDF7B6228F0C' => 'TimecodeIndex',
+);
+
+# ASF header objects
+%Image::ExifTool::ASF::Header = (
+    PROCESS_PROC => \&Image::ExifTool::ASF::ProcessASF,
+    '8CABDCA1-A947-11CF-8EE4-00C00C205365' => {
+        Name => 'FileProperties',
+        SubDirectory => { TagTable => 'Image::ExifTool::ASF::FileProperties' },
+    },
+    'B7DC0791-A9B7-11CF-8EE6-00C00C205365' => {
+        Name => 'StreamProperties',
+        SubDirectory => { TagTable => 'Image::ExifTool::ASF::StreamProperties' },
+    },
+    '5FBF03B5-A92E-11CF-8EE3-00C00C205365' => {
+        Name => 'HeaderExtension',
+        SubDirectory => { TagTable => 'Image::ExifTool::ASF::HeaderExtension', Size => 22 },
+    },
+    '86D15240-311D-11D0-A3A4-00A0C90348F6' => {
+        Name => 'CodecList',
+        SubDirectory => { TagTable => 'Image::ExifTool::ASF::CodecList' },
+    },
+    '1EFB1A30-0B62-11D0-A39B-00A0C90348F6' => 'ScriptCommand',
+    'F487CD01-A951-11CF-8EE6-00C00C205365' => 'Marker',
+    'D6E229DC-35DA-11D1-9034-00A0C90349BE' => 'BitrateMutualExclusion',
+    '75B22635-668E-11CF-A6D9-00AA0062CE6C' => 'ErrorCorrection',
+    '75B22633-668E-11CF-A6D9-00AA0062CE6C' => {
+        Name => 'ContentDescription',
+        SubDirectory => { TagTable => 'Image::ExifTool::ASF::ContentDescr' },
+    },
+    '2211B3FA-BD23-11D2-B4B7-00A0C955FC6E' => {
+        Name => 'ContentBranding',
+        SubDirectory => { TagTable => 'Image::ExifTool::ASF::ContentBranding' },
+    },
+    'D2D0A440-E307-11D2-97F0-00A0C95EA850' => {
+        Name => 'ExtendedContentDescr',
+        SubDirectory => { TagTable => 'Image::ExifTool::ASF::ExtendedDescr' },
+    },
+    '7BF875CE-468D-11D1-8D82-006097C9A2B2' => 'StreamBitrateProps',
+    '2211B3FB-BD23-11D2-B4B7-00A0C955FC6E' => 'ContentEncryption',
+    '298AE614-2622-4C17-B935-DAE07EE9289C' => 'ExtendedContentEncryption',
+    '2211B3FC-BD23-11D2-B4B7-00A0C955FC6E' => 'DigitalSignature',
+    '1806D474-CADF-4509-A4BA-9AABCB96AAE8' => 'Padding',
+);
+
+%Image::ExifTool::ASF::ContentDescr = (
+    PROCESS_PROC => \&ProcessContentDescription,
+    GROUPS => { 2 => 'Video' },
+    0 => 'Title',
+    1 => { Name => 'Author', Groups => { 2 => 'Author' } },
+    2 => { Name => 'Copyright', Groups => { 2 => 'Author' } },
+    3 => 'Description',
+    4 => 'Rating',
+);
+
+%Image::ExifTool::ASF::ContentBranding = (
+    PROCESS_PROC => \&ProcessContentBranding,
+    GROUPS => { 2 => 'Author' },
+    0 => {
+        Name => 'BannerImageType',
+        PrintConv => {
+            0 => 'None',
+            1 => 'Bitmap',
+            2 => 'JPEG',
+            3 => 'GIF',
+        },
+    },
+    1 => { Name => 'BannerImage', Binary => 1 },
+    2 => 'BannerImageURL',
+    3 => 'CopyrightURL',
+);
+
+%Image::ExifTool::ASF::ExtendedDescr = (
+    PROCESS_PROC => \&ProcessExtendedContentDescription,
+    GROUPS => { 2 => 'Video' },
+    ASFLeakyBucketPairs => { Binary => 1 },
+    AspectRatioX => {},
+    AspectRatioY => {},
+    Author => { Groups => { 2 => 'Author' } },
+    AverageLevel => {},
+    BannerImageData => {},
+    BannerImageType => {},
+    BannerImageURL => {},
+    Bitrate => {},
+    Broadcast => {},
+    BufferAverage => {},
+    Can_Skip_Backward => {},
+    Can_Skip_Forward => {},
+    Copyright => { Groups => { 2 => 'Author' } },
+    CopyrightURL => { Groups => { 2 => 'Author' } },
+    CurrentBitrate => {},
+    Description => {},
+    DRM_ContentID => {},
+    DRM_DRMHeader_ContentDistributor => {},
+    DRM_DRMHeader_ContentID => {},
+    DRM_DRMHeader_IndividualizedVersion => {},
+    DRM_DRMHeader_KeyID => {},
+    DRM_DRMHeader_LicenseAcqURL => {},
+    DRM_DRMHeader_SubscriptionContentID => {},
+    DRM_DRMHeader => {},
+    DRM_IndividualizedVersion => {},
+    DRM_KeyID => {},
+    DRM_LASignatureCert => {},
+    DRM_LASignatureLicSrvCert => {},
+    DRM_LASignaturePrivKey => {},
+    DRM_LASignatureRootCert => {},
+    DRM_LicenseAcqURL => {},
+    DRM_V1LicenseAcqURL => {},
+    Duration => {},
+    FileSize => {},
+    HasArbitraryDataStream => {},
+    HasAttachedImages => {},
+    HasAudio => {},
+    HasFileTransferStream => {},
+    HasImage => {},
+    HasScript => {},
+    HasVideo => {},
+    Is_Protected => {},
+    Is_Trusted => {},
+    IsVBR => {},
+    NSC_Address => {},
+    NSC_Description => {},
+    NSC_Email => {},
+    NSC_Name => {},
+    NSC_Phone => {},
+    NumberOfFrames => {},
+    OptimalBitrate => {},
+    PeakValue => {},
+    Rating => {},
+    Seekable => {},
+    Signature_Name => {},
+    Stridable => {},
+    Title => {},
+    VBRPeak => {},
+    # "WM/" tags...
+    AlbumArtist => {},
+    AlbumCoverURL => {},
+    AlbumTitle => {},
+    ASFPacketCount => {},
+    ASFSecurityObjectsSize => {},
+    AudioFileURL => {},
+    AudioSourceURL => {},
+    AuthorURL => { Groups => { 2 => 'Author' } },
+    BeatsPerMinute => {},
+    Category => {},
+    Codec => {},
+    Composer => {},
+    Conductor => {},
+    ContainerFormat => {},
+    ContentDistributor => {},
+    ContentGroupDescription => {},
+    Director => {},
+    DRM => {},
+    DVDID => {},
+    EncodedBy => {},
+    EncodingSettings => {},
+    EncodingTime => { Groups => { 2 => 'Time' } },
+    Genre => {},
+    GenreID => {},
+    InitialKey => {},
+    ISRC => {},
+    Language => {},
+    Lyrics => {},
+    Lyrics_Synchronised => {},
+    MCDI => {},
+    MediaClassPrimaryID => {},
+    MediaClassSecondaryID => {},
+    MediaCredits => {},
+    MediaIsDelay => {},
+    MediaIsFinale => {},
+    MediaIsLive => {},
+    MediaIsPremiere => {},
+    MediaIsRepeat => {},
+    MediaIsSAP => {},
+    MediaIsStereo => {},
+    MediaIsSubtitled => {},
+    MediaIsTape => {},
+    MediaNetworkAffiliation => {},
+    MediaOriginalBroadcastDateTime => { Groups => { 2 => 'Time' } },
+    MediaOriginalChannel => {},
+    MediaStationCallSign => {},
+    MediaStationName => {},
+    ModifiedBy => {},
+    Mood => {},
+    OriginalAlbumTitle => {},
+    OriginalArtist => {},
+    OriginalFilename => {},
+    OriginalLyricist => {},
+    OriginalReleaseTime => { Groups => { 2 => 'Time' } },
+    OriginalReleaseYear => { Groups => { 2 => 'Time' } },
+    ParentalRating => {},
+    ParentalRatingReason => {},
+    PartOfSet => {},
+    PeakBitrate => {},
+    Period => {},
+    Picture => {
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::ASF::Preview',
+        },
+    },
+    PlaylistDelay => {},
+    Producer => {},
+    PromotionURL => {},
+    ProtectionType => {},
+    Provider => {},
+    ProviderCopyright => {},
+    ProviderRating => {},
+    ProviderStyle => {},
+    Publisher => {},
+    RadioStationName => {},
+    RadioStationOwner => {},
+    SharedUserRating => {},
+    StreamTypeInfo => {},
+    SubscriptionContentID => {},
+    SubTitle => {},
+    SubTitleDescription => {},
+    Text => {},
+    ToolName => {},
+    ToolVersion => {},
+    Track => {},
+    TrackNumber => {},
+    UniqueFileIdentifier => {},
+    UserWebURL => {},
+    VideoClosedCaptioning => {},
+    VideoFrameRate => {},
+    VideoHeight => {},
+    VideoWidth => {},
+    WMADRCAverageReference => {},
+    WMADRCAverageTarget => {},
+    WMADRCPeakReference => {},
+    WMADRCPeakTarget => {},
+    WMCollectionGroupID => {},
+    WMCollectionID => {},
+    WMContentID => {},
+    Writer => { Groups => { 2 => 'Author' } },
+    Year => { Groups => { 2 => 'Time' } },
+);
+
+%Image::ExifTool::ASF::Preview = (
+    PROCESS_PROC => \&ProcessPreview,
+    GROUPS => { 2 => 'Video' },
+    0 => {
+        Name => 'PreviewType',
+        PrintConv => {
+            0 => 'Other picture type',
+            1 => '32x32 PNG file icon',
+            2 => 'Other file icon',
+            3 => 'Front album cover',
+            4 => 'Back album cover',
+            5 => 'Leaflet page',
+            6 => 'Media label',
+            7 => 'Lead artist, performer, or soloist',
+            8 => 'Artists or performers',
+            9 => 'Conductor',
+            10 => 'Band or orchestra',
+            11 => 'Composer',
+            12 => 'Lyricist or writer',
+            13 => 'Recording studio or location',
+            14 => 'Recording session',
+            15 => 'Performance',
+            16 => 'Capture from movie or video',
+            17 => 'A bright colored fish',
+            18 => 'Illustration',
+            19 => 'Band or artist logo',
+            20 => 'Publisher or studio logo',
+        },
+    },
+    1 => 'PreviewMimeType',
+    2 => 'PreviewDescription',
+    3 => {
+        Name => 'PreviewImage',
+        ValueConv => '$self->ValidateImage(\$val,$tag)',
+    },
+);
+
+%Image::ExifTool::ASF::FileProperties = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    GROUPS => { 2 => 'Video' },
+    0  => {
+        Name => 'FileID',
+        Format => 'binary[16]',
+        ValueConv => 'Image::ExifTool::ASF::GetGUID($val)',
+    },
+    16 => { Name => 'FileSize',     Format => 'int64u' },
+    24 => {
+        Name => 'CreationDate',
+        Format => 'int64u',
+        Groups => { 2 => 'Time' },
+        # time is in 100 ns intervals since 0:00 UTC Jan 1, 1601
+        ValueConv => q{ # (89 leap years between 1601 and 1970)
+            my $t = $val / 1e7 - (((1970-1601)*365+89)*24*3600);
+            return Image::ExifTool::ConvertUnixTime($t) . 'Z';
+        }
+    },
+    32 => { Name => 'DataPackets',  Format => 'int64u' },
+    40 => {
+        Name => 'PlayDuration',
+        Format => 'int64u',
+        ValueConv => '$val / 1e7',
+        PrintConv => '"$val sec"',
+    },
+    48 => {
+        Name => 'SendDuration',
+        Format => 'int64u',
+        ValueConv => '$val / 1e7',
+        PrintConv => '"$val sec"',
+    },
+    56 => { Name => 'Preroll',      Format => 'int64u' },
+    64 => { Name => 'Flags',        Format => 'int32u' },
+    68 => { Name => 'MinPacketSize',Format => 'int32u' },
+    72 => { Name => 'MaxPacketSize',Format => 'int32u' },
+    76 => { Name => 'MaxBitrate',   Format => 'int32u' },
+);
+
+%Image::ExifTool::ASF::StreamProperties = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    GROUPS => { 2 => 'Video' },
+    NOTES => 'Tags with index 54 and greater are conditional based on the StreamType.',
+    0  => {
+        Name => 'StreamType',
+        Format => 'binary[16]',
+        RawConv => sub { # set ASF_STREAM_TYPE for use in conditional tags
+            my ($val, $exifTool) = @_;
+            $exifTool->{ASF_STREAM_TYPE} = $streamType{GetGUID($val)} || '';
+            return $val;
+        },
+        ValueConv => 'Image::ExifTool::ASF::GetGUID($val)',
+        PrintConv => \%streamType,
+    },
+    16 => {
+        Name => 'ErrorCorrectionType',
+        Format => 'binary[16]',
+        ValueConv => 'Image::ExifTool::ASF::GetGUID($val)',
+        PrintConv => \%errorCorrection,
+    },
+    32 => {
+        Name => 'TimeOffset',
+        Format => 'int64u',
+        ValueConv => '$val / 1e7',
+        PrintConv => '"$val sec"',
+    },
+    48 => {
+        Name => 'StreamNumber',
+        Format => 'int16u',
+        PrintConv => '($val & 0x7f) . ($val & 0x8000 ? " (encrypted)" : "")',
+    },
+    54 => [
+        {
+            Condition => '$self->{ASF_STREAM_TYPE} eq "Audio"',
+            Name => 'AudioCodecID',
+            Format => 'int16u',
+            PrintHex => 1,
+            # from http://www.iana.org/assignments/wave-avi-codec-registry
+            # and http://developers.videolan.org/vlc/vlc/doc/doxygen/html/codecs_8h-source.html
+            PrintConv => {
+                0x0001 => 'Microsoft PCM',
+                0x0002 => 'Microsoft ADPCM',
+                0x0003 => 'IEEE Float',
+                0x0004 => 'Cmpaq VSELP',
+                0x0005 => 'IBM CVSD',
+                0x0006 => 'Microsoft A-Law',
+                0x0007 => 'Microsoft Mu-Law',
+                0x0008 => 'DTS Coherent Acoustics 1',
+                0x0010 => 'OKI ADPCM',
+                0x0011 => 'Intel DVI ADPCM',
+                0x0012 => 'Videologic MediaSpace ADPCM',
+                0x0013 => 'Sierra ADPCM',
+                0x0014 => 'G.723 ADPCM',
+                0x0015 => 'DSP Solution DIGISTD',
+                0x0016 => 'DSP Solution DIGIFIX',
+                0x0017 => 'Dialogic OKI ADPCM',
+                0x0018 => 'MediaVision ADPCM',
+                0x0019 => 'HP CU',
+                0x0020 => 'Yamaha ADPCM',
+                0x0021 => 'Speech Compression Sonarc',
+                0x0022 => 'DSP Group True Speech',
+                0x0023 => 'Echo Speech EchoSC1',
+                0x0024 => 'Audiofile AF36',
+                0x0025 => 'APTX',
+                0x0026 => 'AudioFile AF10',
+                0x0027 => 'Prosody 1612',
+                0x0028 => 'LRC',
+                0x0030 => 'Dolby AC2',
+                0x0031 => 'GSM610',
+                0x0032 => 'MSNAudio',
+                0x0033 => 'Antex ADPCME',
+                0x0034 => 'Control Res VQLPC',
+                0x0035 => 'Digireal',
+                0x0036 => 'DigiADPCM',
+                0x0037 => 'Control Res CR10',
+                0x0038 => 'NMS VBXADPCM',
+                0x0039 => 'Roland RDAC',
+                0x003a => 'EchoSC3',
+                0x003b => 'Rockwell ADPCM',
+                0x003c => 'Rockwell Digit LK',
+                0x003d => 'Xebec',
+                0x0040 => 'Antex Electronics G.721',
+                0x0041 => 'G.728 CELP',
+                0x0042 => 'MSG723',
+                0x0045 => 'G.726 ADPCM',
+                0x0050 => 'MPEG',
+                0x0052 => 'RT24',
+                0x0053 => 'PAC',
+                0x0055 => 'MPEG Layer 3',
+                0x0059 => 'Lucent G.723',
+                0x0060 => 'Cirrus',
+                0x0061 => 'ESPCM',
+                0x0062 => 'Voxware',
+                0x0063 => 'Canopus Atrac',
+                0x0064 => 'G.726 ADPCM',
+                0x0066 => 'DSAT',
+                0x0067 => 'DSAT Display',
+                0x0069 => 'Voxware Byte Aligned',
+                0x0070 => 'Voxware AC8',
+                0x0071 => 'Voxware AC10',
+                0x0072 => 'Voxware AC16',
+                0x0073 => 'Voxware AC20',
+                0x0074 => 'Voxware MetaVoice',
+                0x0075 => 'Voxware MetaSound',
+                0x0076 => 'Voxware RT29HW',
+                0x0077 => 'Voxware VR12',
+                0x0078 => 'Voxware VR18',
+                0x0079 => 'Voxware TQ40',
+                0x0080 => 'Softsound',
+                0x0081 => 'Voxware TQ60',
+                0x0082 => 'MSRT24',
+                0x0083 => 'G.729A',
+                0x0084 => 'MVI MV12',
+                0x0085 => 'DF G.726',
+                0x0086 => 'DF GSM610',
+                0x0088 => 'ISIAudio',
+                0x0089 => 'Onlive',
+                0x0091 => 'SBC24',
+                0x0092 => 'Dolby AC3 SPDIF',
+                0x0097 => 'ZyXEL ADPCM',
+                0x0098 => 'Philips LPCBB',
+                0x0099 => 'Packed',
+                0x00FF => 'MPEG-4',
+                0x0100 => 'Rhetorex ADPCM',
+                0x0101 => 'BeCubed Software IRAT',
+                0x0111 => 'Vivo G.723',
+                0x0112 => 'Vivo Siren',
+                0x0123 => 'Digital G.723',
+                0x0160 => 'Windows Media v1',
+                0x0161 => 'Windows Media v2',
+                0x0162 => 'Windows Media 9 Professional',
+                0x0163 => 'Windows Media 9 Lossless',
+                0x0200 => 'Creative ADPCM',
+                0x0202 => 'Creative FastSpeech8',
+                0x0203 => 'Creative FastSpeech10',
+                0x0220 => 'Quarterdeck',
+                0x0300 => 'FM Towns Snd',
+                0x0400 => 'BTV Digital',
+                0x0680 => 'VME VMPCM',
+                0x1000 => 'OLIGSM',
+                0x1001 => 'OLIADPCM',
+                0x1002 => 'OLICELP',
+                0x1003 => 'OLISBC',
+                0x1004 => 'OLIOPR',
+                0x1100 => 'LH Codec',
+                0x1400 => 'Norris',
+                0x1401 => 'ISIAudio',
+                0x1500 => 'Soundspace Music Compression',
+                0x2000 => 'DVM',
+                0x2001 => 'DTS Coherent Acoustics 2',
+                0x4143 => 'MPEG-4 (Divio)',
+            },
+        },
+        {
+            Condition => '$self->{ASF_STREAM_TYPE} =~ /^(Video|JFIF|Degradable JPEG)$/',
+            Name => 'ImageWidth',
+            Format => 'int32u',
+        },
+    ],
+    56 => {
+        Condition => '$self->{ASF_STREAM_TYPE} eq "Audio"',
+        Name => 'AudioChannels',
+        Format => 'int16u',
+    },
+    58 => [
+        {
+            Condition => '$self->{ASF_STREAM_TYPE} eq "Audio"',
+            Name => 'AudioSampleRate',
+            Format => 'int32u',
+        },
+        {
+            Condition => '$self->{ASF_STREAM_TYPE} =~ /^(Video|JFIF|Degradable JPEG)$/',
+            Name => 'ImageHeight',
+            Format => 'int32u',
+        },
+    ],
+);
+
+%Image::ExifTool::ASF::HeaderExtension = (
+    PROCESS_PROC => \&Image::ExifTool::ASF::ProcessASF,
+    '14E6A5CB-C672-4332-8399-A96952065B5A' => 'ExtendedStreamProps',
+    'A08649CF-4775-4670-8A16-6E35357566CD' => 'AdvancedMutualExcl',
+    'D1465A40-5A79-4338-B71B-E36B8FD6C249' => 'GroupMutualExclusion',
+    'D4FED15B-88D3-454F-81F0-ED5C45999E24' => 'StreamPrioritization',
+    'A69609E6-517B-11D2-B6AF-00C04FD908E9' => 'BandwidthSharing',
+    '7C4346A9-EFE0-4BFC-B229-393EDE415C85' => 'LanguageList',
+    'C5F8CBEA-5BAF-4877-8467-AA8C44FA4CCA' => {
+        Name => 'Metadata',
+        SubDirectory => { TagTable => 'Image::ExifTool::ASF::Metadata' },
+    },
+    '44231C94-9498-49D1-A141-1D134E457054' => {
+        Name => 'MetadataLibrary',
+        SubDirectory => { TagTable => 'Image::ExifTool::ASF::Metadata' },
+    },
+    'D6E229DF-35DA-11D1-9034-00A0C90349BE' => 'IndexParameters',
+    '6B203BAD-3F11-48E4-ACA8-D7613DE2CFA7' => 'TimecodeIndexParms',
+    '75B22630-668E-11CF-A6D9-00AA0062CE6C' => 'Compatibility',
+    '43058533-6981-49E6-9B74-AD12CB86D58C' => 'AdvancedContentEncryption',
+    'ABD3D211-A9BA-11cf-8EE6-00C00C205365' => 'Reserved1',
+);
+
+%Image::ExifTool::ASF::Metadata = (
+    PROCESS_PROC => \&Image::ExifTool::ASF::ProcessMetadata,
+);
+
+%Image::ExifTool::ASF::CodecList = (
+    PROCESS_PROC => \&ProcessCodecList,
+    VideoCodecName => {},
+    VideoCodecDescription => {},
+    AudioCodecName => {},
+    AudioCodecDescription => {},
+    OtherCodecName => {},
+    OtherCodecDescription => {},
+);
+
+#------------------------------------------------------------------------------
+# Generate GUID from 16 bytes of binary data
+# Inputs: 0) data
+# Returns: GUID
+sub GetGUID($)
+{
+    # must do some byte swapping
+    my $buff = unpack('H*',pack('NnnNN',unpack('VvvNN',$_[0])));
+    $buff =~ s/(.{8})(.{4})(.{4})(.{4})/$1-$2-$3-$4-/;
+    return uc($buff);
+}
+
+#------------------------------------------------------------------------------
+# Process ASF content description
+# Inputs: 0) ExifTool object reference, 1) dirInfo ref, 2) tag table reference
+# Returns: 1 on success
+sub ProcessContentDescription($$$)
+{
+    my ($exifTool, $dirInfo, $tagTablePtr) = @_;
+    my $verbose = $exifTool->Options('Verbose');
+    my $dataPt = $$dirInfo{DataPt};
+    my $dirLen = $$dirInfo{DirLen};
+    return 0 if $dirLen < 10;
+    my @len = unpack('v5', $$dataPt);
+    my $pos = 10;
+    my $tag;
+    foreach $tag (0..4) {
+        my $len = shift @len;
+        next unless $len;
+        return 0 if $pos + $len > $dirLen;
+        my $val = $exifTool->Unicode2Charset(substr($$dataPt,$pos,$len),'II');
+        $exifTool->HandleTag($tagTablePtr, $tag, $val);
+        $pos += $len;
+    }
+    return 1;
+}
+
+#------------------------------------------------------------------------------
+# Process ASF content branding
+# Inputs: 0) ExifTool object reference, 1) dirInfo ref, 2) tag table reference
+# Returns: 1 on success
+sub ProcessContentBranding($$$)
+{
+    my ($exifTool, $dirInfo, $tagTablePtr) = @_;
+    my $verbose = $exifTool->Options('Verbose');
+    my $dataPt = $$dirInfo{DataPt};
+    my $dirLen = $$dirInfo{DirLen};
+    return 0 if $dirLen < 40;
+    # decode banner image type
+    $exifTool->HandleTag($tagTablePtr, 0, unpack('V', $$dataPt));
+    # decode banner image, banner URL and copyright URL
+    my $pos = 4;
+    my $tag;
+    foreach $tag (1..3) {
+        return 0 if $pos + 4 > $dirLen;
+        my $size = unpack("x${pos}V", $$dataPt);
+        $pos += 4;
+        next unless $size;
+        return 0 if $pos + $size > $dirLen;
+        my $val = substr($$dataPt, $pos, $size);
+        $exifTool->HandleTag($tagTablePtr, $tag, $val);
+        $pos += $size;
+    }
+    return 1;
+}
+
+#------------------------------------------------------------------------------
+# Read ASF value
+# Inputs: 0) ExifTool object ref, 1) data reference, 2) value offset,
+#         3) format number, 4) size
+# Returns: converted value
+sub ReadASF($$$$$)
+{
+    my ($exifTool, $dataPt, $pos, $format, $size) = @_;
+    my @vals;
+    if ($format == 0) { # unicode string
+        $vals[0] = $exifTool->Unicode2Charset(substr($$dataPt,$pos,$size),'II');
+    } elsif ($format == 2) { # 4-byte boolean
+        @vals = ReadValue($dataPt, $pos, 'int32u', undef, $size);
+        foreach (@vals) {
+            $_ = $_ ? 'True' : 'False';
+        }
+    } elsif ($format == 3) { # int32u
+        @vals = ReadValue($dataPt, $pos, 'int32u', undef, $size);
+    } elsif ($format == 4) { # int64u
+        @vals = ReadValue($dataPt, $pos, 'int64u', undef, $size);
+    } elsif ($format == 5) { # int16u
+        @vals = ReadValue($dataPt, $pos, 'int16u', undef, $size);
+    } else { # any other format (including 1, byte array): return raw data
+        $vals[0] = substr($$dataPt,$pos,$size);
+    }
+    return join ' ', @vals;
+}
+
+#------------------------------------------------------------------------------
+# Process extended content description
+# Inputs: 0) ExifTool object reference, 1) dirInfo ref, 2) tag table reference
+# Returns: 1 on success
+sub ProcessExtendedContentDescription($$$)
+{
+    my ($exifTool, $dirInfo, $tagTablePtr) = @_;
+    my $verbose = $exifTool->Options('Verbose');
+    my $dataPt = $$dirInfo{DataPt};
+    my $dirLen = $$dirInfo{DirLen};
+    return 0 if $dirLen < 2;
+    my $count = Get16u($dataPt, 0);
+    $exifTool->VerboseDir($dirInfo, $count);
+    my $pos = 2;
+    my $i;
+    for ($i=0; $i<$count; ++$i) {
+        return 0 if $pos + 6 > $dirLen;
+        my $nameLen = unpack("x${pos}v", $$dataPt);
+        $pos += 2;
+        return 0 if $pos + $nameLen + 4 > $dirLen;
+        my $tag = Image::ExifTool::Unicode2Latin(substr($$dataPt,$pos,$nameLen),'v');
+        $tag =~ s/^WM\///; # remove leading "WM/"
+        $pos += $nameLen;
+        my ($dType, $dLen) = unpack("x${pos}v2", $$dataPt);
+        my $val = ReadASF($exifTool,$dataPt,$pos+4,$dType,$dLen);
+        $exifTool->HandleTag($tagTablePtr, $tag, $val,
+            DataPt => $dataPt,
+            Start  => $pos + 4,
+            Size   => $dLen,
+        );
+        $pos += 4 + $dLen;
+    }
+    return 1;
+}
+
+#------------------------------------------------------------------------------
+# Process WM/Picture preview
+# Inputs: 0) ExifTool object reference, 1) dirInfo ref, 2) tag table reference
+# Returns: 1 on success
+sub ProcessPreview($$$)
+{
+    my ($exifTool, $dirInfo, $tagTablePtr) = @_;
+    my $verbose = $exifTool->Options('Verbose');
+    my $dataPt = $$dirInfo{DataPt};
+    my $dirStart = $$dirInfo{DirStart};
+    my $dirLen = $$dirInfo{DirLen};
+    return 0 unless $dirLen > 9;
+    # extract picture type and length
+    my ($type, $picLen) = unpack("x${dirStart}CV", $$dataPt);
+    $exifTool->HandleTag($tagTablePtr, 0, $type);
+    # extract mime type and description strings (null-terminated unicode strings)
+    my $n = $dirLen - 5 - $picLen;
+    return 0 if $n & 0x01 or $n < 4;
+    my $str = substr($$dataPt, $dirStart+5, $n);
+    if ($str =~ /^((?:..)*?)\0\0((?:..)*?)\0\0/) {
+        my ($mime, $desc) = ($1, $2);
+        $exifTool->HandleTag($tagTablePtr, 1, $mime);
+        $exifTool->HandleTag($tagTablePtr, 2, $desc) if length $desc;
+    }
+    $exifTool->HandleTag($tagTablePtr, 3, substr($$dataPt, $dirStart+5+$n, $picLen));
+    return 1;
+}
+
+#------------------------------------------------------------------------------
+# Process codec list
+# Inputs: 0) ExifTool object reference, 1) dirInfo ref, 2) tag table reference
+# Returns: 1 on success
+sub ProcessCodecList($$$)
+{
+    my ($exifTool, $dirInfo, $tagTablePtr) = @_;
+    my $verbose = $exifTool->Options('Verbose');
+    my $dataPt = $$dirInfo{DataPt};
+    my $dirLen = $$dirInfo{DirLen};
+    return 0 if $dirLen < 20;
+    my $count = Get32u($dataPt, 16);
+    $exifTool->VerboseDir($dirInfo, $count);
+    my $pos = 20;
+    my $i;
+    my %codecType = ( 1 => 'Video', 2 => 'Audio' );
+    for ($i=0; $i<$count; ++$i) {
+        return 0 if $pos + 8 > $dirLen;
+        my $type = ($codecType{Get16u($dataPt, $pos)} || 'Other') . 'Codec';
+        # stupid Windows programmers: these lengths are in characters (others are in bytes)
+        my $nameLen = Get16u($dataPt, $pos + 2) * 2;
+        $pos += 4;
+        return 0 if $pos + $nameLen + 2 > $dirLen;
+        my $name = $exifTool->Unicode2Charset(substr($$dataPt,$pos,$nameLen),'II');
+        $exifTool->HandleTag($tagTablePtr, "${type}Name", $name);
+        my $descLen = Get16u($dataPt, $pos + $nameLen) * 2;
+        $pos += $nameLen + 2;
+        return 0 if $pos + $descLen + 2 > $dirLen;
+        my $desc = $exifTool->Unicode2Charset(substr($$dataPt,$pos,$descLen),'II');
+        $exifTool->HandleTag($tagTablePtr, "${type}Description", $desc);
+        my $infoLen = Get16u($dataPt, $pos + $descLen);
+        $pos += $descLen + 2 + $infoLen;
+    }
+    return 1;
+}
+
+#------------------------------------------------------------------------------
+# Process ASF metadata library
+# Inputs: 0) ExifTool object reference, 1) dirInfo ref, 2) tag table reference
+# Returns: 1 on success
+sub ProcessMetadata($$$)
+{
+    my ($exifTool, $dirInfo, $tagTablePtr) = @_;
+    my $verbose = $exifTool->Options('Verbose');
+    my $dataPt = $$dirInfo{DataPt};
+    my $dirLen = $$dirInfo{DirLen};
+    return 0 if $dirLen < 2;
+    my $count = Get16u($dataPt, 0);
+    $exifTool->VerboseDir($dirInfo, $count);
+    my $pos = 2;
+    my $i;
+    for ($i=0; $i<$count; ++$i) {
+        return 0 if $pos + 12 > $dirLen;
+        my ($index, $stream, $nameLen, $dType, $dLen) = unpack("x${pos}v4V", $$dataPt);
+        $pos += 12;
+        return 0 if $pos + $nameLen + $dLen > $dirLen;
+        my $tag = Image::ExifTool::Unicode2Latin(substr($$dataPt,$pos,$nameLen),'v');
+        my $val = ReadASF($exifTool,$dataPt,$pos+$nameLen,$dType,$dLen);
+        $exifTool->HandleTag($tagTablePtr, $tag, $val,
+            DataPt => $dataPt,
+            Start  => $pos,
+            Size   => $dLen,
+        );
+        $pos += $nameLen + $dLen;
+    }
+    return 1;
+}
+
+#------------------------------------------------------------------------------
+# Extract information from a ASF file
+# Inputs: 0) ExifTool object reference, 1) dirInfo reference, 2) tag table ref
+# Returns: 1 on success, 0 if this wasn't a valid ASF file
+sub ProcessASF($$;$)
+{
+    my ($exifTool, $dirInfo, $tagTablePtr) = @_;
+    my $raf = $$dirInfo{RAF};
+    my $verbose = $exifTool->Options('Verbose');
+    my $rtnVal = 0;
+    my $pos = 0;
+    my ($buff, $err, @parentTable, @childEnd, %dumpParms);
+
+    if ($verbose > 2) {
+        $dumpParms{MaxLen} = 96 unless $verbose > 3;
+        $dumpParms{Prefix} = $$exifTool{INDENT};
+        $dumpParms{Out} = $exifTool->Options('TextOut');
+    }
+    for (;;) {
+        last unless $raf->Read($buff, 24) == 24;
+        $pos += 24;
+        my $tag = GetGUID($buff);
+        unless ($tagTablePtr) {
+            # verify this is a valid ASF file
+            last unless $tag eq '75B22630-668E-11CF-A6D9-00AA0062CE6C';
+            my $fileType = $exifTool->{FILE_EXT};
+            $fileType = 'ASF' unless $fileType and $fileType =~ /^(ASF|WMV|WMA)$/;
+            $exifTool->SetFileType($fileType);
+            SetByteOrder('II');
+            $tagTablePtr = GetTagTable('Image::ExifTool::ASF::Main');
+            $rtnVal = 1;
+        }
+        my $size = Image::ExifTool::Get64u(\$buff, 16) - 24;
+        if ($size < 0) {
+            $err = 'Invalid ASF object size';
+            last;
+        }
+        if ($size > 0x7fffffff) {
+            $err = 'Large ASF objects not supported';
+            last;
+        }
+        # go back to parent tag table if done with previous children
+        if (@childEnd and $pos >= $childEnd[-1]) {
+            pop @childEnd;
+            $tagTablePtr = pop @parentTable;
+            $exifTool->{INDENT} = substr($exifTool->{INDENT},0,-2);
+            $dumpParms{Prefix} = $exifTool->{INDENT};
+        }
+        my $tagInfo = $exifTool->GetTagInfo($tagTablePtr, $tag);
+        $verbose and $exifTool->VerboseInfo($tag, $tagInfo);
+        if ($tagInfo) {
+            my $subdir = $$tagInfo{SubDirectory};
+            if ($subdir) {
+                my $subTable = GetTagTable($$subdir{TagTable});
+                if ($$subTable{PROCESS_PROC} eq \&ProcessASF) {
+                    if (defined $$subdir{Size}) {
+                        my $s = $$subdir{Size};
+                        if ($verbose > 2) {
+                            $raf->Read($buff, $s) == $s or $err = 'Truncated file', last;
+                            Image::ExifTool::HexDump(\$buff, undef, %dumpParms);
+                        } elsif (not $raf->Seek($s, 1)) {
+                            $err = 'Seek error';
+                            last;
+                        }
+                        # continue processing linearly using subTable
+                        push @parentTable, $tagTablePtr;
+                        push @childEnd, $pos + $size;
+                        $tagTablePtr = $subTable;
+                        $pos += $$subdir{Size};
+                        if ($verbose) {
+                            $exifTool->{INDENT} .= '| ';
+                            $dumpParms{Prefix} = $exifTool->{INDENT};
+                            $exifTool->VerboseDir($$tagInfo{Name});
+                        }
+                        next;
+                    }
+                } elsif ($raf->Read($buff, $size) == $size) {
+                    my %subdirInfo = (
+                        DataPt => \$buff,
+                        DirStart => 0,
+                        DirLen => $size,
+                        DirName => $$tagInfo{Name},
+                    );
+                    if ($verbose > 2) {
+                        Image::ExifTool::HexDump(\$buff, undef, %dumpParms);
+                    }
+                    unless ($exifTool->ProcessDirectory(\%subdirInfo, $subTable)) {
+                        $exifTool->Warn("Error processing $$tagInfo{Name} directory");
+                    }
+                    $pos += $size;
+                    next;
+                } else {
+                    $err = 'Unexpected end of file';
+                    last;
+                }
+            }
+        }
+        if ($verbose > 2) {
+            $raf->Read($buff, $size) == $size or $err = 'Truncated file', last;
+            Image::ExifTool::HexDump(\$buff, undef, %dumpParms);
+        } elsif (not $raf->Seek($size, 1)) { # skip the block
+            $err = 'Seek error';
+            last;
+        }
+        $pos += $size;
+    }
+    $err and $exifTool->Warn($err);
+    return $rtnVal;
+}
+
+1;  # end
+
+__END__
+
+=head1 NAME
+
+Image::ExifTool::ASF - Read ASF/WMA/WMV meta information
+
+=head1 SYNOPSIS
+
+This module is used by Image::ExifTool
+
+=head1 DESCRIPTION
+
+This module contains routines required by Image::ExifTool to extract
+information from Microsoft Advanced Systems Format (ASF) files, including
+Windows Media Audio (WMA) and Windows Media Video (WMV) files.
+
+=head1 AUTHOR
+
+Copyright 2003-2007, Phil Harvey (phil at owl.phy.queensu.ca)
+
+This library is free software; you can redistribute it and/or modify it
+under the same terms as Perl itself.
+
+=head1 REFERENCES
+
+=over 4
+
+=item L<http://www.microsoft.com/windows/windowsmedia/format/asfspec.aspx>
+
+=back
+
+=head1 SEE ALSO
+
+L<Image::ExifTool::TagNames/ASF Tags>,
+L<Image::ExifTool(3pm)|Image::ExifTool>
+
+=cut
+
Index: /gsdl/trunk/perllib/cpan/Image/ExifTool/BMP.pm
===================================================================
--- /gsdl/trunk/perllib/cpan/Image/ExifTool/BMP.pm	(revision 16842)
+++ /gsdl/trunk/perllib/cpan/Image/ExifTool/BMP.pm	(revision 16842)
@@ -0,0 +1,147 @@
+#------------------------------------------------------------------------------
+# File:         BMP.pm
+#
+# Description:  Read BMP meta information
+#
+# Revisions:    07/16/2005 - P. Harvey Created
+#
+# References:   1) http://www.fortunecity.com/skyscraper/windows/364/bmpffrmt.html
+#               2) http://www.fourcc.org/rgb.php
+#------------------------------------------------------------------------------
+
+package Image::ExifTool::BMP;
+
+use strict;
+use vars qw($VERSION);
+use Image::ExifTool qw(:DataAccess :Utils);
+
+$VERSION = '1.02';
+
+# BMP chunks
+%Image::ExifTool::BMP::Main = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    GROUPS => { 2 => 'Image' },
+    NOTES => q{
+        There really isn't much meta information in a BMP file as such, just a bit
+        of image related information.
+    },
+    4 => {
+        Name => 'ImageWidth',
+        Format => 'int32u',
+    },
+    8 => {
+        Name => 'ImageHeight',
+        Format => 'int32u',
+    },
+    12 => {
+        Name => 'Planes',
+        Format => 'int16u',
+    },
+    14 => {
+        Name => 'BitDepth',
+        Format => 'int16u',
+    },
+    16 => {
+        Name => 'Compression',
+        Format => 'int32u',
+        # (formatted as string[4] for some values in AVI images)
+        ValueConv => '$val > 256 ? unpack("A4",pack("V",$val)) : $val',
+        PrintConv => {
+            0 => 'None',
+            1 => '8-Bit RLE',
+            2 => '4-Bit RLE',
+            3 => 'Bitfields',
+            4 => 'JPEG', #2
+            5 => 'PNG', #2
+        },
+    },
+    20 => {
+        Name => 'ImageLength',
+        Format => 'int32u',
+    },
+    24 => {
+        Name => 'PixelsPerMeterX',
+        Format => 'int32u',
+    },
+    28 => {
+        Name => 'PixelsPerMeterY',
+        Format => 'int32u',
+    },
+    32 => {
+        Name => 'NumColors',
+        Format => 'int32u',
+        PrintConv => '$val ? $val : "Use BitDepth"',
+    },
+    36 => {
+        Name => 'NumImportantColors',
+        Format => 'int32u',
+        PrintConv => '$val ? $val : "All"',
+    },
+);
+
+#------------------------------------------------------------------------------
+# Extract EXIF information from a BMP image
+# Inputs: 0) ExifTool object reference, 1) dirInfo reference
+# Returns: 1 on success, 0 if this wasn't a valid BMP file
+sub ProcessBMP($$)
+{
+    my ($exifTool, $dirInfo) = @_;
+    my $raf = $$dirInfo{RAF};
+    my $buff;
+
+    # verify this is a valid BMP file
+    return 0 unless $raf->Read($buff, 14) == 14;
+    return 0 unless $buff =~ /^BM/;
+    return 0 unless $raf->Read($buff, 40) == 40;
+    SetByteOrder('II');
+    return 0 unless Get32u(\$buff, 0) >= 40;    # make sure info is at least 40 bytes
+    $exifTool->SetFileType();   # set the FileType tag
+    my %dirInfo = (
+        DataPt => \$buff,
+        DirStart => 0,
+        DirLen => length($buff),
+    );
+    my $tagTablePtr = GetTagTable('Image::ExifTool::BMP::Main');
+    $exifTool->ProcessDirectory(\%dirInfo, $tagTablePtr);
+    return 1;
+}
+
+1;  # end
+
+__END__
+
+=head1 NAME
+
+Image::ExifTool::BMP - Read BMP meta information
+
+=head1 SYNOPSIS
+
+This module is used by Image::ExifTool
+
+=head1 DESCRIPTION
+
+This module contains definitions required by Image::ExifTool to read BMP
+(Windows Bitmap) images.
+
+=head1 AUTHOR
+
+Copyright 2003-2007, Phil Harvey (phil at owl.phy.queensu.ca)
+
+This library is free software; you can redistribute it and/or modify it
+under the same terms as Perl itself.
+
+=head1 REFERENCES
+
+=over 4
+
+=item L<http://www.fortunecity.com/skyscraper/windows/364/bmpffrmt.html>
+
+=back
+
+=head1 SEE ALSO
+
+L<Image::ExifTool::TagNames/BMP Tags>,
+L<Image::ExifTool(3pm)|Image::ExifTool>
+
+=cut
+
Index: /gsdl/trunk/perllib/cpan/Image/ExifTool/BigTIFF.pm
===================================================================
--- /gsdl/trunk/perllib/cpan/Image/ExifTool/BigTIFF.pm	(revision 16842)
+++ /gsdl/trunk/perllib/cpan/Image/ExifTool/BigTIFF.pm	(revision 16842)
@@ -0,0 +1,277 @@
+#------------------------------------------------------------------------------
+# File:         BigTIFF.pm
+#
+# Description:  Read Big TIFF meta information
+#
+# Revisions:    07/03/2007 - P. Harvey Created
+#
+# References:   1) http://www.awaresystems.be/imaging/tiff/bigtiff.html
+#------------------------------------------------------------------------------
+
+package Image::ExifTool::BigTIFF;
+
+use strict;
+use vars qw($VERSION);
+use Image::ExifTool qw(:DataAccess :Utils);
+use Image::ExifTool::Exif;
+
+$VERSION = '1.00';
+
+my $maxOffset = 0x7fffffff; # currently supported maximum data offset/size
+
+#------------------------------------------------------------------------------
+# Process Big IFD directory
+# Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
+# Returns: 1 on success, otherwise returns 0 and sets a Warning
+sub ProcessBigIFD($$$)
+{
+    my ($exifTool, $dirInfo, $tagTablePtr) = @_;
+    my $raf = $$dirInfo{RAF};
+    my $verbose = $exifTool->{OPTIONS}->{Verbose};
+    my $htmlDump = $exifTool->{HTML_DUMP};
+    my $dirName = $$dirInfo{DirName};
+    my $dirStart = $$dirInfo{DirStart};
+
+    $verbose = -1 if $htmlDump; # mix htmlDump into verbose so we can test for both at once
+
+    # loop through IFD chain
+    for (;;) {
+        if ($dirStart > $maxOffset) {
+            $exifTool->Warn('Huge offsets not yet supported');
+            last;
+        }
+        unless ($raf->Seek($dirStart, 0)) {
+            $exifTool->Warn("Bad $dirName offset");
+            return 0;
+        }
+        my ($dirBuff, $index);
+        unless ($raf->Read($dirBuff, 8) == 8) {
+            $exifTool->Warn("Truncated $dirName count");
+            return 0;
+        }
+        my $numEntries = Image::ExifTool::Get64u(\$dirBuff, 0);
+        $verbose > 0 and $exifTool->VerboseDir($dirName, $numEntries);
+        my $bsize = $numEntries * 20;
+        if ($bsize > $maxOffset) {
+            $exifTool->Warn('Huge directory counts not yet supported');
+            last;
+        }
+        my $bufPos = $raf->Tell();
+        unless ($raf->Read($dirBuff, $bsize) == $bsize) {
+            $exifTool->Warn("Truncated $dirName directory");
+            return 0;
+        }
+        my $nextIFD;
+        $raf->Read($nextIFD, 8) == 8 or undef $nextIFD; # try to read next IFD pointer
+        if ($htmlDump) {
+            $exifTool->HtmlDump($bufPos-8, 8, "$dirName entries", "Entry count: $numEntries");
+            if (defined $nextIFD) {
+                my $tip = sprintf("Offset: 0x%.8x", Image::ExifTool::Get64u(\$nextIFD, 0));
+                $exifTool->HtmlDump($bufPos + 20 * $numEntries, 8, "Next IFD", $tip, 0);
+            }
+        }
+        # loop through all entries in this BigTIFF IFD
+        for ($index=0; $index<$numEntries; ++$index) {
+            my $entry = 20 * $index;
+            my $tagID = Get16u(\$dirBuff, $entry);
+            my $format = Get16u(\$dirBuff, $entry+2);
+            my $count = Image::ExifTool::Get64u(\$dirBuff, $entry+4);
+            my $formatSize = $Image::ExifTool::Exif::formatSize[$format];
+            unless (defined $formatSize) {
+                $exifTool->HtmlDump($bufPos+$entry,20,"[invalid IFD entry]",
+                         "Bad format value: $format", 1);
+                # warn unless the IFD was just padded with zeros
+                $exifTool->Warn(sprintf("Unknown format ($format) for $dirName tag 0x%x",$tagID));
+                return 0; # assume corrupted IFD
+            }
+            my $formatStr = $Image::ExifTool::Exif::formatName[$format];
+            my $size = $count * $formatSize;
+            my $tagInfo = $exifTool->GetTagInfo($tagTablePtr, $tagID);
+            next unless $tagInfo or $verbose;
+            my $valuePtr = $entry + 12;
+            my ($valBuff, $valBase);
+            if ($size > 8) {
+                if ($size > $maxOffset) {
+                    $exifTool->Warn("Can't yet handle $dirName entry $index (huge size)");
+                    next;
+                }
+                $valuePtr = Image::ExifTool::Get64u(\$dirBuff, $valuePtr);
+                if ($valuePtr > $maxOffset) {
+                    $exifTool->Warn("Can't yet handle $dirName entry $index (huge offset)");
+                    next;
+                }
+                unless ($raf->Seek($valuePtr, 0) and $raf->Read($valBuff, $size) == $size) {
+                    $exifTool->Warn("Error reading $dirName entry $index");
+                    next;
+                }
+                $valBase = 0;
+            } else {
+                $valBuff = substr($dirBuff, $valuePtr, $size);
+                $valBase = $bufPos;
+            }
+            my $val = ReadValue(\$valBuff, 0, $formatStr, $count, $size);
+            if ($htmlDump) {
+                my ($tagName, $colName);
+                if ($tagID == 0x927c and $dirName eq 'ExifIFD') {
+                    $tagName = 'MakerNotes';
+                } elsif ($tagInfo) {
+                    $tagName = $$tagInfo{Name};
+                } else {
+                    $tagName = sprintf("Tag 0x%.4x",$tagID);
+                }
+                my $dname = sprintf("$dirName-%.2d", $index);
+                # build our tool tip
+                my $tip = sprintf("Tag ID: 0x%.4x\\n", $tagID) .
+                          "Format: $formatStr\[$count]\\nSize: $size bytes\\n";
+                if ($size > 8) {
+                    $tip .= sprintf("Value offset: 0x%.8x\\n", $valuePtr);
+                    $colName = "<span class=H>$tagName</span>";
+                } else {
+                    $colName = $tagName;
+                }
+                my $tval = (length $val < 32) ? $val : substr($val,0,28) . '[...]';
+                if ($formatStr =~ /^(string|undef|binary)/) {
+                    # translate all characters that could mess up JavaScript
+                    $tval =~ tr/\\\x00-\x1f\x7f-\xff/./;
+                    $tval =~ tr/"/'/;
+                    $tval = "$tval";
+                } elsif ($tagInfo and Image::ExifTool::IsInt($tval)) {
+                    if ($$tagInfo{IsOffset}) {
+                        $tval = sprintf('0x%.4x', $tval);
+                    } elsif ($$tagInfo{PrintHex}) {
+                        $tval = sprintf('0x%x', $tval);
+                    }
+                }
+                $tip .= "Value: $tval";
+                $exifTool->HtmlDump($entry+$bufPos, 20, "$dname $colName", $tip, 1);
+                if ($size > 8) {
+                    # add value data block
+                    my $flg = ($tagInfo and $$tagInfo{SubDirectory} and $$tagInfo{MakerNotes}) ? 4 : 0;
+                    $exifTool->HtmlDump($valuePtr,$size,"$tagName value",'SAME', $flg);
+                }
+            }
+            if ($tagInfo and $$tagInfo{SubIFD}) {
+                # process all SubIFD's as BigTIFF
+                $verbose > 0 and $exifTool->VerboseInfo($tagID, $tagInfo,
+                    Table   => $tagTablePtr,
+                    Index   => $index,
+                    Value   => $val,
+                    DataPt  => \$valBuff,
+                    DataPos => $valBase + $valuePtr,
+                    Start   => 0,
+                    Size    => $size,
+                    Format  => $formatStr,
+                    Count   => $count,
+                );
+                my @offsets = split ' ', $val;
+                my $i;
+                for ($i=0; $i<scalar(@offsets); ++$i) {
+                    my $subdirName = $$tagInfo{Name};
+                    $subdirName .= $i if $i;
+                    my %subdirInfo = (
+                        RAF      => $raf,
+                        DataPos  => 0,
+                        DirStart => $offsets[$i],
+                        DirName  => $subdirName,
+                        Parent   => $dirInfo,
+                    );
+                    $exifTool->ProcessDirectory(\%subdirInfo, $tagTablePtr, \&ProcessBigIFD);
+                }
+            } else {
+                my $tagKey = $exifTool->HandleTag($tagTablePtr, $tagID, $val,
+                    Index   => $index,
+                    DataPt  => \$valBuff,
+                    DataPos => $valBase + $valuePtr,
+                    Start   => 0,
+                    Size    => $size,
+                    Format  => $formatStr,
+                    TagInfo => $tagInfo,
+                    RAF     => $raf,
+                );
+                $tagKey and $exifTool->SetGroup1($tagKey, $dirName);
+            }
+        }
+        last unless $dirName =~ /^(IFD|SubIFD)(\d*)$/;
+        $dirName = $1 . (($2 || 0) + 1);
+        defined $nextIFD or $exifTool->Warn("Bad $dirName pointer"), return 0;
+        $dirStart = Image::ExifTool::Get64u(\$nextIFD, 0);
+        $dirStart or last;
+    }
+    return 1;
+}
+
+#------------------------------------------------------------------------------
+# Extract meta information from a BigTIFF image
+# Inputs: 0) ExifTool object reference, 1) dirInfo reference
+# Returns: 1 on success, 0 if this wasn't a valid BigTIFF image
+sub ProcessBTF($$)
+{
+    my ($exifTool, $dirInfo) = @_;
+    my $raf = $$dirInfo{RAF};
+    my $buff;
+
+    return 0 unless $raf->Read($buff, 16) == 16;
+    return 0 unless $buff =~ /^(MM\0\x2b\0\x08\0\0|II\x2b\0\x08\0\0\0)/;
+    if ($$dirInfo{OutFile}) {
+        $exifTool->Error('ExifTool does not support writing of BigTIFF images');
+        return 1;
+    }
+    $exifTool->SetFileType('BTF'); # set the FileType tag
+    SetByteOrder(substr($buff, 0, 2));
+    my $offset = Image::ExifTool::Get64u(\$buff, 8);
+    if ($exifTool->{HTML_DUMP}) {
+        my $o = (GetByteOrder() eq 'II') ? 'Little' : 'Big';
+        $exifTool->HtmlDump(0, 8, "BigTIFF header", "Byte order: $o endian", 0);
+        $exifTool->HtmlDump(8, 8, "IFD0 pointer", sprintf("Offset: 0x%.8x",$offset), 0);
+    }
+    my %dirInfo = (
+        RAF      => $raf,
+        DataPos  => 0,
+        DirStart => $offset,
+        DirName  => 'IFD0',
+        Parent   => 'BigTIFF',
+    );
+    my $tagTablePtr = GetTagTable('Image::ExifTool::Exif::Main');
+    $exifTool->ProcessDirectory(\%dirInfo, $tagTablePtr, \&ProcessBigIFD);
+    return 1;
+}
+
+1;  # end
+
+__END__
+
+=head1 NAME
+
+Image::ExifTool::BigTIFF - Read Big TIFF meta information
+
+=head1 SYNOPSIS
+
+This module is used by Image::ExifTool
+
+=head1 DESCRIPTION
+
+This module contains routines required by Image::ExifTool to read meta
+information in BigTIFF images.
+
+=head1 AUTHOR
+
+Copyright 2003-2007, Phil Harvey (phil at owl.phy.queensu.ca)
+
+This library is free software; you can redistribute it and/or modify it
+under the same terms as Perl itself.
+
+=head1 REFERENCES
+
+=over 4
+
+=item L<http://www.awaresystems.be/imaging/tiff/bigtiff.html>
+
+=back
+
+=head1 SEE ALSO
+
+L<Image::ExifTool::TagNames/EXIF Tags>,
+L<Image::ExifTool(3pm)|Image::ExifTool>
+
+=cut
+
Index: /gsdl/trunk/perllib/cpan/Image/ExifTool/BuildTagLookup.pm
===================================================================
--- /gsdl/trunk/perllib/cpan/Image/ExifTool/BuildTagLookup.pm	(revision 16842)
+++ /gsdl/trunk/perllib/cpan/Image/ExifTool/BuildTagLookup.pm	(revision 16842)
@@ -0,0 +1,1541 @@
+#------------------------------------------------------------------------------
+# File:         BuildTagLookup.pm
+#
+# Description:  Utility to build tag lookup tables in Image::ExifTool::TagLookup.pm
+#
+# Revisions:    12/31/2004 - P. Harvey Created
+#               02/15/2005 - PH Added ability to generate TagNames documentation
+#
+# Notes:        Documentation for the tag tables may either be placed in the
+#               %docs hash below or in a NOTES entry in the table itself, and
+#               individual tags may have their own Notes entry.
+#------------------------------------------------------------------------------
+
+package Image::ExifTool::BuildTagLookup;
+
+use strict;
+require Exporter;
+
+BEGIN {
+    # prevent ExifTool from loading the user config file
+    $Image::ExifTool::noConfig = 1;
+}
+
+use vars qw($VERSION @ISA);
+use Image::ExifTool qw(:Utils :Vars);
+use Image::ExifTool::Shortcuts;
+use Image::ExifTool::HTML qw(EscapeHTML);
+use Image::ExifTool::IPTC;
+use Image::ExifTool::Canon;
+use Image::ExifTool::Nikon;
+
+$VERSION = '1.59';
+@ISA = qw(Exporter);
+
+sub NumbersFirst;
+
+# colors for html pages
+my $noteFont = "<span class=n>";
+my $noteFontSmall = "<span class='n s'>";
+
+my $docType = q{<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+        "http://www.w3.org/TR/html4/loose.dtd">
+};
+
+
+my $caseInsensitive;    # flag to ignore case when sorting tag names
+
+# Descriptions for the TagNames documentation
+# Note: POD headers in these descriptions start with '~' instead of '=' to keep
+# from confusing POD parsers which apparently parse inside quoted strings.
+my %docs = (
+    PodHeader => q{
+~head1 NAME
+
+Image::ExifTool::TagNames - ExifTool tag name documentation
+
+~head1 DESCRIPTION
+
+This document contains a complete list of ExifTool tag names, organized into
+tables based on information type.  Tag names are used to indicate the
+specific meta information that is extracted or written in an image.
+
+~head1 TAG TABLES
+},
+    ExifTool => q{
+The tables listed below give the names of all tags recognized by ExifTool.
+},
+    ExifTool2 => q{
+B<Tag ID>, B<Index> or B<Sequence> is given in the first column of each
+table.  A B<Tag ID> is the computer-readable equivalent of a tag name, and
+is the identifier that is actually stored in the file.  An B<Index> refers
+to the location of a value when found at a fixed position within a data
+block, and B<Sequence> gives the order of values for a serial data stream.
+
+A B<Tag Name> is the handle by which the information is accessed in
+ExifTool.  In some instances, more than one name may correspond to a single
+tag ID.  In these cases, the actual name used depends on the context in
+which the information is found.  Case is not significant for tag names.  A
+question mark after a tag name indicates that the information is either not
+understood, not verified, or not very useful -- these tags are not extracted
+by ExifTool unless the Unknown (-u) option is enabled.  Be aware that some
+tag names are different than the descriptions printed out by default when
+extracting information with exiftool.  To see the tag names instead of the
+descriptions, use C<exiftool -s>.
+
+The B<Writable> column indicates whether the tag is writable by ExifTool.
+Anything but an C<N> in this column means the tag is writable.  A C<Y>
+indicates writable information that is either unformatted or written using
+the existing format.  Other expressions give details about the information
+format, and vary depending on the general type of information.  The format
+name may be followed by a number in square brackets to indicate the number
+of values written, or the number of characters in a fixed-length string
+(including a null terminator which is added if required).
+
+An asterisk (C<*>) after an entry in the B<Writable> column indicates a
+"protected" tag which is not writable directly, but is set via a Composite
+tag.  A tilde (C<~>) indicates a tag this is only writable when print
+conversion is disabled (by setting PrintConv to 0, or using the -n option).
+A slash (C</>) indicates an "avoided" tag that is not created unless the
+group is specified (due to name conflicts with other tags).  An exclamation
+point (C<!>) indicates a tag that is considered unsafe to write under normal
+circumstances.  These "unsafe" tags are not set when calling
+SetNewValuesFromFile() or when using the exiftool -TagsFromFile option
+unless specified explicitly, and care should be taken when editing them
+manually since they may affect the way an image is rendered.  A plus sign
+(C<+>) indicates a "list" tag which supports multiple instances.
+
+The HTML version of these tables also list possible B<Values> for
+discrete-valued tags, as well as B<Notes> for some tags.
+
+B<Note>: If you are familiar with common meta-information tag names, you may
+find that some ExifTool tag names are different than expected.  The usual
+reason for this is to make the tag names more consistent across different
+types of meta information.  To determine a tag name, either consult this
+documentation or run C<exiftool -s> on a file containing the information in
+question.
+},
+    EXIF => q{
+EXIF stands for "Exchangeable Image File Format".  This type of information
+is formatted according to the TIFF specification, and may be found in JPG,
+TIFF, PNG, MIFF and WDP images, as well as many TIFF-based RAW images.
+
+The EXIF meta information is organized into different Image File Directories
+(IFD's) within an image.  The names of these IFD's correspond to the
+ExifTool family 1 group names.  When writing EXIF information, the default
+B<Group> listed below is used unless another group is specified.
+
+Also listed in the table below are TIFF, DNG, WDP and other tags which are
+not part of the EXIF specification, but may co-exist with EXIF tags in some
+images.
+},
+    GPS => q{
+These GPS tags are part of the EXIF standard, and are stored in a separate
+IFD within the EXIF information.
+
+ExifTool is very flexible about the input format when writing lat/long
+coordinates, and will accept from 1 to 3 floating point numbers (for decimal
+degrees, degrees and minutes, or degrees, minutes and seconds) separated by
+just about anything, and will format them properly according to the EXIF
+specification.
+
+Some GPS tags have values which are fixed-length strings. For these, the
+indicated string lengths include a null terminator which is added
+automatically by ExifTool.  Remember that the descriptive values are used
+when writing (ie. 'Above Sea Level', not '0') unless the print conversion is
+disabled (with '-n' on the command line, or the PrintConv option in the
+API).
+},
+    XMP => q{
+XMP stands for "Extensible Metadata Platform", an XML/RDF-based metadata
+format which is being pushed by Adobe.  Information in this format can be
+embedded in many different image file types including JPG, JP2, TIFF, PNG,
+MIFF, PS, PDF, PSD and DNG, as well as audio file formats supporting ID3v2
+information.
+
+The XMP B<Tag ID>'s aren't listed because in most cases they are identical
+to the B<Tag Name>.
+
+All XMP information is stored as character strings.  The B<Writable> column
+specifies the information format:  C<integer> is a string of digits
+(possibly beginning with a '+' or '-'), C<real> is a floating point number,
+C<rational> is two C<integer> strings separated by a '/' character, C<date>
+is a date/time string in the format "YYYY:MM:DD HH:MM:SS[+/-HH:MM]",
+C<boolean> is either "True" or "False", and C<lang-alt> is a list of string
+alternatives in different languages.
+
+Individual languages for C<lang-alt> tags are accessed by suffixing the tag
+name with a '-', followed by an RFC 3066 language code (ie. "XMP:Title-fr",
+or "Rights-en-US").  A C<lang-alt> tag with no language code accesses the
+"x-default" language, but causes other languages to be deleted when writing.
+The "x-default" language code may be specified when writing a new value to
+write only the default language, but note that all languages are still
+deleted if "x-default" tag is deleted.  When reading, "x-default" is not
+specified.
+
+The XMP tags are organized according to schema B<Namespace> in the following
+tables.  Note that a few of the longer namespace prefixes given below have
+been shortened for convenience (since the family 1 group names are derived
+from these by adding a leading "XMP-").  In cases where a tag name exists in
+more than one namespace, less common namespaces are avoided when writing.
+However, any namespace may be written by specifying a family 1 group name
+for the tag, ie) XMP-exif:Contrast or XMP-crs:Contrast.
+
+ExifTool will extract XMP information even if it is not listed in these
+tables.  For example, the C<pdfx> namespace doesn't have a predefined set of
+tag names because it is used to store application-defined PDF information,
+but this information is extracted by ExifTool anyway.
+},
+    IPTC => q{
+IPTC stands for "International Press Telecommunications Council".  This is
+an older meta information format that is slowly being phased out in favor of
+XMP.  IPTC information may be embedded in JPG, TIFF, PNG, MIFF, PS, PDF, PSD
+and DNG images.
+
+The IPTC specification dictates a length for ASCII (C<string> or C<digits>)
+values.  These lengths are given in square brackets after the B<Writable>
+format name.  For tags where a range of lengths is allowed, the minimum and
+maximum lengths are separated by a comma within the brackets.  IPTC strings
+are not null terminated.
+
+IPTC information is separated into different records, each of which has its
+own set of tags.
+},
+    Photoshop => q{
+Photoshop tags are found in PSD files, as well as inside embedded Photoshop
+information in many other file types (JPEG, TIFF, PDF, PNG to name a few).
+
+Many Photoshop tags are marked as Unknown (indicated by a question mark
+after the tag name) because the information they provide is not very useful
+under normal circumstances (and because Adobe denied my application for
+their file format documentation -- apparently open source software is too
+big a concept for them).  These unknown tags are not extracted unless the
+Unknown (-u) option is used.
+},
+    PrintIM => q{
+The format of the PrintIM information is known, however no PrintIM tags have
+been decoded.  Use the Unknown (-u) option to extract PrintIM information.
+},
+    Kodak => q{
+The Kodak maker notes aren't in standard IFD format, and the format varies
+frequently with different models.  Some information has been decoded, but
+much of the Kodak information remains unknown.
+},
+    'Kodak SpecialEffects' => q{
+The Kodak SpecialEffects and Borders tags are found in sub-IFD's within the
+Kodak JPEG APP3 "Meta" segment.
+},
+    Minolta => q{
+These tags are used by Minolta and Konica/Minolta cameras.  Minolta doesn't
+make things easy for decoders because the meaning of some tags and the
+location where some information is stored is different for different camera
+models.  (Take MinoltaQuality for example, which may be located in 5
+different places.)
+},
+    Olympus => q{
+Tags 0x0000 through 0x0103 are used by some older Olympus cameras, and are
+the same as Konica/Minolta tags.  The Olympus tags are also used for Epson
+and Agfa cameras.
+},
+    Panasonic => q{
+Panasonic tags are also used for Leica cameras.
+},
+    Pentax => q{
+The Pentax tags are also used in Asahi cameras.
+},
+    Sigma => q{
+These tags are used in Sigma/Foveon cameras.
+},
+    Sony => q{
+The maker notes in images from current Sony camera models contain a wealth
+of information, but very little is known about these tags.  Use the ExifTool
+Unknown (-u) or Verbose (-v) options to see information about the unknown
+tags.
+},
+    CanonRaw => q{
+These tags apply to CRW-format Canon RAW files and information in the APP0
+"CIFF" segment of JPEG images.  When writing CanonRaw/CIFF information, the
+length of the information is preserved (and the new information is truncated
+or padded as required) unless B<Writable> is C<resize>. Currently, only
+JpgFromRaw and ThumbnailImage are allowed to change size.
+},
+    Unknown => q{
+The following tags are decoded in unsupported maker notes.  Use the Unknown
+(-u) option to display other unknown tags.
+},
+    PDF => q{
+The tags listed in the PDF tables below are those which are used by ExifTool
+to extract meta information, but they are only a small fraction of the total
+number of available PDF tags.
+},
+    DNG => q{
+The main DNG tags are found in the EXIF table.  The tables below define only
+information found within structures of these main DNG tag values.
+},
+    MPEG => q{
+The MPEG format doesn't specify any file-level meta information.  In lieu of
+this, information is extracted from the first audio and video frame headers
+in the file.
+},
+    Real => q{
+ExifTool recognizes three basic types of Real audio/video files: 1)
+RealMedia (RM, RV and RMVB), 2) RealAudio (RA), and 3) Real Metafile (RAM
+and RPM).
+},
+    Extra => q{
+The extra tags represent information found in the image but not associated
+with any other tag group.  The three writable "pseudo" tags (Filename,
+Directory and FileModifyDate) may be written without the need to rewrite the
+file since their values are not contained within the file data.
+},
+    Composite => q{
+The values of the composite tags are derived from the values of other tags.
+These are convenience tags which are calculated after all other information
+is extracted.
+},
+    Shortcuts => q{
+Shortcut tags are convenience tags that represent one or more other tag
+names.  They are used like regular tags to read and write the information
+for a specified set of tags.
+
+The shortcut tags below have been pre-defined, but user-defined shortcuts
+may be added via the %Image::ExifTool::Shortcuts::UserDefined lookup in the
+~/.ExifTool_config file.  See the Image::ExifTool::Shortcuts documentation
+for more details.
+},
+    PodTrailer => q{
+~head1 NOTES
+
+This document generated automatically by
+L<Image::ExifTool::BuildTagLookup|Image::ExifTool::BuildTagLookup>.
+
+~head1 AUTHOR
+
+Copyright 2003-2007, Phil Harvey (phil at owl.phy.queensu.ca)
+
+This library is free software; you can redistribute it and/or modify it
+under the same terms as Perl itself.
+
+~head1 SEE ALSO
+
+L<Image::ExifTool(3pm)|Image::ExifTool>
+
+~cut
+},
+);
+
+
+#------------------------------------------------------------------------------
+# New - create new BuildTagLookup object
+# Inputs: 0) reference to BuildTagLookup object or BuildTagLookup class name
+sub new
+{
+    local $_;
+    my $that = shift;
+    my $class = ref($that) || $that || 'Image::ExifTool::BuildTagLookup';
+    my $self = bless {}, $class;
+    my (%subdirs, %isShortcut);
+    my %count = (
+        'unique tag names' => 0,
+        'total tags' => 0,
+    );
+#
+# loop through all tables, accumulating TagLookup and TagName information
+#
+    my (%tagNameInfo, %id, %longID, %longName, %shortName, %tableNum,
+        %tagLookup, %tagExists, %tableWritable, %sepTable, %compositeModules);
+    $self->{TAG_NAME_INFO} = \%tagNameInfo;
+    $self->{TAG_ID} = \%id;
+    $self->{LONG_ID} = \%longID;
+    $self->{LONG_NAME} = \%longName;
+    $self->{SHORT_NAME} = \%shortName;
+    $self->{TABLE_NUM} = \%tableNum;
+    $self->{TAG_LOOKUP} = \%tagLookup;
+    $self->{TAG_EXISTS} = \%tagExists;
+    $self->{TABLE_WRITABLE} = \%tableWritable;
+    $self->{SEPARATE_TABLE} = \%sepTable;
+    $self->{COMPOSITE_MODULES} = \%compositeModules;
+    $self->{COUNT} = \%count;
+
+    Image::ExifTool::LoadAllTables();
+    my @tableNames = sort keys %allTables;
+    push @tableNames, 'Image::ExifTool::Shortcuts::Main'; # add Shortcuts last
+    my $tableNum = 0;
+    my $tableName;
+    # create lookup for short table names
+    foreach $tableName (@tableNames) {
+        my $short = $tableName;
+        $short =~ s/^Image::ExifTool:://;
+        $short =~ s/::Main$//;
+        $short =~ s/::/ /;
+        $short =~ s/(.*)Tags$/\u$1/;
+        $short =~ s/^Exif\b/EXIF/;
+        $shortName{$tableName} = $short;    # remember short name
+        $tableNum{$tableName} = $tableNum++;
+    }
+    # make lookup table to check for shortcut tags
+    my $tag;
+    foreach $tag (keys %Image::ExifTool::Shortcuts::Main) {
+        my $entry = $Image::ExifTool::Shortcuts::Main{$tag};
+        # ignore if shortcut tag name includes itself
+        next if ref $entry eq 'ARRAY' and grep /^$tag$/, @$entry;
+        $isShortcut{lc($tag)} = 1;
+    }
+    foreach $tableName (@tableNames) {
+        # create short table name
+        my $short = $shortName{$tableName};
+        my $info = $tagNameInfo{$tableName} = [ ];
+        my ($table, $shortcut);
+        if ($short eq 'Shortcuts') {
+            # can't use GetTagTable() for Shortcuts (not a normal table)
+            $table = \%Image::ExifTool::Shortcuts::Main;
+            $shortcut = 1;
+        } else {
+            $table = GetTagTable($tableName);
+        }
+        my $tableNum = $tableNum{$tableName};
+        my $writeProc = $table->{WRITE_PROC};
+        $longID{$tableName} = 0;
+        $longName{$tableName} = 0;
+        # save all tag names
+        my ($tagID, $binaryTable, $noID, $isIPTC);
+        $isIPTC = 1 if $$table{WRITE_PROC} and $$table{WRITE_PROC} eq \&Image::ExifTool::IPTC::WriteIPTC;
+        $noID = 1 if $short =~ /^(Composite|XMP|Extra|Shortcuts|ASF.*)$/ or $table->{VARS}->{NO_ID};
+        if (($table->{VARS} and $table->{VARS}->{INDEX}) or
+            ($table->{PROCESS_PROC} and
+             $table->{PROCESS_PROC} eq \&Image::ExifTool::ProcessBinaryData))
+        {
+            $binaryTable = 1;
+            $id{$tableName} = 'Index';
+        } elsif ($table->{PROCESS_PROC} and
+            $table->{PROCESS_PROC} eq \&Image::ExifTool::Canon::ProcessSerialData)
+        {
+            $binaryTable = 1;
+            $id{$tableName} = 'Sequence';
+        } elsif ($isIPTC and $$table{PROCESS_PROC}) { #only the main IPTC table has a PROCESS_PROC
+            $id{$tableName} = 'Record';
+        } elsif (not $noID) {
+            $id{$tableName} = 'Tag ID';
+        }
+        $caseInsensitive = ($tableName =~ /::XMP::/);
+        my @keys = sort NumbersFirst TagTableKeys($table);
+        my $defFormat = $table->{FORMAT};
+        if (not $defFormat and $table->{PROCESS_PROC} and
+            $table->{PROCESS_PROC} eq \&Image::ExifTool::ProcessBinaryData)
+        {
+            $defFormat = 'int8u';   # use default format for binary data tables
+        }
+TagID:  foreach $tagID (@keys) {
+            my ($tagInfo, @tagNames, $subdir, $format, @values);
+            my (@infoArray, @require, @writeGroup, @writable);
+            if ($shortcut) {
+                # must build a dummy tagInfo list since Shortcuts is not a normal table
+                $tagInfo = { Name => $tagID, Writable => 1, Require => { } };
+                my $i;
+                for ($i=0; $i<@{$$table{$tagID}}; ++$i) {
+                    $tagInfo->{Require}->{$i} = $table->{$tagID}->[$i];
+                }
+                @infoArray = ( $tagInfo );
+            } else {
+                @infoArray = GetTagInfoList($table,$tagID);
+            }
+            $format = $defFormat;
+            foreach $tagInfo (@infoArray) {
+                if ($$tagInfo{Notes}) {
+                    my $note = $$tagInfo{Notes};
+                    # remove leading/trailing blank lines
+                    $note =~ s/(^\s+|\s+$)//g;
+                    # remove leading/trailing spaces on each line
+                    $note =~ s/(^[ \t]+|[ \t]+$)//mg;
+                    push @values, "($note)";
+                }
+                my $writable;
+                if (defined $$tagInfo{Writable}) {
+                    $writable = $$tagInfo{Writable};
+                } elsif (not $$tagInfo{SubDirectory}) {
+                    $writable = $$table{WRITABLE};
+                }
+                my $writeGroup;
+                $writeGroup = $$tagInfo{WriteGroup};
+                unless ($writeGroup) {
+                    $writeGroup = $$table{WRITE_GROUP} if $writable;
+                    $writeGroup = '-' unless $writeGroup;
+                }
+                $format = $$tagInfo{Format} if defined $$tagInfo{Format};
+                if ($$tagInfo{SubDirectory}) {
+                    # don't show XMP structure tags
+                    next TagID if $short =~ /^XMP /;
+                    $subdir = 1;
+                    my $subTable = $tagInfo->{SubDirectory}->{TagTable} || $tableName;
+                    push @values, $shortName{$subTable}
+                } else {
+                    $subdir = 0;
+                }
+                my $type;
+                foreach $type ('Require','Desire') {
+                    my $require = $$tagInfo{$type};
+                    if ($require) {
+                        foreach (sort { $a <=> $b } keys %$require) {
+                            push @require, $$require{$_};
+                        }
+                    }
+                }
+                my $printConv = $$tagInfo{PrintConv};
+                if (ref($printConv) =~ /^(HASH|ARRAY)$/) {
+                    my (@printConvList, @indexList, $index);
+                    if (ref $printConv eq 'ARRAY') {
+                        for ($index=0; $index<@$printConv; ++$index) {
+                            next if ref $$printConv[$index] ne 'HASH';
+                            push @printConvList, $$printConv[$index];
+                            push @indexList, $index;
+                        }
+                        $printConv = shift @printConvList;
+                        $index = shift @indexList;
+                    }
+                    while (defined $printConv) {
+                        push @values, "[Value $index]" if defined $index;
+                        if ($$tagInfo{SeparateTable}) {
+                            $subdir = 1;
+                            my $s = $$tagInfo{SeparateTable};
+                            $s = $$tagInfo{Name} if $s eq '1';
+                            # add module name if not specified
+                            $s =~ / / or ($short =~ /^(\w+)/ and $s = "$1 $s");
+                            push @values, $s;
+                            $sepTable{$s} = $printConv;
+                            # add PrintHex flag to PrintConv so we can check it later
+                            $$printConv{PrintHex} = 1 if $$tagInfo{PrintHex};
+                        } else {
+                            $caseInsensitive = 0;
+                            my @pk = sort NumbersFirst keys %$printConv;
+                            my $bits;
+                            foreach (@pk) {
+                                next if $_ eq '';
+                                $_ eq 'BITMASK' and $bits = $$printConv{$_}, next;
+                                my $index;
+                                if ($$tagInfo{PrintHex}) {
+                                    $index = sprintf('0x%x',$_);
+                                } elsif (/^[-+]?\d+$/) {
+                                    $index = $_;
+                                } else {
+                                    # ignore unprintable values
+                                    next if /[\x00-\x1f\x80-\xff]/;
+                                    $index = "'$_'";
+                                }
+                                push @values, "$index = " . $$printConv{$_};
+                            }
+                            if ($bits) {
+                                my @pk = sort NumbersFirst keys %$bits;
+                                foreach (@pk) {
+                                    push @values, "Bit $_ = " . $$bits{$_};
+                                }
+                            }
+                        }
+                        last unless @printConvList;
+                        $printConv = shift @printConvList;
+                        $index = shift @indexList;
+                    }
+                } elsif ($printConv and $printConv =~ /DecodeBits\(\$val,\s*(\{.*\})\s*\)/s) {
+                    my $bits = eval $1;
+                    if ($@) {
+                        warn $@;
+                    } else {
+                        my @pk = sort NumbersFirst keys %$bits;
+                        foreach (@pk) {
+                            push @values, "Bit $_ = " . $$bits{$_};
+                        }
+                    }
+                }
+                if ($subdir and not $$tagInfo{SeparateTable}) {
+                    # subdirectories are only writable if specified explicitly
+                    $writable = '-' . ($$tagInfo{Writable} ? $writable : '');
+                } else {
+                    # not writable if we can't do the inverse conversions
+                    my $noPrintConvInv;
+                    if ($writable) {
+                        foreach ('PrintConv','ValueConv') {
+                            next unless $$tagInfo{$_};
+                            next if $$tagInfo{$_ . 'Inv'};
+                            next if ref($$tagInfo{$_}) =~ /^(HASH|ARRAY)$/;
+                            next if $$tagInfo{WriteAlso};
+                            if ($_ eq 'ValueConv') {
+                                undef $writable;
+                            } else {
+                                $noPrintConvInv = 1;
+                            }
+                        }
+                    }
+                    if (not $writable) {
+                        $writable = 'N';
+                    } else {
+                        $writable eq '1' and $writable = $format ? $format : 'Y';
+                        my $count = $$tagInfo{Count} || 1;
+                        # adjust count to Writable size if different than Format
+                        if ($writable and $format and $writable ne $format and
+                            $Image::ExifTool::Exif::formatNumber{$writable} and
+                            $Image::ExifTool::Exif::formatNumber{$format})
+                        {
+                            my $n1 = $Image::ExifTool::Exif::formatNumber{$format};
+                            my $n2 = $Image::ExifTool::Exif::formatNumber{$writable};
+                            $count *= $Image::ExifTool::Exif::formatSize[$n1] /
+                                      $Image::ExifTool::Exif::formatSize[$n2];
+                        }
+                        if ($count != 1) {
+                            $count = 'n' if $count < 0;
+                            $writable .= "[$count]";
+                        }
+                        $writable .= '~' if $noPrintConvInv;
+                        # add a '*' if this tag is protected or a '~' for unsafe tags
+                        if ($$tagInfo{Protected}) {
+                            $writable .= '*' if $$tagInfo{Protected} & 0x02;
+                            $writable .= '!' if $$tagInfo{Protected} & 0x01;
+                        }
+                        $writable .= '/' if $$tagInfo{Avoid};
+                    }
+                    $writable .= '+' if $$tagInfo{List};
+                    # separate tables link like subdirectories (flagged with leading '-')
+                    $writable = "-$writable" if $subdir;
+                }
+                # don't duplicate a tag name unless an entry is different
+                my $name = $$tagInfo{Name};
+                my $lcName = lc($name);
+                # check for conflicts with shortcut names
+                if ($isShortcut{$lcName} and $short ne 'Shortcuts' and
+                    ($$tagInfo{Writable} or not $$tagInfo{SubDirectory}))
+                {
+                    warn "WARNING: $short $name is a shortcut tag!\n";
+                }
+                $name .= '?' if $$tagInfo{Unknown};
+                unless (@tagNames and $tagNames[-1] eq $name and
+                    $writeGroup[-1] eq $writeGroup and $writable[-1] eq $writable)
+                {
+                    push @tagNames, $name;
+                    push @writeGroup, $writeGroup;
+                    push @writable, $writable;
+                }
+#
+# add this tag to the tag lookup unless PROCESS_PROC is 0 or shortcut tag
+#
+                next if $shortcut or (defined $$table{PROCESS_PROC} and not $$table{PROCESS_PROC});
+                # count our tags
+                if ($$tagInfo{SubDirectory}) {
+                    $subdirs{$lcName} or $subdirs{$lcName} = 0;
+                    ++$subdirs{$lcName};
+                } else {
+                    ++$count{'total tags'};
+                    unless ($tagExists{$lcName} and (not $subdirs{$lcName} or $subdirs{$lcName} == $tagExists{$lcName})) {
+                        ++$count{'unique tag names'};
+                    }
+                }
+                $tagExists{$lcName} or $tagExists{$lcName} = 0;
+                ++$tagExists{$lcName};
+                # only add writable tags to lookup table (for speed)
+                my $wflag = $$tagInfo{Writable};
+                next unless $writeProc and ($wflag or ($$table{WRITABLE} and
+                    not defined $wflag and not $$tagInfo{SubDirectory}));
+                $tagLookup{$lcName} or $tagLookup{$lcName} = { };
+                # remember number for this table
+                my $tagIDs = $tagLookup{$lcName}->{$tableNum};
+                # must allow for duplicate tags with the same name in a single table!
+                if ($tagIDs) {
+                    if (ref $tagIDs eq 'HASH') {
+                        $$tagIDs{$tagID} = 1;
+                        next;
+                    } elsif ($tagID eq $tagIDs) {
+                        next;
+                    } else {
+                        $tagIDs = { $tagIDs => 1, $tagID => 1 };
+                    }
+                } else {
+                    $tagIDs = $tagID;
+                }
+                $tableWritable{$tableName} = 1;
+                $tagLookup{$lcName}->{$tableNum} = $tagIDs;
+                if ($short eq 'Composite' and $$tagInfo{Module}) {
+                    $compositeModules{$lcName} = $$tagInfo{Module};
+                }
+            }
+#
+# save TagName information
+#
+            my $tagIDstr;
+            if ($tagID =~ /^\d+$/) {
+                if ($binaryTable or $isIPTC or ($short =~ /^CanonCustom/ and $tagID < 256)) {
+                    $tagIDstr = $tagID;
+                } else {
+                    $tagIDstr = sprintf("0x%.4x",$tagID);
+                }
+            } elsif ($short eq 'DICOM') {
+                ($tagIDstr = $tagID) =~ s/_/,/;
+            } else {
+                # convert non-printable characters to hex escape sequences
+                if ($tagID =~ s/([\x00-\x1f\x7f-\xff])/'\x'.unpack('H*',$1)/eg) {
+                    $tagID =~ s/\\x00/\\0/g;
+                    next if $tagID eq 'jP\x1a\x1a'; # ignore abnormal JP2 signature tag
+                    $tagIDstr = qq{"$tagID"};
+                } else {
+                    $tagIDstr = "'$tagID'";
+                }
+            }
+            my $len = length $tagIDstr;
+            $longID{$tableName} = $len if $longID{$tableName} < $len;
+            foreach (@tagNames) {
+                $len = length $_;
+                $longName{$tableName} = $len if $longName{$tableName} < $len;
+            }
+            push @$info, [ $tagIDstr, \@tagNames, \@writable, \@values, \@require, \@writeGroup ];
+        }
+    }
+    return $self;
+}
+
+#------------------------------------------------------------------------------
+# Rewrite this file to build the lookup tables
+# Inputs: 0) BuildTagLookup object reference
+#         1) output tag lookup module name (ie. 'lib/Image/ExifTool/TagLookup.pm')
+# Returns: true on success
+sub WriteTagLookup($$)
+{
+    local $_;
+    my ($self, $file) = @_;
+    my $tagLookup = $self->{TAG_LOOKUP};
+    my $tagExists = $self->{TAG_EXISTS};
+    my $tableWritable = $self->{TABLE_WRITABLE};
+#
+# open/create necessary files and transfer file headers
+#
+    my $tmpFile = "${file}_tmp";
+    open(INFILE,$file) or warn("Can't open $file\n"), return 0;
+    unless (open(OUTFILE,">$tmpFile")) {
+        warn "Can't create temporary file $tmpFile\n";
+        close(INFILE);
+        return 0;
+    }
+    my $success;
+    while (<INFILE>) {
+        print OUTFILE $_ or last;
+        if (/^#\+{4} Begin/) {
+            $success = 1;
+            last;
+        }
+    }
+    print OUTFILE "\n# list of tables containing writable tags\n";
+    print OUTFILE "my \@tableList = (\n";
+
+#
+# write table list
+#
+    my @tableNames = sort keys %allTables;
+    my $tableName;
+    my %wrNum;      # translate from allTables index to writable tables index
+    my $count = 0;
+    my $num = 0;
+    foreach $tableName (@tableNames) {
+        if ($$tableWritable{$tableName}) {
+            print OUTFILE "\t'$tableName',\n";
+            $wrNum{$count} = $num++;
+        }
+        $count++;
+    }
+#
+# write the tag lookup table
+#
+    my $tag;
+    # verify that certain critical tag names aren't duplicated
+    foreach $tag (qw{filename directory}) {
+        next unless $$tagLookup{$tag};
+        my $n = scalar keys %{$$tagLookup{$tag}};
+        warn "Warning: $n writable '$tag' tags!\n" if $n > 1;
+    }
+    print OUTFILE ");\n\n# lookup for all writable tags\nmy \%tagLookup = (\n";
+    foreach $tag (sort keys %$tagLookup) {
+        print OUTFILE "\t'$tag' => { ";
+        my @tableNums = sort { $a <=> $b } keys %{$$tagLookup{$tag}};
+        my (@entries, $tableNum);
+        foreach $tableNum (@tableNums) {
+            my $tagID = $$tagLookup{$tag}->{$tableNum};
+            my $entry;
+            if (ref $tagID eq 'HASH') {
+                my @tagIDs = sort keys %$tagID;
+                foreach (@tagIDs) {
+                    if (/^\d+$/) {
+                        $_ = sprintf("0x%x",$_);
+                    } else {
+                        my $quot = "'";
+                        # escape non-printable characters in tag ID if necessary
+                        $quot = '"' if s/[\x00-\x1f,\x7f-\xff]/sprintf('\\x%.2x',ord($&))/ge;
+                        $_ = $quot . $_ . $quot;
+                    }
+                }
+                $entry = '[' . join(',', @tagIDs) . ']';
+            } elsif ($tagID =~ /^\d+$/) {
+                $entry = sprintf("0x%x",$tagID);
+            } else {
+                $entry = "'$tagID'";
+            }
+            my $wrNum = $wrNum{$tableNum};
+            push @entries, "$wrNum => $entry";
+        }
+        print OUTFILE join(', ', @entries);
+        print OUTFILE " },\n";
+    }
+#
+# write tag exists lookup
+#
+    print OUTFILE ");\n\n# lookup for non-writable tags to check if the name exists\n";
+    print OUTFILE "my \%tagExists = (\n";
+    foreach $tag (sort keys %$tagExists) {
+        next if $$tagLookup{$tag};
+        print OUTFILE "\t'$tag' => 1,\n";
+    }
+#
+# write module lookup for writable composite tags
+#
+    my $compositeModules = $self->{COMPOSITE_MODULES};
+    print OUTFILE ");\n\n# module names for writable Composite tags\n";
+    print OUTFILE "my \%compositeModules = (\n";
+    foreach (sort keys %$compositeModules) {
+        print OUTFILE "\t'$_' => '$$compositeModules{$_}',\n";
+    }
+    print OUTFILE ");\n\n";
+#
+# finish writing TagLookup.pm and clean up
+#
+    if ($success) {
+        $success = 0;
+        while (<INFILE>) {
+            $success or /^#\+{4} End/ or next;
+            print OUTFILE $_;
+            $success = 1;
+        }
+    }
+    close(INFILE);
+    close(OUTFILE) or $success = 0;
+#
+# return success code
+#
+    if ($success) {
+        rename($tmpFile, $file);
+    } else {
+        unlink($tmpFile);
+        warn "Error rewriting file\n";
+    }
+    return $success;
+}
+
+#------------------------------------------------------------------------------
+# sort numbers first numerically, then strings alphabetically (case insensitive)
+sub NumbersFirst
+{
+    my $rtnVal;
+    my $bNum = ($b =~ /^-?[0-9]+$/);
+    if ($a =~ /^-?[0-9]+$/) {
+        $rtnVal = ($bNum ? $a <=> $b : -1);
+    } elsif ($bNum) {
+        $rtnVal = 1;
+    } else {
+        my ($a2, $b2) = ($a, $b);
+        # expand numbers to 3 digits (with restrictions to avoid messing up ascii-hex tags)
+        $a2 =~ s/(\d+)/sprintf("%.3d",$1)/eg if $a2 =~ /^(APP)?[0-9 ]*$/ and length($a2)<16;
+        $b2 =~ s/(\d+)/sprintf("%.3d",$1)/eg if $b2 =~ /^(APP)?[0-9 ]*$/ and length($b2)<16;
+        $caseInsensitive and $rtnVal = (lc($a2) cmp lc($b2));
+        $rtnVal or $rtnVal = ($a2 cmp $b2);
+    }
+    return $rtnVal;
+}
+
+#------------------------------------------------------------------------------
+# Convert pod documentation to pod
+# (funny, I know, but the pod headings must be hidden to prevent confusing
+#  the pod parser)
+# Inputs: 0) string
+sub Doc2Pod($)
+{
+    my $doc = shift;
+    $doc =~ s/\n~/\n=/g;
+    return $doc;
+}
+
+#------------------------------------------------------------------------------
+# Convert pod documentation to html
+# Inputs: 0) string
+sub Doc2Html($)
+{
+    my $doc = EscapeHTML(shift);
+    $doc =~ s/\n\n/<\/p>\n\n<p>/g;
+    $doc =~ s/B&lt;(.*?)&gt;/<b>$1<\/b>/sg;
+    $doc =~ s/C&lt;(.*?)&gt;/<code>$1<\/code>/sg;
+    $doc =~ s/L&lt;(.*?)&gt;/<a href="$1">$1<\/a>/sg;
+    return $doc;
+}
+
+#------------------------------------------------------------------------------
+# Get the order that we want to print the tables in the documentation
+# Returns: tables in the order we want
+sub GetTableOrder()
+{
+    my %gotTable;
+    my $count = 0;
+    my @tableNames = @tableOrder;
+    my (@orderedTables, %mainTables, @outOfOrder);
+    my $lastTable = '';
+
+    while (@tableNames) {
+        my $tableName = shift @tableNames;
+        next if $gotTable{$tableName};
+        if ($tableName =~ /^Image::ExifTool::(\w+)::Main/) {
+            $mainTables{$1} = 1;
+        } elsif ($lastTable and not $tableName =~ /^${lastTable}::/) {
+            push @outOfOrder, $tableName;
+        }
+        ($lastTable) = ($tableName =~ /^(Image::ExifTool::\w+)/);
+        push @orderedTables, $tableName;
+        $gotTable{$tableName} = 1;
+        my $table = GetTagTable($tableName);
+        # recursively scan through tables in subdirectories
+        my @moreTables;
+        $caseInsensitive = ($tableName =~ /::XMP::/);
+        my @keys = sort NumbersFirst TagTableKeys($table);
+        foreach (@keys) {
+            my @infoArray = GetTagInfoList($table,$_);
+            my $tagInfo;
+            foreach $tagInfo (@infoArray) {
+                my $subdir = $$tagInfo{SubDirectory} or next;
+                $tableName = $$subdir{TagTable} or next;
+                next if $gotTable{$tableName};  # next if table already loaded
+                push @moreTables, $tableName;   # must scan this one too
+            }
+        }
+        unshift @tableNames, @moreTables;
+    }
+    # clean up the order for tables which are out of order
+    # (groups all Canon and Kodak tables together)
+    my %fixOrder;
+    foreach (@outOfOrder) {
+        next unless /^Image::ExifTool::(\w+)/;
+        # only re-order tables which have a corresponding main table
+        next unless $mainTables{$1};
+        $fixOrder{$1} = [];     # fix the order of these tables
+    }
+    my (@sortedTables, %fixPos, $pos);
+    foreach (@orderedTables) {
+        if (/^Image::ExifTool::(\w+)/ and $fixOrder{$1}) {
+            my $fix = $fixOrder{$1};
+            unless (@$fix) {
+                $pos = @sortedTables;
+                $fixPos{$pos} or $fixPos{$pos} = [];
+                push @{$fixPos{$pos}}, $1;
+            }
+            push @{$fix}, $_;
+        } else {
+            push @sortedTables, $_;
+        }
+    }
+    # insert back in better order
+    foreach $pos (reverse sort { $a <=> $b } keys %fixPos) {
+        my $fix = $fixPos{$pos};
+        foreach (@$fix) {
+            splice(@sortedTables, $pos, 0, @{$fixOrder{$_}});
+        }
+    }
+    # tweak the table order
+    my %tweakOrder = (
+        JPEG    => '-',     # JPEG comes first
+        IPTC    => 'Exif',  # put IPTC after EXIF,
+        GPS     => 'XMP',   # etc...
+        GeoTiff => 'GPS',
+        Leaf    => 'Kodak',
+        Unknown => 'Sony',
+        DNG     => 'Unknown',
+        PrintIM => 'ICC_Profile',
+        Olympus => 'NikonCapture',
+        Pentax  => 'Panasonic',
+        Ricoh   => 'Pentax',
+        Sanyo   => 'Ricoh',
+        PhotoMechanic => 'FotoStation',
+    );
+    my @tweak = sort keys %tweakOrder;
+    while (@tweak) {
+        my $table = shift @tweak;
+        my $first = $tweakOrder{$table};
+        if ($tweakOrder{$first}) {
+            push @tweak, $table;    # must defer this till later
+            next;
+        }
+        delete $tweakOrder{$table}; # because the table won't move again
+        my @moving = grep /^Image::ExifTool::$table\b/, @sortedTables;
+        my @notMoving = grep !/^Image::ExifTool::$table\b/, @sortedTables;
+        my @after;
+        while (@notMoving) {
+            last if $notMoving[-1] =~ /^Image::ExifTool::$first\b/;
+            unshift @after, pop @notMoving;
+        }
+        @sortedTables = (@notMoving, @moving, @after);
+    }
+    return @sortedTables
+}
+
+#------------------------------------------------------------------------------
+# Open HTMLFILE and print header and description
+# Inputs: 0) Filename, 1) optional category
+# Returns: True on success
+my %createdFiles;
+sub OpenHtmlFile($;$$)
+{
+    my ($htmldir, $category, $sepTable) = @_;
+    my ($htmlFile, $head, $title, $url, $class);
+    my $top = '';
+
+    if ($category) {
+        my @names = split ' ', $category;
+        $class = shift @names;
+        $htmlFile = "$htmldir/TagNames/$class.html";
+        $head = $category . ($sepTable ? ' Values' : ' Tags');
+        ($title = $head) =~ s/ .* / /;
+        @names and $url = join '_', @names;
+    } else {
+        $htmlFile = "$htmldir/TagNames/index.html";
+        $category = $class = 'ExifTool';
+        $head = $title = 'ExifTool Tag Names';
+    }
+    if ($createdFiles{$htmlFile}) {
+        open(HTMLFILE,">>${htmlFile}_tmp") or return 0;
+    } else {
+        open(HTMLFILE,">${htmlFile}_tmp") or return 0;
+        print HTMLFILE "$docType<html>\n<head>\n<title>$title</title>\n";
+        print HTMLFILE "<link rel=stylesheet type='text/css' href='style.css' title='Style'>\n";
+        print HTMLFILE "</head>\n<body>\n";
+        if ($category ne $class and $docs{$class}) {
+            print HTMLFILE "<h2 class=top>$class Tags</h2>\n" or return 0;
+            print HTMLFILE '<p>',Doc2Html($docs{$class}),"</p>\n" or return 0;
+        } else {
+            $top = " class=top";
+        }
+    }
+    $head = "<a name='$url'>$head</a>" if $url;
+    print HTMLFILE "<h2$top>$head</h2>\n" or return 0;
+    print HTMLFILE '<p>',Doc2Html($docs{$category}),"</p>\n" if $docs{$category};
+    $createdFiles{$htmlFile} = 1;
+    return 1;
+}
+
+#------------------------------------------------------------------------------
+# Close all html files and write trailers
+# Returns: true on success
+# Inputs: 0) BuildTagLookup object reference
+sub CloseHtmlFiles($)
+{
+    my $self = shift;
+    my $preserveDate = $$self{PRESERVE_DATE};
+    my $success = 1;
+    # get the date
+    my ($sec,$min,$hr,$day,$mon,$yr) = localtime;
+    my @month = qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec);
+    $yr += 1900;
+    my $date = "$month[$mon] $day, $yr";
+    my $htmlFile;
+    my $countNewFiles = 0;
+    my $countSameFiles = 0;
+    foreach $htmlFile (keys %createdFiles) {
+        my $tmpFile = $htmlFile . '_tmp';
+        my $fileDate = $date;
+        if ($preserveDate) {
+            my @lines = `grep '<i>Last revised' $htmlFile`;
+            $fileDate = $1 if @lines and $lines[-1] =~ m{<i>Last revised (.*)</i>};
+        }
+        open(HTMLFILE,">>$tmpFile") or $success = 0, next;
+        # write the trailers
+        print HTMLFILE "<hr>\n";
+        print HTMLFILE "(This document generated automatically by Image::ExifTool::BuildTagLookup)\n";
+        print HTMLFILE "<br><i>Last revised $fileDate</i>\n";
+        print HTMLFILE "<p class=lf><a href=";
+        if ($htmlFile =~ /index\.html$/) {
+            print HTMLFILE "'../index.html'>&lt;-- Back to ExifTool home page</a></p>\n";
+        } else {
+            print HTMLFILE "'index.html'>&lt;-- ExifTool Tag Names</a></p>\n"
+        }
+        print HTMLFILE "</body>\n</html>\n" or $success = 0;
+        close HTMLFILE or $success = 0;
+        # check for differences and only use new file if it was changed
+        # (so the date only gets updated if changes were really made)
+        my $useNewFile;
+        if ($success) {
+            open (TEMPFILE, $tmpFile) or $success = 0, last;
+            if (open (HTMLFILE, $htmlFile)) {
+                while (<HTMLFILE>) {
+                    my $newLine = <TEMPFILE>;
+                    if (defined $newLine) {
+                        next if /^<br><i>Last revised/;
+                        next if $_ eq $newLine;
+                    }
+                    # files are different -- use the new file
+                    $useNewFile = 1;
+                    last;
+                }
+                $useNewFile = 1 if <TEMPFILE>;
+                close HTMLFILE;
+            } else {
+                $useNewFile = 1;
+            }
+            close TEMPFILE;
+            if ($useNewFile) {
+                ++$countNewFiles;
+                rename $tmpFile, $htmlFile or warn("Error renaming temporary file\n"), $success = 0;
+            } else {
+                ++$countSameFiles;
+                unlink $tmpFile;   # erase new file and use existing file
+            }
+        }
+        last unless $success;
+    }
+    # save number of files processed so we can check the results later
+    $self->{COUNT}->{'HTML files changed'} = $countNewFiles;
+    $self->{COUNT}->{'HTML files unchanged'} = $countSameFiles;
+    return $success;
+}
+
+#------------------------------------------------------------------------------
+# Write the TagName HTML and POD documentation
+# Inputs: 0) BuildTagLookup object reference
+#         1) output pod file (ie. 'lib/Image/ExifTool/TagNames.pod')
+#         2) output html directory (ie. 'html')
+# Returns: true on success
+sub WriteTagNames($$)
+{
+    my ($self, $podFile, $htmldir) = @_;
+    my ($tableName, $short, $url, @sepTables);
+    my $tagNameInfo = $self->{TAG_NAME_INFO} or return 0;
+    my $idTitle = $self->{TAG_ID};
+    my $shortName = $self->{SHORT_NAME};
+    my $sepTable = $self->{SEPARATE_TABLE};
+    my $success = 1;
+    my %htmlFiles;
+    my $columns = 6;    # number of columns in html index
+    my $percent = int(100 / $columns);
+
+    # open the file and write the header
+    open(PODFILE,">$podFile") or return 0;
+    print PODFILE Doc2Pod($docs{PodHeader}), $docs{ExifTool}, $docs{ExifTool2};
+    mkdir "$htmldir/TagNames";
+    OpenHtmlFile($htmldir) or return 0;
+    print HTMLFILE "<blockquote>\n";
+    print HTMLFILE "<table width='100%' class=frame><tr><td>\n";
+    print HTMLFILE "<table width='100%' class=inner cellspacing=1><tr class=h>\n";
+    print HTMLFILE "<th colspan=$columns><span class=l>Tag Table Index</span></th></tr>\n";
+    print HTMLFILE "<tr class=b><td width='$percent%'>\n";
+    # write the index
+    my @tableNames = GetTableOrder();
+    push @tableNames, 'Image::ExifTool::Shortcuts::Main';   # do Shortcuts last
+    # get list of headings and add any missing ones
+    my $heading = 'xxx';
+    my (@tableIndexNames, @headings);
+    foreach $tableName (@tableNames) {
+        $short = $$shortName{$tableName};
+        my @names = split ' ', $short;
+        my $class = shift @names;
+        if (@names) {
+            # add heading for tables without a Main
+            unless ($heading eq $class) {
+                $heading = $class;
+                push @tableIndexNames, $heading;
+                push @headings, $heading;
+            }
+        } else {
+            $heading = $short;
+            push @headings, $heading;
+        }
+        push @tableIndexNames, $tableName;
+    }
+    @tableNames = @tableIndexNames;
+    # print html index of headings only
+    my $count = 0;
+    my $lines = int((scalar(@headings) + $columns - 1) / $columns);
+    foreach $tableName (@headings) {
+        if ($count) {
+            if ($count % $lines) {
+                print HTMLFILE "<br>\n";
+            } else {
+                print HTMLFILE "</td><td width='$percent%'>\n";
+            }
+        }
+        $short = $$shortName{$tableName};
+        $short = $tableName unless $short;
+        $url = "$short.html";
+        print HTMLFILE "<a href='$url'>$short</a>";
+        ++$count;
+    }
+    print HTMLFILE "\n</td></tr></table></td></tr></table></blockquote>\n";
+    print HTMLFILE '<p>',Doc2Html($docs{ExifTool2}),"</p>\n";
+    # write all the tag tables
+    while (@tableNames or @sepTables) {
+        while (@sepTables) {
+            $tableName = shift @sepTables;
+            my $printConv = $$sepTable{$tableName};
+            next unless ref $printConv eq 'HASH';
+            $$sepTable{$tableName} = 1;
+            my $notes = $$printConv{Notes};
+            if ($notes) {
+                # remove unnecessary whitespace
+                $notes =~ s/(^\s+|\s+$)//g;
+                $notes =~ s/(^[ \t]+|[ \t]+$)//mg;
+            }
+            my $head = $tableName;
+            $head =~ s/.* //;
+            close HTMLFILE;
+            if (OpenHtmlFile($htmldir, $tableName, 1)) {
+                print HTMLFILE Doc2Html($notes), "\n" if $notes;
+                print HTMLFILE "<blockquote>\n";
+                print HTMLFILE "<table class=frame><tr><td>\n";
+                print HTMLFILE "<table class='inner sep' cellspacing=1>\n";
+                my $align = ' class=r';
+                my $wid = 0;
+                my @keys;
+                foreach (sort NumbersFirst keys %$printConv) {
+                    next if /^(Notes|PrintHex)$/;
+                    $align = '' if $align and /[^\d]/;
+                    my $w = length($_) + length($$printConv{$_});
+                    $wid = $w if $wid < $w;
+                    push @keys, $_;
+                }
+                $wid = length($tableName)+7 if $wid < length($tableName)+7;
+                # print in multiple columns if there is room
+                my $cols = int(80 / ($wid + 4));
+                $cols = 1 if $cols < 1 or $cols > @keys;
+                my $rows = int((scalar(@keys) + $cols - 1) / $cols);
+                my ($r, $c);
+                print HTMLFILE '<tr class=h>';
+                for ($c=0; $c<$cols; ++$c) {
+                    print HTMLFILE "<th>Value</th><th>$head</th>";
+                }
+                print HTMLFILE "</tr>\n";
+                for ($r=0; $r<$rows; ++$r) {
+                    print HTMLFILE '<tr>';
+                    for ($c=0; $c<$cols; ++$c) {
+                        my $key = $keys[$r + $c*$rows];
+                        my ($index, $prt);
+                        if (defined $key) {
+                            $index = $key;
+                            $prt = "= $$printConv{$key}";
+                            if ($$printConv{PrintHex}) {
+                                $index = sprintf('0x%x',$index);
+                            } elsif ($index !~ /^[-+]?\d+$/) {
+                                $index = "'" . EscapeHTML($index) . "'";
+                            }
+                        } else {
+                            $index = $prt = '&nbsp;';
+                        }
+                        my ($ic, $pc);
+                        if ($c & 0x01) {
+                            $pc = ' class=b';
+                            $ic = $align ? " class='r b'" : $pc;
+                        } else {
+                            $ic = $align;
+                            $pc = '';
+                        }
+                        print HTMLFILE "<td$ic>$index</td><td$pc>$prt</td>\n";
+                    }
+                    print HTMLFILE '</tr>';
+                }
+                print HTMLFILE "</table></td></tr></table></blockquote>\n\n";
+            }
+        }
+        last unless @tableNames;
+        $tableName = shift @tableNames;
+        $short = $$shortName{$tableName};
+        unless ($short) {
+            # this is just an index heading
+            print PODFILE "\n=head2 $tableName Tags\n";
+            print PODFILE $docs{$tableName} if $docs{$tableName};
+            next;
+        }
+        my $info = $$tagNameInfo{$tableName};
+        my $id = $$idTitle{$tableName};
+        my ($hid, $showGrp);
+        # widths of the different columns in the POD documentation
+        my ($wID,$wTag,$wReq,$wGrp) = (8,36,24,10);
+        my $composite = $short eq 'Composite' ? 1 : 0;
+        my $derived = $composite ? '<th>Derived From</th>' : '';
+        if ($short eq 'Shortcuts') {
+            $derived = '<th>Refers To</th>';
+            $composite = 2;
+        }
+        my $podIdLen = $self->{LONG_ID}->{$tableName};
+        my $notes;
+        unless ($composite == 2) {
+            my $table = GetTagTable($tableName);
+            $notes = $$table{NOTES};
+        }
+        my $prefix;
+        if ($notes) {
+            # remove unnecessary whitespace
+            $notes =~ s/(^\s+|\s+$)//g;
+            $notes =~ s/(^[ \t]+|[ \t]+$)//mg;
+            if ($notes =~ /leading '(.*?_)' which/) {
+                $prefix = $1;
+                $podIdLen -= length $prefix;
+            }
+        }
+        if ($podIdLen <= $wID) {
+            $podIdLen = $wID;
+        } elsif ($short eq 'DICOM') {
+            $podIdLen = 10;
+        } else {
+            # align tag names in secondary columns if possible
+            my $col = ($podIdLen <= 10) ? 12 : 20;
+            $podIdLen = $col if $podIdLen < $col;
+        }
+        $id = '' if $short =~ /^XMP/;
+        if ($id) {
+            ($hid = "<th>$id</th>") =~ s/ /&nbsp;/g;
+            $wTag -= $podIdLen - $wID;
+            $wID = $podIdLen;
+            my $longTag = $self->{LONG_NAME}->{$tableName};
+            if ($wTag < $longTag) {
+                $wID -= $longTag - $wTag;
+                $wTag = $longTag;
+                warn "Notice: Long tags in $tableName table\n";
+            }
+        } elsif ($short !~ /^(Composite|Shortcuts)/) {
+            $wTag += 9;
+            $hid = '';
+        } else {
+            $hid = '';
+            $wTag += $wID - $wReq if $composite;
+        }
+        if ($short eq 'EXIF') {
+            $derived = '<th>Group</th>';
+            $showGrp = 1;
+            $wTag -= $wGrp + 1;
+        }
+        my $head = ($short =~ / /) ? 'head3' : 'head2';
+        print PODFILE "\n=$head $short Tags\n";
+        print PODFILE $docs{$short} if $docs{$short};
+        print PODFILE "\n$notes\n" if $notes;
+        my $line = "\n";
+        if ($id) {
+            # shift over 'Index' heading by one character for a bit more balance
+            $id = " $id" if $id eq 'Index';
+            $line .= sprintf "  %-${wID}s", $id;
+        } else {
+            $line .= ' ';
+        }
+        my $tagNameHeading = ($short eq 'XMP') ? 'Namespace' : 'Tag Name';
+        $line .= sprintf " %-${wTag}s", $tagNameHeading;
+        $line .= sprintf " %-${wReq}s", $composite == 2 ? 'Refers To' : 'Derived From' if $composite;
+        $line .= sprintf " %-${wGrp}s", 'Group' if $showGrp;
+        $line .= ' Writable';
+        print PODFILE $line;
+        $line =~ s/Sequence /Sequence\t/;   # don't want an underline after 'Sequence'
+        $line =~ s/\S/-/g;
+        $line =~ s/- -/---/g;
+        $line =~ tr/\t/ /;
+        print PODFILE $line,"\n";
+        close HTMLFILE;
+        OpenHtmlFile($htmldir, $short) or $success = 0;
+        print HTMLFILE '<p>',Doc2Html($notes), "</p>\n" if $notes;
+        print HTMLFILE "<blockquote>\n";
+        print HTMLFILE "<table class=frame><tr><td>\n";
+        print HTMLFILE "<table class=inner cellspacing=1>\n";
+        print HTMLFILE "<tr class=h>$hid<th>$tagNameHeading</th>\n";
+        print HTMLFILE "<th>Writable</th>$derived<th>Values / ${noteFont}Notes</span></th></tr>\n";
+        my $rowClass = 1;
+        my $infoCount = 0;
+        my $infoList;
+        foreach $infoList (@$info) {
+            ++$infoCount;
+            my ($tagIDstr, $tagNames, $writable, $values, $require, $writeGroup) = @$infoList;
+            my ($align, $idStr, $w);
+            if (not $id) {
+                $idStr = '  ';
+            } elsif ($tagIDstr =~ /^\d+$/) {
+                $w = $wID - 3;
+                $idStr = sprintf "  %${w}d    ", $tagIDstr;
+                $align = " class=r";
+            } else {
+                $tagIDstr =~ s/^'$prefix/'/ if $prefix;
+                $w = $wID;
+                if (length $tagIDstr > $w) {
+                    # put tag name on next line if ID is too long
+                    $idStr = "  $tagIDstr\n   " . (' ' x $w);
+                    warn "Notice: Split $$tagNames[0] line\n";
+                } else {
+                    $idStr = sprintf "  %-${w}s ", $tagIDstr;
+                }
+                $align = '';
+            }
+            my @reqs;
+            my @tags = @$tagNames;
+            my @wGrp = @$writeGroup;
+            my @vals = @$writable;
+            my $wrStr = shift @vals;
+            my $subdir;
+            # if this is a subdirectory, print subdir name (from values) instead of writable
+            if ($wrStr =~ /^-/) {
+                $subdir = 1;
+                @vals = @$values;
+                # remove Notes if subdir has Notes as well
+                shift @vals if $vals[0] =~ /^\(/ and @vals >= @$writable;
+                foreach (@vals) { /^\(/ and $_ = '-' }
+                my $i;  # fill in any missing entries from non-directory tags
+                for ($i=0; $i<@$writable; ++$i) {
+                    $vals[$i] = $$writable[$i] unless defined $vals[$i];
+                }
+                if ($$sepTable{$vals[0]}) {
+                    $wrStr =~ s/^-//;
+                    $wrStr = 'N' unless $wrStr;
+                } else {
+                    $wrStr = $vals[0];
+                }
+                shift @vals;
+            }
+            my $tag = shift @tags;
+            printf PODFILE "%s%-${wTag}s", $idStr, $tag;
+            warn "Warning: Pushed $tag\n" if $id and length($tag) > $wTag;
+            printf PODFILE " %-${wGrp}s", shift(@wGrp) || '-' if $showGrp;
+            if ($composite) {
+                @reqs = @$require;
+                $w = $wReq; # Keep writable column in line
+                length($tag) > $wTag and $w -= length($tag) - $wTag;
+                printf PODFILE " %-${w}s", shift(@reqs) || '';
+            }
+            printf PODFILE " $wrStr\n";
+            my $n = 0;
+            while (@tags or @reqs or @vals) {
+                my $more = (@tags or @reqs);
+                $line = '  ';
+                $line .= ' 'x($wID+1) if $id;
+                $line .= sprintf("%-${wTag}s", shift(@tags) || '');
+                $line .= sprintf(" %-${wReq}s", shift(@reqs) || '') if $composite;
+                $line .= sprintf(" %-${wGrp}s", shift(@wGrp) || '-') if $showGrp;
+                ++$n;
+                if (@vals) {
+                    my $val = shift @vals;
+                    # use writable if this is a note
+                    my $wrStr = $$writable[$n];
+                    if ($subdir and ($val =~ /^\(/ or $val =~ /=/ or ($wrStr and $wrStr !~ /^-/))) {
+                        $val = $wrStr;
+                        if (defined $val) {
+                            $val =~ s/^-//;
+                        } else {
+                            # done with tag if nothing else to print
+                            last unless $more;
+                        }
+                    }
+                    $line .= " $val" if defined $val;
+                }
+                $line =~ s/\s+$//;  # trim trailing white space
+                print PODFILE "$line\n";
+            }
+            my @htmlTags;
+            foreach (@$tagNames) {
+                push @htmlTags, EscapeHTML($_);
+            }
+            $rowClass = $rowClass ? '' : " class=b";
+            my $isSubdir;
+            if ($$writable[0] =~ /^-/) {
+                $isSubdir = 1;
+                foreach (@$writable) {
+                    s/^-(.+)/$1/;
+                }
+            }
+            print HTMLFILE "<tr$rowClass>\n";
+            print HTMLFILE "<td$align>$tagIDstr</td>\n" if $id;
+            print HTMLFILE "<td>", join("\n  <br>",@htmlTags), "</td>\n";
+            print HTMLFILE "<td class=c>",join('<br>',@$writable),"</td>\n";
+            print HTMLFILE '<td>',join("\n  <br>",@$require),"</td>\n" if $composite;
+            print HTMLFILE "<td class=c>",join('<br>',@$writeGroup),"</td>\n" if $showGrp;
+            print HTMLFILE "<td>";
+            my $close = '';
+            my @values;
+            if (@$values) {
+                if ($isSubdir) {
+                    my $smallNote;
+                    foreach (@$values) {
+                        if (/^\(/) {
+                            $smallNote = 1 if $n <= 1;
+                            push @values, ($smallNote ? $noteFontSmall : $noteFont) . "$_</span>";
+                            next;
+                        }
+                        /=/ and push(@values, $_), next;
+                        my @names = split;
+                        $url = (shift @names) . '.html';
+                        @names and $url .= '#' . join '_', @names;
+                        my $suffix = ' Tags';
+                        if ($$sepTable{$_}) {
+                            push @sepTables, $_;
+                            $suffix = ' Values';
+                        }
+                        push @values, "--&gt; <a href='$url'>$_$suffix</a>";
+                    }
+                    # put small note last
+                    $smallNote and push @values, shift @values;
+                } else {
+                    foreach (@$values) {
+                        $_ = EscapeHTML($_);
+                        /^\(/ and $_ = "$noteFont$_</span>";
+                        push @values, $_;
+                    }
+                    print HTMLFILE "<span class=s>";
+                    $close = '</span>';
+                }
+            } else {
+                push @values, '&nbsp;';
+            }
+            print HTMLFILE join("\n  <br>",@values),"$close</td></tr>\n";
+        }
+        unless ($infoCount) {
+            printf PODFILE "  [no tags known]\n";
+            my $cols = 3;
+            ++$cols if $hid;
+            ++$cols if $derived;
+            print HTMLFILE "<tr><td colspan=$cols class=c>[no tags known]</td></tr>\n";
+        }
+        print HTMLFILE "</table></td></tr></table></blockquote>\n\n";
+    }
+    close(HTMLFILE) or $success = 0;
+    CloseHtmlFiles($self) or $success = 0;
+    print PODFILE Doc2Pod($docs{PodTrailer}) or $success = 0;
+    close(PODFILE) or $success = 0;
+    return $success;
+}
+
+1;  # end
+
+
+__END__
+
+=head1 NAME
+
+Image::ExifTool::BuildTagLookup - Build ExifTool tag lookup tables
+
+=head1 DESCRIPTION
+
+This module is used to generate the tag lookup tables in
+Image::ExifTool::TagLookup.pm and tag name documentation in
+Image::ExifTool::TagNames.pod, as well as HTML tag name documentation.  It
+is used before each new ExifTool release to update the lookup tables and
+documentation.
+
+=head1 SYNOPSIS
+
+  use Image::ExifTool::BuildTagLookup;
+
+  $builder = new Image::ExifTool::BuildTagLookup;
+
+  $ok = $builder->WriteTagLookup('lib/Image/ExifTool/TagLookup.pm');
+
+  $ok = $builder->WriteTagNames('lib/Image/ExifTool/TagNames.pod','html');
+
+=head1 AUTHOR
+
+Copyright 2003-2007, Phil Harvey (phil at owl.phy.queensu.ca)
+
+This library is free software; you can redistribute it and/or modify it
+under the same terms as Perl itself.
+
+=head1 SEE ALSO
+
+L<Image::ExifTool(3pm)|Image::ExifTool>,
+L<Image::ExifTool::TagLookup(3pm)|Image::ExifTool::TagLookup>,
+L<Image::ExifTool::TagNames(3pm)|Image::ExifTool::TagNames>
+
+=cut
Index: /gsdl/trunk/perllib/cpan/Image/ExifTool/Canon.pm
===================================================================
--- /gsdl/trunk/perllib/cpan/Image/ExifTool/Canon.pm	(revision 16842)
+++ /gsdl/trunk/perllib/cpan/Image/ExifTool/Canon.pm	(revision 16842)
@@ -0,0 +1,3302 @@
+#------------------------------------------------------------------------------
+# File:         Canon.pm
+#
+# Description:  Canon EXIF maker notes tags
+#
+# Revisions:    11/25/03 - P. Harvey Created
+#               12/03/03 - P. Harvey Figured out lots more tags and added
+#                            CanonAFInfo
+#               02/17/04 - Michael Rommel Added IxusAFPoint
+#               01/27/05 - P. Harvey Disable validation of CanonAFInfo
+#               01/30/05 - P. Harvey Added a few more tags (ref 4)
+#               02/10/06 - P. Harvey Decode a lot of new tags (ref 12)
+#
+# References:   1) http://park2.wakwak.com/~tsuruzoh/Computer/Digicams/exif-e.html
+#               2) Michael Rommel private communication (Digital Ixus)
+#               3) Daniel Pittman private communication (PowerShot S70)
+#               4) http://www.wonderland.org/crw/
+#               5) Juha Eskelinen private communication (20D)
+#               6) Richard S. Smith private communication (20D)
+#               7) Denny Priebe private communication (1D MkII)
+#               8) Irwin Poche private communication
+#               9) Michael Tiemann private communication (1D MkII)
+#              10) Volker Gering private communication (1D MkII)
+#              11) "cip" private communication
+#              12) Rainer Honle private communication (5D)
+#              13) http://www.cybercom.net/~dcoffin/dcraw/
+#              14) (bozi) http://www.cpanforum.com/threads/2476 and /2563
+#              15) http://homepage3.nifty.com/kamisaka/makernote/makernote_canon.htm and
+#                  http://homepage3.nifty.com/kamisaka/makernote/CanonLens.htm (2006/07/04)
+#              16) Emil Sit private communication (30D)
+#              17) http://www.asahi-net.or.jp/~xp8t-ymzk/s10exif.htm
+#              18) Samson Tai private communication (G7)
+#              19) Warren Stockton private communication
+#              20) Bogdan private communication
+#------------------------------------------------------------------------------
+
+package Image::ExifTool::Canon;
+
+use strict;
+use vars qw($VERSION %canonModelID);
+use Image::ExifTool qw(:DataAccess :Utils);
+use Image::ExifTool::Exif;
+
+sub WriteCanon($$$);
+sub ProcessSerialData($$$);
+
+$VERSION = '1.77';
+
+my %canonLensTypes = ( #4
+    1 => 'Canon EF 50mm f/1.8',
+    2 => 'Canon EF 28mm f/2.8',
+    3 => 'Canon EF 135mm f/2.8 Soft', #15
+    4 => 'Sigma UC Zoom 35-135mm f/4-5.6',
+    6 => 'Sigma 18-125mm F3.5-5.6 DC IF ASP or Tokina AF193-2 19-35mm f/3.5-4.5',
+    7 => 'Canon EF 100-300mm F5.6L', #15
+    # 10 can be 3 different Sigma lenses:
+    # Sigma 50mm f/2.8 EX or Sigma 28mm f/1.8
+    # or Sigma 105mm f/2.8 Macro EX (ref 15)
+    10 => 'Canon EF 50mm f/2.5 Macro or Sigma', #10/4/15
+    11 => 'Canon EF 35mm f/2', #9
+    13 => 'Canon EF 15mm f/2.8', #9
+    21 => 'Canon EF 80-200mm f/2.8L',
+    22 => 'Tokina AT-X280AF PRO 28-80mm F2.8 ASPHERICAL', #15
+    # 26 can also be 2 Tamron lenses: (ref 15)
+    # Tamron SP AF 90mm f/2.8 Di Macro or Tamron SP AF 180mm F3.5 Di Macro
+    26 => 'Canon EF 100mm f/2.8 Macro or Cosina 100mm f/3.5 Macro AF or Tamron',
+    # 28 can be: (ref 15)
+    # - Tamron SP AF 28-105mm f/2.8 LD Aspherical IF
+    # - Tamron SP AF 28-75mm F/2.8 XR Di LD Aspherical [ IF ] Macro
+    28 => 'Tamron AF Aspherical 28-200mm f/3.8-5.6 or 28-75mm f/2.8 or 28-105mm f/2.8',#4/11/14/15
+    29 => 'Canon EF 50mm f/1.8 MkII',
+    31 => 'Tamron SP AF 300mm f/2.8 LD IF', #15
+    32 => 'Canon EF 24mm f/2.8 or Sigma 15mm f/2.8 EX Fisheye', #10/11
+    39 => 'Canon EF 75-300mm f/4-5.6',
+    40 => 'Canon EF 28-80mm f/3.5-5.6',
+    43 => 'Canon EF 28-105mm f/4-5.6', #10
+    45 => 'Canon EF-S 18-55mm f/3.5-5.6', #PH
+    124 => 'Canon MP-E 65mm f/2.8 1-5x Macro Photo', #9
+    125 => 'Canon TS-E 24mm f/3.5L',
+    126 => 'Canon TS-E 45mm f/2.8', #15
+    127 => 'Canon TS-E 90mm f/2.8', #15
+    130 => 'Canon EF 50mm f/1.0L', #10/15
+    131 => 'Sigma 17-35mm f2.8-4 EX Aspherical HSM',
+    134 => 'Canon EF 600mm f/4L IS', #15
+    135 => 'Canon EF 200mm f/1.8L',
+    136 => 'Canon EF 300mm f/2.8L',
+    137 => 'Canon EF 85mm f/1.2L', #10
+    139 => 'Canon EF 400mm f/2.8L',
+    141 => 'Canon EF 500mm f/4.5L',
+    142 => 'Canon EF 300mm f/2.8L IS', #15
+    143 => 'Canon EF 500mm f/4L IS', #15
+    149 => 'Canon EF 100mm f/2', #9
+    # 150 can be: (ref 15)
+    # Sigma 20mm EX F1.8/Sigma 30mm F1.4 DC HSM/Sigma 24mm F1.8 DG Macro EX
+    150 => 'Canon EF 14mm f/2.8L or Sigma 20mm EX f/1.8', #10/4
+    151 => 'Canon EF 200mm f/2.8L',
+    # 152 can be: (ref 15)
+    # Sigma 12-24mm F4.5-5.6 EX DG ASPHERICAL HSM
+    # Sigma 14mm F2.8 EX Aspherical HSM
+    # Sigma 10-20mm F4-5.6 (ref 14)
+    # Sigma 100-300 F4 (ref Bozi)
+    152 => 'Sigma Lens (various models)',
+    # 153 is Tamron AF 28-300mm, 18-200mm F3.5-6.3 Di II or
+    #        Tamron 18-250mm F3.5-6.3 Di II_LD Aspherical (IF) Macro (ref PH)
+    153 => 'Canon EF 35-350mm f/3.5-5.6L or Tamron or Sigma Bigma', #PH/15
+    155 => 'Canon EF 85mm f/1.8 USM',
+    156 => 'Canon EF 28-105mm f/3.5-4.5 USM',
+    160 => 'Canon EF 20-35mm f/3.5-4.5 USM',
+    161 => 'Canon EF 28-70mm f/2.8L or Sigma 24-70mm EX f/2.8 or Tamron 90mm f/2.8',
+    165 => 'Canon EF 70-200mm f/2.8 L',
+    166 => 'Canon EF 70-200mm f/2.8 L + x1.4',
+    167 => 'Canon EF 70-200mm f/2.8 L + x2',
+    168 => 'Canon EF 28mm f/1.8 USM', #15
+    169 => 'Canon EF17-35mm f/2.8L or Sigma 15-30mm f/3.5-4.5 EX DG Aspherical', #15/4
+    170 => 'Canon EF 200mm f/2.8L II', #9
+    # the following value is used by 2 different Sigma lenses (ref 14):
+    # Sigma 180mm EX HSM Macro f/3.5 or Sigma APO Macro 150mm F3.5 EX DG IF HSM
+    # 173 => 'Canon EF 180mm Macro f/3.5L or Sigma 180mm EX HSM Macro f/3.5', #9
+    173 => 'Canon EF 180mm Macro f/3.5L or Sigma 180mm F3.5 or 150mm f/2.8 Macro',
+    174 => 'Canon EF 135mm f/2L', #9
+    176 => 'Canon EF 24-85mm f/3.5-4.5 USM',
+    177 => 'Canon EF 300mm f/4L IS', #9
+    178 => 'Canon EF 28-135mm f/3.5-5.6 IS',
+    180 => 'Canon EF 35mm f/1.4L', #9
+    181 => 'Canon EF 100-400mm f/4.5-5.6L IS + x1.4', #15
+    182 => 'Canon EF 100-400mm f/4.5-5.6L IS + x2',
+    183 => 'Canon EF 100-400mm f/4.5-5.6L IS',
+    184 => 'Canon EF 400mm f/2.8L + x2', #15
+    186 => 'Canon EF 70-200mm f/4L', #9
+    190 => 'Canon EF 100mm f/2.8 Macro',
+    191 => 'Canon EF 400mm f/4 DO IS', #9
+    # 196 Canon 75-300mm F4? #15
+    197 => 'Canon EF 75-300mm f/4-5.6 IS',
+    198 => 'Canon EF 50mm f/1.4 USM', #9
+    202 => 'Canon EF 28-80 f/3.5-5.6 USM IV',
+    211 => 'Canon EF 28-200mm f/3.5-5.6', #15
+    213 => 'Canon EF 90-300mm f/4.5-5.6',
+    214 => 'Canon EF-S 18-55mm f/3.5-4.5 USM', #PH
+    224 => 'Canon EF 70-200mm f/2.8L IS USM', #11
+    225 => 'Canon EF 70-200mm f/2.8L IS USM + x1.4', #11
+    226 => 'Canon EF 70-200mm f/2.8L IS USM + x2', #14
+    229 => 'Canon EF 16-35mm f/2.8L', #PH
+    230 => 'Canon EF 24-70mm f/2.8L', #9
+    231 => 'Canon EF 17-40mm f/4L',
+    232 => 'Canon EF 70-300mm f/4.5-5.6 DO IS USM', #15
+    234 => 'Canon EF-S 17-85mm f4-5.6 IS USM', #19
+    235 => 'Canon EF-S10-22mm F3.5-4.5 USM', #15
+    236 => 'Canon EF-S60mm F2.8 Macro USM', #15
+    237 => 'Canon EF 24-105mm f/4L IS', #15
+    238 => 'Canon EF 70-300mm f/4-5.6 IS USM', #15
+    239 => 'Canon EF 85mm f/1.2L II USM', #15
+    240 => 'Canon EF-S 17-55mm f/2.8 IS USM', #15
+    241 => 'Canon EF 50mm f/1.2L USM', #15
+    242 => 'Canon EF 70-200mm f/4L IS USM', #PH
+);
+
+# Canon model ID numbers (PH)
+%canonModelID = (
+    0x1010000 => 'PowerShot A30',
+    0x1040000 => 'PowerShot S300 / Digital IXUS 300 / IXY Digital 300',
+    0x1060000 => 'PowerShot A20',
+    0x1080000 => 'PowerShot A10',
+    0x1090000 => 'PowerShot S110 / Digital IXUS v / IXY Digital 200',
+    0x1100000 => 'PowerShot G2',
+    0x1110000 => 'PowerShot S40',
+    0x1120000 => 'PowerShot S30',
+    0x1130000 => 'PowerShot A40',
+    0x1140000 => 'EOS D30',
+    0x1150000 => 'PowerShot A100',
+    0x1160000 => 'PowerShot S200 / Digital IXUS v2 / IXY Digital 200a',
+    0x1170000 => 'PowerShot A200',
+    0x1180000 => 'PowerShot S330 / Digital IXUS 330 / IXY Digital 300a',
+    0x1190000 => 'PowerShot G3',
+    0x1210000 => 'PowerShot S45',
+    0x1230000 => 'PowerShot SD100 / Digital IXUS II / IXY Digital 30',
+    0x1240000 => 'PowerShot S230 / Digital IXUS v3 / IXY Digital 320',
+    0x1250000 => 'PowerShot A70',
+    0x1260000 => 'PowerShot A60',
+    0x1270000 => 'PowerShot S400 / Digital IXUS 400 / IXY Digital 400',
+    0x1290000 => 'PowerShot G5',
+    0x1300000 => 'PowerShot A300',
+    0x1310000 => 'PowerShot S50',
+    0x1340000 => 'PowerShot A80',
+    0x1350000 => 'PowerShot SD10 / Digital IXUS i / IXY Digital L',
+    0x1360000 => 'PowerShot S1 IS',
+    0x1370000 => 'PowerShot Pro1',
+    0x1380000 => 'PowerShot S70',
+    0x1390000 => 'PowerShot S60',
+    0x1400000 => 'PowerShot G6',
+    0x1410000 => 'PowerShot S500 / Digital IXUS 500 / IXY Digital 500',
+    0x1420000 => 'PowerShot A75',
+    0x1440000 => 'PowerShot SD110 / Digital IXUS IIs / IXY Digital 30a',
+    0x1450000 => 'PowerShot A400',
+    0x1470000 => 'PowerShot A310',
+    0x1490000 => 'PowerShot A85',
+    0x1520000 => 'PowerShot S410 / Digital IXUS 430 / IXY Digital 450',
+    0x1530000 => 'PowerShot A95',
+    0x1540000 => 'PowerShot SD300 / Digital IXUS 40 / IXY Digital 50',
+    0x1550000 => 'PowerShot SD200 / Digital IXUS 30 / IXY Digital 40',
+    0x1560000 => 'PowerShot A520',
+    0x1570000 => 'PowerShot A510',
+    0x1590000 => 'PowerShot SD20 / Digital IXUS i5 / IXY Digital L2',
+    0x1640000 => 'PowerShot S2 IS',
+    0x1650000 => 'PowerShot SD430 / IXUS Wireless / IXY Wireless',
+    0x1660000 => 'PowerShot SD500 / Digital IXUS 700 / IXY Digital 600',
+    0x1668000 => 'EOS D60',
+    0x1700000 => 'PowerShot SD30 / Digital IXUS i zoom / IXY Digital L3',
+    0x1740000 => 'PowerShot A430',
+    0x1750000 => 'PowerShot A410',
+    0x1760000 => 'PowerShot S80',
+    0x1780000 => 'PowerShot A620',
+    0x1790000 => 'PowerShot A610',
+    0x1800000 => 'PowerShot SD630 / Digital IXUS 65 / IXY Digital 80',
+    0x1810000 => 'PowerShot SD450 / Digital IXUS 55 / IXY Digital 60',
+    0x1820000 => 'PowerShot TX1',
+    0x1870000 => 'PowerShot SD400 / Digital IXUS 50 / IXY Digital 55',
+    0x1880000 => 'PowerShot A420',
+    0x1890000 => 'PowerShot SD900 / Digital IXUS 900 Ti / IXY Digital 1000',
+    0x1900000 => 'PowerShot SD550 / Digital IXUS 750 / IXY Digital 700',
+    0x1920000 => 'PowerShot A700',
+    0x1940000 => 'PowerShot SD700 IS / Digital IXUS 800 IS / IXY Digital 800 IS',
+    0x1950000 => 'PowerShot S3 IS',
+    0x1960000 => 'PowerShot A540',
+    0x1970000 => 'PowerShot SD600 / Digital IXUS 60 / IXY Digital 70',
+    0x1980000 => 'PowerShot G7',
+    0x1990000 => 'PowerShot A530',
+    0x2000000 => 'PowerShot SD800 IS / Digital IXUS 850 IS / IXY Digital 900 IS',
+    0x2010000 => 'PowerShot SD40 / Digital IXUS i7 / IXY Digital L4',
+    0x2020000 => 'PowerShot A710 IS',
+    0x2030000 => 'PowerShot A640',
+    0x2040000 => 'PowerShot A630',
+    0x2090000 => 'PowerShot S5 IS',
+    0x2100000 => 'PowerShot A460',
+    0x2120000 => 'PowerShot SD850 IS / Digital IXUS 950 IS / IXY Digital 810 IS',
+    0x2130000 => 'PowerShot A570 IS',
+    0x2140000 => 'PowerShot A560',
+    0x2150000 => 'PowerShot SD750 / Digital IXUS 75 / IXY Digital 90',
+    0x2160000 => 'PowerShot SD1000 / Digital IXUS 70 / IXY Digital 10',
+    0x2180000 => 'PowerShot A550',
+    0x2190000 => 'PowerShot A450',
+    0x2230000 => 'PowerShot G9',
+    0x2240000 => 'PowerShot A650 IS',
+    0x2260000 => 'PowerShot A720 IS',
+    0x2290000 => 'PowerShot SX100 IS',
+    0x2300000 => 'PowerShot SD950 IS / Digital IXUS 960 IS / IXY Digital 2000 IS',
+    0x2310000 => 'PowerShot SD870 IS / Digital IXUS 860 IS / IXY Digital 910 IS',
+    0x3010000 => 'PowerShot Pro90 IS',
+    0x4040000 => 'PowerShot G1',
+    0x6040000 => 'PowerShot S100 / Digital IXUS / IXY Digital',
+    0x4007d675 => 'HV10',
+    0x4007d777 => 'iVIS DC50',
+    0x4007d778 => 'iVIS HV20',
+    0x80000001 => 'EOS-1D',
+    0x80000167 => 'EOS-1DS',
+    0x80000168 => 'EOS 10D',
+    0x80000169 => 'EOS-1D Mark III',
+    0x80000170 => 'EOS Digital Rebel / 300D / Kiss Digital',
+    0x80000174 => 'EOS-1D Mark II',
+    0x80000175 => 'EOS 20D',
+    0x80000188 => 'EOS-1Ds Mark II',
+    0x80000189 => 'EOS Digital Rebel XT / 350D / Kiss Digital N',
+    0x80000190 => 'EOS 40D',
+    0x80000213 => 'EOS 5D',
+    0x80000215 => 'EOS-1Ds Mark III',
+    0x80000232 => 'EOS-1D Mark II N',
+    0x80000234 => 'EOS 30D',
+    0x80000236 => 'EOS Digital Rebel XTi / 400D / Kiss Digital X',
+);
+
+my %canonQuality = (
+    1 => 'Economy',
+    2 => 'Normal',
+    3 => 'Fine',
+    4 => 'RAW',
+    5 => 'Superfine',
+);
+my %canonImageSize = (
+    0 => 'Large',
+    1 => 'Medium',
+    2 => 'Small',
+    5 => 'Medium 1', #PH
+    6 => 'Medium 2', #PH
+    7 => 'Medium 3', #PH
+    8 => 'Postcard', #PH (SD200 1600x1200 with DateStamp option)
+    9 => 'Widescreen', #PH (SD900 3648x2048)
+);
+my %canonWhiteBalance = (
+    0 => 'Auto',
+    1 => 'Daylight',
+    2 => 'Cloudy',
+    3 => 'Tungsten',
+    4 => 'Fluorescent',
+    5 => 'Flash',
+    6 => 'Custom',
+    7 => 'Black & White',
+    8 => 'Shade',
+    9 => 'Manual Temperature (Kelvin)',
+    10 => 'PC Set1', #PH
+    11 => 'PC Set2', #PH
+    12 => 'PC Set3', #PH
+    14 => 'Daylight Fluorescent', #3
+    15 => 'Custom 1', #PH
+    16 => 'Custom 2', #PH
+    17 => 'Underwater', #3
+);
+
+# picture styles used by the 5D
+# (styles 0x4X may be downloaded from Canon)
+my %pictureStyles = ( #12
+    0x00 => 'None', #PH
+    0x01 => 'Standard', #PH guess (1D)
+    0x02 => 'Set 1', #PH guess (1D)
+    0x03 => 'Set 2', #PH guess (1D)
+    0x04 => 'Set 3', #PH guess (1D)
+    0x21 => 'User Def. 1',
+    0x22 => 'User Def. 2',
+    0x23 => 'User Def. 3',
+    # "External" styles currently available from Canon are Nostalgia, Clear,
+    # Twilight and Emerald.  The "User Def" styles change to these "External"
+    # codes when these styles are installed in the camera
+    0x41 => 'External 1',
+    0x42 => 'External 2',
+    0x43 => 'External 3',
+    0x81 => 'Standard',
+    0x82 => 'Portrait',
+    0x83 => 'Landscape',
+    0x84 => 'Neutral',
+    0x85 => 'Faithful',
+    0x86 => 'Monochrome',
+);
+my %userDefStyles = ( #12
+    0x41 => 'Nostalgia',
+    0x42 => 'Clear',
+    0x43 => 'Twilight',
+    0x81 => 'Standard',
+    0x82 => 'Portrait',
+    0x83 => 'Landscape',
+    0x84 => 'Neutral',
+    0x85 => 'Faithful',
+    0x86 => 'Monochrome',
+);
+
+# ValueConv that makes long values binary type
+my %longBin = (
+    ValueConv => 'length($val) > 64 ? \$val : $val',
+    ValueConvInv => '$val',
+);
+
+#------------------------------------------------------------------------------
+# Canon EXIF Maker Notes
+%Image::ExifTool::Canon::Main = (
+    WRITE_PROC => \&WriteCanon,
+    CHECK_PROC => \&Image::ExifTool::Exif::CheckExif,
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
+    0x1 => {
+        Name => 'CanonCameraSettings',
+        SubDirectory => {
+            Validate => 'Image::ExifTool::Canon::Validate($dirData,$subdirStart,$size)',
+            TagTable => 'Image::ExifTool::Canon::CameraSettings',
+        },
+    },
+    0x2 => {
+        Name => 'CanonFocalLength',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::Canon::FocalLength',
+        },
+    },
+    0x3 => {
+        Name => 'CanonFlashInfo',
+        Unknown => 1,
+    },
+    0x4 => {
+        Name => 'CanonShotInfo',
+        SubDirectory => {
+            Validate => 'Image::ExifTool::Canon::Validate($dirData,$subdirStart,$size)',
+            TagTable => 'Image::ExifTool::Canon::ShotInfo',
+        },
+    },
+    0x5 => {
+        Name => 'CanonPanorama',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::Canon::Panorama',
+        },
+    },
+    0x6 => {
+        Name => 'CanonImageType',
+        Writable => 'string',
+    },
+    0x7 => {
+        Name => 'CanonFirmwareVersion',
+        Writable => 'string',
+    },
+    0x8 => {
+        Name => 'FileNumber',
+        Writable => 'int32u',
+        PrintConv => '$_=$val,s/(\d+)(\d{4})/$1-$2/,$_',
+        PrintConvInv => '$val=~s/-//g;$val',
+    },
+    0x9 => {
+        Name => 'OwnerName',
+        Writable => 'string',
+    },
+    0xa => {
+        Name => 'ColorInfoD30',
+        SubDirectory => {
+            Validate => 'Image::ExifTool::Canon::Validate($dirData,$subdirStart,$size)',
+            TagTable => 'Image::ExifTool::Canon::ColorInfoD30',
+        },
+    },
+    0xc => [   # square brackets for a conditional list
+        {
+            # D30
+            Name => 'SerialNumber',
+            Description => 'Camera Body No.',
+            Condition => '$$self{CameraModel} =~ /EOS D30\b/',
+            Writable => 'int32u',
+            PrintConv => 'sprintf("%x-%.5d",$val>>16,$val&0xffff)',
+            PrintConvInv => '$val=~/(.*)-(\d+)/ ? (hex($1)<<16)+$2 : undef',
+        },
+        {
+            # serial number of 1D/1Ds/1D Mark II/1Ds Mark II is usually
+            # displayed w/o leeding zeros (ref 7) (1D uses 6 digits - PH)
+            Name => 'SerialNumber',
+            Description => 'Camera Body No.',
+            Condition => '$$self{CameraModel} =~ /EOS-1D/',
+            Writable => 'int32u',
+            PrintConv => 'sprintf("%.6u",$val)',
+            PrintConvInv => '$val',
+        },
+        {
+            # all other models (D60,300D,350D,REBEL,10D,20D,etc)
+            Name => 'SerialNumber',
+            Description => 'Camera Body No.',
+            Writable => 'int32u',
+            PrintConv => 'sprintf("%.10u",$val)',
+            PrintConvInv => '$val',
+        },
+    ],
+    0xd => [
+        {
+            Name => 'CanonCameraInfo',
+            Condition => '$$self{CameraModel} =~ /\b(1D(?! Mark III)|5D)/',
+            SubDirectory => {
+                TagTable => 'Image::ExifTool::Canon::CameraInfo',
+            },
+        },
+        {
+            Name => 'CanonCameraInfo1DmkIII',
+            Condition => '$$self{CameraModel} =~ /\b1D Mark III/',
+            SubDirectory => {
+                TagTable => 'Image::ExifTool::Canon::CameraInfo1DmkIII',
+            },
+        },
+        {
+            Name => 'CanonCameraInfo2',
+            # valid if format is int32u[138] or int32u[148]
+            Condition => q{ $format eq 'int32u' and ($count == 138 or $count == 148) },
+            SubDirectory => {
+                TagTable => 'Image::ExifTool::Canon::CameraInfo2',
+            },
+        },
+        {
+            Name => 'CanonCameraInfoUnknown32',
+            Condition => q{ $format =~ /^int32/ },
+            SubDirectory => {
+                TagTable => 'Image::ExifTool::Canon::CameraInfoUnknown32',
+            },
+        },
+        {
+            Name => 'CanonCameraInfoUnknown16',
+            Condition => q{ $format =~ /^int16/ },
+            SubDirectory => {
+                TagTable => 'Image::ExifTool::Canon::CameraInfoUnknown16',
+            },
+        },
+        {
+            Name => 'CanonCameraInfoUnknown8',
+            SubDirectory => {
+                TagTable => 'Image::ExifTool::Canon::CameraInfoUnknown8',
+            },
+        },
+    ],
+    0xe => {
+        Name => 'CanonFileLength',
+        Writable => 'int32u',
+        Groups => { 2 => 'Image' },
+    },
+    0xf => [
+        {   # used by 1DmkII, 1DsMkII and 1DmkIIN
+            Name => 'CustomFunctions1D',
+            Condition => '$$self{CameraModel} =~ /EOS-1D/',
+            SubDirectory => {
+                Validate => 'Image::ExifTool::Canon::Validate($dirData,$subdirStart,$size)',
+                TagTable => 'Image::ExifTool::CanonCustom::Functions1D',
+            },
+        },
+        {
+            Name => 'CustomFunctions5D',
+            Condition => '$$self{CameraModel} =~ /EOS 5D/',
+            SubDirectory => {
+                Validate => 'Image::ExifTool::Canon::Validate($dirData,$subdirStart,$size)',
+                TagTable => 'Image::ExifTool::CanonCustom::Functions5D',
+            },
+        },
+        {
+            Name => 'CustomFunctions10D',
+            Condition => '$$self{CameraModel} =~ /EOS 10D/',
+            SubDirectory => {
+                Validate => 'Image::ExifTool::Canon::Validate($dirData,$subdirStart,$size)',
+                TagTable => 'Image::ExifTool::CanonCustom::Functions10D',
+            },
+        },
+        {
+            Name => 'CustomFunctions20D',
+            Condition => '$$self{CameraModel} =~ /EOS 20D/',
+            SubDirectory => {
+                Validate => 'Image::ExifTool::Canon::Validate($dirData,$subdirStart,$size)',
+                TagTable => 'Image::ExifTool::CanonCustom::Functions20D',
+            },
+        },
+        {
+            Name => 'CustomFunctions30D',
+            Condition => '$$self{CameraModel} =~ /EOS 30D/',
+            SubDirectory => {
+                Validate => 'Image::ExifTool::Canon::Validate($dirData,$subdirStart,$size)',
+                TagTable => 'Image::ExifTool::CanonCustom::Functions30D',
+            },
+        },
+        {
+            Name => 'CustomFunctions350D',
+            Condition => '$$self{CameraModel} =~ /\b(350D|REBEL XT|Kiss Digital N)\b/',
+            SubDirectory => {
+                Validate => 'Image::ExifTool::Canon::Validate($dirData,$subdirStart,$size)',
+                TagTable => 'Image::ExifTool::CanonCustom::Functions350D',
+            },
+        },
+        {
+            Name => 'CustomFunctions400D',
+            Condition => '$$self{CameraModel} =~ /\b(400D|REBEL XTi|Kiss Digital X|K236)\b/',
+            SubDirectory => {
+                Validate => 'Image::ExifTool::Canon::Validate($dirData,$subdirStart,$size)',
+                TagTable => 'Image::ExifTool::CanonCustom::Functions400D',
+            },
+        },
+        {
+            Name => 'CustomFunctionsD30',
+            Condition => '$$self{CameraModel} =~ /EOS D30\b/',
+            SubDirectory => {
+                Validate => 'Image::ExifTool::Canon::Validate($dirData,$subdirStart,$size)',
+                TagTable => 'Image::ExifTool::CanonCustom::FunctionsD30',
+            },
+        },
+        {
+            Name => 'CustomFunctionsD60',
+            Condition => '$$self{CameraModel} =~ /EOS D60\b/',
+            SubDirectory => {
+                # the stored size in the D60 apparently doesn't include the size word:
+                Validate => 'Image::ExifTool::Canon::Validate($dirData,$subdirStart,$size-2,$size)',
+                # (D60 custom functions are basically the same as D30)
+                TagTable => 'Image::ExifTool::CanonCustom::FunctionsD30',
+            },
+        },
+        {
+            Name => 'CustomFunctionsUnknown',
+            SubDirectory => {
+                Validate => 'Image::ExifTool::Canon::Validate($dirData,$subdirStart,$size)',
+                TagTable => 'Image::ExifTool::CanonCustom::FuncsUnknown',
+            },
+        },
+    ],
+    0x10 => { #PH
+        Name => 'CanonModelID',
+        Writable => 'int32u',
+        PrintHex => 1,
+        SeparateTable => 1,
+        PrintConv => \%canonModelID,
+    },
+    0x12 => {
+        Name => 'CanonAFInfo',
+        # not really a condition -- just need to store the count for later
+        Condition => '$$self{AFInfoCount} = $count',
+        SubDirectory => {
+            # this record does not begin with a length word, so it
+            # has to be validated differently
+            Validate => 'Image::ExifTool::Canon::ValidateAFInfo($dirData,$subdirStart,$size)',
+            TagTable => 'Image::ExifTool::Canon::AFInfo',
+        },
+    },
+    # 0x13 - ('0 159 15 104' for widescreen image, '0 0 0 0' otherwise) - PH (A570IS)
+    0x15 => { #PH
+        # display format for serial number
+        Name => 'SerialNumberFormat',
+        Writable => 'int32u',
+        PrintHex => 1,
+        PrintConv => {
+            0x90000000 => 'Format 1',
+            0xa0000000 => 'Format 2',
+        },
+    },
+    0x1c => { #PH (A570IS)
+        Name => 'DateStampMode',
+        Writable => 'int16u',
+        Notes => 'only used in postcard mode',
+        PrintConv => {
+            0 => 'Off',
+            1 => 'Date',
+            2 => 'Date & Time',
+        },
+    },
+    0x1d => { #PH
+        Name => 'MyColors',
+        SubDirectory => {
+            Validate => 'Image::ExifTool::Canon::Validate($dirData,$subdirStart,$size)',
+            TagTable => 'Image::ExifTool::Canon::MyColors',
+        },
+    },
+    0x1e => { #PH
+        Name => 'FirmwareRevision',
+        Writable => 'int32u',
+        # as a hex number: 0xAVVVRR00, where (a bit of guessing here...)
+        #  A = 'a' for alpha, 'b' for beta?
+        #  V = version? (100,101 for normal releases, 100,110,120,130,170 for alpha/beta)
+        #  R = revision? (01-07, except 00 for alpha/beta releases)
+        PrintConv => q{
+            my $rev = sprintf("%.8x", $val);
+            my ($rel, $v1, $v2, $r1, $r2) = ($rev =~ /^(.)(.)(..)0?(.+)(..)$/);
+            my %r = ( a => 'Alpha ', b => 'Beta ', '0' => '' );
+            $rel = defined $r{$rel} ? $r{$rel} : "Unknown($rel) ";
+            return "$rel$v1.$v2 rev $r1.$r2",
+        },
+        PrintConvInv => q{
+            $_=$val; s/Alpha ?/a/i; s/Beta ?/b/i;
+            s/Unknown ?\((.)\)/$1/i; s/ ?rev ?(.)\./0$1/; s/ ?rev ?//;
+            tr/a-fA-F0-9//dc; return hex $_;
+        },
+    },
+    # 0x1f - used for red-eye-corrected images - PH (A570IS)
+    # 0x22 - values 1 and 2 are 2 and 1 for flash pics, 0 otherwise - PH (A570IS)
+    0x24 => { #PH
+        Name => 'FaceDetect1',
+        SubDirectory => {
+            Validate => 'Image::ExifTool::Canon::Validate($dirData,$subdirStart,$size)',
+            TagTable => 'Image::ExifTool::Canon::FaceDetect1',
+        },
+    },
+    0x25 => { #PH
+        Name => 'FaceDetect2',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::Canon::FaceDetect2',
+        },
+    },
+    0x26 => { #PH (A570IS,1DmkIII)
+        Name => 'CanonAFInfo2',
+        SubDirectory => {
+            Validate => 'Image::ExifTool::Canon::Validate($dirData,$subdirStart,$size)',
+            TagTable => 'Image::ExifTool::Canon::AFInfo2',
+        },
+    },
+    # 0x27 - value 1 is 1 for high ISO pictures, 0 otherwise
+    0x83 => { #PH
+        Name => 'OriginalDecisionData',
+        Writable => 'int32u',
+    },
+    0x90 => {   # used by 1D and 1Ds
+        Name => 'CustomFunctions1D',
+        SubDirectory => {
+            Validate => 'Image::ExifTool::Canon::Validate($dirData,$subdirStart,$size)',
+            TagTable => 'Image::ExifTool::CanonCustom::Functions1D',
+        },
+    },
+    0x91 => { #PH
+        Name => 'PersonalFunctions',
+        SubDirectory => {
+            Validate => 'Image::ExifTool::Canon::Validate($dirData,$subdirStart,$size)',
+            TagTable => 'Image::ExifTool::CanonCustom::PersonalFuncs',
+        },
+    },
+    0x92 => { #PH
+        Name => 'PersonalFunctionValues',
+        SubDirectory => {
+            Validate => 'Image::ExifTool::Canon::Validate($dirData,$subdirStart,$size)',
+            TagTable => 'Image::ExifTool::CanonCustom::PersonalFuncValues',
+        },
+    },
+    0x93 => {
+        Name => 'CanonFileInfo',
+        SubDirectory => {
+            Validate => 'Image::ExifTool::Canon::Validate($dirData,$subdirStart,$size)',
+            TagTable => 'Image::ExifTool::Canon::FileInfo',
+        },
+    },
+    0x94 => { #PH
+        # AF points for 1D (45 points in 5 rows)
+        Name => 'AFPointsInFocus1D',
+        Notes => 'EOS 1D -- 5 rows: A1-7, B1-10, C1-11, D1-10, E1-7, center point is C6',
+        PrintConv => 'Image::ExifTool::Canon::PrintAFPoints1D($val)',
+    },
+    0x95 => { #PH (observed in 5D sample image)
+        Name => 'LensType',
+        Writable => 'string',
+    },
+    0x96 => { #PH
+        Name => 'InternalSerialNumber',
+        Writable => 'string',
+    },
+    0x97 => { #PH
+        Name => 'DustRemovalData',
+        # some interesting stuff is stored in here, like LensType and InternalSerialNumber...
+        Binary => 1,
+    },
+    0x99 => { #PH (EOS 1D Mark III, 40D, etc)
+        Name => 'CustomFunctions2',
+        SubDirectory => {
+            Validate => 'Image::ExifTool::Canon::Validate($dirData,$subdirStart,$size)',
+            TagTable => 'Image::ExifTool::CanonCustom::Functions2',
+        },
+    },
+    # 0x9a - 5 numbers, the 2nd and 3rd are imagewidth/height (EOS 1DmkIII and 40D)
+    0xa0 => {
+        Name => 'ProccessingInfo',
+        SubDirectory => {
+            Validate => 'Image::ExifTool::Canon::Validate($dirData,$subdirStart,$size)',
+            TagTable => 'Image::ExifTool::Canon::Processing',
+        },
+    },
+    0xa1 => { Name => 'ToneCurveTable', %longBin }, #PH
+    0xa2 => { Name => 'SharpnessTable', %longBin }, #PH
+    0xa3 => { Name => 'SharpnessFreqTable', %longBin }, #PH
+    0xa4 => { Name => 'WhiteBalanceTable', %longBin }, #PH
+    0xa9 => {
+        Name => 'ColorBalance',
+        SubDirectory => {
+            # this offset is necessary because the table is interpreted as short rationals
+            # (4 bytes long) but the first entry is 2 bytes into the table.
+            Start => '$valuePtr + 2',
+            Validate => 'Image::ExifTool::Canon::Validate($dirData,$subdirStart-2,$size+2)',
+            TagTable => 'Image::ExifTool::Canon::ColorBalance',
+        },
+    },
+    # 0xaa - looks like maybe measured color balance (inverse of RGGBLevels)? - PH
+    0xae => {
+        Name => 'ColorTemperature',
+        Writable => 'int16u',
+    },
+    0xb0 => { #PH
+        Name => 'CanonFlags',
+        SubDirectory => {
+            Validate => 'Image::ExifTool::Canon::Validate($dirData,$subdirStart,$size)',
+            TagTable => 'Image::ExifTool::Canon::Flags',
+        },
+    },
+    0xb1 => { #PH
+        Name => 'ModifiedInfo',
+        SubDirectory => {
+            Validate => 'Image::ExifTool::Canon::Validate($dirData,$subdirStart,$size)',
+            TagTable => 'Image::ExifTool::Canon::ModifiedInfo',
+        },
+    },
+    0xb2 => { Name => 'ToneCurveMatching', %longBin }, #PH
+    0xb3 => { Name => 'WhiteBalanceMatching', %longBin }, #PH
+    0xb4 => { #PH
+        Name => 'ColorSpace',
+        Writable => 'int16u',
+        PrintConv => {
+            1 => 'sRGB',
+            2 => 'Adobe RGB',
+        },
+    },
+    0xb6 => {
+        Name => 'PreviewImageInfo',
+        SubDirectory => {
+            # Note: first word if this block is the total number of words, not bytes!
+            Validate => 'Image::ExifTool::Canon::Validate($dirData,$subdirStart,$size/2)',
+            TagTable => 'Image::ExifTool::Canon::PreviewImageInfo',
+        },
+    },
+    0xd0 => { #PH
+        Name => 'VRDOffset',
+        Writable => 'int32u',
+        OffsetPair => 1, # (just used as a flag, since this tag has no pair)
+        Protected => 2,
+        DataTag => 'CanonVRD',
+        Notes => 'offset of VRD "recipe data" if it exists',
+    },
+    0xe0 => { #12
+        Name => 'SensorInfo',
+        SubDirectory => {
+            Validate => 'Image::ExifTool::Canon::Validate($dirData,$subdirStart,$size)',
+            TagTable => 'Image::ExifTool::Canon::SensorInfo',
+        },
+    },
+    0x4001 => [ #13
+        {   # (int16u[582]) - 20D and 350D
+            Condition => '$count == 582',
+            Name => 'ColorBalance1',
+            SubDirectory => {
+                TagTable => 'Image::ExifTool::Canon::ColorBalance1',
+            },
+        },
+        {   # (int16u[653]) - 1DmkII and 1DSmkII
+            Condition => '$count == 653',
+            Name => 'ColorBalance2',
+            SubDirectory => {
+                TagTable => 'Image::ExifTool::Canon::ColorBalance2',
+            },
+        },
+        {   # (int16u[796]) - 1DmkIIN, 5D, 30D, 400D
+            Condition => '$count == 796',
+            Name => 'ColorBalance3',
+            SubDirectory => {
+                TagTable => 'Image::ExifTool::Canon::ColorBalance3',
+            },
+        },
+        {
+            Name => 'ColorBalanceUnknown',
+            %longBin,
+        },
+    ],
+    0x4002 => { #PH
+        # unknown data block in some JPEG and CR2 images
+        # (5kB for most models, but 22kb for 5D and 30D)
+        Name => 'UnknownBlock1',
+        Format => 'undef',
+        Flags => [ 'Unknown', 'Binary' ],
+    },
+    0x4003 => { #PH
+        Name => 'ColorInfo',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::Canon::ColorInfo',
+        },
+    },
+    0x4005 => { #PH
+        Name => 'UnknownBlock2',
+        Notes => 'unknown 49kB block, not copied to JPEG images',
+        # 'Drop' because not found in JPEG images (too large for APP1 anyway)
+        Flags => [ 'Unknown', 'Binary', 'Drop' ],
+    },
+    0x4008 => { #PH guess (1DmkIII)
+        Name => 'BlackLevel',
+        Unknown => 1,
+    },
+);
+
+#..............................................................................
+# Canon camera settings (MakerNotes tag 0x01)
+# BinaryData (keys are indices into the int16s array)
+%Image::ExifTool::Canon::CameraSettings = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
+    CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
+    WRITABLE => 1,
+    FORMAT => 'int16s',
+    FIRST_ENTRY => 1,
+    DATAMEMBER => [ 25 ],   # FocalUnits necessary for writing
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
+    1 => {
+        Name => 'MacroMode',
+        PrintConv => {
+            1 => 'Macro',
+            2 => 'Normal',
+        },
+    },
+    2 => {
+        Name => 'Self-timer',
+        # Custom timer mode if bit 0x4000 is set - PH (A570IS)
+        PrintConv => q{
+            return 'Off' unless $val;
+            return (($val&0xfff) / 10) . ' sec' . ($val & 0x4000 ? ', Custom' : '');
+        },
+        PrintConvInv => q{
+            return 0 if $val =~ /^Off/i;
+            $val =~ s/\s*sec$//i;
+            $val =~ s/,?\s*Custom$//i ? ($val*10) | 0x4000 : $val*10;
+        },
+    },
+    3 => {
+        Name => 'Quality',
+        PrintConv => \%canonQuality,
+    },
+    4 => {
+        Name => 'CanonFlashMode',
+        PrintConv => {
+            0 => 'Off',
+            1 => 'Auto',
+            2 => 'On',
+            3 => 'Red-eye reduction',
+            4 => 'Slow-sync',
+            5 => 'Red-eye reduction (Auto)',
+            6 => 'Red-eye reduction (On)',
+            16 => 'External flash', # not set in D30 or 300D
+        },
+    },
+    5 => {
+        Name => 'ContinuousDrive',
+        PrintConv => {
+            0 => 'Single',
+            1 => 'Continuous',
+            2 => 'Movie', #PH
+            3 => 'Continuous, Speed Priority', #PH
+            4 => 'Continuous, Low', #PH
+            5 => 'Continuous, High', #PH
+        },
+    },
+    7 => {
+        Name => 'FocusMode',
+        PrintConv => {
+            0 => 'One-shot AF',
+            1 => 'AI Servo AF',
+            2 => 'AI Focus AF',
+            3 => 'Manual Focus',
+            4 => 'Single',
+            5 => 'Continuous',
+            6 => 'Manual Focus',
+           16 => 'Pan Focus', #PH
+        },
+    },
+    9 => { #PH
+        Name => 'RecordMode',
+        PrintConv => {
+            1 => 'JPEG',
+            2 => 'CRW+THM', # (300D,etc)
+            3 => 'AVI+THM', # (30D)
+            4 => 'TIF', # +THM? (1Ds) (unconfirmed)
+            5 => 'TIF+JPEG', # (1D) (unconfirmed)
+            6 => 'CR2', # +THM? (1D,30D,350D)
+            7 => 'CR2+JPEG', # (S30)
+        },
+    },
+    10 => {
+        Name => 'CanonImageSize',
+        PrintConv => \%canonImageSize,
+    },
+    11 => {
+        Name => 'EasyMode',
+        PrintConv => {
+            0 => 'Full auto',
+            1 => 'Manual',
+            2 => 'Landscape',
+            3 => 'Fast shutter',
+            4 => 'Slow shutter',
+            5 => 'Night',
+            6 => 'Gray Scale', #PH
+            7 => 'Sepia',
+            8 => 'Portrait',
+            9 => 'Sports',
+            10 => 'Macro',
+            11 => 'Black & White', #PH
+            12 => 'Pan focus',
+            13 => 'Vivid', #PH
+            14 => 'Neutral', #PH
+            15 => 'Flash Off',  #8
+            16 => 'Long Shutter', #PH
+            17 => 'Super Macro', #PH
+            18 => 'Foliage', #PH
+            19 => 'Indoor', #PH
+            20 => 'Fireworks', #PH
+            21 => 'Beach', #PH
+            22 => 'Underwater', #PH
+            23 => 'Snow', #PH
+            24 => 'Kids & Pets', #PH
+            25 => 'Night Snapshot', #PH
+            26 => 'Digital Macro', #PH
+            27 => 'My Colors', #PH
+            28 => 'Still Image', #15 (animation frame?)
+            30 => 'Color Accent', #18
+            31 => 'Color Swap', #18
+            32 => 'Aquarium', #18
+            33 => 'ISO 3200', #18
+        },
+    },
+    12 => {
+        Name => 'DigitalZoom',
+        PrintConv => {
+            0 => 'None',
+            1 => '2x',
+            2 => '4x',
+            3 => 'Other',  # value obtained from 2*#37/#36
+        },
+    },
+    13 => {
+        Name => 'Contrast',
+        RawConv => '$val == 0x7fff ? undef : $val',
+        PrintConv => 'Image::ExifTool::Exif::PrintParameter($val)',
+        PrintConvInv => '$val=~/normal/i ? 0 : $val',
+    },
+    14 => {
+        Name => 'Saturation',
+        RawConv => '$val == 0x7fff ? undef : $val',
+        PrintConv => 'Image::ExifTool::Exif::PrintParameter($val)',
+        PrintConvInv => '$val=~/normal/i ? 0 : $val',
+    },
+    15 => {
+        Name => 'Sharpness',
+        RawConv => '$val == 0x7fff ? undef : $val',
+        PrintConv => 'Image::ExifTool::Exif::PrintParameter($val)',
+        PrintConvInv => '$val=~/normal/i ? 0 : $val',
+    },
+    16 => {
+        Name => 'CameraISO',
+        RawConv => '$val != 0x7fff ? $val : undef',
+        ValueConv => 'Image::ExifTool::Canon::CameraISO($val)',
+        ValueConvInv => 'Image::ExifTool::Canon::CameraISO($val,1)',
+    },
+    17 => {
+        Name => 'MeteringMode',
+        PrintConv => {
+            0 => 'Default', # older Ixus
+            1 => 'Spot',
+            2 => 'Average', #PH
+            3 => 'Evaluative',
+            4 => 'Partial',
+            5 => 'Center-weighted averaging',
+        },
+    },
+    18 => {
+        # this is always 2 for the 300D - PH
+        Name => 'FocusRange',
+        PrintConv => {
+            0 => 'Manual',
+            1 => 'Auto',
+            2 => 'Not Known',
+            3 => 'Macro',
+            4 => 'Very Close', #PH
+            5 => 'Close', #PH
+            6 => 'Middle Range', #PH
+            7 => 'Far Range',
+            8 => 'Pan Focus',
+            9 => 'Super Macro', #PH
+            10=> 'Infinity', #PH
+        },
+    },
+    19 => {
+        Name => 'AFPoint',
+        Flags => 'PrintHex',
+        RawConv => '$val==0 ? undef : $val',
+        PrintConv => {
+            0x2005 => 'Manual AF point selection',
+            0x3000 => 'None (MF)',
+            0x3001 => 'Auto AF point selection',
+            0x3002 => 'Right',
+            0x3003 => 'Center',
+            0x3004 => 'Left',
+            0x4001 => 'Auto AF point selection',
+            0x4006 => 'Face Detect', #PH (A570IS)
+        },
+    },
+    20 => {
+        Name => 'CanonExposureMode',
+        PrintConv => {
+            0 => 'Easy',
+            1 => 'Program AE',
+            2 => 'Shutter speed priority AE',
+            3 => 'Aperture-priority AE',
+            4 => 'Manual',
+            5 => 'Depth-of-field AE',
+            6 => 'M-Dep', #PH
+        },
+    },
+    22 => { #4
+        Name => 'LensType',
+        RawConv => '$val ? $val : undef', # don't use if value is zero
+        SeparateTable => 1,
+        PrintConv => \%canonLensTypes,
+    },
+    23 => {
+        Name => 'LongFocal',
+        Format => 'int16u',
+        # this is a bit tricky, but we need the FocalUnits to convert this to mm
+        RawConvInv => '$val * ($$self{FocalUnits} || 1)',
+        ValueConv => '$val / ($$self{FocalUnits} || 1)',
+        ValueConvInv => '$val',
+        PrintConv => '"${val}mm"',
+        PrintConvInv => '$val=~s/mm//;$val',
+    },
+    24 => {
+        Name => 'ShortFocal',
+        Format => 'int16u',
+        RawConvInv => '$val * ($$self{FocalUnits} || 1)',
+        ValueConv => '$val / ($$self{FocalUnits} || 1)',
+        ValueConvInv => '$val',
+        PrintConv => '"${val}mm"',
+        PrintConvInv => '$val=~s/mm//;$val',
+    },
+    25 => {
+        Name => 'FocalUnits',
+        DataMember => 'FocalUnits',
+        RawConv => '$$self{FocalUnits} = $val',
+    },
+    26 => { #9
+        Name => 'MaxAperture',
+        RawConv => '$val > 0 ? $val : undef',
+        ValueConv => 'exp(Image::ExifTool::Canon::CanonEv($val)*log(2)/2)',
+        ValueConvInv => 'Image::ExifTool::Canon::CanonEvInv(log($val)*2/log(2))',
+        PrintConv => 'sprintf("%.2g",$val)',
+        PrintConvInv => '$val',
+    },
+    27 => { #PH
+        Name => 'MinAperture',
+        RawConv => '$val > 0 ? $val : undef',
+        ValueConv => 'exp(Image::ExifTool::Canon::CanonEv($val)*log(2)/2)',
+        ValueConvInv => 'Image::ExifTool::Canon::CanonEvInv(log($val)*2/log(2))',
+        PrintConv => 'sprintf("%.2g",$val)',
+        PrintConvInv => '$val',
+    },
+    28 => {
+        Name => 'FlashActivity',
+        RawConv => '$val==-1 ? undef : $val',
+    },
+    29 => {
+        Name => 'FlashBits',
+        PrintConv => { BITMASK => {
+            0 => 'Manual', #PH
+            1 => 'TTL', #PH
+            2 => 'A-TTL', #PH
+            3 => 'E-TTL', #PH
+            4 => 'FP sync enabled',
+            7 => '2nd-curtain sync used',
+            11 => 'FP sync used',
+            13 => 'Built-in',
+            14 => 'External',
+        } },
+    },
+    32 => {
+        Name => 'FocusContinuous',
+        RawConv => '$val==-1 ? undef : $val',
+        PrintConv => {
+            0 => 'Single',
+            1 => 'Continuous',
+        },
+    },
+    33 => { #PH
+        Name => 'AESetting',
+        RawConv => '$val==-1 ? undef : $val',
+        PrintConv => {
+            0 => 'Normal AE',
+            1 => 'Exposure Compensation',
+            2 => 'AE Lock',
+            3 => 'AE Lock + Exposure Comp.',
+            4 => 'No AE',
+        },
+    },
+    34 => { #PH
+        Name => 'ImageStabilization',
+        RawConv => '$val==-1 ? undef : $val',
+        PrintConv => {
+            0 => 'Off',
+            1 => 'On',
+            2 => 'On, Shot Only', #15
+            3 => 'On, Panning', #PH (A570IS)
+        },
+    },
+    35 => { #PH
+        Name => 'DisplayAperture',
+        RawConv => '$val ? $val : undef',
+        ValueConv => '$val / 10',
+        ValueConvInv => '$val * 10',
+    },
+    36 => 'ZoomSourceWidth', #PH
+    37 => 'ZoomTargetWidth', #PH
+    40 => { #PH
+        Name => 'PhotoEffect',
+        RawConv => '$val==-1 ? undef : $val',
+        PrintConv => {
+            0 => 'Off',
+            1 => 'Vivid',
+            2 => 'Neutral',
+            3 => 'Smooth',
+            4 => 'Sepia',
+            5 => 'B&W',
+            6 => 'Custom',
+            100 => 'My Color Data',
+        },
+    },
+    41 => { #PH (A570IS)
+        Name => 'ManualFlashOutput',
+        PrintHex => 1,
+        PrintConv => {
+            0 => 'n/a',
+            0x500 => 'Full',
+            0x502 => 'Medium',
+            0x504 => 'Low',
+            0x7fff => 'n/a', # (EOS models)
+        },
+    },
+    # 41 => non-zero for manual flash intensity - PH (A570IS)
+    42 => {
+        Name => 'ColorTone',
+        RawConv => '$val == 0x7fff ? undef : $val',
+        PrintConv => 'Image::ExifTool::Exif::PrintParameter($val)',
+        PrintConvInv => '$val=~/normal/i ? 0 : $val',
+    },
+);
+
+# focal length information (MakerNotes tag 0x02)
+%Image::ExifTool::Canon::FocalLength = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
+    CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
+    WRITABLE => 1,
+    FORMAT => 'int16u',
+    FIRST_ENTRY => 0,
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Image' },
+    0 => { #9
+        Name => 'FocalType',
+        RawConv => '$val ? $val : undef', # don't use if value is zero
+	    PrintConv => {
+            1 => 'Fixed',
+            2 => 'Zoom',
+        },
+    },
+    1 => {
+        Name => 'FocalLength',
+        # the EXIF FocalLength is more reliable, so set this priority to zero
+        Priority => 0,
+        RawConv => '$val ? $val : undef', # don't use if value is zero
+        RawConvInv => q{
+            my $focalUnits = $$self{FocalUnits};
+            unless ($focalUnits) {
+                $focalUnits = 1;
+                # (this happens when writing FocalLength to CRW images)
+                $self->Warn("FocalUnits not available for FocalLength conversion (1 assumed)");
+            }
+            return $val * $focalUnits;
+        },
+        ValueConv => '$val / ($$self{FocalUnits} || 1)',
+        ValueConvInv => '$val',
+        PrintConv => '"${val}mm"',
+        PrintConvInv => '$val=~s/mm//;$val',
+    },
+    2 => { #4
+        Name => 'FocalPlaneXSize',
+        Notes => 'not valid for 1DmkIII',
+        Condition => '$$self{CameraModel} !~ /1D Mark III/',
+        # focal plane image dimensions in 1/1000 inch -- convert to mm
+        RawConv => '$val < 40 ? undef : $val',  # must be reasonable
+        ValueConv => '$val * 25.4 / 1000',
+        ValueConvInv => 'int($val * 1000 / 25.4 + 0.5)',
+        PrintConv => 'sprintf("%.2fmm",$val)',
+        PrintConvInv => '$val=~s/\s*mm$//;$val',
+    },
+    3 => {
+        Name => 'FocalPlaneYSize',
+        Notes => 'not valid for 1DmkIII',
+        Condition => '$$self{CameraModel} !~ /1D Mark III/',
+        RawConv => '$val < 40 ? undef : $val',  # must be reasonable
+        ValueConv => '$val * 25.4 / 1000',
+        ValueConvInv => 'int($val * 1000 / 25.4 + 0.5)',
+        PrintConv => 'sprintf("%.2fmm",$val)',
+        PrintConvInv => '$val=~s/\s*mm$//;$val',
+    },
+);
+
+# Canon shot information (MakerNotes tag 0x04)
+# BinaryData (keys are indices into the int16s array)
+%Image::ExifTool::Canon::ShotInfo = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
+    CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
+    WRITABLE => 1,
+    FORMAT => 'int16s',
+    FIRST_ENTRY => 1,
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Image' },
+    1 => { #PH
+        Name => 'AutoISO',
+        Notes => 'actual ISO used = BaseISO * AutoISO / 100',
+        ValueConv => 'exp($val/32*log(2))*100',
+        ValueConvInv => '32*log($val/100)/log(2)',
+        PrintConv => 'sprintf("%.0f",$val)',
+        PrintConvInv => '$val',
+    },
+    2 => {
+        Name => 'BaseISO',
+        Priority => 0,
+        RawConv => '$val ? $val : undef',
+        ValueConv => 'exp($val/32*log(2))*100/32',
+        ValueConvInv => '32*log($val*32/100)/log(2)',
+        PrintConv => 'sprintf("%.0f",$val)',
+        PrintConvInv => '$val',
+    },
+    3 => { #9/PH
+        Name => 'MeasuredEV',
+        Notes => q{
+            this the Canon name for what should properly be called MeasuredLV, and is
+            offset by about -5 EV from the calculated LV for most models
+        },
+        ValueConv => '$val / 32',
+        ValueConvInv => '$val * 32',
+        PrintConv => 'sprintf("%.2f",$val)',
+        PrintConvInv => '$val',
+    },
+    4 => { #2, 9
+        Name => 'TargetAperture',
+        RawConv => '$val > 0 ? $val : undef',
+        ValueConv => 'exp(Image::ExifTool::Canon::CanonEv($val)*log(2)/2)',
+        ValueConvInv => 'Image::ExifTool::Canon::CanonEvInv(log($val)*2/log(2))',
+        PrintConv => 'sprintf("%.2g",$val)',
+        PrintConvInv => '$val',
+    },
+    5 => { #2
+        Name => 'TargetExposureTime',
+        RawConv => '$val > 0 ? $val : undef',
+        ValueConv => 'exp(-Image::ExifTool::Canon::CanonEv($val)*log(2))',
+        ValueConvInv => 'Image::ExifTool::Canon::CanonEvInv(-log($val)/log(2))',
+        PrintConv => 'Image::ExifTool::Exif::PrintExposureTime($val)',
+        PrintConvInv => 'eval $val',
+    },
+    6 => {
+        Name => 'ExposureCompensation',
+        ValueConv => 'Image::ExifTool::Canon::CanonEv($val)',
+        ValueConvInv => 'Image::ExifTool::Canon::CanonEvInv($val)',
+        PrintConv => 'Image::ExifTool::Exif::ConvertFraction($val)',
+        PrintConvInv => 'eval $val',
+    },
+    7 => {
+        Name => 'WhiteBalance',
+        PrintConv => \%canonWhiteBalance,
+    },
+    8 => { #PH
+        Name => 'SlowShutter',
+        PrintConv => {
+            0 => 'Off',
+            1 => 'Night Scene',
+            2 => 'On',
+            3 => 'None',
+        },
+    },
+    9 => {
+        Name => 'SequenceNumber',
+        Description => 'Shot Number In Continuous Burst',
+    },
+    10 => { #PH/17
+        Name => 'OpticalZoomCode',
+        Groups => { 2 => 'Camera' },
+        Notes => 'for many PowerShot models, a this is 0-6 for wide-tele zoom',
+        # (for many models, 0-6 represent 0-100% zoom, but it is always 8 for
+        #  EOS models, and I have seen values of 16,20,28,32 and 39 too...)
+    },
+    # 11 - (8 for all EOS samples, [0,8] for other models - PH)
+    13 => { #PH
+        Name => 'FlashGuideNumber',
+        RawConv => '$val == -1 ? undef : $val',
+        ValueConv => '$val / 32',
+        ValueConvInv => '$val * 32',
+    },
+    # AF points for Ixus and IxusV cameras - 02/17/04 M. Rommel (also D30/D60 - PH)
+    14 => { #2
+        Name => 'AFPointsInFocus',
+        Notes => 'used by D30, D60 and some PowerShot/Ixus models',
+        Groups => { 2 => 'Camera' },
+        Flags => 'PrintHex',
+        RawConv => '$val==0 ? undef : $val',
+        PrintConv => {
+            0x3000 => 'None (MF)',
+            0x3001 => 'Right',
+            0x3002 => 'Center',
+            0x3003 => 'Center+Right',
+            0x3004 => 'Left',
+            0x3005 => 'Left+Right',
+            0x3006 => 'Left+Center',
+            0x3007 => 'All',
+        },
+    },
+    15 => {
+        Name => 'FlashExposureComp',
+        Description => 'Flash Exposure Compensation',
+        ValueConv => 'Image::ExifTool::Canon::CanonEv($val)',
+        ValueConvInv => 'Image::ExifTool::Canon::CanonEvInv($val)',
+        PrintConv => 'Image::ExifTool::Exif::ConvertFraction($val)',
+        PrintConvInv => 'eval $val',
+    },
+    16 => {
+        Name => 'AutoExposureBracketing',
+        PrintConv => {
+            -1 => 'On',
+            0 => 'Off',
+            1 => 'On (shot 1)',
+            2 => 'On (shot 2)',
+            3 => 'On (shot 3)',
+        },
+    },
+    17 => {
+        Name => 'AEBBracketValue',
+        ValueConv => 'Image::ExifTool::Canon::CanonEv($val)',
+        ValueConvInv => 'Image::ExifTool::Canon::CanonEvInv($val)',
+        PrintConv => 'Image::ExifTool::Exif::ConvertFraction($val)',
+        PrintConvInv => 'eval $val',
+    },
+    19 => {
+        Name => 'FocusDistanceUpper',
+        ValueConv => '$val * 0.01',
+        ValueConvInv => '$val / 0.01',
+    },
+    20 => {
+        Name => 'FocusDistanceLower',
+        ValueConv => '$val * 0.01',
+        ValueConvInv => '$val / 0.01',
+    },
+    21 => {
+        Name => 'FNumber',
+        Priority => 0,
+        RawConv => '$val ? $val : undef',
+        # approximate big translation table by simple calculation - PH
+        ValueConv => 'exp(Image::ExifTool::Canon::CanonEv($val)*log(2)/2)',
+        ValueConvInv => 'Image::ExifTool::Canon::CanonEvInv(log($val)*2/log(2))',
+        PrintConv => 'sprintf("%.2g",$val)',
+        PrintConvInv => '$val',
+    },
+    22 => [
+        {
+            Name => 'ExposureTime',
+            # encoding is different for 20D and 350D (darn!)
+            # (but note that encoding is the same for TargetExposureTime - PH)
+            Condition => '$$self{CameraModel} =~ /\b(20D|350D|REBEL XT|Kiss Digital N)\b/',
+            Priority => 0,
+            RawConv => '$val ? $val : undef',
+            # approximate big translation table by simple calculation - PH
+            ValueConv => 'exp(-Image::ExifTool::Canon::CanonEv($val)*log(2))*1000/32',
+            ValueConvInv => 'Image::ExifTool::Canon::CanonEvInv(-log($val*32/1000)/log(2))',
+            PrintConv => 'Image::ExifTool::Exif::PrintExposureTime($val)',
+            PrintConvInv => 'eval $val',
+        },
+        {
+            Name => 'ExposureTime',
+            Priority => 0,
+            RawConv => '$val ? $val : undef',
+            # approximate big translation table by simple calculation - PH
+            ValueConv => 'exp(-Image::ExifTool::Canon::CanonEv($val)*log(2))',
+            ValueConvInv => 'Image::ExifTool::Canon::CanonEvInv(-log($val)/log(2))',
+            PrintConv => 'Image::ExifTool::Exif::PrintExposureTime($val)',
+            PrintConvInv => 'eval $val',
+        },
+    ],
+    24 => {
+        Name => 'BulbDuration',
+        ValueConv => '$val / 10',
+        ValueConvInv => '$val * 10',
+    },
+    # 25 - (usually 0, but 1 for 2s timer?, 19 for small AVI, 14 for large
+    #       AVI, and -6 and -10 for shots 1 and 2 with stitch assist - PH)
+    26 => { #15
+        Name => 'CameraType',
+        Groups => { 2 => 'Camera' },
+        PrintConv => {
+            248 => 'EOS High-end',
+            250 => 'Compact',
+            252 => 'EOS Mid-range',
+            255 => 'DV Camera', #PH
+        },
+    },
+    27 => {
+        Name => 'AutoRotate',
+        PrintConv => {
+           -1 => 'Rotated by Software',
+            0 => 'None',
+            1 => 'Rotate 90 CW',
+            2 => 'Rotate 180',
+            3 => 'Rotate 270 CW',
+        },
+    },
+    28 => { #15
+        Name => 'NDFilter',
+        PrintConv => { 0 => 'Off', 1 => 'On' },
+    },
+    29 => {
+        Name => 'Self-timer2',
+        RawConv => '$val >= 0 ? $val : undef',
+        ValueConv => '$val / 10',
+        ValueConvInv => '$val * 10',
+    },
+    33 => { #PH (A570IS)
+        Name => 'FlashOutput',
+        Notes => 'this has a maximum value of 500 for models like the A570IS',
+    },
+);
+
+# Canon camera information (MakerNotes tag 0x0d)
+# (ref 12 unless otherwise noted)
+%Image::ExifTool::Canon::CameraInfo = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
+    CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
+    WRITABLE => 1,
+    FORMAT => 'int8s',
+    FIRST_ENTRY => 0,
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
+    NOTES => q{
+        Information in the "CameraInfo" records is tricky to decode because the
+        encodings are very different than in other Canon records (even sometimes
+        switching endianness between values within a single camera), plus there is
+        considerable variation in format from model to model. The table below is
+        common to the 1D, 1DS, 1DmkII, 1DSmkII and 5D, but individual tags may not
+        be valid for all these models.
+    },
+    0x03 => { #PH
+        Name => 'FNumber',
+        Groups => { 2 => 'Image' },
+        Condition => '$$self{CameraModel} =~ /EOS 5D/',
+        Format => 'int8u',
+        Notes => '5D only',
+        Priority => 0,
+        RawConv => '$val ? $val : undef',
+        ValueConv => 'exp(($val-8)/16*log(2))',
+        ValueConvInv => 'log($val)*16/log(2)+8',
+        PrintConv => 'sprintf("%.2g",$val)',
+        PrintConvInv => '$val',
+    },
+    0x04 => { #9
+        Name => 'ExposureTime',
+        Groups => { 2 => 'Image' },
+        Format => 'int8u',
+        Priority => 0,
+        RawConv => '$val ? $val : undef',
+        ValueConv => 'exp(4*log(2)*(1-Image::ExifTool::Canon::CanonEv($val-24)))',
+        ValueConvInv => 'Image::ExifTool::Canon::CanonEvInv(1-log($val)/(4*log(2)))+24',
+        PrintConv => 'Image::ExifTool::Exif::PrintExposureTime($val)',
+        PrintConvInv => 'eval $val',
+    },
+    0x06 => { #PH
+        Name => 'ISO',
+        Groups => { 2 => 'Image' },
+        Condition => '$$self{CameraModel} =~ /EOS 5D/',
+        Format => 'int8u',
+        Notes => '5D only',
+        Priority => 0,
+        ValueConv => '100*exp(($val/8-9)*log(2))',
+        ValueConvInv => '(log($val/100)/log(2)+9)*8',
+        PrintConv => 'sprintf("%.0f",$val)',
+        PrintConvInv => '$val',
+    },
+    0x09 => { #9
+        Name => 'FocalLength',
+        Format => 'int16u',
+        Condition => '$$self{CameraModel} =~ /\b1Ds? Mark II\b/',
+        Notes => '1DmkII and 1DsMkII only',
+        # the EXIF FocalLength is more reliable, so set this priority to zero
+        Priority => 0,
+        # ignore if zero and change to big-endian
+        RawConv => '$val ? unpack("n",pack("v",$val)) : undef',
+        PrintConv => '"${val}mm"',
+        PrintConvInv => '$val=~s/mm//;$val',
+    },
+    0x0d => { #9
+        Name => 'LensType',
+        Format => 'int8u',
+        SeparateTable => 1,
+        RawConv => '$val ? $val : undef', # don't use if value is zero
+        PrintConv => \%canonLensTypes,
+    },
+    0x11 => { #9
+        Name => 'ShortFocal',
+        Format => 'int16u',
+        Condition => '$$self{CameraModel} =~ /\b1Ds? Mark II\b/',
+        Notes => '1DmkII and 1DsMkII only',
+        # the EXIF ShortFocal is more reliable, so set this priority to zero
+        Priority => 0,
+        ValueConv => 'unpack("n",pack("v",$val))',
+        ValueConvInv => 'unpack("v",pack("n",$val))',
+        PrintConv => '"${val}mm"',
+        PrintConvInv => '$val=~s/mm//;$val',
+    },
+    0x13 => { #9
+        Name => 'LongFocal',
+        Format => 'int16u',
+        Condition => '$$self{CameraModel} =~ /\b1Ds? Mark II\b/',
+        Notes => '1DmkII and 1DsMkII only',
+        # the EXIF LongFocal is more reliable, so set this priority to zero
+        Priority => 0,
+        ValueConv => 'unpack("n",pack("v",$val))',
+        ValueConvInv => 'unpack("v",pack("n",$val))',
+        PrintConv => '"${val}mm"',
+        PrintConvInv => '$val=~s/mm//;$val',
+    },
+    0x2d => { #9
+        Name => 'FocalType',
+        Format => 'int8u',
+        Condition => '$$self{CameraModel} =~ /\b1Ds? Mark II\b/',
+        Notes => '1DmkII and 1DSmkII only',
+        Priority => 0,
+        PrintConv => {
+           0 => 'Fixed',
+           2 => 'Zoom',
+        },
+    },
+    0x38 => {
+        Name => 'AFPointsInFocus5D',
+        Format => 'undef[2]',
+        Condition => '$$self{CameraModel} =~ /EOS 5D/',
+        Notes => 'EOS 5D, bit definitions are for big-endian int16u',
+        ValueConv => 'unpack("n",$val)',
+        ValueConvInv => 'pack("n",$val)',
+        PrintConv => { BITMASK => {
+            0 => 'Center',
+            1 => 'Top',
+            2 => 'Bottom',
+            3 => 'Upper-left',
+            4 => 'Upper-right',
+            5 => 'Lower-left',
+            6 => 'Lower-right',
+            7 => 'Left',
+            8 => 'Right',
+            9 => 'AI Servo1',
+           10 => 'AI Servo2',
+           11 => 'AI Servo3',
+           12 => 'AI Servo4',
+           13 => 'AI Servo5',
+           14 => 'AI Servo6',
+        } },
+    },
+    0x6c => {
+        Name => 'PictureStyle',
+        Format => 'int16u',
+        Condition => '$$self{CameraModel} =~ /EOS(-1Ds? Mark II| 5D)$/',
+        Notes => '1DmkII, 1DSmkII and 5D only',
+        PrintHex => 1,
+        PrintConv => \%pictureStyles,
+    },
+    0xa4 => { #PH (5D)
+        Name => 'FirmwareRevision',
+        Condition => '$$self{CameraModel} =~ /EOS 5D/',
+        Notes => '5D only',
+        Format => 'string[8]',
+    },
+    0xac => { #PH (5D)
+        Name => 'ShortOwnerName',
+        Format => 'string[16]',
+        Condition => '$$self{CameraModel} =~ /EOS 5D/',
+        Notes => '5D only',
+    },
+    0xd0 => {
+        Name => 'ImageNumber',
+        Groups => { 2 => 'Image' },
+        Format => 'int16u',
+        Condition => '$$self{CameraModel} =~ /EOS 5D/',
+        Notes => '5D only',
+        ValueConv => '$val + 1',
+        ValueConvInv => '$val - 1',
+    },
+    0xe8 => {
+        Name => 'ContrastStandard',
+        PrintConv => 'Image::ExifTool::Exif::PrintParameter($val)',
+        PrintConvInv => '$val=~/normal/i ? 0 : $val',
+    },
+    0xe9 => {
+        Name => 'ContrastPortrait',
+        PrintConv => 'Image::ExifTool::Exif::PrintParameter($val)',
+        PrintConvInv => '$val=~/normal/i ? 0 : $val',
+    },
+    0xea => {
+        Name => 'ContrastLandscape',
+        PrintConv => 'Image::ExifTool::Exif::PrintParameter($val)',
+        PrintConvInv => '$val=~/normal/i ? 0 : $val',
+    },
+    0xeb => {
+        Name => 'ContrastNeutral',
+        PrintConv => 'Image::ExifTool::Exif::PrintParameter($val)',
+        PrintConvInv => '$val=~/normal/i ? 0 : $val',
+    },
+    0xec => {
+        Name => 'ContrastFaithful',
+        PrintConv => 'Image::ExifTool::Exif::PrintParameter($val)',
+        PrintConvInv => '$val=~/normal/i ? 0 : $val',
+    },
+    0xed => {
+        Name => 'ContrastMonochrome',
+        PrintConv => 'Image::ExifTool::Exif::PrintParameter($val)',
+        PrintConvInv => '$val=~/normal/i ? 0 : $val',
+    },
+    0xee => {
+        Name => 'ContrastUserDef1',
+        PrintConv => 'Image::ExifTool::Exif::PrintParameter($val)',
+        PrintConvInv => '$val=~/normal/i ? 0 : $val',
+    },
+    0xef => {
+        Name => 'ContrastUserDef2',
+        PrintConv => 'Image::ExifTool::Exif::PrintParameter($val)',
+        PrintConvInv => '$val=~/normal/i ? 0 : $val',
+    },
+    0xf0 => {
+        Name => 'ContrastUserDef3',
+        PrintConv => 'Image::ExifTool::Exif::PrintParameter($val)',
+        PrintConvInv => '$val=~/normal/i ? 0 : $val',
+    },
+    # sharpness values are 0-7
+    0xf1 => 'SharpnessStandard',
+    0xf2 => 'SharpnessPortrait',
+    0xf3 => 'SharpnessLandscape',
+    0xf4 => 'SharpnessNeutral',
+    0xf5 => 'SharpnessFaithful',
+    0xf6 => 'SharpnessMonochrome',
+    0xf7 => 'SharpnessUserDef1',
+    0xf8 => 'SharpnessUserDef2',
+    0xf9 => 'SharpnessUserDef3',
+    0xfa => 'SaturationStandard',
+    0xfb => 'SaturationPortrait',
+    0xfc => 'SaturationLandscape',
+    0xfd => 'SaturationNeutral',
+    0xfe => 'SaturationFaithful',
+    0xff => {
+        Name => 'FilterEffectMonochrome',
+        PrintConv => {
+            0 => 'None',
+            1 => 'Yellow',
+            2 => 'Orange',
+            3 => 'Red',
+            4 => 'Green',
+        },
+    },
+    0x100 => 'SaturationUserDef1',
+    0x101 => 'SaturationUserDef2',
+    0x102 => 'SaturationUserDef3',
+    0x103 => 'ColorToneStandard',
+    0x104 => 'ColorTonePortrait',
+    0x105 => 'ColorToneLandscape',
+    0x106 => 'ColorToneNeutral',
+    0x107 => 'ColorToneFaithful',
+    0x108 => {
+        Name => 'ToningEffectMonochrome',
+        PrintConv => {
+            0 => 'None',
+            1 => 'Sepia',
+            2 => 'Blue',
+            3 => 'Purple',
+            4 => 'Green',
+        },
+    },
+    0x109 => 'ColorToneUserDef1',
+    0x10a => 'ColorToneUserDef2',
+    0x10b => 'ColorToneUserDef3',
+    0x10c => {
+        Name => 'UserDef1PictureStyle',
+        Format => 'int16u',
+        PrintHex => 1,
+        PrintConv => \%userDefStyles,
+    },
+    0x10e => {
+        Name => 'UserDef2PictureStyle',
+        Format => 'int16u',
+        PrintHex => 1,
+        PrintConv => \%userDefStyles,
+    },
+    0x110 => {
+        Name => 'UserDef3PictureStyle',
+        Format => 'int16u',
+        PrintHex => 1,
+        PrintConv => \%userDefStyles,
+    },
+    0x11c => {
+        Name => 'TimeStamp',
+        Format => 'int32u',
+        Groups => { 2 => 'Time' },
+        Shift => 'Time',
+        RawConv => '$val ? $val : undef',
+        ValueConv => 'ConvertUnixTime($val)',
+        ValueConvInv => 'GetUnixTime($val)',
+        PrintConv => '$self->ConvertDateTime($val)',
+        PrintConvInv => '$val',
+    },
+);
+
+# Canon camera information for 1DmkIII (MakerNotes tag 0x0d) - PH
+%Image::ExifTool::Canon::CameraInfo1DmkIII = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
+    CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
+    WRITABLE => 1,
+    FORMAT => 'int8s',
+    FIRST_ENTRY => 0,
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
+    NOTES => 'CameraInfo tags valid for the EOS 1D Mark III.',
+    0x03 => {
+        Name => 'FNumber',
+        Groups => { 2 => 'Image' },
+        Format => 'int8u',
+        Priority => 0,
+        RawConv => '$val ? $val : undef',
+        ValueConv => 'exp(($val-8)/16*log(2))',
+        ValueConvInv => 'log($val)*16/log(2)+8',
+        PrintConv => 'sprintf("%.2g",$val)',
+        PrintConvInv => '$val',
+    },
+    0x04 => { #9
+        Name => 'ExposureTime',
+        Groups => { 2 => 'Image' },
+        Format => 'int8u',
+        Priority => 0,
+        RawConv => '$val ? $val : undef',
+        ValueConv => 'exp(4*log(2)*(1-Image::ExifTool::Canon::CanonEv($val-24)))',
+        ValueConvInv => 'Image::ExifTool::Canon::CanonEvInv(1-log($val)/(4*log(2)))+24',
+        PrintConv => 'Image::ExifTool::Exif::PrintExposureTime($val)',
+        PrintConvInv => 'eval $val',
+    },
+    0x06 => {
+        Name => 'ISO',
+        Groups => { 2 => 'Image' },
+        Format => 'int8u',
+        Priority => 0,
+        ValueConv => '100*exp(($val/8-9)*log(2))',
+        ValueConvInv => '(log($val/100)/log(2)+9)*8',
+        PrintConv => 'sprintf("%.0f",$val)',
+        PrintConvInv => '$val',
+    },
+    0x1d => {
+        Name => 'FocalLength',
+        Format => 'int16u',
+        # the EXIF FocalLength is more reliable, so set this priority to zero
+        # (just to make things confusing, the focal lengths are big-endian,
+        #  while other values are stored little-endian)
+        Priority => 0,
+        # ignore if zero and change to big-endian
+        RawConv => '$val ? unpack("n",pack("v",$val)) : undef',
+        PrintConv => '"${val}mm"',
+        PrintConvInv => '$val=~s/mm//;$val',
+    },
+    0x86 => {
+        Name => 'PictureStyle',
+        Format => 'int8u',
+        PrintHex => 1,
+        PrintConv => \%pictureStyles,
+    },
+    0x113 => {
+        Name => 'ShortFocal',
+        Format => 'int16u',
+        # the EXIF ShortFocal is more reliable, so set this priority to zero
+        Priority => 0,
+        # byte order is big-endian
+        ValueConv => 'unpack("n",pack("v",$val))',
+        ValueConvInv => 'unpack("v",pack("n",$val))',
+        PrintConv => '"${val}mm"',
+        PrintConvInv => '$val=~s/mm//;$val',
+    },
+    0x115 => {
+        Name => 'LongFocal',
+        Format => 'int16u',
+        # the EXIF LongFocal is more reliable, so set this priority to zero
+        Priority => 0,
+        # byte order is big-endian
+        ValueConv => 'unpack("n",pack("v",$val))',
+        ValueConvInv => 'unpack("v",pack("n",$val))',
+        PrintConv => '"${val}mm"',
+        PrintConvInv => '$val=~s/mm//;$val',
+    },
+    0x172 => {
+        Name => 'FileNumber',
+        Groups => { 2 => 'Image' },
+        Format => 'int32u',
+        ValueConv => '$val + 1',
+        ValueConvInv => '$val - 1',
+    },
+    0x176 => {
+        Name => 'ShutterCount',
+        Notes => 'may be valid only for some 1DmkIII copies, even running the same firmware',
+        Format => 'int32u',
+        ValueConv => '$val + 1',
+        ValueConvInv => '$val - 1',
+    },
+    0x45e => {
+        Name => 'TimeStamp',
+        Format => 'int32u',
+        Groups => { 2 => 'Time' },
+        Shift => 'Time',
+        RawConv => '$val ? $val : undef',
+        ValueConv => 'ConvertUnixTime($val)',
+        ValueConvInv => 'GetUnixTime($val)',
+        PrintConv => '$self->ConvertDateTime($val)',
+        PrintConvInv => '$val',
+    },
+);
+
+# type 2 Canon camera information (MakerNotes tag 0x0d) - PH
+%Image::ExifTool::Canon::CameraInfo2 = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
+    CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
+    WRITABLE => 1,
+    FORMAT => 'int32s',
+    FIRST_ENTRY => 0,
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
+    NOTES => 'These tags used by a number of newer PowerShot models.',
+    0x00 => {
+        Name => 'ISO',
+        Groups => { 2 => 'Image' },
+        Priority => 0,
+        ValueConv => '100*exp(($val/100-4)*log(2))',
+        ValueConvInv => '(log($val/100)/log(2)+4)*100',
+        PrintConv => 'sprintf("%.0f",$val)',
+        PrintConvInv => '$val',
+    },
+    0x05 => {
+        Name => 'FNumber',
+        Groups => { 2 => 'Image' },
+        Priority => 0,
+        ValueConv => 'exp(($val+16)/200*log(2))',
+        ValueConvInv => 'log($val)*200/log(2)-16',
+        PrintConv => 'sprintf("%.2g",$val)',
+        PrintConvInv => '$val',
+    },
+    0x06 => {
+        Name => 'ExposureTime',
+        Groups => { 2 => 'Image' },
+        Priority => 0,
+        RawConv => '$val ? $val : undef',
+        ValueConv => 'exp(-($val+24)/100*log(2))',
+        ValueConvInv => '-log($val)*100/log(2)-24',
+        PrintConv => 'Image::ExifTool::Exif::PrintExposureTime($val)',
+        PrintConvInv => 'eval $val',
+    },
+    0x17 => 'Rotation', # usually the same as Orientation (but not always! why?)
+);
+
+# unknown Canon camera information (MakerNotes tag 0x0d) - PH
+%Image::ExifTool::Canon::CameraInfoUnknown32 = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
+    CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
+    WRITABLE => 1,
+    FORMAT => 'int32s',
+    FIRST_ENTRY => 0,
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
+    NOTES => 'Unknown CameraInfo tags are divided into 3 tables based on format size.',
+);
+
+# unknown Canon camera information (MakerNotes tag 0x0d) - PH
+%Image::ExifTool::Canon::CameraInfoUnknown16 = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
+    CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
+    WRITABLE => 1,
+    FORMAT => 'int16s',
+    FIRST_ENTRY => 0,
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
+);
+
+# unknown Canon camera information (MakerNotes tag 0x0d) - PH
+%Image::ExifTool::Canon::CameraInfoUnknown8 = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
+    CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
+    WRITABLE => 1,
+    FORMAT => 'int8s',
+    FIRST_ENTRY => 0,
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
+);
+
+# Canon panorama information (MakerNotes tag 0x05)
+%Image::ExifTool::Canon::Panorama = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
+    CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
+    WRITABLE => 1,
+    FORMAT => 'int16s',
+    FIRST_ENTRY => 0,
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Image' },
+    2 => 'PanoramaFrame',
+    5 => {
+        Name => 'PanoramaDirection',
+        PrintConv => {
+            0 => 'Left to Right',
+            1 => 'Right to Left',
+            2 => 'Bottom to Top',
+            3 => 'Top to Bottom',
+            4 => '2x2 Matrix (Clockwise)',
+        },
+     },
+);
+
+# AF information (MakerNotes tag 0x12) - PH
+%Image::ExifTool::Canon::AFInfo = (
+    PROCESS_PROC => \&ProcessSerialData,
+    FORMAT => 'int16u',
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
+    NOTES => q{
+        Auto-focus information used by many older Canon models.  The values in this
+        record are sequential, and some have variable sizes based on the value of
+        NumAFPoints (which may be 1,5,7,9,15,45 or 53).  The AFArea coordinates are
+        given in a system where the image has dimensions given by AFImageWidth and
+        AFImageHeight, and 0,0 is the image center. The direction of the Y axis
+        depends on the camera model, with positive Y upwards for EOS models, but
+        apparently downwards for PowerShot models.
+    },
+    0 => {
+        Name => 'NumAFPoints',
+    },
+    1 => {
+        Name => 'ValidAFPoints',
+        Notes => 'number of AF points valid in the following information',
+    },
+    2 => {
+        Name => 'CanonImageWidth',
+        Groups => { 2 => 'Image' },
+    },
+    3 => {
+        Name => 'CanonImageHeight',
+        Groups => { 2 => 'Image' },
+    },
+    4 => {
+        Name => 'AFImageWidth',
+        Notes => 'size of image in AF coordinates',
+    },
+    5 => 'AFImageHeight',
+    6 => 'AFAreaWidth',
+    7 => 'AFAreaHeight',
+    8 => {
+        Name => 'AFAreaXPositions',
+        Format => 'int16s[$val{0}]',
+    },
+    9 => {
+        Name => 'AFAreaYPositions',
+        Format => 'int16s[$val{0}]',
+    },
+    10 => {
+        Name => 'AFPointsInFocus',
+        Format => 'int16s[int(($val{0}+15)/16)]',
+        PrintConv => 'Image::ExifTool::DecodeBits($val, undef, 16)',
+    },
+    11 => [
+        {
+            Name => 'PrimaryAFPoint',
+            Condition => q{
+                $$self{CameraModel} !~ /EOS/ and
+                (not $$self{AFInfoCount} or $$self{AFInfoCount} != 36)
+            },
+        },
+        {
+            # (some PowerShot 9-point systems put PrimaryAFPoint after 8 unknown values)
+            Name => 'Canon_AFInfo_0x000b',
+            Condition => '$$self{CameraModel} !~ /EOS/',
+            Format => 'int16u[8]',
+            Unknown => 1,
+        },
+        # (serial processing stops here for EOS cameras)
+    ],
+    12 => 'PrimaryAFPoint',
+);
+
+# newer AF information (MakerNotes tag 0x26) - PH (A570IS,1DmkIII,40D)
+# (Note: this tag is out of sequence in A570IS maker notes)
+%Image::ExifTool::Canon::AFInfo2 = (
+    PROCESS_PROC => \&ProcessSerialData,
+    FORMAT => 'int16u',
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
+    NOTES => q{
+        Newer version of the AFInfo record containing much of the same information
+        (and coordinate confusion) as the older version.  In this record, values of
+        9 and 45 have been observed for NumAFPoints.
+    },
+    0 => {
+        Name => 'AFInfoSize',
+        Unknown => 1, # normally don't print this out
+    },
+    1 => {
+        Name => 'AFMode',
+        PrintConv => {
+            0 => 'Off (Manual Focus)',
+            2 => 'Single-point AF',
+            4 => 'Multi-point AF', # AiAF on A570IS
+            5 => 'Face Detect AF',
+        },
+    },
+    2 => {
+        Name => 'NumAFPoints',
+        RawConv => '$$self{NumAFPoints} = $val', # save for later
+    },
+    3 => {
+        Name => 'ValidAFPoints',
+        Notes => 'number of AF points valid in the following information',
+    },
+    4 => {
+        Name => 'CanonImageWidth',
+        Groups => { 2 => 'Image' },
+    },
+    5 => {
+        Name => 'CanonImageWidth',
+        Groups => { 2 => 'Image' },
+    },
+    6 => {
+        Name => 'AFImageWidth',
+        Notes => 'size of image in AF coordinates',
+    },
+    7 => 'AFImageHeight',
+    8 => {
+        Name => 'AFAreaWidths',
+        Format => 'int16s[$val{2}]',
+    },
+    9 => {
+        Name => 'AFAreaHeights',
+        Format => 'int16s[$val{2}]',
+    },
+    10 => {
+        Name => 'AFAreaXPositions',
+        Format => 'int16s[$val{2}]',
+    },
+    11 => {
+        Name => 'AFAreaYPositions',
+        Format => 'int16s[$val{2}]',
+    },
+    12 => {
+        Name => 'AFPointsInFocus',
+        Format => 'int16s[int(($val{2}+15)/16)]',
+        PrintConv => 'Image::ExifTool::DecodeBits($val, undef, 16)',
+    },
+    13 => [
+        {
+            Name => 'AFPointsSelected',
+            Condition => '$$self{CameraModel} =~ /EOS/',
+            Format => 'int16s[int(($val{2}+15)/16)]',
+            PrintConv => 'Image::ExifTool::DecodeBits($val, undef, 16)',
+        },
+        {
+            Name => 'Canon_AFInfo2_0x000d',
+            Format => 'int16s[int(($val{2}+15)/16)+1]',
+            Unknown => 1,
+        },
+    ],
+    14 => {
+        # usually, but not always, the lowest number AF point in focus
+        Name => 'PrimaryAFPoint',
+        Condition => '$$self{CameraModel} !~ /EOS/',
+    },        
+);
+
+# my color mode information (MakerNotes tag 0x1d) - PH (A570IS)
+%Image::ExifTool::Canon::MyColors = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
+    CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
+    WRITABLE => 1,
+    FORMAT => 'int16u',
+    FIRST_ENTRY => 0,
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Image' },
+    0x02 => {
+        Name => 'MyColorMode',
+        PrintConv => {
+            0 => 'Off',
+            9 => 'Custom',
+            12 => 'Vivid',
+            13 => 'Neutral',
+            14 => 'Sepia',
+            15 => 'B&W',
+        },
+    },
+);
+
+# face detect information (MakerNotes tag 0x24) - PH (A570IS)
+%Image::ExifTool::Canon::FaceDetect1 = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
+    CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
+    WRITABLE => 1,
+    FORMAT => 'int16u',
+    FIRST_ENTRY => 0,
+    DATAMEMBER => [ 0x02 ],
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Image' },
+    0x02 => {
+        Name => 'FacesDetected',
+        DataMember => 'FacesDetected',
+        RawConv => '$$self{FacesDetected} = $val',
+    },
+    0x03 => 'FaceDetectFrameWidth',
+    0x04 => 'FaceDetectFrameHeight',
+    0x08 => {
+        Name => 'Face0Position',
+        Format => 'int16s[2]',
+        RawConv => '$$self{FacesDetected} < 1 ? undef: $val',
+        Notes => q{
+            X-Y coordinates for the center of each face in the Face Detect frame at the
+            time of focus lock. "0 0" is the center, and positive X and Y are to the
+            right and downwards respectively
+        },
+    },
+    0x0a => {
+        Name => 'Face1Position',
+        Format => 'int16s[2]',
+        RawConv => '$$self{FacesDetected} < 2 ? undef : $val',
+    },
+    0x0c => {
+        Name => 'Face2Position',
+        Format => 'int16s[2]',
+        RawConv => '$$self{FacesDetected} < 3 ? undef : $val',
+    },
+    0x0e => {
+        Name => 'Face3Position',
+        Format => 'int16s[2]',
+        RawConv => '$$self{FacesDetected} < 4 ? undef : $val',
+    },
+    0x10 => {
+        Name => 'Face4Position',
+        Format => 'int16s[2]',
+        RawConv => '$$self{FacesDetected} < 5 ? undef : $val',
+    },
+    0x12 => {
+        Name => 'Face5Position',
+        Format => 'int16s[2]',
+        RawConv => '$$self{FacesDetected} < 6 ? undef : $val',
+    },
+    0x14 => {
+        Name => 'Face6Position',
+        Format => 'int16s[2]',
+        RawConv => '$$self{FacesDetected} < 7 ? undef : $val',
+    },
+    0x16 => {
+        Name => 'Face7Position',
+        Format => 'int16s[2]',
+        RawConv => '$$self{FacesDetected} < 8 ? undef : $val',
+    },
+    0x18 => {
+        Name => 'Face8Position',
+        Format => 'int16s[2]',
+        RawConv => '$$self{FacesDetected} < 9 ? undef : $val',
+    },
+);
+
+# more face detect information (MakerNotes tag 0x25) - PH (A570IS)
+%Image::ExifTool::Canon::FaceDetect2 = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
+    CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
+    WRITABLE => 1,
+    FORMAT => 'int8u',
+    FIRST_ENTRY => 0,
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Image' },
+    0x02 => 'FacesDetected',
+);
+
+# Preview image information (MakerNotes tag 0xb6)
+# - The 300D writes a 1536x1024 preview image that is accessed
+#   through this information - decoded by PH 12/14/03
+%Image::ExifTool::Canon::PreviewImageInfo = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
+    CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
+    WRITABLE => 1,
+    FORMAT => 'int32u',
+    FIRST_ENTRY => 1,
+    IS_OFFSET => [ 5 ],   # tag 5 is 'IsOffset'
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Image' },
+# the size of the preview block in 2-byte increments
+#    0 => {
+#        Name => 'PreviewImageInfoWords',
+#    },
+    1 => {
+        Name => 'PreviewQuality',
+        PrintConv => \%canonQuality,
+    },
+    2 => {
+        Name => 'PreviewImageLength',
+        OffsetPair => 5,   # point to associated offset
+        DataTag => 'PreviewImage',
+        Protected => 2,
+    },
+    3 => 'PreviewImageWidth',
+    4 => 'PreviewImageHeight',
+    5 => {
+        Name => 'PreviewImageStart',
+        Flags => 'IsOffset',
+        OffsetPair => 2,  # associated byte count tagID
+        DataTag => 'PreviewImage',
+        Protected => 2,
+    },
+    6 => {
+        Name => 'PreviewFocalPlaneXResolution',
+        Format => 'rational64s',
+        PrintConv => 'sprintf("%.1f",$val)',
+        PrintConvInv => '$val',
+    },
+    8 => {
+        Name => 'PreviewFocalPlaneYResolution',
+        Format => 'rational64s',
+        PrintConv => 'sprintf("%.1f",$val)',
+        PrintConvInv => '$val',
+    },
+# the following 2 values look like they are really 4 shorts
+# taking the values of 1,4,4 and 2 respectively - don't know what they are though
+#    10 => {
+#        Name => 'PreviewImageUnknown1',
+#        PrintConv => 'sprintf("0x%x",$val)',
+#    },
+#    11 => {
+#        Name => 'PreviewImageUnknown2',
+#        PrintConv => 'sprintf("0x%x",$val)',
+#    },
+);
+
+# Sensor information (MakerNotes tag 0xe0) (ref 12)
+%Image::ExifTool::Canon::SensorInfo = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    FORMAT => 'int16s',
+    FIRST_ENTRY => 1,
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Image' },
+    # Note: Don't make these writable because it confuses Canon decoding software
+    # if these are changed
+    1 => 'SensorWidth',
+    2 => 'SensorHeight',
+    5 => 'SensorLeftBorder', #2
+    6 => 'SensorTopBorder', #2
+    7 => 'SensorRightBorder', #2
+    8 => 'SensorBottomBorder', #2
+);
+
+# File number information (MakerNotes tag 0x93)
+%Image::ExifTool::Canon::FileInfo = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
+    CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
+    WRITABLE => 1,
+    FORMAT => 'int16s',
+    FIRST_ENTRY => 1,
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Image' },
+    1 => [
+        { #5
+            Name => 'FileNumber',
+            Condition => '$$self{CameraModel} =~ /\b(20D|350D|REBEL XT|Kiss Digital N)\b/',
+            Format => 'int32u',
+            # Thanks to Juha Eskelinen for figuring this out:
+            # [this is an odd bit mapping -- it looks like the file number exists as
+            # a 16-bit integer containing the high bits, followed by an 8-bit integer
+            # with the low bits.  But it is more convenient to have this in a single
+            # word, so some bit manipulations are necessary... - PH]
+            # The bit pattern of the 32-bit word is:
+            #   31....24 23....16 15.....8 7......0
+            #   00000000 ffffffff DDDDDDDD ddFFFFFF
+            #     0 = zero bits (not part of the file number?)
+            #     f/F = low/high bits of file number
+            #     d/D = low/high bits of directory number
+            # The directory and file number are then converted into decimal
+            # and separated by a '-' to give the file number used in the 20D
+            ValueConv => '(($val&0xffc0)>>6)*10000+(($val>>16)&0xff)+(($val&0x3f)<<8)',
+            ValueConvInv => q{
+                my $d = int($val/10000);
+                my $f = $val - $d * 10000;
+                return (($d<<6) & 0xffc0) + (($f & 0xff)<<16) + (($f>>8) & 0x3f);
+            },
+            PrintConv => '$_=$val,s/(\d+)(\d{4})/$1-$2/,$_',
+            PrintConvInv => '$val=~s/-//g;$val',
+        },
+        { #16
+            Name => 'FileNumber',
+            Condition => '$$self{CameraModel} =~ /\b(30D|400D|REBEL XTi|Kiss Digital X|K236)\b/',
+            Format => 'int32u',
+            Notes => q{
+                the location of the upper 4 bits of the directory number is a mystery for
+                the EOS 30D, so the reported directory number will be incorrect for original
+                images with a directory number of 164 or greater
+            },
+            # Thanks to Emil Sit for figuring this out:
+            # [more insane bit maniplations like the 20D/350D above, but this time we
+            # appear to have lost the upper 4 bits of the directory number (this was
+            # verified through tests with directory numbers 100, 222, 801 and 999) - PH]
+            # The bit pattern for the 30D is: (see 20D notes above for more information)
+            #   31....24 23....16 15.....8 7......0
+            #   00000000 ffff0000 ddddddFF FFFFFFFF
+            # [NOTE: the 4 high order directory bits don't appear in this record, but
+            # I have chosen to write them into bits 16-19 since these 4 zero bits look
+            # very suspicious, and are a convenient place to store this information - PH]
+            ValueConv  => q{
+                my $d = ($val & 0xffc00) >> 10;
+                # we know there are missing bits if directory number is < 100
+                $d += 0x40 while $d < 100;  # (repair the damage as best we can)
+                return $d*10000 + (($val&0x3ff)<<4) + (($val>>20)&0x0f);
+            },
+            ValueConvInv => q{
+                my $d = int($val/10000);
+                my $f = $val - $d * 10000;
+                return ($d << 10) + (($f>>4)&0x3ff) + (($f&0x0f)<<20);
+            },
+            PrintConv => '$_=$val,s/(\d+)(\d{4})/$1-$2/,$_',
+            PrintConvInv => '$val=~s/-//g;$val',
+        },
+        { #7 (1D, 1Ds)
+            Name => 'ShutterCount',
+            Condition => 'GetByteOrder() eq "MM"',
+            Format => 'int32u',
+        },
+        { #7 (1DmkII, 1DsMkII, 1DsMkIIN)
+            Name => 'ShutterCount',
+            Condition => '$$self{CameraModel} =~ /\b1Ds? Mark II\b/',
+            Format => 'int32u',
+            ValueConv => '($val>>16)|(($val&0xffff)<<16)',
+            ValueConvInv => '($val>>16)|(($val&0xffff)<<16)',
+        },
+        # 5D gives a single byte value (unknown)
+        # 40D stores all zeros
+    ],
+    3 => { #PH
+        Name => 'BracketMode',
+        PrintConv => {
+            0 => 'Off',
+            1 => 'AEB',
+            2 => 'FEB',
+            3 => 'ISO',
+            4 => 'WB',
+        },
+    },
+    4 => 'BracketValue', #PH
+    5 => 'BracketShotNumber', #PH
+    6 => { #PH
+        Name => 'RawJpgQuality',
+        RawConv => '$val<=0 ? undef : $val',
+        PrintConv => \%canonQuality,
+    },
+    7 => { #PH
+        Name => 'RawJpgSize',
+        RawConv => '$val<0 ? undef : $val',
+        PrintConv => \%canonImageSize,
+    },
+    8 => { #PH
+        Name => 'NoiseReduction',
+        RawConv => '$val<0 ? undef : $val',
+        PrintConv => {
+            0 => 'Off',
+            # what do these modes mean?:
+            1 => 'On (mode 1)',
+            2 => 'On (mode 2)',
+            3 => 'On (mode 3)', # (1DmkII,5D)
+            4 => 'On (mode 4)', # (30D)
+        },
+    },
+    9 => { #PH
+        Name => 'WBBracketMode',
+        PrintConv => {
+            0 => 'Off',
+            1 => 'On (shift AB)',
+            2 => 'On (shift GM)',
+        },
+    },
+    12 => 'WBBracketValueAB', #PH
+    13 => 'WBBracketValueGM', #PH
+    14 => { #PH
+        Name => 'FilterEffect',
+        RawConv => '$val==-1 ? undef : $val',
+        PrintConv => {
+            0 => 'None',
+            1 => 'Yellow',
+            2 => 'Orange',
+            3 => 'Red',
+            4 => 'Green',
+        },
+    },
+    15 => { #PH
+        Name => 'ToningEffect',
+        RawConv => '$val==-1 ? undef : $val',
+        PrintConv => {
+            0 => 'None',
+            1 => 'Sepia',
+            2 => 'Blue',
+            3 => 'Purple',
+            4 => 'Green',
+        },
+    },
+);
+
+# color information (MakerNotes tag 0xa0)
+%Image::ExifTool::Canon::Processing = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
+    CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
+    WRITABLE => 1,
+    FORMAT => 'int16s',
+    FIRST_ENTRY => 1,
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Image' },
+    1 => { #PH
+        Name => 'ToneCurve',
+        PrintConv => {
+            0 => 'Standard',
+            1 => 'Manual',
+            2 => 'Custom',
+        },
+    },
+    2 => { #12
+        Name => 'Sharpness',
+        Notes => '1D and 5D only',
+        Condition => '$$self{CameraModel} =~ /\b(1D|5D)/',
+    },
+    3 => { #PH
+        Name => 'SharpnessFrequency',
+        PrintConv => {
+            0 => 'n/a',
+            1 => 'Lowest',
+            2 => 'Low',
+            3 => 'Standard',
+            4 => 'High',
+            5 => 'Highest',
+        },
+    },
+    4 => 'SensorRedLevel', #PH
+    5 => 'SensorBlueLevel', #PH
+    6 => 'WhiteBalanceRed', #PH
+    7 => 'WhiteBalanceBlue', #PH
+    8 => { #PH
+        Name => 'WhiteBalance',
+        RawConv => '$val < 0 ? undef : $val',
+        PrintConv => \%canonWhiteBalance,
+    },
+    9 => 'ColorTemperature', #6
+    10 => { #12
+        Name => 'PictureStyle',
+        PrintHex => 1,
+        PrintConv => \%pictureStyles,
+    },
+    11 => { #PH
+        Name => 'DigitalGain',
+        ValueConv => '$val / 10',
+        ValueConvInv => '$val * 10',
+    },
+    12 => { #PH
+        Name => 'WBShiftAB',
+        Notes => 'positive is a shift toward red',
+    },
+    13 => { #PH
+        Name => 'WBShiftGM',
+        Notes => 'positive is a shift toward yellow/green',
+    },
+);
+
+# D30 color information (MakerNotes tag 0x0a)
+%Image::ExifTool::Canon::ColorInfoD30 = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
+    CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
+    WRITABLE => 1,
+    FORMAT => 'int16s',
+    FIRST_ENTRY => 1,
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
+    9 => 'ColorTemperature',
+    10 => 'ColorMatrix',
+);
+
+# Color balance information (MakerNotes tag 0xa9) (ref PH)
+%Image::ExifTool::Canon::ColorBalance = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
+    CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
+    NOTES => 'This table is used by the 10D and 300D.',
+    FORMAT => 'int16u',
+    FIRST_ENTRY => 0,
+    WRITABLE => 1,
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
+    # red,green1,green2,blue (ref 2)
+    0  => { Name => 'WB_RGGBLevelsAuto',       Format => 'int16u[4]' },
+    4  => { Name => 'WB_RGGBLevelsDaylight',   Format => 'int16u[4]' },
+    8  => { Name => 'WB_RGGBLevelsShade',      Format => 'int16u[4]' },
+    12 => { Name => 'WB_RGGBLevelsCloudy',     Format => 'int16u[4]' },
+    16 => { Name => 'WB_RGGBLevelsTungsten',   Format => 'int16u[4]' },
+    20 => { Name => 'WB_RGGBLevelsFluorescent',Format => 'int16u[4]' },
+    24 => { Name => 'WB_RGGBLevelsFlash',      Format => 'int16u[4]' },
+    28 => { Name => 'WB_RGGBLevelsCustom',     Format => 'int16u[4]' },
+    32 => { Name => 'WB_RGGBLevelsKelvin',     Format => 'int16u[4]' },
+);
+
+# Color balance (MakerNotes tag 0x4001) (ref 12)
+%Image::ExifTool::Canon::ColorBalance1 = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
+    CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
+    NOTES => 'This table is used by the 20D and 350D.',
+    FORMAT => 'int16u',
+    FIRST_ENTRY => 0,
+    WRITABLE => 1,
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
+    25 => { Name => 'WB_RGGBLevelsAsShot',      Format => 'int16u[4]' },
+    29 => 'ColorTempAsShot',
+    30 => { Name => 'WB_RGGBLevelsAuto',        Format => 'int16u[4]' },
+    34 => 'ColorTempAuto',
+    35 => { Name => 'WB_RGGBLevelsDaylight',    Format => 'int16u[4]' },
+    39 => 'ColorTempDaylight',
+    40 => { Name => 'WB_RGGBLevelsShade',       Format => 'int16u[4]' },
+    44 => 'ColorTempShade',
+    45 => { Name => 'WB_RGGBLevelsCloudy',      Format => 'int16u[4]' },
+    49 => 'ColorTempCloudy',
+    50 => { Name => 'WB_RGGBLevelsTungsten',    Format => 'int16u[4]' },
+    54 => 'ColorTempTungsten',
+    55 => { Name => 'WB_RGGBLevelsFluorescent', Format => 'int16u[4]' },
+    59 => 'ColorTempFluorescent',
+    60 => { Name => 'WB_RGGBLevelsFlash',       Format => 'int16u[4]' },
+    64 => 'ColorTempFlash',
+    65 => { Name => 'WB_RGGBLevelsCustom1',     Format => 'int16u[4]' },
+    69 => 'ColorTempCustom1',
+    70 => { Name => 'WB_RGGBLevelsCustom2',     Format => 'int16u[4]' },
+    74 => 'ColorTempCustom2',
+);
+
+# Color balance (MakerNotes tag 0x4001) (ref 12)
+%Image::ExifTool::Canon::ColorBalance2 = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
+    CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
+    NOTES => 'This table is used by the 1DmkII and 1DSmkII.',
+    FORMAT => 'int16u',
+    FIRST_ENTRY => 0,
+    WRITABLE => 1,
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
+    24 => { Name => 'WB_RGGBLevelsAsShot',      Format => 'int16u[4]' },
+    28 => 'ColorTempAsShot',
+    29 => { Name => 'WB_RGGBLevelsUnknown',     Format => 'int16u[4]', Unknown => 1 },
+    33 => { Name => 'ColorTempUnknown', Unknown => 1 },
+    34 => { Name => 'WB_RGGBLevelsAuto',        Format => 'int16u[4]' },
+    38 => 'ColorTempAuto',
+    39 => { Name => 'WB_RGGBLevelsDaylight',    Format => 'int16u[4]' },
+    43 => 'ColorTempDaylight',
+    44 => { Name => 'WB_RGGBLevelsShade',       Format => 'int16u[4]' },
+    48 => 'ColorTempShade',
+    49 => { Name => 'WB_RGGBLevelsCloudy',      Format => 'int16u[4]' },
+    53 => 'ColorTempCloudy',
+    54 => { Name => 'WB_RGGBLevelsTungsten',    Format => 'int16u[4]' },
+    58 => 'ColorTempTungsten',
+    59 => { Name => 'WB_RGGBLevelsFluorescent', Format => 'int16u[4]' },
+    63 => 'ColorTempFluorescent',
+    64 => { Name => 'WB_RGGBLevelsKelvin',      Format => 'int16u[4]' },
+    68 => 'ColorTempKelvin',
+    69 => { Name => 'WB_RGGBLevelsFlash',       Format => 'int16u[4]' },
+    73 => 'ColorTempFlash',
+    74 => { Name => 'WB_RGGBLevelsUnknown2',    Format => 'int16u[4]', Unknown => 1 },
+    78 => { Name => 'ColorTempUnknown2', Unknown => 1 },
+    79 => { Name => 'WB_RGGBLevelsUnknown3',    Format => 'int16u[4]', Unknown => 1 },
+    83 => { Name => 'ColorTempUnknown3', Unknown => 1 },
+    84 => { Name => 'WB_RGGBLevelsUnknown4',    Format => 'int16u[4]', Unknown => 1 },
+    88 => { Name => 'ColorTempUnknown4', Unknown => 1 },
+    89 => { Name => 'WB_RGGBLevelsUnknown5',    Format => 'int16u[4]', Unknown => 1 },
+    93 => { Name => 'ColorTempUnknown5', Unknown => 1 },
+    94 => { Name => 'WB_RGGBLevelsUnknown6',    Format => 'int16u[4]', Unknown => 1 },
+    98 => { Name => 'ColorTempUnknown6', Unknown => 1 },
+    99 => { Name => 'WB_RGGBLevelsUnknown7',    Format => 'int16u[4]', Unknown => 1 },
+    103 => { Name => 'ColorTempUnknown7', Unknown => 1 },
+    104 => { Name => 'WB_RGGBLevelsUnknown8',   Format => 'int16u[4]', Unknown => 1 },
+    108 => { Name => 'ColorTempUnknown8', Unknown => 1 },
+    109 => { Name => 'WB_RGGBLevelsUnknown9',   Format => 'int16u[4]', Unknown => 1 },
+    113 => { Name => 'ColorTempUnknown9', Unknown => 1 },
+    114 => { Name => 'WB_RGGBLevelsUnknown10',  Format => 'int16u[4]', Unknown => 1 },
+    118 => { Name => 'ColorTempUnknown10', Unknown => 1 },
+    119 => { Name => 'WB_RGGBLevelsUnknown11',  Format => 'int16u[4]', Unknown => 1 },
+    123 => { Name => 'ColorTempUnknown11', Unknown => 1 },
+    124 => { Name => 'WB_RGGBLevelsUnknown12',  Format => 'int16u[4]', Unknown => 1 },
+    128 => { Name => 'ColorTempUnknown12', Unknown => 1 },
+    129 => { Name => 'WB_RGGBLevelsUnknown13',  Format => 'int16u[4]', Unknown => 1 },
+    133 => { Name => 'ColorTempUnknown13', Unknown => 1 },
+    134 => { Name => 'WB_RGGBLevelsUnknown14',  Format => 'int16u[4]', Unknown => 1 },
+    138 => { Name => 'ColorTempUnknown14', Unknown => 1 },
+    139 => { Name => 'WB_RGGBLevelsUnknown15',  Format => 'int16u[4]', Unknown => 1 },
+    143 => { Name => 'ColorTempUnknown15', Unknown => 1 },
+    144 => { Name => 'WB_RGGBLevelsPC1',        Format => 'int16u[4]' },
+    148 => 'ColorTempPC1',
+    149 => { Name => 'WB_RGGBLevelsPC2',        Format => 'int16u[4]' },
+    153 => 'ColorTempPC2',
+    154 => { Name => 'WB_RGGBLevelsPC3',        Format => 'int16u[4]' },
+    158 => 'ColorTempPC3',
+    159 => { Name => 'WB_RGGBLevelsUnknown16',  Format => 'int16u[4]', Unknown => 1 },
+    163 => { Name => 'ColorTempUnknown16', Unknown => 1 },
+);
+
+# Color balance (MakerNotes tag 0x4001) (ref 12)
+%Image::ExifTool::Canon::ColorBalance3 = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
+    CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
+    NOTES => 'This table is used by the 1DmkIIN, 5D, 30D and 400D.',
+    FORMAT => 'int16u',
+    FIRST_ENTRY => 0,
+    WRITABLE => 1,
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
+    63 => { Name => 'WB_RGGBLevelsAsShot',      Format => 'int16u[4]' },
+    67 => 'ColorTempAsShot',
+    68 => { Name => 'WB_RGGBLevelsAuto',        Format => 'int16u[4]' },
+    72 => 'ColorTempAuto',
+    # not sure exactly what 'Measured' values mean...
+    73 => { Name => 'WB_RGGBLevelsMeasured',    Format => 'int16u[4]' },
+    77 => 'ColorTempMeasured',
+    78 => { Name => 'WB_RGGBLevelsDaylight',    Format => 'int16u[4]' },
+    82 => 'ColorTempDaylight',
+    83 => { Name => 'WB_RGGBLevelsShade',       Format => 'int16u[4]' },
+    87 => 'ColorTempShade',
+    88 => { Name => 'WB_RGGBLevelsCloudy',      Format => 'int16u[4]' },
+    92 => 'ColorTempCloudy',
+    93 => { Name => 'WB_RGGBLevelsTungsten',    Format => 'int16u[4]' },
+    97 => 'ColorTempTungsten',
+    98 => { Name => 'WB_RGGBLevelsFluorescent', Format => 'int16u[4]' },
+    102 => 'ColorTempFluorescent',
+    103 => { Name => 'WB_RGGBLevelsKelvin',     Format => 'int16u[4]' },
+    107 => 'ColorTempKelvin',
+    108 => { Name => 'WB_RGGBLevelsFlash',      Format => 'int16u[4]' },
+    112 => 'ColorTempFlash',
+    113 => { Name => 'WB_RGGBLevelsPC1',        Format => 'int16u[4]' },
+    117 => 'ColorTempPC1',
+    118 => { Name => 'WB_RGGBLevelsPC2',        Format => 'int16u[4]' },
+    122 => 'ColorTempPC2',
+    123 => { Name => 'WB_RGGBLevelsPC3',        Format => 'int16u[4]' },
+    127 => 'ColorTempPC3',
+    128 => { Name => 'WB_RGGBLevelsCustom',     Format => 'int16u[4]' },
+    132 => 'ColorTempCustom',
+);
+
+# Color information (MakerNotes tag 0x4003) (ref PH)
+%Image::ExifTool::Canon::ColorInfo = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
+    CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
+    WRITABLE => 1,
+    FORMAT => 'int16s',
+    FIRST_ENTRY => 1,
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
+    1 => {
+        Condition => '$$self{CameraMake} =~ /EOS-1D/',
+        Name => 'Saturation',
+    },
+    2 => 'ColorHue',
+    3 => {
+        Name => 'ColorSpace',
+        RawConv => '$val ? $val : undef', # ignore tag if zero
+        PrintConv => {
+            1 => 'sRGB',
+            2 => 'Adobe RGB',
+        },
+    },
+);
+
+# Flags information (MakerNotes tag 0xb0) (ref PH)
+%Image::ExifTool::Canon::Flags = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
+    CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
+    WRITABLE => 1,
+    FORMAT => 'int16s',
+    FIRST_ENTRY => 1,
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
+    1 => 'ModifiedParamFlag',
+);
+
+# Modified information (MakerNotes tag 0xb1) (ref PH)
+%Image::ExifTool::Canon::ModifiedInfo = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
+    CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
+    WRITABLE => 1,
+    FORMAT => 'int16s',
+    FIRST_ENTRY => 1,
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
+    1 => {
+        Name => 'ModifiedToneCurve',
+        PrintConv => {
+            0 => 'Standard',
+            1 => 'Manual',
+            2 => 'Custom',
+        },
+    },
+    2 => {
+        Name => 'ModifiedSharpness',
+        Notes => '1D and 5D only',
+        Condition => '$$self{CameraModel} =~ /\b(1D|5D)/',
+    },
+    3 => {
+        Name => 'ModifiedSharpnessFreq',
+        PrintConv => {
+            0 => 'n/a',
+            1 => 'Smoothest',
+            2 => 'Smooth',
+            3 => 'Standard',
+            4 => 'Sharp',
+            5 => 'Sharpest',
+        },
+    },
+    4 => 'ModifiedSensorRedLevel',
+    5 => 'ModifiedSensorBlueLevel',
+    6 => 'ModifiedWhiteBalanceRed',
+    7 => 'ModifiedWhiteBalanceBlue',
+    8 => {
+        Name => 'ModifiedWhiteBalance',
+        PrintConv => \%canonWhiteBalance,
+    },
+    9 => 'ModifiedColorTemp',
+    10 => {
+        Name => 'ModifiedPictureStyle',
+        PrintHex => 1,
+        PrintConv => \%pictureStyles,
+    },
+    11 => {
+        Name => 'ModifiedDigitalGain',
+        ValueConv => '$val / 10',
+        ValueConvInv => '$val * 10',
+    },
+);
+
+# canon composite tags
+%Image::ExifTool::Canon::Composite = (
+    GROUPS => { 2 => 'Camera' },
+    DriveMode => {
+        Require => {
+            0 => 'ContinuousDrive',
+            1 => 'Self-timer',
+        },
+        ValueConv => '$val[0] ? 0 : ($val[1] ? 1 : 2)',
+        PrintConv => {
+            0 => 'Continuous shooting',
+            1 => 'Self-timer Operation',
+            2 => 'Single-frame shooting',
+        },
+    },
+    Lens => {
+        Require => {
+            0 => 'ShortFocal',
+            1 => 'LongFocal',
+        },
+        ValueConv => '$val[0]',
+        PrintConv => 'Image::ExifTool::Canon::PrintFocalRange(@val)',
+    },
+    Lens35efl => {
+        Description => 'Lens',
+        Require => {
+            0 => 'ShortFocal',
+            1 => 'LongFocal',
+            3 => 'Lens',
+        },
+        Desire => {
+            2 => 'ScaleFactor35efl',
+        },
+        ValueConv => '$val[3] * ($val[2] ? $val[2] : 1)',
+        PrintConv => '$prt[3] . ($val[2] ? sprintf(" (35mm equivalent: %s)",Image::ExifTool::Canon::PrintFocalRange(@val)) : "")',
+    },
+    ShootingMode => {
+        Require => {
+            0 => 'CanonExposureMode',
+            1 => 'EasyMode',
+        },
+        ValueConv => '$val[0] ? $val[0] : $val[1] + 10',
+        PrintConv => '$val[0] ? $prt[0] : $prt[1]',
+    },
+    FlashType => {
+        Require => {
+            0 => 'FlashBits',
+        },
+        RawConv => '$val[0] ? $val : undef',
+        ValueConv => '$val[0]&(1<<14)? 1 : 0',
+        PrintConv => {
+            0 => 'Built-In Flash',
+            1 => 'External',
+        },
+    },
+    RedEyeReduction => {
+        Require => {
+            0 => 'CanonFlashMode',
+            1 => 'FlashBits',
+        },
+        RawConv => '$val[1] ? $val : undef',
+        ValueConv => '($val[0]==3 or $val[0]==4 or $val[0]==6) ? 1 : 0',
+        PrintConv => {
+            0 => 'Off',
+            1 => 'On',
+        },
+    },
+    # fudge to display simple Flash On/Off for Canon cameras only
+    FlashOn => {
+        Description => 'Flash',
+        Desire => {
+            0 => 'FlashBits',
+            1 => 'Flash',
+        },
+        ValueConv => 'Image::ExifTool::Canon::FlashOn(@val)',
+        PrintConv => {
+            0 => 'Off',
+            1 => 'On',
+        },
+    },
+    # same as FlashExposureComp, but undefined if no flash
+    ConditionalFEC => {
+        Description => 'Flash Exposure Compensation',
+        Require => {
+            0 => 'FlashExposureComp',
+            1 => 'FlashBits',
+        },
+        RawConv => '$val[1] ? $val : undef',
+        ValueConv => '$val[0]',
+        PrintConv => '$prt[0]',
+    },
+    # hack to assume 1st curtain unless we see otherwise
+    ShutterCurtainHack => {
+        Description => 'Shutter Curtain Sync',
+        Desire => {
+            0 => 'ShutterCurtainSync',
+        },
+        Require => {
+            1 => 'FlashBits',
+        },
+        RawConv => '$val[1] ? $val : undef',
+        ValueConv => 'defined($val[0]) ? $val[0] : 0',
+        PrintConv => {
+            0 => '1st-curtain sync',
+            1 => '2nd-curtain sync',
+        },
+    },
+    WB_RGGBLevels => {
+        Require => {
+            0 => 'Canon:WhiteBalance',
+        },
+        Desire => {
+            1 => 'WB_RGGBLevelsAsShot',
+            # indices of the following entries correspond to Canon:WhiteBalance + 2
+            2 => 'WB_RGGBLevelsAuto',
+            3 => 'WB_RGGBLevelsDaylight',
+            4 => 'WB_RGGBLevelsCloudy',
+            5 => 'WB_RGGBLevelsTungsten',
+            6 => 'WB_RGGBLevelsFluorescent',
+            7 => 'WB_RGGBLevelsFlash',
+            8 => 'WB_RGGBLevelsCustom',
+           10 => 'WB_RGGBLevelsShade',
+           11 => 'WB_RGGBLevelsKelvin',
+        },
+        ValueConv => '$val[1] ? $val[1] : $val[($val[0] || 0) + 2]',
+    },
+    ISO => {
+        Priority => 0,  # let EXIF:ISO take priority
+        Desire => {
+            0 => 'Canon:CameraISO',
+            1 => 'Canon:BaseISO',
+            2 => 'Canon:AutoISO',
+        },
+        Notes => 'use CameraISO if numerical, otherwise calculate as BaseISO * AutoISO / 100',
+        ValueConv => q{
+            return $val[0] if $val[0] and $val[0] =~ /^\d+$/;
+            return undef unless $val[1] and $val[2];
+            return $val[1] * $val[2] / 100;
+        },
+        PrintConv => 'sprintf("%.0f",$val)',
+    },
+    DigitalZoom => {
+        Require => {
+            0 => 'Canon:ZoomSourceWidth',
+            1 => 'Canon:ZoomTargetWidth',
+            2 => 'Canon:DigitalZoom',
+        },
+        RawConv => q{
+            return undef unless $val[2] == 3 and $val[0];
+            return $val[1] / $val[0];
+        },
+        PrintConv => 'sprintf("%.2fx",$val)',
+    }
+);
+
+# add our composite tags
+Image::ExifTool::AddCompositeTags('Image::ExifTool::Canon');
+
+
+#------------------------------------------------------------------------------
+# Validate first word of Canon binary data
+# Inputs: 0) data pointer, 1) offset, 2-N) list of valid values
+# Returns: true if data value is the same
+sub Validate($$@)
+{
+    my ($dataPt, $offset, @vals) = @_;
+    # the first 16-bit value is the length of the data in bytes
+    my $dataVal = Image::ExifTool::Get16u($dataPt, $offset);
+    my $val;
+    foreach $val (@vals) {
+        return 1 if $val == $dataVal;
+    }
+    return undef;
+}
+
+#------------------------------------------------------------------------------
+# Validate CanonAFInfo
+# Inputs: 0) data pointer, 1) offset, 2) size
+# Returns: true if data appears valid
+sub ValidateAFInfo($$$)
+{
+    my ($dataPt, $offset, $size) = @_;
+    return 0 if $size < 24; # must be at least 24 bytes long (PowerShot Pro1)
+    my $af = Get16u($dataPt, $offset);
+    return 0 if $af !~ /^(1|5|7|9|15|45|53)$/; # check NumAFPoints
+    my $w1 = Get16u($dataPt, $offset + 4);
+    my $h1 = Get16u($dataPt, $offset + 6);
+    return 0 unless $h1 and $w1;
+    my $f1 = $w1 / $h1;
+    # check for normal aspect ratio
+    return 1 if abs($f1 - 1.33) < 0.01 or abs($f1 - 1.67) < 0.01;
+    # ZoomBrowser can modify this for rotated images (ref Joshua Bixby)
+    return 1 if abs($f1 - 0.75) < 0.01 or abs($f1 - 0.60) < 0.01;
+    my $w2 = Get16u($dataPt, $offset + 8);
+    my $h2 = Get16u($dataPt, $offset + 10);
+    return 0 unless $h2 and $w2;
+    # compare aspect ratio with AF image size
+    # (but the Powershot AFImageHeight is odd, hence the test above)
+    return 0 if $w1 eq $h1;
+    my $f2 = $w2 / $h2;
+    return 1 if abs(1-$f1/$f2) < 0.01;
+    return 1 if abs(1-$f1*$f2) < 0.01;
+    return 0;
+}
+
+#------------------------------------------------------------------------------
+# Convert the CameraISO value
+# Inputs: 0) value, 1) set for inverse conversion
+sub CameraISO($;$)
+{
+    my ($val, $inv) = @_;
+    my $rtnVal;
+    my %isoLookup = (
+         0 => 'n/a',
+        14 => 'Auto High', #PH (S3IS)
+        15 => 'Auto',
+        16 => 50,
+        17 => 100,
+        18 => 200,
+        19 => 400,
+        20 => 800, #PH
+    );
+    if ($inv) {
+        $rtnVal = Image::ExifTool::ReverseLookup($val, \%isoLookup);
+        if (not defined $rtnVal and Image::ExifTool::IsInt($val)) {
+            $rtnVal = ($val & 0x3fff) | 0x4000;
+        }
+    } elsif ($val != 0x7fff) {
+        if ($val & 0x4000) {
+            $rtnVal = $val & 0x3fff;
+        } else {
+            $rtnVal = $isoLookup{$val} || "Unknown ($val)";
+        }
+    }
+    return $rtnVal;
+}
+
+#------------------------------------------------------------------------------
+# Print range of focal lengths
+# Inputs: 0) short focal, 1) long focal, 2) optional scaling factor
+sub PrintFocalRange(@)
+{
+    my ($short, $long, $scale) = @_;
+
+    $scale or $scale = 1;
+    if ($short == $long) {
+        return sprintf("%.1fmm", $short * $scale);
+    } else {
+        return sprintf("%.1f - %.1fmm", $short * $scale, $long * $scale);
+    }
+}
+
+#------------------------------------------------------------------------------
+# process a serial stream of binary data
+# Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
+# Returns: 1 on success
+# Notes: The tagID's for serial stream tags are consecutive indices beginning
+#        at 0, and the corresponding values must be contiguous in memory.
+#        "Unknown" tags must be used to skip padding or unknown values.
+sub ProcessSerialData($$$)
+{
+    my ($exifTool, $dirInfo, $tagTablePtr) = @_;
+    my $dataPt = $$dirInfo{DataPt};
+    my $offset = $$dirInfo{DirStart};
+    my $size = $$dirInfo{DirLen};
+    my $base = $$dirInfo{Base} || 0;
+    my $verbose = $exifTool->Options('Verbose');
+    my $dataPos = $$dirInfo{DataPos} || 0;
+
+    # temporarily set Unknown option so GetTagInfo() will return existing unknown tags
+    # (require to maintain serial data synchronization)
+    my $unknown = $exifTool->Options(Unknown => 1);
+    # but disable unknown tag generation (because processing ends when we run out of tags)
+    $$exifTool{NO_UNKNOWN} = 1;
+
+    $verbose and $exifTool->VerboseDir('SerialData', undef, $size);
+
+    # get default format ('int8u' unless specified)
+    my $defaultFormat = $$tagTablePtr{FORMAT} || 'int8u';
+
+    my ($index, %val);
+    my $pos = 0;
+    for ($index=0; $$tagTablePtr{$index} and $pos <= $size; ++$index) {
+        my $tagInfo = $exifTool->GetTagInfo($tagTablePtr, $index) or last;
+        my $format = $$tagInfo{Format};
+        my $count = 1;
+        if ($format) {
+            if ($format =~ /(.*)\[(.*)\]/) {
+                $format = $1;
+                $count = $2;
+                # evaluate count to allow count to be based on previous values
+                #### eval Format (%val, $size)
+                $count = eval $count;
+                $@ and warn("Format $$tagInfo{Name}: $@"), last;
+            } elsif ($format eq 'string') {
+                # allow string with no specified count to run to end of block
+                $count = ($size > $pos) ? $size - $pos : 0;
+            }
+        } else {
+            $format = $defaultFormat;
+        }
+        my $len = (Image::ExifTool::FormatSize($format) || 1) * $count;
+        last if $pos + $len > $size;
+        my $val = ReadValue($dataPt, $pos+$offset, $format, $count, $size-$pos);
+        last unless defined $val;
+        if ($verbose) {
+            $exifTool->VerboseInfo($index, $tagInfo,
+                Index  => $index,
+                Table  => $tagTablePtr,
+                Value  => $val,
+                DataPt => $dataPt,
+                Size   => $len,
+                Start  => $pos+$offset,
+                Addr   => $pos+$offset+$base+$dataPos,
+                Format => $format,
+                Count  => $count,
+            );
+        }
+        $val{$index} = $val;
+        if ($$tagInfo{SubDirectory}) {
+            my $subTablePtr = GetTagTable($tagInfo->{SubDirectory}->{TagTable});
+            my %dirInfo = (
+                DataPt => \$val,
+                DataPos => $dataPos + $pos,
+                DirStart => 0,
+                DirLen => length($val),
+            );
+            $exifTool->ProcessDirectory(\%dirInfo, $subTablePtr);
+        } elsif (not $$tagInfo{Unknown} or $unknown) {
+            # don't extract zero-length information
+            $exifTool->FoundTag($tagInfo, $val) if $count;
+        }
+        $pos += $len;
+    }
+    $exifTool->Options(Unknown => $unknown);    # restore Unknown option
+    delete $$exifTool{NO_UNKNOWN};
+    return 1;
+}
+
+#------------------------------------------------------------------------------
+# Print 1D AF points
+# Inputs: 0) value to convert
+# Focus point pattern:
+#          A1  A2  A3  A4  A5  A6  A7
+#    B1  B2  B3  B4  B5  B6  B7  B8  B9  B10
+#  C1  C2  C3  C4  C5  C6  C7  C9  C9  C10  C11
+#    D1  D2  D3  D4  D5  D6  D7  D8  D9  D10
+#          E1  E2  E3  E4  E5  E6  E7
+sub PrintAFPoints1D($)
+{
+    my $val = shift;
+    return 'Unknown' unless length $val == 8;
+    # these are the x/y positions of each bit in the AF point mask
+    # (x is upper 3 bits / y is lower 5 bits)
+    my @focusPts = (0,0,
+              0x04,0x06,0x08,0x0a,0x0c,0x0e,0x10,         0,0,
+      0x21,0x23,0x25,0x27,0x29,0x2b,0x2d,0x2f,0x31,0x33,
+    0x40,0x42,0x44,0x46,0x48,0x4a,0x4c,0x4d,0x50,0x52,0x54,
+      0x61,0x63,0x65,0x67,0x69,0x6b,0x6d,0x6f,0x71,0x73,  0,0,
+              0x84,0x86,0x88,0x8a,0x8c,0x8e,0x90,   0,0,0,0,0
+    );
+    my $focus = unpack('C',$val);
+    my @bits = split //, unpack('b*',substr($val,1));
+    my @rows = split //, '  AAAAAAA  BBBBBBBBBBCCCCCCCCCCCDDDDDDDDDD  EEEEEEE     ';
+    my ($focusing, $focusPt, @points);
+    my $lastRow = '';
+    my $col = 0;
+    foreach $focusPt (@focusPts) {
+        my $row = shift @rows;
+        $col = ($row eq $lastRow) ? $col + 1 : 1;
+        $lastRow = $row;
+        $focusing = "$row$col" if $focus eq $focusPt;
+        push @points, "$row$col" if shift @bits;
+    }
+    $focusing or $focusing = ($focus eq 0xff) ? 'Auto' : sprintf('Unknown (0x%.2x)',$focus);
+    return "$focusing (" . join(',',@points) . ')';
+}
+
+#------------------------------------------------------------------------------
+# Decide whether flash was on or off
+sub FlashOn(@)
+{
+    my @val = @_;
+
+    if (defined $val[0]) {
+        return $val[0] ? 1 : 0;
+    }
+    if (defined $val[1]) {
+        return $val[1]&0x07 ? 1 : 0;
+    }
+    return undef;
+}
+
+#------------------------------------------------------------------------------
+# Convert Canon hex-based EV (modulo 0x20) to real number
+# Inputs: 0) value to convert
+# ie) 0x00 -> 0
+#     0x0c -> 0.33333
+#     0x10 -> 0.5
+#     0x14 -> 0.66666
+#     0x20 -> 1   ...  etc
+sub CanonEv($)
+{
+    my $val = shift;
+    my $sign;
+    # temporarily make the number positive
+    if ($val < 0) {
+        $val = -$val;
+        $sign = -1;
+    } else {
+        $sign = 1;
+    }
+    my $frac = $val & 0x1f;
+    $val -= $frac;      # remove fraction
+    # Convert 1/3 and 2/3 codes
+    if ($frac == 0x0c) {
+        $frac = 0x20 / 3;
+    } elsif ($frac == 0x14) {
+        $frac = 0x40 / 3;
+    }
+    return $sign * ($val + $frac) / 0x20;
+}
+
+#------------------------------------------------------------------------------
+# Convert number to Canon hex-based EV (modulo 0x20)
+# Inputs: 0) number
+# Returns: Canon EV code
+sub CanonEvInv($)
+{
+    my $num = shift;
+    my $sign;
+    # temporarily make the number positive
+    if ($num < 0) {
+        $num = -$num;
+        $sign = -1;
+    } else {
+        $sign = 1;
+    }
+    my $val = int($num);
+    my $frac = $num - $val;
+    if (abs($frac - 0.33) < 0.05) {
+        $frac = 0x0c
+    } elsif (abs($frac - 0.67) < 0.05) {
+        $frac = 0x14;
+    } else {
+        $frac = int($frac * 0x20 + 0.5);
+    }
+    return $sign * ($val * 0x20 + $frac);
+}
+
+#------------------------------------------------------------------------------
+# Write Canon maker notes
+# Inputs: 0) ExifTool object reference, 1) dirInfo ref, 2) tag table ref
+# Returns: data block (may be empty if no Exif data) or undef on error
+sub WriteCanon($$$)
+{
+    my ($exifTool, $dirInfo, $tagTablePtr) = @_;
+    $exifTool or return 1;    # allow dummy access to autoload this package
+    my $dirData = Image::ExifTool::Exif::WriteExif($exifTool, $dirInfo, $tagTablePtr);
+    # add trailer which is written by some Canon models (it's a TIFF header)
+    if (defined $dirData and length $dirData and $$dirInfo{Fixup}) {
+        $dirData .= GetByteOrder() . Set16u(42) . Set32u(0);
+        $dirInfo->{Fixup}->AddFixup(length($dirData) - 4);
+    }
+    return $dirData;
+}
+
+#------------------------------------------------------------------------------
+1;  # end
+
+__END__
+
+=head1 NAME
+
+Image::ExifTool::Canon - Canon EXIF maker notes tags
+
+=head1 SYNOPSIS
+
+This module is loaded automatically by Image::ExifTool when required.
+
+=head1 DESCRIPTION
+
+This module contains definitions required by Image::ExifTool to interpret
+Canon maker notes in EXIF information.
+
+=head1 AUTHOR
+
+Copyright 2003-2007, Phil Harvey (phil at owl.phy.queensu.ca)
+
+This library is free software; you can redistribute it and/or modify it
+under the same terms as Perl itself.
+
+=head1 REFERENCES
+
+=over 4
+
+=item L<http://park2.wakwak.com/~tsuruzoh/Computer/Digicams/exif-e.html>
+
+=item L<http://www.wonderland.org/crw/>
+
+=item L<http://www.cybercom.net/~dcoffin/dcraw/>
+
+=item L<http://homepage3.nifty.com/kamisaka/makernote/makernote_canon.htm>
+
+=item (...plus lots of testing with my 300D and my daughter's A570IS!)
+
+=back
+
+=head1 ACKNOWLEDGEMENTS
+
+Thanks Michael Rommel and Daniel Pittman for information they provided about
+the Digital Ixus and PowerShot S70 cameras, Juha Eskelinen and Emil Sit for
+figuring out the 20D and 30D FileNumber, Denny Priebe for figuring out a
+couple of 1D tags, and Michael Tiemann and Rainer Honle for decoding a
+number of new tags.  Also thanks to everyone who made contributions to the
+LensType lookup list or the meanings of other tag values.
+
+=head1 SEE ALSO
+
+L<Image::ExifTool::TagNames/Canon Tags>,
+L<Image::ExifTool(3pm)|Image::ExifTool>
+
+=cut
Index: /gsdl/trunk/perllib/cpan/Image/ExifTool/CanonCustom.pm
===================================================================
--- /gsdl/trunk/perllib/cpan/Image/ExifTool/CanonCustom.pm	(revision 16842)
+++ /gsdl/trunk/perllib/cpan/Image/ExifTool/CanonCustom.pm	(revision 16842)
@@ -0,0 +1,2058 @@
+#------------------------------------------------------------------------------
+# File:         CanonCustom.pm
+#
+# Description:  Read and write Canon Custom functions
+#
+# Revisions:    11/25/2003  - P. Harvey Created
+#
+# References:   1) http://park2.wakwak.com/~tsuruzoh/Computer/Digicams/exif-e.html
+#               2) Christian Koller private communication (tests with the 20D)
+#               3) Rainer Honle private communication (tests with the 5D)
+#------------------------------------------------------------------------------
+
+package Image::ExifTool::CanonCustom;
+
+use strict;
+use vars qw($VERSION);
+use Image::ExifTool qw(:DataAccess);
+use Image::ExifTool::Canon;
+use Image::ExifTool::Exif;
+
+$VERSION = '1.17';
+
+sub ProcessCanonCustom($$$);
+sub ProcessCanonCustom2($$$);
+sub WriteCanonCustom($$$);
+sub WriteCanonCustom2($$$);
+sub CheckCanonCustom($$$);
+sub ConvertPFn($);
+sub ConvertPFnInv($);
+
+my %onOff = ( 0 => 'On', 1 => 'Off' );
+my %offOn = ( 0 => 'Off', 1 => 'On' );
+my %disableEnable = ( 0 => 'Disable', 1 => 'Enable' );
+my %enableDisable = ( 0 => 'Enable', 1 => 'Disable' );
+my %convPFn = ( PrintConv => \&ConvertPfn, PrintConvInv => \&ConvertPfnInv );
+
+#------------------------------------------------------------------------------
+# Custom functions for the 1D
+# CanonCustom (keys are custom function number)
+%Image::ExifTool::CanonCustom::Functions1D = (
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
+    PROCESS_PROC => \&ProcessCanonCustom,
+    WRITE_PROC => \&WriteCanonCustom,
+    CHECK_PROC => \&CheckCanonCustom,
+    WRITABLE => 'int8u',
+    NOTES => q{
+        These custom functions are used by all 1D models up to but not including the
+        Mark III.
+    },
+    0 => {
+        Name => 'FocusingScreen',
+        PrintConv => {
+            0 => 'Ec-N, R',
+            1 => 'Ec-A,B,C,CII,CIII,D,H,I,L',
+        },
+    },
+    1 => {
+        Name => 'FinderDisplayDuringExposure',
+        PrintConv => \%offOn,
+    },
+    2 => {
+        Name => 'ShutterReleaseNoCFCard',
+        Description => 'Shutter Release W/O CF Card',
+        PrintConv => {
+            0 => 'Yes',
+            1 => 'No',
+        },
+    },
+    3 => {
+        Name => 'ISOSpeedExpansion',
+        Description => 'ISO Speed Expansion',
+        PrintConv => {
+            0 => 'No',
+            1 => 'Yes',
+        },
+    },
+    4 => {
+        Name => 'ShutterAELButton',
+        Description => 'Shutter Button/AEL Button',
+        PrintConv => {
+            0 => 'AF/AE lock stop',
+            1 => 'AE lock/AF',
+            2 => 'AF/AF lock, No AE lock',
+            3 => 'AE/AF, No AE lock',
+        },
+    },
+    5 => {
+        Name => 'ManualTv',
+        Description => 'Manual Tv/Av For M',
+        PrintConv => {
+            0 => 'Tv=Main/Av=Control',
+            1 => 'Tv=Control/Av=Main',
+            2 => 'Tv=Main/Av=Main w/o lens',
+            3 => 'Tv=Control/Av=Main w/o lens',
+        },
+    },
+    6 => {
+        Name => 'ExposureLevelIncrements',
+        PrintConv => {
+            0 => '1/3-stop set, 1/3-stop comp.',
+            1 => '1-stop set, 1/3-stop comp.',
+            2 => '1/2-stop set, 1/2-stop comp.',
+        },
+    },
+    7 => {
+        Name => 'USMLensElectronicMF',
+        PrintConv => {
+            0 => 'Turns on after one-shot AF',
+            1 => 'Turns off after one-shot AF',
+            2 => 'Always turned off',
+        },
+    },
+    8 => {
+        Name => 'LCDPanels',
+        Description => 'Top/Back LCD Panels',
+        PrintConv => {
+            0 => 'Remain. shots/File no.',
+            1 => 'ISO/Remain. shots',
+            2 => 'ISO/File no.',
+            3 => 'Shots in folder/Remain. shots',
+        },
+    },
+    9 => {
+        Name => 'AEBSequence',
+        PrintConv => {
+            0 => '0,-,+/Enabled',
+            1 => '0,-,+/Disabled',
+            2 => '-,0,+/Enabled',
+            3 => '-,0,+/Disabled',
+        },
+    },
+    10 => {
+        Name => 'AFPointIllumination',
+        PrintConv => {
+            0 => 'On',
+            1 => 'Off',
+            2 => 'On without dimming',
+            3 => 'Brighter',
+        },
+    },
+    11 => {
+        Name => 'AFPointSelection',
+        PrintConv => {
+            0 => 'H=AF+Main/V=AF+Command',
+            1 => 'H=Comp+Main/V=Comp+Command',
+            2 => 'H=Command only/V=Assist+Main',
+            3 => 'H=FEL+Main/V=FEL+Command',
+        },
+    },
+    12 => {
+        Name => 'MirrorLockup',
+        PrintConv => \%disableEnable,
+    },
+    13 => {
+        Name => 'AFPointSpotMetering',
+        Description => 'No. AF Points/Spot Metering',
+        PrintConv => {
+            0 => '45/Center AF point',
+            1 => '11/Active AF point',
+            2 => '11/Center AF point',
+            3 => '9/Active AF point',
+        },
+    },
+    14 => {
+        Name => 'FillFlashAutoReduction',
+        PrintConv => \%enableDisable,
+    },
+    15 => {
+        Name => 'ShutterCurtainSync',
+        PrintConv => {
+            0 => '1st-curtain sync',
+            1 => '2nd-curtain sync',
+        },
+    },
+    16 => {
+        Name => 'SafetyShiftInAvOrTv',
+        PrintConv => \%disableEnable,
+    },
+    17 => {
+        Name => 'AFPointActivationArea',
+        PrintConv => {
+            0 => 'Single AF point',
+            1 => 'Expanded (TTL. of 7 AF points)',
+            2 => 'Automatic expanded (max. 13)',
+        },
+    },
+    18 => {
+        Name => 'SwitchToRegisteredAFPoint',
+        PrintConv => {
+            0 => 'Assist + AF',
+            1 => 'Assist',
+            2 => 'Only while pressing assist',
+        },
+    },
+    19 => {
+        Name => 'LensAFStopButton',
+        PrintConv => {
+            0 => 'AF stop',
+            1 => 'AF start',
+            2 => 'AE lock while metering',
+            3 => 'AF point: M -> Auto / Auto -> Ctr.',
+            4 => 'AF mode: ONE SHOT <-> AI SERVO',
+            5 => 'IS start',
+        },
+    },
+    20 => {
+        Name => 'AIServoTrackingSensitivity',
+        PrintConv => {
+            0 => 'Standard',
+            1 => 'Slow',
+            2 => 'Moderately slow',
+            3 => 'Moderately fast',
+            4 => 'Fast',
+        },
+    },
+);
+
+# Custom functions for the 5D (ref 3)
+%Image::ExifTool::CanonCustom::Functions5D = (
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
+    PROCESS_PROC => \&ProcessCanonCustom,
+    WRITE_PROC => \&WriteCanonCustom,
+    CHECK_PROC => \&CheckCanonCustom,
+    WRITABLE => 'int8u',
+    0 => {
+        Name => 'FocusingScreen',
+        PrintConv => {
+            0 => 'Ee-A',
+            1 => 'Ee-D',
+            2 => 'Ee-S',
+        },
+    },
+    1 => {
+        Name => 'SetFunctionWhenShooting',
+        PrintConv => {
+            0 => 'Default (no function)',
+            1 => 'Change quality',
+            2 => 'Change Parameters',
+            3 => 'Menu display',
+            4 => 'Image replay',
+        },
+    },
+    2 => {
+        Name => 'LongExposureNoiseReduction',
+        PrintConv => {
+            0 => 'Off',
+            1 => 'Auto',
+            2 => 'On',
+        },
+    },
+    3 => {
+        Name => 'FlashSyncSpeedAv',
+        PrintConv => {
+            0 => 'Auto',
+            1 => '1/200 Fixed',
+        },
+    },
+    4 => {
+        Name => 'Shutter-AELock',
+        PrintConv => {
+            0 => 'AF/AE lock',
+            1 => 'AE lock/AF',
+            2 => 'AF/AF lock, No AE lock',
+            3 => 'AE/AF, No AE lock',
+        },
+    },
+    5 => {
+        Name => 'AFAssistBeam',
+        PrintConv => {
+            0 => 'Emits',
+            1 => 'Does not emit',
+        },
+    },
+    6 => {
+        Name => 'ExposureLevelIncrements',
+        PrintConv => {
+            0 => '1/3 Stop',
+            1 => '1/2 Stop',
+        },
+    },
+    7 => {
+        Name => 'FlashFiring',
+        PrintConv => {
+            0 => 'Fires',
+            1 => 'Does not fire',
+        },
+    },
+    8 => {
+        Name => 'ISOExpansion',
+        PrintConv => \%offOn,
+    },
+    9 => {
+        Name => 'AEBSequence',
+        PrintConv => {
+            0 => '0,-,+/Enabled',
+            1 => '0,-,+/Disabled',
+            2 => '-,0,+/Enabled',
+            3 => '-,0,+/Disabled',
+        },
+    },
+    10 => {
+        Name => 'SuperimposedDisplay',
+        PrintConv => \%onOff,
+    },
+    11 => {
+        Name => 'MenuButtonDisplayPosition',
+        PrintConv => {
+            0 => 'Previous (top if power off)',
+            1 => 'Previous',
+            2 => 'Top',
+        },
+    },
+    12 => {
+        Name => 'MirrorLockup',
+        PrintConv => \%disableEnable,
+    },
+    13 => {
+        Name => 'AFPointSelectionMethod',
+        PrintConv => {
+            0 => 'Normal',
+            1 => 'Multi-controller direct',
+            2 => 'Quick Control Dial direct',
+        },
+    },
+    14 => {
+        Name => 'ETTLII',
+        Description => 'E-TTL II',
+        PrintConv => {
+            0 => 'Evaluative',
+            1 => 'Average',
+        },
+    },
+    15 => {
+        Name => 'ShutterCurtainSync',
+        PrintConv => {
+            0 => '1st-curtain sync',
+            1 => '2nd-curtain sync',
+        },
+    },
+    16 => {
+        Name => 'SafetyShiftInAvOrTv',
+        PrintConv => \%disableEnable,
+    },
+    17 => {
+        Name => 'AFPointActivationArea',
+        PrintConv => {
+            0 => 'Standard',
+            1 => 'Expanded',
+        },
+    },
+    18 => {
+        Name => 'LCDDisplayReturnToShoot',
+        PrintConv => {
+            0 => 'With Shutter Button only',
+            1 => 'Also with * etc.',
+        },
+    },
+    19 => {
+        Name => 'LensAFStopButton',
+        PrintConv => {
+            0 => 'AF stop',
+            1 => 'AF start',
+            2 => 'AE lock while metering',
+            3 => 'AF point: M -> Auto / Auto -> Ctr.',
+            4 => 'ONE SHOT <-> AI SERVO',
+            5 => 'IS start',
+        },
+    },
+    20 => {
+        Name => 'AddOriginalDecisionData',
+        PrintConv => \%offOn,
+    },
+);
+
+# Custom functions for 10D
+%Image::ExifTool::CanonCustom::Functions10D = (
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
+    PROCESS_PROC => \&ProcessCanonCustom,
+    WRITE_PROC => \&WriteCanonCustom,
+    CHECK_PROC => \&CheckCanonCustom,
+    WRITABLE => 'int8u',
+    1 => {
+        Name => 'SetButtonFunction',
+        PrintConv => {
+            0 => 'Not assigned',
+            1 => 'Change quality',
+            2 => 'Change parameters',
+            3 => 'Menu display',
+            4 => 'Image replay',
+        },
+    },
+    2 => {
+        Name => 'ShutterReleaseNoCFCard',
+        Description => 'Shutter Release W/O CF Card',
+        PrintConv => {
+            0 => 'Yes',
+            1 => 'No',
+        },
+    },
+    3 => {
+        Name => 'FlashSyncSpeedAv',
+        PrintConv => {
+            0 => 'Auto',
+            1 => '1/200 Fixed',
+        },
+    },
+    4 => {
+        Name => 'Shutter-AELock',
+        PrintConv => {
+            0 => 'AF/AE lock',
+            1 => 'AE lock/AF',
+            2 => 'AF/AF lock, No AE lock',
+            3 => 'AE/AF, No AE lock',
+        },
+    },
+    5 => {
+        Name => 'AFAssist',
+        Description => 'AF Assist/Flash Firing',
+        PrintConv => {
+            0 => 'Emits/Fires',
+            1 => 'Does not emit/Fires',
+            2 => 'Only ext. flash emits/Fires',
+            3 => 'Emits/Does not fire',
+        },
+    },
+    6 => {
+        Name => 'ExposureLevelIncrements',
+        PrintConv => {
+            0 => '1/2 Stop',
+            1 => '1/3 Stop',
+        },
+    },
+    7 => {
+        Name => 'AFPointRegistration',
+        PrintConv => {
+            0 => 'Center',
+            1 => 'Bottom',
+            2 => 'Right',
+            3 => 'Extreme Right',
+            4 => 'Automatic',
+            5 => 'Extreme Left',
+            6 => 'Left',
+            7 => 'Top',
+        },
+    },
+    8 => {
+        Name => 'RawAndJpgRecording',
+        PrintConv => {
+            0 => 'RAW+Small/Normal',
+            1 => 'RAW+Small/Fine',
+            2 => 'RAW+Medium/Normal',
+            3 => 'RAW+Medium/Fine',
+            4 => 'RAW+Large/Normal',
+            5 => 'RAW+Large/Fine',
+        },
+    },
+    9 => {
+        Name => 'AEBSequence',
+        PrintConv => {
+            0 => '0,-,+/Enabled',
+            1 => '0,-,+/Disabled',
+            2 => '-,0,+/Enabled',
+            3 => '-,0,+/Disabled',
+        },
+    },
+    10 => {
+        Name => 'SuperimposedDisplay',
+        PrintConv => \%onOff,
+    },
+    11 => {
+        Name => 'MenuButtonDisplayPosition',
+        PrintConv => {
+            0 => 'Previous (top if power off)',
+            1 => 'Previous',
+            2 => 'Top',
+        },
+    },
+    12 => {
+        Name => 'MirrorLockup',
+        PrintConv => \%disableEnable,
+    },
+    13 => {
+        Name => 'AssistButtonFunction',
+        PrintConv => {
+            0 => 'Normal',
+            1 => 'Select Home Position',
+            2 => 'Select HP (while pressing)',
+            3 => 'Av+/- (AF point by QCD)',
+            4 => 'FE lock',
+        },
+    },
+    14 => {
+        Name => 'FillFlashAutoReduction',
+        PrintConv => \%enableDisable,
+    },
+    15 => {
+        Name => 'ShutterCurtainSync',
+        PrintConv => {
+            0 => '1st-curtain sync',
+            1 => '2nd-curtain sync',
+        },
+    },
+    16 => {
+        Name => 'SafetyShiftInAvOrTv',
+        PrintConv => \%disableEnable,
+    },
+    17 => {
+        Name => 'LensAFStopButton',
+        PrintConv => {
+            0 => 'AF Stop',
+            1 => 'Operate AF',
+            2 => 'Lock AE and start timer',
+        },
+    },
+);
+
+# Custom functions for the 20D (ref 2)
+%Image::ExifTool::CanonCustom::Functions20D = (
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
+    PROCESS_PROC => \&ProcessCanonCustom,
+    WRITE_PROC => \&WriteCanonCustom,
+    CHECK_PROC => \&CheckCanonCustom,
+    WRITABLE => 'int8u',
+    0 => {
+        Name => 'SetFunctionWhenShooting',
+        PrintConv => {
+            0 => 'Default (no function)',
+            1 => 'Change quality',
+            2 => 'Change Parameters',
+            3 => 'Menu display',
+            4 => 'Image replay',
+        },
+    },
+    1 => {
+        Name => 'LongExposureNoiseReduction',
+        PrintConv => \%offOn,
+    },
+    2 => {
+        Name => 'FlashSyncSpeedAv',
+        PrintConv => {
+            0 => 'Auto',
+            1 => '1/250 Fixed',
+        },
+    },
+    3 => {
+        Name => 'Shutter-AELock',
+        PrintConv => {
+            0 => 'AF/AE lock',
+            1 => 'AE lock/AF',
+            2 => 'AF/AF lock, No AE lock',
+            3 => 'AE/AF, No AE lock',
+        },
+    },
+    4 => {
+        Name => 'AFAssistBeam',
+        PrintConv => {
+            0 => 'Emits',
+            1 => 'Does not emit',
+            2 => 'Only ext. flash emits',
+        },
+    },
+    5 => {
+        Name => 'ExposureLevelIncrements',
+        PrintConv => {
+            0 => '1/3 Stop',
+            1 => '1/2 Stop',
+        },
+    },
+    6 => {
+        Name => 'FlashFiring',
+        PrintConv => {
+            0 => 'Fires',
+            1 => 'Does not fire',
+        },
+    },
+    7 => {
+        Name => 'ISOExpansion',
+        PrintConv => \%offOn,
+    },
+    8 => {
+        Name => 'AEBSequence',
+        PrintConv => {
+            0 => '0,-,+/Enabled',
+            1 => '0,-,+/Disabled',
+            2 => '-,0,+/Enabled',
+            3 => '-,0,+/Disabled',
+        },
+    },
+    9 => {
+        Name => 'SuperimposedDisplay',
+        PrintConv => \%onOff,
+    },
+    10 => {
+        Name => 'MenuButtonDisplayPosition',
+        PrintConv => {
+            0 => 'Previous (top if power off)',
+            1 => 'Previous',
+            2 => 'Top',
+        },
+    },
+    11 => {
+        Name => 'MirrorLockup',
+        PrintConv => \%disableEnable,
+    },
+    12 => {
+        Name => 'AFPointSelectionMethod',
+        PrintConv => {
+            0 => 'Normal',
+            1 => 'Multi-controller direct',
+            2 => 'Quick Control Dial direct',
+        },
+    },
+    13 => {
+        Name => 'ETTLII',
+        Description => 'E-TTL II',
+        PrintConv => {
+            0 => 'Evaluative',
+            1 => 'Average',
+        },
+    },
+    14 => {
+        Name => 'ShutterCurtainSync',
+        PrintConv => {
+            0 => '1st-curtain sync',
+            1 => '2nd-curtain sync',
+        },
+    },
+    15 => {
+        Name => 'SafetyShiftInAvOrTv',
+        PrintConv => \%disableEnable,
+    },
+    16 => {
+        Name => 'LensAFStopButton',
+        PrintConv => {
+            0 => 'AF stop',
+            1 => 'AF start',
+            2 => 'AE lock while metering',
+            3 => 'AF point: M -> Auto / Auto -> Ctr.',
+            4 => 'ONE SHOT <-> AI SERVO',
+            5 => 'IS start',
+        },
+    },
+    17 => {
+        Name => 'AddOriginalDecisionData',
+        PrintConv => \%offOn,
+    },
+);
+
+# Custom functions for the 30D (PH)
+%Image::ExifTool::CanonCustom::Functions30D = (
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
+    PROCESS_PROC => \&ProcessCanonCustom,
+    WRITE_PROC => \&WriteCanonCustom,
+    CHECK_PROC => \&CheckCanonCustom,
+    WRITABLE => 'int8u',
+    1 => {
+        Name => 'SetFunctionWhenShooting',
+        PrintConv => {
+            0 => 'Default (no function)',
+            1 => 'Change quality',
+            2 => 'Change Picture Style',
+            3 => 'Menu display',
+            4 => 'Image replay',
+        },
+    },
+    2 => {
+        Name => 'LongExposureNoiseReduction',
+        PrintConv => {
+            0 => 'Off',
+            1 => 'Auto',
+            2 => 'On',
+        },
+    },
+    3 => {
+        Name => 'FlashSyncSpeedAv',
+        PrintConv => {
+            0 => 'Auto',
+            1 => '1/250 Fixed',
+        },
+    },
+    4 => {
+        Name => 'Shutter-AELock',
+        PrintConv => {
+            0 => 'AF/AE lock',
+            1 => 'AE lock/AF',
+            2 => 'AF/AF lock, No AE lock',
+            3 => 'AE/AF, No AE lock',
+        },
+    },
+    5 => {
+        Name => 'AFAssistBeam',
+        PrintConv => {
+            0 => 'Emits',
+            1 => 'Does not emit',
+            2 => 'Only ext. flash emits',
+        },
+    },
+    6 => {
+        Name => 'ExposureLevelIncrements',
+        PrintConv => {
+            0 => '1/3 Stop',
+            1 => '1/2 Stop',
+        },
+    },
+    7 => {
+        Name => 'FlashFiring',
+        PrintConv => {
+            0 => 'Fires',
+            1 => 'Does not fire',
+        },
+    },
+    8 => {
+        Name => 'ISOExpansion',
+        PrintConv => \%offOn,
+    },
+    9 => {
+        Name => 'AEBSequence',
+        PrintConv => {
+            0 => '0,-,+/Enabled',
+            1 => '0,-,+/Disabled',
+            2 => '-,0,+/Enabled',
+            3 => '-,0,+/Disabled',
+        },
+    },
+    10 => {
+        Name => 'SuperimposedDisplay',
+        PrintConv => \%onOff,
+    },
+    11 => {
+        Name => 'MenuButtonDisplayPosition',
+        PrintConv => {
+            0 => 'Previous (top if power off)',
+            1 => 'Previous',
+            2 => 'Top',
+        },
+    },
+    12 => {
+        Name => 'MirrorLockup',
+        PrintConv => \%disableEnable,
+    },
+    13 => {
+        Name => 'AFPointSelectionMethod',
+        PrintConv => {
+            0 => 'Normal',
+            1 => 'Multi-controller direct',
+            2 => 'Quick Control Dial direct',
+        },
+    },
+    14 => {
+        Name => 'ETTLII',
+        Description => 'E-TTL II',
+        PrintConv => {
+            0 => 'Evaluative',
+            1 => 'Average',
+        },
+    },
+    15 => {
+        Name => 'ShutterCurtainSync',
+        PrintConv => {
+            0 => '1st-curtain sync',
+            1 => '2nd-curtain sync',
+        },
+    },
+    16 => {
+        Name => 'SafetyShiftInAvOrTv',
+        PrintConv => \%disableEnable,
+    },
+    17 => {
+        Name => 'MagnifiedView',
+        PrintConv => {
+            0 => 'Image playback only',
+            1 => 'Image review and playback',
+        },
+    },
+    18 => {
+        Name => 'LensAFStopButton',
+        PrintConv => {
+            0 => 'AF stop',
+            1 => 'AF start',
+            2 => 'AE lock while metering',
+            3 => 'AF point: M -> Auto / Auto -> Ctr.',
+            4 => 'ONE SHOT <-> AI SERVO',
+            5 => 'IS start',
+        },
+    },
+    19 => {
+        Name => 'AddOriginalDecisionData',
+        PrintConv => \%offOn,
+    },
+);
+
+# Custom functions for the 350D (PH)
+%Image::ExifTool::CanonCustom::Functions350D = (
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
+    PROCESS_PROC => \&ProcessCanonCustom,
+    WRITE_PROC => \&WriteCanonCustom,
+    CHECK_PROC => \&CheckCanonCustom,
+    WRITABLE => 'int8u',
+    0 => {
+        Name => 'SetButtonCrossKeysFunc',
+        PrintConv => {
+            0 => 'Normal',
+            1 => 'Set: Quality',
+            2 => 'Set: Parameter',
+            3 => 'Set: Playback',
+            4 => 'Cross keys: AF point select',
+        },
+    },
+    1 => {
+        Name => 'LongExposureNoiseReduction',
+        PrintConv => \%offOn,
+    },
+    2 => {
+        Name => 'FlashSyncSpeedAv',
+        PrintConv => {
+            0 => 'Auto',
+            1 => '1/200 Fixed',
+        },
+    },
+    3 => {
+        Name => 'Shutter-AELock',
+        PrintConv => {
+            0 => 'AF/AE lock',
+            1 => 'AE lock/AF',
+            2 => 'AF/AF lock, No AE lock',
+            3 => 'AE/AF, No AE lock',
+        },
+    },
+    4 => {
+        Name => 'AFAssistBeam',
+        PrintConv => {
+            0 => 'Emits',
+            1 => 'Does not emit',
+            2 => 'Only ext. flash emits',
+        },
+    },
+    5 => {
+        Name => 'ExposureLevelIncrements',
+        PrintConv => {
+            0 => '1/3 Stop',
+            1 => '1/2 Stop',
+        },
+    },
+    6 => {
+        Name => 'MirrorLockup',
+        PrintConv => \%disableEnable,
+    },
+    7 => {
+        Name => 'ETTLII',
+        Description => 'E-TTL II',
+        PrintConv => {
+            0 => 'Evaluative',
+            1 => 'Average',
+        },
+    },
+    8 => {
+        Name => 'ShutterCurtainSync',
+        PrintConv => {
+            0 => '1st-curtain sync',
+            1 => '2nd-curtain sync',
+        },
+    },
+);
+
+# Custom functions for the 400D (PH)
+%Image::ExifTool::CanonCustom::Functions400D = (
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
+    PROCESS_PROC => \&ProcessCanonCustom,
+    WRITE_PROC => \&WriteCanonCustom,
+    CHECK_PROC => \&CheckCanonCustom,
+    WRITABLE => 'int8u',
+    0 => {
+        Name => 'SetButtonCrossKeysFunc',
+        PrintConv => {
+            0 => 'Set: Picture Style',
+            1 => 'Set: Quality',
+            2 => 'Set: Flash Exposure Comp',
+            3 => 'Set: Playback',
+            4 => 'Cross keys: AF point select',
+        },
+    },
+    1 => {
+        Name => 'LongExposureNoiseReduction',
+        PrintConv => {
+            0 => 'Off',
+            1 => 'Auto',
+            2 => 'On',
+        },
+    },
+    2 => {
+        Name => 'FlashSyncSpeedAv',
+        PrintConv => {
+            0 => 'Auto',
+            1 => '1/200 Fixed',
+        },
+    },
+    3 => {
+        Name => 'Shutter-AELock',
+        PrintConv => {
+            0 => 'AF/AE lock',
+            1 => 'AE lock/AF',
+            2 => 'AF/AF lock, No AE lock',
+            3 => 'AE/AF, No AE lock',
+        },
+    },
+    4 => {
+        Name => 'AFAssistBeam',
+        PrintConv => {
+            0 => 'Emits',
+            1 => 'Does not emit',
+            2 => 'Only ext. flash emits',
+        },
+    },
+    5 => {
+        Name => 'ExposureLevelIncrements',
+        PrintConv => {
+            0 => '1/3 Stop',
+            1 => '1/2 Stop',
+        },
+    },
+    6 => {
+        Name => 'MirrorLockup',
+        PrintConv => \%disableEnable,
+    },
+    7 => {
+        Name => 'ETTLII',
+        Description => 'E-TTL II',
+        PrintConv => {
+            0 => 'Evaluative',
+            1 => 'Average',
+        },
+    },
+    8 => {
+        Name => 'ShutterCurtainSync',
+        PrintConv => {
+            0 => '1st-curtain sync',
+            1 => '2nd-curtain sync',
+        },
+    },
+    9 => {
+        Name => 'MagnifiedView',
+        PrintConv => {
+            0 => 'Image playback only',
+            1 => 'Image review and playback',
+        },
+    },
+    10 => {
+        Name => 'LCDDisplayAtPowerOn',
+        PrintConv => {
+            0 => 'Display',
+            1 => 'Retain power off status',
+        },
+    },
+);
+
+# Custom functions for the D30/D60
+%Image::ExifTool::CanonCustom::FunctionsD30 = (
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
+    PROCESS_PROC => \&ProcessCanonCustom,
+    WRITE_PROC => \&WriteCanonCustom,
+    CHECK_PROC => \&CheckCanonCustom,
+    NOTES => 'Custom functions for the EOS-D30 and D60.',
+    WRITABLE => 'int8u',
+    1 => {
+        Name => 'LongExposureNoiseReduction',
+        PrintConv => \%offOn,
+    },
+    2 => {
+        Name => 'Shutter-AELock',
+        PrintConv => {
+            0 => 'AF/AE lock',
+            1 => 'AE lock/AF',
+            2 => 'AF/AF lock',
+            3 => 'AE+release/AE+AF',
+        },
+    },
+    3 => {
+        Name => 'MirrorLockup',
+        PrintConv => \%disableEnable,
+    },
+    4 => {
+        Name => 'ExposureLevelIncrements',
+        PrintConv => {
+            0 => '1/2 Stop',
+            1 => '1/3 Stop',
+        },
+    },
+    5 => {
+        Name => 'AFAssist',
+        PrintConv => {
+            0 => 'Emits/Fires',
+            1 => 'Does not emit/Fires',
+            2 => 'Only ext. flash emits/Fires',
+            3 => 'Emits/Does not fire',
+        },
+    },
+    6 => {
+        Name => 'FlashSyncSpeedAv',
+        PrintConv => {
+            0 => 'Auto',
+            1 => '1/200 Fixed',
+        },
+    },
+    7 => {
+        Name => 'AEBSequence',
+        PrintConv => {
+            0 => '0,-,+/Enabled',
+            1 => '0,-,+/Disabled',
+            2 => '-,0,+/Enabled',
+            3 => '-,0,+/Disabled',
+        },
+    },
+    8 => {
+        Name => 'ShutterCurtainSync',
+        PrintConv => {
+            0 => '1st-curtain sync',
+            1 => '2nd-curtain sync',
+        },
+    },
+    9 => {
+        Name => 'LensAFStopButton',
+        PrintConv => {
+            0 => 'AF Stop',
+            1 => 'Operate AF',
+            2 => 'Lock AE and start timer',
+        },
+    },
+    10 => {
+        Name => 'FillFlashAutoReduction',
+        PrintConv => \%enableDisable,
+    },
+    11 => {
+        Name => 'MenuButtonReturn',
+        PrintConv => {
+            0 => 'Top',
+            1 => 'Previous (volatile)',
+            2 => 'Previous',
+        },
+    },
+    12 => {
+        Name => 'SetButtonFunction',
+        PrintConv => {
+            0 => 'Not assigned',
+            1 => 'Change quality',
+            2 => 'Change ISO speed',
+            3 => 'Select parameters',
+        },
+    },
+    13 => {
+        Name => 'SensorCleaning',
+        PrintConv => \%disableEnable,
+    },
+    14 => {
+        Name => 'SuperimposedDisplay',
+        PrintConv => \%onOff,
+    },
+    15 => {
+        Name => 'ShutterReleaseNoCFCard',
+        Description => 'Shutter Release W/O CF Card',
+        PrintConv => {
+            0 => 'Yes',
+            1 => 'No',
+        },
+    },
+);
+
+# Custom functions for unknown cameras
+%Image::ExifTool::CanonCustom::FuncsUnknown = (
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
+    PROCESS_PROC => \&ProcessCanonCustom,
+);
+
+# 1D personal function settings (ref PH)
+%Image::ExifTool::CanonCustom::PersonalFuncs = (
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
+    CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
+    WRITABLE => 1,
+    NOTES => 'Personal function settings for the EOS-1D.',
+    FORMAT => 'int16u',
+    FIRST_ENTRY => 1,
+    1 => { Name => 'PF0CustomFuncRegistration', %convPFn },
+    2 => { Name => 'PF1DisableShootingModes',   %convPFn },
+    3 => { Name => 'PF2DisableMeteringModes',   %convPFn },
+    4 => { Name => 'PF3ManualExposureMetering', %convPFn },
+    5 => { Name => 'PF4ExposureTimeLimits',     %convPFn },
+    6 => { Name => 'PF5ApertureLimits',         %convPFn },
+    7 => { Name => 'PF6PresetShootingModes',    %convPFn },
+    8 => { Name => 'PF7BracketContinuousShoot', %convPFn },
+    9 => { Name => 'PF8SetBracketShots',        %convPFn },
+    10 => { Name => 'PF9ChangeBracketSequence', %convPFn },
+    11 => { Name => 'PF10RetainProgramShift',   %convPFn },
+    #12 => { Name => 'PF11Unused',               %convPFn },
+    #13 => { Name => 'PF12Unused',               %convPFn },
+    14 => { Name => 'PF13DrivePriority',        %convPFn },
+    15 => { Name => 'PF14DisableFocusSearch',   %convPFn },
+    16 => { Name => 'PF15DisableAFAssistBeam',  %convPFn },
+    17 => { Name => 'PF16AutoFocusPointShoot',  %convPFn },
+    18 => { Name => 'PF17DisableAFPointSel',    %convPFn },
+    19 => { Name => 'PF18EnableAutoAFPointSel', %convPFn },
+    20 => { Name => 'PF19ContinuousShootSpeed', %convPFn },
+    21 => { Name => 'PF20LimitContinousShots',  %convPFn },
+    22 => { Name => 'PF21EnableQuietOperation', %convPFn },
+    #23 => { Name => 'PF22Unused',               %convPFn },
+    24 => { Name => 'PF23SetTimerLengths',      %convPFn },
+    25 => { Name => 'PF24LightLCDDuringBulb',   %convPFn },
+    26 => { Name => 'PF25DefaultClearSettings', %convPFn },
+    27 => { Name => 'PF26ShortenReleaseLag',    %convPFn },
+    28 => { Name => 'PF27ReverseDialRotation',  %convPFn },
+    29 => { Name => 'PF28NoQuickDialExpComp',   %convPFn },
+    30 => { Name => 'PF29QuickDialSwitchOff',   %convPFn },
+    31 => { Name => 'PF30EnlargementMode',      %convPFn },
+    32 => { Name => 'PF31OriginalDecisionData', %convPFn },
+);
+
+# 1D personal function values (ref PH)
+%Image::ExifTool::CanonCustom::PersonalFuncValues = (
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
+    CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
+    WRITABLE => 1,
+    FORMAT => 'int16u',
+    FIRST_ENTRY => 1,
+    1 => 'PF1Value',
+    2 => 'PF2Value',
+    3 => 'PF3Value',
+    4 => {
+        Name => 'PF4ExposureTimeMin',
+        RawConv => '$val > 0 ? $val : 0',
+        ValueConv => 'exp(-Image::ExifTool::Canon::CanonEv($val*4)*log(2))*1000/8',
+        ValueConvInv => 'Image::ExifTool::Canon::CanonEvInv(-log($val*8/1000)/log(2))/4',
+        PrintConv => 'Image::ExifTool::Exif::PrintExposureTime($val)',
+        PrintConvInv => 'eval $val',
+    },
+    5 => {
+        Name => 'PF4ExposureTimeMax',
+        RawConv => '$val > 0 ? $val : 0',
+        ValueConv => 'exp(-Image::ExifTool::Canon::CanonEv($val*4)*log(2))*1000/8',
+        ValueConvInv => 'Image::ExifTool::Canon::CanonEvInv(-log($val*8/1000)/log(2))/4',
+        PrintConv => 'Image::ExifTool::Exif::PrintExposureTime($val)',
+        PrintConvInv => 'eval $val',
+    },
+    6 => {
+        Name => 'PF5ApertureMin',
+        RawConv => '$val > 0 ? $val : 0',
+        ValueConv => 'exp(Image::ExifTool::Canon::CanonEv($val*4-32)*log(2)/2)',
+        ValueConvInv => '(Image::ExifTool::Canon::CanonEvInv(log($val)*2/log(2))+32)/4',
+        PrintConv => 'sprintf("%.2g",$val)',
+        PrintConvInv => '$val',
+    },
+    7 => {
+        Name => 'PF5ApertureMax',
+        RawConv => '$val > 0 ? $val : 0',
+        ValueConv => 'exp(Image::ExifTool::Canon::CanonEv($val*4-32)*log(2)/2)',
+        ValueConvInv => '(Image::ExifTool::Canon::CanonEvInv(log($val)*2/log(2))+32)/4',
+        PrintConv => 'sprintf("%.2g",$val)',
+        PrintConvInv => '$val',
+    },
+    8 => 'PF8BracketShots',
+    9 => 'PF19ShootingSpeedLow',
+    10 => 'PF19ShootingSpeedHigh',
+    11 => 'PF20MaxContinousShots',
+    12 => 'PF23ShutterButtonTime',
+    13 => 'PF23FELockTime',
+    14 => 'PF23PostReleaseTime',
+    15 => 'PF25AEMode',
+    16 => 'PF25MeteringMode',
+    17 => 'PF25DriveMode',
+    18 => 'PF25AFMode',
+    19 => 'PF25AFPointSel',
+    20 => 'PF25ImageSize',
+    21 => 'PF25WBMode',
+    22 => 'PF25Parameters',
+    23 => 'PF25ColorMatrix',
+    24 => 'PF27Value',
+);
+
+# Custom functions used by the 1D Mark III and later models (ref PH)
+%Image::ExifTool::CanonCustom::Functions2 = (
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
+    PROCESS_PROC => \&ProcessCanonCustom2,
+    CHECK_PROC => \&Image::ExifTool::Exif::CheckExif,
+    WRITE_PROC => \&WriteCanonCustom2,
+    WRITABLE => 'int32s',
+    NOTES => q{
+        Beginning with the EOS 1D Mark III, Canon finally created a set of custom
+        function tags which are consistent across models.  The EOS 1D Mark III has
+        57 custom function tags divided into four main groups: 1. Exposure
+        (0x0101-0x010f), 2. Image (0x0201-0x0203), Flash Exposure (0x0304-0x0306)
+        and Display (0x0407-0x0409), 3. Auto Focus (0x0501-0x050e) and Drive
+        (0x060f-0x0611), and 4. Operation (0x0701-0x070a) and Others
+        (0x080b-0x0810).  The table below lists tags used by the EOS 1D Mark III, as
+        well as newer values and tags used by the EOS 40D.  It is expected that
+        future models will re-use some of these tags (possibly with minor value
+        changes), as well as defining additional new tags of their own.
+    },
+    # grouped in 4 groups:
+    # 1) Exposure
+    0x0101 => [
+        {
+            Name => 'ExposureLevelIncrements',
+            Condition => '$$self{CameraModel} =~ /\b40D\b/',
+            Notes => '40D',
+            PrintConv => {
+                0 => '1/3 Stop',
+                1 => '1/2 Stop',
+            },
+        },
+        {
+            Name => 'ExposureLevelIncrements',
+            Notes => '1D Mark III',
+            PrintConv => {
+                0 => '1/3-stop set, 1/3-stop comp.',
+                1 => '1-stop set, 1/3-stop comp.',
+                2 => '1/2-stop set, 1/2-stop comp.',
+            },
+        },
+    ],
+    0x0102 => {
+        Name => 'ISOSpeedIncrements',
+        PrintConv => {
+            0 => '1/3-stop',
+            1 => '1-stop',
+        },
+    },
+    0x0103 => [
+        {
+            Name => 'ISOExpansion',
+            Condition => '$$self{CameraModel} =~ /\b40D\b/',
+            Notes => '40D',
+            PrintConv => {
+                0 => 'Off',
+                1 => 'On',
+            },
+        },
+        {
+            Name => 'ISOSpeedRange',
+            Notes => '1D Mark III',
+            Count => 3,
+            # (this decoding may not be valid for CR2 images?)
+            ValueConv => [
+                undef,
+                '$val < 1000 ? exp(($val/8-9)*log(2))*100 : 0', # (educated guess)
+                '$val < 1000 ? exp(($val/8-9)*log(2))*100 : 0', # (educated guess)
+            ],
+            ValueConvInv => [
+                undef,
+                '$val ? int(8*(log($val/100)/log(2)+9) + 0.5) : 0xffffffff',
+                '$val ? int(8*(log($val/100)/log(2)+9) + 0.5) : 0xffffffff',
+            ],
+            PrintConv => [
+                \%disableEnable,
+                'sprintf("Max %.0f",$val)',
+                'sprintf("Min %.0f",$val)',
+            ],
+            PrintConvInv => [
+                undef,
+                '$val=~/([\d.]+)/ ? $1 : 0',
+                '$val=~/([\d.]+)/ ? $1 : 0',
+            ],
+        },
+    ],
+    0x0104 => {
+        Name => 'AEBAutoCancel',
+        PrintConv => \%onOff,
+    },
+    0x0105 => {
+        Name => 'AEBSequence',
+        Notes => 'value of 2 not used by 40D',
+        PrintConv => {
+            0 => '0,-,+',
+            1 => '-,0,+',
+            2 => '+,0,-',
+        },
+    },
+    0x0106 => {
+        Name => 'AEBShotCount',
+        PrintConv => {
+            0 => 3,
+            1 => 2,
+            2 => 5,
+            3 => 7,
+        },
+    },
+    0x0107 => {
+        Name => 'SpotMeterLinkToAFPoint',
+        PrintConv => {
+            0 => 'Disable (use center AF point)',
+            1 => 'Enable (use active AF point)',
+        },
+    },
+    0x0108 => {
+        Name => 'SafetyShift',
+        Notes => 'value of 2 not used by 40D',
+        PrintConv => {
+            0 => 'Disable',
+            1 => 'Enable (Tv/Av)',
+            2 => 'Enable (ISO speed)',
+        },
+    },
+    0x0109 => {
+        Name => 'UsableShootingModes',
+        Count => 2,
+        PrintConv => [
+            \%disableEnable,
+            'sprintf("Flags 0x%x",$val)', # (M, Tv, Av, P, Bulb)
+        ],
+        PrintConvInv => [
+            undef,
+            '$val=~/0x([\dA-F]+)/i ? hex($1) : undef',
+        ],
+    },
+    0x010a => {
+        Name => 'UsableMeteringModes',
+        Count => 2,
+        PrintConv => [
+            \%disableEnable,
+            'sprintf("Flags 0x%x",$val)', # (evaluative,partial,spot,center-weighted average)
+        ],
+        PrintConvInv => [
+            undef,
+            '$val=~/0x([\dA-F]+)/i ? hex($1) : undef',
+        ],
+    },
+    0x010b => {
+        Name => 'ExposureModeInManual',
+        PrintConv => {
+            0 => 'Specified metering mode',
+            1 => 'Evaluative metering',
+            2 => 'Partial metering',
+            3 => 'Spot metering',
+            4 => 'Center-weighted average',
+        },
+    },
+    0x010c => {
+        Name => 'ShutterSpeedRange',
+        Count => 3,
+        ValueConv => [
+            undef,
+            'exp(-($val/8-7)*log(2))',
+            'exp(-($val/8-7)*log(2))',
+        ],
+        ValueConvInv => [
+            undef,
+            'int(-8*(log($val)/log(2)-7) + 0.5)',
+            'int(-8*(log($val)/log(2)-7) + 0.5)',
+        ],
+        PrintConv => [
+            \%disableEnable,
+            '"Hi " . Image::ExifTool::Exif::PrintExposureTime($val)',
+            '"Lo " . Image::ExifTool::Exif::PrintExposureTime($val)',
+        ],
+        PrintConvInv => [
+            undef,
+            '$val=~m{([\d./]+)} ? eval $1 : 0',
+            '$val=~m{([\d./]+)} ? eval $1 : 0',
+        ],
+    },
+    0x010d => {
+        Name => 'ApertureRange',
+        Count => 3,
+        ValueConv => [
+            undef,
+            'exp(($val/8-1)*log(2)/2)',
+            'exp(($val/8-1)*log(2)/2)',
+        ],
+        ValueConvInv => [
+            undef,
+            'int(8*(log($val)*2/log(2)+1) + 0.5)',
+            'int(8*(log($val)*2/log(2)+1) + 0.5)',
+        ],
+        PrintConv => [
+            \%disableEnable,
+            'sprintf("Closed %.2g",$val)',
+            'sprintf("Open %.2g",$val)',
+        ],
+        PrintConvInv => [
+            undef,
+            '$val=~/([\d.]+)/ ? $1 : 0',
+            '$val=~/([\d.]+)/ ? $1 : 0',
+        ],
+    },
+    0x010e => {
+        Name => 'ApplyShootingMeteringMode',
+        Count => 8,
+        PrintConv => [
+            \%disableEnable,
+        ],
+    },
+    0x010f => [
+        {
+            Name => 'FlashSyncSpeedAv',
+            Condition => '$$self{CameraModel} =~ /\b40D\b/',
+            Notes => '40D',
+            PrintConv => {
+                0 => 'Auto',
+                1 => '1/250 Fixed',
+            },
+        },
+        {
+            Name => 'FlashSyncSpeedAv',
+            Notes => '1D Mark III',
+            PrintConv => {
+                0 => 'Auto',
+                1 => '1/300 Fixed',
+            },
+        },
+    ],
+    #### 2a) Image
+    0x0201 => {
+        Name => 'LongExposureNoiseReduction',
+        PrintConv => {
+            0 => 'Off',
+            1 => 'Auto',
+            2 => 'On',
+        },
+    },
+    0x0202 => {
+        Name => 'HighISONoiseReduction',
+        PrintConv => \%offOn,
+    },
+    0x0203 => {
+        Name => 'HighlightTonePriority',
+        PrintConv => \%disableEnable
+    },
+    #### 2b) Flash exposure
+    0x0304 => {
+        Name => 'ETTLII',
+        Description => 'E-TTL II',
+        PrintConv => {
+            0 => 'Evaluative',
+            1 => 'Average',
+        },
+    },
+    0x0305 => {
+        Name => 'ShutterCurtainSync',
+        PrintConv => {
+            0 => '1st-curtain sync',
+            1 => '2nd-curtain sync',
+        },
+    },
+    0x0306 => {
+        Name => 'FlashFiring',
+        PrintConv => {
+            0 => 'Fires',
+            1 => 'Does not fire',
+        },
+    },
+    #### 2c) Display
+    0x0407 => {
+        Name => 'ViewInfoDuringExposure',
+        PrintConv => \%disableEnable,
+    },
+    0x0408 => {
+        Name => 'LCDIlluminationDuringBulb',
+        PrintConv => \%offOn,
+    },
+    0x0409 => {
+        Name => 'InfoButtonWhenShooting',
+        PrintConv => {
+            0 => 'Displays camera settings',
+            1 => 'Displays shooting functions',
+        },
+    },
+    #### 3a) Auto focus
+    0x0501 => {
+        Name => 'USMLensElectronicMF',
+        PrintConv => {
+            0 => 'Enable after one-shot AF',
+            1 => 'Disable after one-shot AF',
+            2 => 'Disable in AF mode',
+        },
+    },
+    0x0502 => {
+        Name => 'AIServoTrackingSensitivity',
+        PrintConv => {
+           -2 => 'Slow',
+           -1 => 'Medium Slow',
+            0 => 'Standard',
+            1 => 'Medium Fast',
+            2 => 'Fast',
+        },
+    },
+    0x0503 => {
+        Name => 'AIServoImagePriority',
+        PrintConv => {
+            0 => '1: AF, 2: Tracking',
+            1 => '1: AF, 2: Drive speed',
+            2 => '1: Release, 2: Drive speed',
+        },
+    },
+    0x0504 => {
+        Name => 'AIServoTrackingMethod',
+        PrintConv => {
+            0 => 'Main focus point priority',
+            1 => 'Continuous AF track priority',
+        },
+    },
+    0x0505 => {
+        Name => 'LensDriveNoAF',
+        PrintConv => {
+            0 => 'Focus search on',
+            1 => 'Focus search off',
+        },
+    },
+    0x0506 => {
+        Name => 'LensAFStopButton',
+        Notes => 'value of 6 not used by 40D',
+        PrintConv => {
+            0 => 'AF stop',
+            1 => 'AF start',
+            2 => 'AE lock',
+            3 => 'AF point: M->Auto/Auto->ctr',
+            4 => 'One Shot <-> AI servo',
+            5 => 'IS start',
+            6 => 'Switch to registered AF point',
+        },
+    },
+    0x0507 => {
+        Name => 'AFMicroadjustment',
+        Count => 5,
+        PrintConv => [
+            {
+                0 => 'Disable',
+                1 => 'Adjust all by same amount',
+                2 => 'Adjust by lens',
+            },
+            # DECODE OTHER VALUES
+        ],
+    },
+    0x0508 => {
+        Name => 'AFExpansionWithSelectedPoint',
+        PrintConv => {
+            0 => 'Disable',
+            1 => 'Enable (left/right assist points)',
+            2 => 'Enable (surrounding assist points)',
+        },
+    },
+    0x0509 => {
+        Name => 'SelectableAFPoint',
+        PrintConv => {
+            0 => '19 points',
+            1 => 'Inner 9 points',
+            2 => 'Outer 9 points',
+        },
+    },
+    0x050a => {
+        Name => 'SwitchToRegisteredAFPoint',
+        PrintConv => \%disableEnable,
+    },
+    0x050b => {
+        Name => 'AFPointAutoSelection',
+        PrintConv => {
+            0 => 'Control-direct:disable/Main:enable',
+            1 => 'Control-direct:disable/Main:disable',
+            2 => 'Control-direct:enable/Main:enable',
+        },
+    },
+    0x050c => {
+        Name => 'AFPointDisplayDuringFocus',
+        PrintConv => {
+            0 => 'On',
+            1 => 'Off',
+            2 => 'On (when focus achieved)',
+        },
+    },
+    0x050d => {
+        Name => 'AFPointBrightness',
+        PrintConv => {
+            0 => 'Normal',
+            1 => 'Brighter',
+        },
+    },
+    0x050e => {
+        Name => 'AFAssistBeam',
+        Notes => 'value of 2 not used by 1D Mark III',
+        PrintConv => {
+            0 => 'Emits',
+            1 => 'Does not emit',
+            2 => 'Only ext. flash emits',
+        },
+    },
+    0x050f => { # new for 40D
+        Name => 'AFPointSelectionMethod',
+        PrintConv => {
+            0 => 'Normal',
+            1 => 'Multi-controller direct',
+            2 => 'Quick Control Dial direct',
+        },
+    },
+    0x0510 => { # new for 40D
+        Name => 'SuperimposedDisplay',
+        PrintConv => \%onOff,
+    },
+    0x0511 => { # new for 40D
+        Name => 'AFDuringLiveView',
+        PrintConv => \%disableEnable,
+    },
+    #### 3b) Drive
+    0x060f => {
+        Name => 'MirrorLockup',
+        Notes => 'value of 2 not used by 40D',
+        PrintConv => {
+            0 => 'Disable',
+            1 => 'Enable',
+            2 => 'Enable: Down with Set',
+        },
+    },
+    0x0610 => {
+        Name => 'ContinuousShootingSpeed',
+        Count => 3,
+        PrintConv => [
+            \%disableEnable,
+            '"Hi $val"',
+            '"Lo $val"',
+        ],
+        PrintConvInv => [
+            undef,
+            '$val=~/(\d+)/ ? $1 : 0',
+            '$val=~/(\d+)/ ? $1 : 0',
+        ],
+    },
+    0x0611 => {
+        Name => 'ContinuousShotLimit',
+        Count => 2,
+        PrintConv => [
+            \%disableEnable,
+            '"$val shots"',
+        ],
+        PrintConvInv => [
+            undef,
+            '$val=~/(\d+)/ ? $1 : 0',
+        ],
+    },
+    #### 4a) Operation
+    0x0701 => {
+        Name => 'ShutterButtonAFOnButton',
+        PrintConv => {
+            0 => 'Metering + AF start',
+            1 => 'Metering + AF start/AF stop',
+            2 => 'Metering start/Meter + AF start',
+            3 => 'AE lock/Metering + AF start',
+            4 => 'Metering + AF start / disable',
+        },
+    },
+    0x0702 => {
+        Name => 'AFOnAELockButtonSwitch',
+        PrintConv => \%disableEnable,
+    },
+    0x0703 => {
+        Name => 'QuickControlDialInMeter',
+        PrintConv => {
+            0 => 'Exposure comp/Aperture',
+            1 => 'AF point selection',
+            2 => 'ISO speed',
+        },
+    },
+    0x0704 => [
+        {
+            Name => 'SetButtonWhenShooting',
+            Condition => '$$self{CameraModel} =~ /\b40D\b/',
+            Notes => '40D',
+            PrintConv => {
+                0 => 'Normal (disabled)',
+                1 => 'Image quality',
+                2 => 'Picture style',
+                3 => 'Menu display',
+                4 => 'Image playback',
+            },
+        },
+        {
+            Name => 'SetButtonWhenShooting',
+            Notes => '1D Mark III',
+            PrintConv => {
+                0 => 'Normal (disabled)',
+                1 => 'White balance',
+                2 => 'Image size',
+                3 => 'ISO speed',
+                4 => 'Picture style',
+                5 => 'Record func. + media/folder',
+                6 => 'Menu display',
+                7 => 'Image playback',
+            },
+        },
+    ],
+    0x0705 => {
+        Name => 'ManualTv',
+        Description => 'Manual Tv/Av For M',
+        PrintConv => {
+            0 => 'Tv=Main/Av=Control',
+            1 => 'Tv=Control/Av=Main',
+        },
+    },
+    0x0706 => {
+        Name => 'DialDirectionTvAv',
+        PrintConv => {
+            0 => 'Normal',
+            1 => 'Reversed',
+        },
+    },
+    0x0707 => {
+        Name => 'AvSettingWithoutLens',
+        PrintConv => \%disableEnable,
+    },
+    0x0708 => {
+        Name => 'WBMediaImageSizeSetting',
+        PrintConv => {
+            0 => 'Rear LCD panel',
+            1 => 'LCD monitor',
+        },
+    },
+    0x0709 => {
+        Name => 'LockMicrophoneButton',
+        PrintConv => {
+            0 => 'Protect (holding:sound rec.)',
+            1 => 'Sound rec. (protect:disable)',
+        },
+    },
+    0x070a => {
+        Name => 'ButtonFunctionControlOff',
+        PrintConv => {
+            0 => 'Normal (enable)',
+            1 => 'Disable main, Control, Multi-control',
+        },
+    },
+    #### 4b) Others
+    0x080b => [
+        {
+            Name => 'FocusingScreen',
+            Condition => '$$self{CameraModel} =~ /\b40D\b/',
+            Notes => '40D',
+            PrintConv => {
+                0 => 'Ef-A',
+                1 => 'Ef-D',
+                2 => 'Ef-S',
+            },
+        },
+        {
+            Name => 'FocusingScreen',
+            Notes => '1D Mark III',
+            PrintConv => {
+                0 => 'Ec-CIV',
+                1 => 'Ec-A,B,C,CII,CIII,D,H,I,L',
+                2 => 'Ec-S',
+                3 => 'Ec-N,R',
+            },
+        },
+    ],
+    0x080c => {
+        Name => 'TimerLength',
+        Count => 4,
+        PrintConv => [
+            \%disableEnable,
+            '"6 sec: $val"',
+            '"16 sec: $val"',
+            '"After release: $val"',
+        ],
+        PrintConvInv => [
+            undef,
+            '$val=~/(\d+)$/ ? $1 : 0',
+            '$val=~/(\d+)$/ ? $1 : 0',
+            '$val=~/(\d+)$/ ? $1 : 0',
+        ],
+    },
+    0x080d => {
+        Name => 'ShortReleaseTimeLag',
+        PrintConv => \%disableEnable,
+    },
+    0x080e => {
+        Name => 'AddAspectRatioInfo',
+        PrintConv => {
+            0 => 'Off',
+            1 => '6:6',
+            2 => '3:4',
+            3 => '4:5',
+            4 => '6:7',
+            5 => '10:12',
+            6 => '5:7',
+        },
+    },
+    0x080f => {
+        Name => 'AddOriginalDecisionData',
+        PrintConv => \%offOn,
+    },
+    0x0810 => {
+        Name => 'LiveViewExposureSimulation',
+        PrintConv => {
+            0 => 'Disable (LCD auto adjust)',
+            1 => 'Enable (simulates exposure)',
+        },
+    },
+);
+
+#------------------------------------------------------------------------------
+# Conversion routines
+# Inputs: 0) value to convert
+sub ConvertPfn($)
+{
+    my $val = shift;
+    return $val ? ($val==1 ? 'On' : "On ($val)") : "Off";
+}
+sub ConvertPfnInv($)
+{
+    my $val = shift;
+    return $1 if $val =~ /(\d+)/;
+    return  1 if $val =~ /on/i;
+    return  0 if $val =~ /off/i;
+    return undef;
+}
+
+#------------------------------------------------------------------------------
+# Read/Write Canon custom 2 directory (used by 1D Mark III)
+# Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
+# Returns: 1 on success
+sub ProcessCanonCustom2($$$)
+{
+    my ($exifTool, $dirInfo, $tagTablePtr) = @_;
+    my $dataPt = $$dirInfo{DataPt};
+    my $offset = $$dirInfo{DirStart};
+    my $size = $$dirInfo{DirLen};
+    my $write = $$dirInfo{Write};
+    my $verbose = $exifTool->Options('Verbose');
+    my $newTags;
+
+    # first entry in array must be the size
+    my $len = Get16u($dataPt, $offset);
+    unless ($len == $size and $len >= 8) {
+        $exifTool->Warn("Invalid CanonCustom2 data");
+        return 0;
+    }
+    # get group count
+    my $count = Get32u($dataPt, $offset + 4);
+    if ($write) {
+        $newTags = $exifTool->GetNewTagInfoHash($tagTablePtr);
+        $exifTool->VPrint(0, "  Rewriting CanonCustom2\n") if $verbose;
+    } elsif ($verbose) {
+        $exifTool->VerboseDir('CanonCustom2', $count);
+    }
+    my $pos = $offset + 8;
+    my $end = $offset + $size;
+    # loop through group records
+    for (; $pos<$end; ) {
+        last if $pos + 12 > $end;
+        my $recNum = Get32u($dataPt, $pos);
+        my $recLen = Get32u($dataPt, $pos + 4);
+        my $recCount = Get32u($dataPt, $pos + 8);
+        last if $recLen < 8;    # must be at least 8 bytes for recNum and recLen
+        $pos += 12;
+        my $recPos = $pos;
+        my $recEnd = $pos + $recLen;
+        if ($verbose and not $write) {
+            $exifTool->VerboseDir("CanonCustom2 group $recNum", $recCount);
+        }
+        my ($i, $num, $tag);
+        for ($i=0; $recPos + 8 < $recEnd; ++$i, $recPos+=4*$num) {
+            $tag = Get32u($dataPt, $recPos);
+            $num = Get32u($dataPt, $recPos + 4);
+            $recPos += 8;
+            last if $recPos + $num * 4 > $recEnd;
+            my $val = ReadValue($dataPt, $recPos, 'int32s', $num, $num * 4);
+            if ($write) {
+                # write new value
+                my $tagInfo = $$newTags{$tag};
+                next unless $tagInfo;
+                my $newValueHash = $exifTool->GetNewValueHash($tagInfo);
+                next unless Image::ExifTool::IsOverwriting($newValueHash, $val);
+                my $newVal = Image::ExifTool::GetNewValues($newValueHash);
+                next unless defined $newVal;    # can't delete from a custom table
+                WriteValue($newVal, 'int32s', $num, $dataPt, $recPos);
+                if ($verbose > 1) {
+                    $exifTool->VPrint(0, "    - CanonCustom:$$tagInfo{Name} = '$val'\n",
+                                         "    + CanonCustom:$$tagInfo{Name} = '$newVal'\n");
+                }
+                ++$exifTool->{CHANGED};
+            } else {
+                # extract the value
+                my $oldInfo = $$tagTablePtr{$tag};
+                $exifTool->HandleTag($tagTablePtr, $tag, $val,
+                    Index  => $i,
+                    Format => 'int32u',
+                    Count  => $num,
+                    Size   => $num * 4,
+                );
+                my $tagInfo = $$tagTablePtr{$tag};
+                # generate properly formatted description if we just added the tag
+                if ($tagInfo and not $oldInfo) {
+                    ($$tagInfo{Description} = $$tagInfo{Name}) =~ tr/_/ /;
+                    $$tagInfo{Description} =~ s/CanonCustom Functions/Canon Custom Functions /;
+                }
+            }
+        }
+        $pos += $recLen - 8;
+    }
+    if ($pos != $end) {
+        $exifTool->Warn('Possibly corrupted CanonCustom2 data');
+        return 0;
+    }
+    return 1;
+}
+
+#------------------------------------------------------------------------------
+# Write Canon custom 2 data
+# Inputs: 0) ExifTool object reference, 1) dirInfo hash ref, 2) tag table ref
+# Returns: New custom data block or undefined on error
+sub WriteCanonCustom2($$$)
+{
+    my ($exifTool, $dirInfo, $tagTablePtr) = @_;
+    $exifTool or return 1;    # allow dummy access to autoload this package
+    my $dataPt = $$dirInfo{DataPt};
+    # edit a copy of the custom function 2 data
+    my $buff = substr($$dataPt, $$dirInfo{DirStart}, $$dirInfo{DirLen});
+    my %dirInfo = (
+        DataPt   => \$buff,
+        DirStart => 0,
+        DirLen   => $$dirInfo{DirLen},
+        Write    => 1,
+    );
+    ProcessCanonCustom2($exifTool, \%dirInfo, $tagTablePtr) or return undef;
+    return $buff;
+}
+
+#------------------------------------------------------------------------------
+# Process Canon custom directory
+# Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
+# Returns: 1 on success
+sub ProcessCanonCustom($$$)
+{
+    my ($exifTool, $dirInfo, $tagTablePtr) = @_;
+    my $dataPt = $$dirInfo{DataPt};
+    my $offset = $$dirInfo{DirStart};
+    my $size = $$dirInfo{DirLen};
+    my $verbose = $exifTool->Options('Verbose');
+
+    # first entry in array must be the size
+    my $len = Get16u($dataPt,$offset);
+    unless ($len == $size or ($$exifTool{CameraModel}=~/\bD60\b/ and $len+2 == $size)) {
+        $exifTool->Warn("Invalid CanonCustom data");
+        return 0;
+    }
+    $verbose and $exifTool->VerboseDir('CanonCustom', $size/2-1);
+    my $pos;
+    for ($pos=2; $pos<$size; $pos+=2) {
+        # ($pos is position within custom directory)
+        my $val = Get16u($dataPt,$offset+$pos);
+        my $tag = ($val >> 8);
+        $val = ($val & 0xff);
+        $exifTool->HandleTag($tagTablePtr, $tag, $val,
+            Index  => $pos/2-1,
+            Format => 'int8u',
+            Count  => 1,
+            Size   => 1,
+        );
+    }
+    return 1;
+}
+
+#------------------------------------------------------------------------------
+# Check new value for Canon custom data block
+# Inputs: 0) ExifTool object reference, 1) tagInfo hash ref, 2) raw value ref
+# Returns: error string or undef (and may modify value) on success
+sub CheckCanonCustom($$$)
+{
+    my ($exifTool, $tagInfo, $valPtr) = @_;
+    return Image::ExifTool::CheckValue($valPtr, 'int8u');
+}
+
+#------------------------------------------------------------------------------
+# Write Canon custom data
+# Inputs: 0) ExifTool object reference, 1) dirInfo hash ref, 2) tag table ref
+# Returns: New custom data block or undefined on error
+sub WriteCanonCustom($$$)
+{
+    my ($exifTool, $dirInfo, $tagTablePtr) = @_;
+    $exifTool or return 1;    # allow dummy access to autoload this package
+    my $dataPt = $$dirInfo{DataPt};
+    my $dirStart = $$dirInfo{DirStart} || 0;
+    my $dirLen = $$dirInfo{DirLen} || length($$dataPt) - $dirStart;
+    my $dirName = $$dirInfo{DirName};
+    my $verbose = $exifTool->Options('Verbose');
+    my $newData = substr($$dataPt, $dirStart, $dirLen) or return undef;
+    $dataPt = \$newData;
+
+    # first entry in array must be the size
+    my $len = Get16u($dataPt, 0);
+    unless ($len == $dirLen or ($$exifTool{CameraModel}=~/\bD60\b/ and $len+2 == $dirLen)) {
+        $exifTool->Warn("Invalid CanonCustom data");
+        return undef;
+    }
+    my $newTags = $exifTool->GetNewTagInfoHash($tagTablePtr);
+    my $pos;
+    for ($pos=2; $pos<$dirLen; $pos+=2) {
+        my $val = Get16u($dataPt, $pos);
+        my $tag = ($val >> 8);
+        my $tagInfo = $$newTags{$tag};
+        next unless $tagInfo;
+        my $newValueHash = $exifTool->GetNewValueHash($tagInfo);
+        $val = ($val & 0xff);
+        next unless Image::ExifTool::IsOverwriting($newValueHash, $val);
+        my $newVal = Image::ExifTool::GetNewValues($newValueHash);
+        next unless defined $newVal;    # can't delete from a custom table
+        Set16u(($newVal & 0xff) + ($tag << 8), $dataPt, $pos);
+        if ($verbose > 1) {
+            $exifTool->VPrint(0, "    - $dirName:$$tagInfo{Name} = '$val'\n",
+                                 "    + $dirName:$$tagInfo{Name} = '$newVal'\n");
+        }
+        ++$exifTool->{CHANGED};
+    }
+    return $newData;
+}
+
+
+1;  # end
+
+__END__
+
+=head1 NAME
+
+Image::ExifTool::CanonCustom - Read and Write Canon custom functions
+
+=head1 SYNOPSIS
+
+This module is loaded automatically by Image::ExifTool when required.
+
+=head1 DESCRIPTION
+
+The Canon custom functions meta information is very specific to the
+camera model, and is found in both the EXIF maker notes and in the
+Canon RAW files.  This module contains the definitions necessary for
+Image::ExifTool to read this information.
+
+=head1 AUTHOR
+
+Copyright 2003-2007, Phil Harvey (phil at owl.phy.queensu.ca)
+
+This library is free software; you can redistribute it and/or modify it
+under the same terms as Perl itself.
+
+=head1 REFERENCES
+
+=over 4
+
+=item L<http://park2.wakwak.com/~tsuruzoh/Computer/Digicams/exif-e.html>
+
+=back
+
+=head1 ACKNOWLEDGEMENTS
+
+Thanks to Christian Koller for his work in decoding the 20D custom
+functions, and Rainer Honle for decoding the 5D custom functions.
+
+=head1 SEE ALSO
+
+L<Image::ExifTool::TagNames/Canon Tags>,
+L<Image::ExifTool(3pm)|Image::ExifTool>
+
+=cut
Index: /gsdl/trunk/perllib/cpan/Image/ExifTool/CanonRaw.pm
===================================================================
--- /gsdl/trunk/perllib/cpan/Image/ExifTool/CanonRaw.pm	(revision 16842)
+++ /gsdl/trunk/perllib/cpan/Image/ExifTool/CanonRaw.pm	(revision 16842)
@@ -0,0 +1,844 @@
+#------------------------------------------------------------------------------
+# File:         CanonRaw.pm
+#
+# Description:  Read Canon RAW (CRW) meta information
+#
+# Revisions:    11/25/2003 - P. Harvey Created
+#               12/02/2003 - P. Harvey Completely reworked and figured out many
+#                            more tags
+#               01/19/2004 - P. Harvey Added CleanRaw()
+#
+# References:   1) http://www.cybercom.net/~dcoffin/dcraw/
+#               2) http://www.wonderland.org/crw/
+#               3) http://xyrion.org/ciff/CIFFspecV1R04.pdf
+#------------------------------------------------------------------------------
+
+package Image::ExifTool::CanonRaw;
+
+use strict;
+use vars qw($VERSION $AUTOLOAD %crwTagFormat);
+use Image::ExifTool qw(:DataAccess);
+use Image::ExifTool::Exif;
+use Image::ExifTool::Canon;
+
+$VERSION = '1.42';
+
+sub WriteCRW($$);
+sub ProcessCanonRaw($$$);
+sub WriteCanonRaw($$$);
+sub CheckCanonRaw($$$);
+sub InitMakerNotes($);
+sub SaveMakerNotes($);
+sub BuildMakerNotes($$$$$$);
+
+# formats for CRW tag types (($tag >> 8) & 0x38)
+# Note: don't define format for undefined types
+%crwTagFormat = (
+    0x00 => 'int8u',
+    0x08 => 'string',
+    0x10 => 'int16u',
+    0x18 => 'int32u',
+  # 0x20 => 'undef',
+  # 0x28 => 'undef',
+  # 0x30 => 'undef',
+);
+
+# Canon raw file tag table
+# Note: Tag ID's have upper 2 bits set to zero, since these 2 bits
+# just specify the location of the information
+%Image::ExifTool::CanonRaw::Main = (
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
+    PROCESS_PROC => \&ProcessCanonRaw,
+    WRITE_PROC => \&WriteCanonRaw,
+    CHECK_PROC => \&CheckCanonRaw,
+    WRITABLE => 1,
+    0x0000 => { Name => 'NullRecord', Writable => 'undef' }, #3
+    0x0001 => { #3
+        Name => 'FreeBytes',
+        Format => 'undef',
+        Binary => 1,
+    },
+    0x0032 => { Name => 'CanonColorInfo1', Writable => 0 },
+    0x0805 => [
+        # this tag is found in more than one directory...
+        {
+            Condition => '$self->{DIR_NAME} eq "ImageDescription"',
+            Name => 'CanonFileDescription',
+            Writable => 'string[32]',
+        },
+        {
+            Name => 'UserComment',
+            Writable => 'string[256]',
+        },
+    ],
+    0x080a => {
+        Name => 'CanonRawMakeModel',
+        Writable => 0,
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::CanonRaw::MakeModel',
+        },
+    },
+    0x080b => { Name => 'CanonFirmwareVersion', Writable => 'string[32]' },
+    0x080c => { Name => 'ComponentVersion',     Writable => 'string'     }, #3
+    0x080d => { Name => 'ROMOperationMode',     Writable => 'string[8]'  }, #3
+    0x0810 => { Name => 'OwnerName',            Writable => 'string[32]' },
+    0x0815 => { Name => 'CanonImageType',       Writable => 'string[32]' },
+    0x0816 => { Name => 'OriginalFileName',     Writable => 'string[32]' },
+    0x0817 => { Name => 'ThumbnailFileName',    Writable => 'string[32]' },
+    0x100a => { #3
+        Name => 'TargetImageType',
+        Writable => 'int16u',
+        PrintConv => {
+            0 => 'Real-world Subject',
+            1 => 'Written Document',
+        },
+    },
+    0x1010 => { #3
+        Name => 'ShutterReleaseMethod',
+        Writable => 'int16u',
+        PrintConv => {
+            0 => 'Single Shot',
+            2 => 'Continuous Shooting',
+        },
+    },
+    0x1011 => { #3
+        Name => 'ShutterReleaseTiming',
+        Writable => 'int16u',
+        PrintConv => {
+            0 => 'Priority on shutter',
+            1 => 'Priority on focus',
+        },
+    },
+    0x1016 => { Name => 'ReleaseSetting',       Writable => 'int16u' }, #3
+    0x101c => { Name => 'BaseISO',              Writable => 'int16u' }, #3
+    0x1028=> { #PH
+        Name => 'CanonFlashInfo',
+        Writable => 'int16u',
+        Count => 4,
+        Unknown => 1,
+    },
+    0x1029 => {
+        Name => 'CanonFocalLength',
+        Writable => 0,
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::Canon::FocalLength',
+        },
+    },
+    0x102a => {
+        Name => 'CanonShotInfo',
+        Writable => 0,
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::Canon::ShotInfo',
+        },
+    },
+    0x102c => { Name => 'CanonColorInfo2', Writable => 0 },
+    0x102d => {
+        Name => 'CanonCameraSettings',
+        Writable => 0,
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::Canon::CameraSettings',
+        },
+    },
+    0x1031 => {
+        Name => 'SensorInfo',
+        Writable => 0,
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::Canon::SensorInfo',
+        },
+    },
+    # this tag has only be verified for the 10D in CRW files, but the D30 and D60
+    # also produce CRW images and have CustomFunction information in their JPEG's
+    0x1033 => [
+        {
+            Name => 'CustomFunctions10D',
+            Condition => '$self->{CameraModel} =~ /EOS 10D/',
+            SubDirectory => {
+                Validate => 'Image::ExifTool::Canon::Validate($dirData,$subdirStart,$size)',
+                TagTable => 'Image::ExifTool::CanonCustom::Functions10D',
+            },
+        },
+        {
+            Name => 'CustomFunctionsD30',
+            Condition => '$self->{CameraModel} =~ /EOS D30\b/',
+            SubDirectory => {
+                Validate => 'Image::ExifTool::Canon::Validate($dirData,$subdirStart,$size)',
+                TagTable => 'Image::ExifTool::CanonCustom::FunctionsD30',
+            },
+        },
+        {
+            Name => 'CustomFunctionsD60',
+            Condition => '$self->{CameraModel} =~ /EOS D60\b/',
+            SubDirectory => {
+                # the stored size in the D60 apparently doesn't include the size word:
+                Validate => 'Image::ExifTool::Canon::Validate($dirData,$subdirStart,$size-2,$size)',
+                # (D60 custom functions are basically the same as D30)
+                TagTable => 'Image::ExifTool::CanonCustom::FunctionsD30',
+            },
+        },
+        {
+            Name => 'CustomFunctionsUnknown',
+            SubDirectory => {
+                Validate => 'Image::ExifTool::Canon::Validate($dirData,$subdirStart,$size)',
+                TagTable => 'Image::ExifTool::CanonCustom::FuncsUnknown',
+            },
+        },
+    ],
+    0x1038 => {
+        Name => 'CanonAFInfo',
+        Writable => 0,
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::Canon::AFInfo',
+        },
+    },
+    0x1093 => {
+        Name => 'CanonFileInfo',
+        SubDirectory => {
+            Validate => 'Image::ExifTool::Canon::Validate($dirData,$subdirStart,$size)',
+            TagTable => 'Image::ExifTool::Canon::FileInfo',
+        },
+    },
+    0x10a9 => {
+        Name => 'ColorBalance',
+        Writable => 0,
+        SubDirectory => {
+            # this offset is necessary because the table contains short rationals
+            # (4 bytes long) but the first entry is 2 bytes into the table.
+            Start => '2',
+            TagTable => 'Image::ExifTool::Canon::ColorBalance',
+        },
+    },
+    0x10b5 => { #PH
+        Name => 'RawJpgInfo',
+        SubDirectory => {
+            Validate => 'Image::ExifTool::Canon::Validate($dirData,$subdirStart,$size)',
+            TagTable => 'Image::ExifTool::CanonRaw::RawJpgInfo',
+        },
+    },
+    0x10ae => {
+        Name => 'ColorTemperature',
+        Writable => 'int16u',
+    },
+    0x10b4 => {
+        Name => 'ColorSpace',
+        Writable => 'int16u',
+        PrintConv => {
+            1 => 'sRGB',
+            2 => 'Adobe RGB',
+            0xffff => 'Uncalibrated',
+        },
+    },
+    0x1803 => { #3
+        Name => 'ImageFormat',
+        Writable => 0,
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::CanonRaw::ImageFormat',
+        },
+    },
+    0x1804 => { Name => 'RecordID', Writable => 'int32u' }, #3
+    0x1806 => { #3
+        Name => 'SelfTimerTime',
+        Writable => 'int32u',
+        ValueConv => '$val / 1000',
+        ValueConvInv => '$val * 1000',
+        PrintConv => '"$val sec"',
+        PrintConvInv => '$val=~s/\s*sec.*//;$val',
+    },
+    0x1807 => {
+        Name => 'TargetDistanceSetting',
+        Format => 'float',
+        PrintConv => '"$val mm"',
+        PrintConvInv => '$val=~s/\s*mm$//;$val',
+    },
+    0x180b => {
+        Name => 'SerialNumber',
+        Writable => 'int32u',
+        Description => 'Camera Body No.',
+        PrintConv => 'sprintf("%.10d",$val)',
+        PrintConvInv => '$val',
+    },
+    0x180e => {
+        Name => 'TimeStamp',
+        Writable => 0,
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::CanonRaw::TimeStamp',
+        },
+    },
+    0x1810 => {
+        Name => 'ImageInfo',
+        Writable => 0,
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::CanonRaw::ImageInfo',
+        },
+    },
+    0x1813 => { #3
+        Name => 'FlashInfo',
+        Writable => 0,
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::CanonRaw::FlashInfo',
+        },
+    },
+    0x1814 => { #3
+        Name => 'MeasuredEV',
+        Notes => q{
+            this the Canon name for what should properly be called MeasuredLV, and is
+            offset by about -5 EV from the calculated LV for most models
+        },
+        Format => 'float',
+    },
+    0x1817 => {
+        Name => 'FileNumber',
+        Writable => 'int32u',
+        Groups => { 2 => 'Image' },
+        PrintConv => '$_=$val;s/(\d+)(\d{4})/$1-$2/;$_',
+        PrintConvInv => '$_=$val;s/-//;$_',
+    },
+    0x1818 => { #3
+        Name => 'ExposureInfo',
+        Writable => 0,
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::CanonRaw::ExposureInfo',
+        },
+    },
+    0x1834 => { #PH
+        Name => 'CanonModelID',
+        Writable => 'int32u',
+        PrintHex => 1,
+        Notes => q{
+            this is the complete list of model ID numbers, but note that many of these
+            models do not produce CRW images
+        },
+        SeparateTable => 'Canon CanonModelID',
+        PrintConv => \%Image::ExifTool::Canon::canonModelID,
+    },
+    0x1835 => {
+        Name => 'DecoderTable',
+        Writable => 0,
+    },
+    0x183b => { #PH
+        # display format for serial number
+        Name => 'SerialNumberFormat',
+        Writable => 'int32u',
+        PrintHex => 1,
+        PrintConv => {
+            0x90000000 => 'Format 1',
+            0xa0000000 => 'Format 2',
+        },
+    },
+    0x2005 => {
+        Name => 'RawData',
+        Writable => 0,
+        Binary => 1,
+    },
+    0x2007 => {
+        Name => 'JpgFromRaw',
+        Writable => 'resize',  # 'resize' allows this value to change size
+        Permanent => 0,
+        ValueConv => '$self->ValidateImage(\$val,$tag)',
+        ValueConvInv => '$val',
+    },
+    0x2008 => {
+        Name => 'ThumbnailImage',
+        Writable => 'resize',  # 'resize' allows this value to change size
+        WriteCheck => '$self->CheckImage(\$val)',
+        Permanent => 0,
+        ValueConv => '$self->ValidateImage(\$val,$tag)',
+        ValueConvInv => '$val',
+    },
+    # the following entries are subdirectories
+    # (any 0x28 and 0x30 tag types are handled automatically by the decoding logic)
+    0x2804 => {
+        Name => 'ImageDescription',
+        SubDirectory => { },
+        Writable => 0,
+    },
+    0x2807 => { #3
+        Name => 'CameraObject',
+        SubDirectory => { },
+        Writable => 0,
+    },
+    0x3002 => { #3
+        Name => 'ShootingRecord',
+        SubDirectory => { },
+        Writable => 0,
+    },
+    0x3003 => { #3
+        Name => 'MeasuredInfo',
+        SubDirectory => { },
+        Writable => 0,
+    },
+    0x3004 => { #3
+        Name => 'CameraSpecification',
+        SubDirectory => { },
+        Writable => 0,
+    },
+    0x300a => { #3
+        Name => 'ImageProps',
+        SubDirectory => { },
+        Writable => 0,
+    },
+    0x300b => {
+        Name => 'ExifInformation',
+        SubDirectory => { },
+        Writable => 0,
+    },
+);
+
+# Canon binary data blocks
+%Image::ExifTool::CanonRaw::MakeModel = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
+    CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
+    DATAMEMBER => [ 0, 6 ], # indices of data members to extract when writing
+    WRITABLE => 1,
+    FORMAT => 'string',
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
+    # (can't specify a first entry because this isn't
+    # a simple binary table with fixed offsets)
+    0 => {
+        Name => 'Make',
+        Format => 'string[6]',  # "Canon\0"
+        DataMember => 'CameraMake',
+        RawConv => '$self->{CameraMake} = $val',
+    },
+    6 => {
+        Name => 'Model',
+        Format => 'string[$size-6]',
+        Description => 'Camera Model Name',
+        DataMember => 'CameraModel',
+        RawConv => '$self->{CameraModel} = $val',
+    },
+);
+
+%Image::ExifTool::CanonRaw::TimeStamp = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
+    CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
+    WRITABLE => 1,
+    FORMAT => 'int32u',
+    FIRST_ENTRY => 0,
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Time' },
+    0 => {
+        Name => 'DateTimeOriginal',
+        Description => 'Date/Time Original',
+        Shift => 'Time',
+        ValueConv => 'ConvertUnixTime($val)',
+        ValueConvInv => 'GetUnixTime($val)',
+        PrintConv => '$self->ConvertDateTime($val)',
+        PrintConvInv => '$val',
+    },
+    1 => { #3
+        Name => 'TimeZoneCode',
+        Format => 'int32s',
+        ValueConv => '$val / 3600',
+        ValueConvInv => '$val * 3600',
+    },
+    2 => { #3
+        Name => 'TimeZoneInfo',
+        Notes => 'set to 0x80000000 if TimeZoneCode is valid',
+    },
+);
+
+%Image::ExifTool::CanonRaw::ImageFormat = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
+    CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
+    WRITABLE => 1,
+    FORMAT => 'int32u',
+    FIRST_ENTRY => 0,
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Image' },
+    0 => {
+        Name => 'FileFormat',
+        Flags => 'PrintHex',
+        PrintConv => {
+            0x00010000 => 'JPEG (lossy)',
+            0x00010002 => 'JPEG (non-quantization)',
+            0x00010003 => 'JPEG (lossy/non-quantization toggled)',
+            0x00020001 => 'CRW',
+        },
+    },
+    1 => {
+        Name => 'TargetCompressionRatio',
+        Format => 'float',
+    },
+);
+
+%Image::ExifTool::CanonRaw::RawJpgInfo = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
+    CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
+    WRITABLE => 1,
+    FORMAT => 'int16u',
+    FIRST_ENTRY => 1,
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Image' },
+#    0 => 'RawJpgInfoSize',
+    1 => { #PH
+        Name => 'RawJpgQuality',
+        PrintConv => {
+            1 => 'Economy',
+            2 => 'Normal',
+            3 => 'Fine',
+            5 => 'Superfine',
+        },
+    },
+    2 => { #PH
+        Name => 'RawJpgSize',
+        PrintConv => {
+            0 => 'Large',
+            1 => 'Medium',
+            2 => 'Small',
+        },
+    },
+    3 => 'RawJpgWidth', #PH
+    4 => 'RawJpgHeight', #PH
+);
+
+%Image::ExifTool::CanonRaw::FlashInfo = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
+    CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
+    WRITABLE => 1,
+    FORMAT => 'float',
+    FIRST_ENTRY => 0,
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Image' },
+    0 => 'FlashGuideNumber',
+    1 => 'FlashThreshold',
+);
+
+%Image::ExifTool::CanonRaw::ExposureInfo = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
+    CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
+    WRITABLE => 1,
+    FORMAT => 'float',
+    FIRST_ENTRY => 0,
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Image' },
+    0 => 'ExposureCompensation',
+    1 => 'TvValue',
+    2 => 'AvValue',
+);
+
+%Image::ExifTool::CanonRaw::ImageInfo = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
+    CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
+    FORMAT => 'int32u',
+    FIRST_ENTRY => 1,
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Image' },
+    # Note: Don't make these writable (except rotation) because it confuses
+    # Canon decoding software if the are changed
+    0 => 'ImageWidth', #3
+    1 => 'ImageHeight', #3
+    2 => { #3
+        Name => 'PixelAspectRatio',
+        Format => 'float',
+    },
+    3 => {
+        Name => 'Rotation',
+        Format => 'int32s',
+        Writable => 'int32s',
+    },
+    4 => 'ComponentBitDepth', #3
+    5 => 'ColorBitDepth', #3
+    6 => 'ColorBW', #3
+);
+
+#------------------------------------------------------------------------------
+# AutoLoad our writer routines when necessary
+#
+sub AUTOLOAD
+{
+    return Image::ExifTool::DoAutoLoad($AUTOLOAD, @_);
+}
+
+#------------------------------------------------------------------------------
+# Process Raw file directory
+# Inputs: 0) ExifTool object reference
+#         1) directory information reference, 2) tag table reference
+# Returns: 1 on success
+sub ProcessCanonRaw($$$)
+{
+    my ($exifTool, $dirInfo, $rawTagTable) = @_;
+    my $blockStart = $$dirInfo{DirStart};
+    my $blockSize = $$dirInfo{DirLen};
+    my $raf = $$dirInfo{RAF} or return 0;
+    my $buff;
+    my $verbose = $exifTool->Options('Verbose');
+    my $buildMakerNotes = $exifTool->Options('MakerNotes');
+
+    # 4 bytes at end of block give directory position within block
+    $raf->Seek($blockStart+$blockSize-4, 0) or return 0;
+    $raf->Read($buff, 4) == 4 or return 0;
+    my $dirOffset = Get32u(\$buff,0) + $blockStart;
+    $raf->Seek($dirOffset, 0) or return 0;
+    $raf->Read($buff, 2) == 2 or return 0;
+    my $entries = Get16u(\$buff,0);         # get number of entries in directory
+    # read the directory (10 bytes per entry)
+    $raf->Read($buff, 10 * $entries) == 10 * $entries or return 0;
+
+    $verbose and $exifTool->VerboseDir('Raw', $entries);
+    my $index;
+    for ($index=0; $index<$entries; ++$index) {
+        my $pt = 10 * $index;
+        my $tag = Get16u(\$buff, $pt);
+        my $size = Get32u(\$buff, $pt+2);
+        my $valuePtr = Get32u(\$buff, $pt+6);
+        my $ptr = $valuePtr + $blockStart;  # all pointers relative to block start
+        if ($tag & 0x8000) {
+            $exifTool->Warn('Bad CRW directory entry');
+            return 1;
+        }
+        my $tagID = $tag & 0x3fff;          # get tag ID
+        my $tagType = ($tag >> 8) & 0x38;   # get tag type
+        my $valueInDir = ($tag & 0x4000);   # flag for value in directory
+        my $tagInfo = $exifTool->GetTagInfo($rawTagTable, $tagID);
+        if (($tagType==0x28 or $tagType==0x30) and not $valueInDir) {
+            # this type of tag specifies a raw subdirectory
+            my $name;
+            $tagInfo and $name = $$tagInfo{Name};
+            $name or $name = sprintf("CanonRaw_0x%.4x", $tag);
+            my %subdirInfo = (
+                DirName  => $name,
+                DataLen  => 0,
+                DirStart => $ptr,
+                DirLen   => $size,
+                Nesting  => $$dirInfo{Nesting} + 1,
+                RAF      => $raf,
+                Parent   => $$dirInfo{DirName},
+            );
+            if ($verbose) {
+                my $fakeInfo = { Name => $name, SubDirectory => { } };
+                $exifTool->VerboseInfo($tagID, $fakeInfo,
+                    'Index'  => $index,
+                    'Size'   => $size,
+                    'Start'  => $ptr,
+                );
+            }
+            $exifTool->ProcessDirectory(\%subdirInfo, $rawTagTable);
+            next;
+        }
+        my ($valueDataPos, $count, $subdir);
+        my $format = $crwTagFormat{$tagType};
+        if ($tagInfo) {
+            $subdir = $$tagInfo{SubDirectory};
+            $format = $$tagInfo{Format} if $$tagInfo{Format};
+            $count = $$tagInfo{Count};
+        }
+        # get value data
+        my ($value, $delRawConv);
+        if ($valueInDir) {  # is the value data in the directory?
+            # this type of tag stores the value in the 'size' and 'ptr' fields
+            $valueDataPos = $dirOffset + $pt + 4;
+            $size = 8;
+            $value = substr($buff, $pt+2, $size);
+            # set count to 1 by default for normal values in directory
+            $count = 1 if not defined $count and $format and
+                          $format ne 'string' and not $subdir;
+        } else {
+            $valueDataPos = $ptr;
+            if ($size <= 512 or ($verbose > 2 and $size <= 65536)
+                or ($tagInfo and ($$tagInfo{SubDirectory}
+                or grep(/^$$tagInfo{Name}$/i, $exifTool->GetRequestedTags()) )))
+            {
+                # read value if size is small or specifically requested
+                # or if this is a SubDirectory
+                unless ($raf->Seek($ptr, 0) and $raf->Read($value, $size) == $size) {
+                    $exifTool->Warn(sprintf("Error reading %d bytes from 0x%x",$size,$ptr));
+                    next;
+                }
+            } else {
+                $value = "Binary data $size bytes";
+                if ($tagInfo) {
+                    if ($exifTool->Options('Binary')) {
+                        # read the value anyway
+                        unless ($raf->Seek($ptr, 0) and $raf->Read($value, $size) == $size) {
+                            $exifTool->Warn(sprintf("Error reading %d bytes from 0x%x",$size,$ptr));
+                            next;
+                        }
+                    }
+                    # force this to be a binary (scalar reference)
+                    $$tagInfo{RawConv} = '\$val';
+                    $delRawConv = 1;
+                }
+                $size = length $value;
+                undef $format;
+            }
+        }
+        # set count from tagInfo count if necessary
+        if ($format and not $count) {
+            # set count according to format and size
+            my $fnum = $Image::ExifTool::Exif::formatNumber{$format};
+            my $fsiz = $Image::ExifTool::Exif::formatSize[$fnum];
+            $count = int($size / $fsiz);
+        }
+        if ($verbose) {
+            my $val = $value;
+            $format and $val = ReadValue(\$val, 0, $format, $count, $size);
+            $exifTool->VerboseInfo($tagID, $tagInfo,
+                Table   => $rawTagTable,
+                Index   => $index,
+                Value   => $val,
+                DataPt  => \$value,
+                DataPos => $valueDataPos,
+                Size    => $size,
+                Format  => $format,
+                Count   => $count,
+            );
+        }
+        if ($buildMakerNotes) {
+            # build maker notes information if requested
+            BuildMakerNotes($exifTool, $tagID, $tagInfo, \$value, $format, $count);
+        }
+        next unless defined $tagInfo;
+
+        if ($subdir) {
+            my $name = $$tagInfo{Name};
+            my $newTagTable;
+            if ($$subdir{TagTable}) {
+                $newTagTable = Image::ExifTool::GetTagTable($$subdir{TagTable});
+                unless ($newTagTable) {
+                    warn "Unknown tag table $$subdir{TagTable}\n";
+                    next;
+                }
+            } else {
+                warn "Must specify TagTable for SubDirectory $name\n";
+                next;
+            }
+            my $subdirStart = 0;
+            #### eval Start ()
+            $subdirStart = eval $$subdir{Start} if $$subdir{Start};
+            my $dirData = \$value;
+            my %subdirInfo = (
+                Name     => $name,
+                DataPt   => $dirData,
+                DataLen  => $size,
+                DataPos  => $valueDataPos,
+                DirStart => $subdirStart,
+                DirLen   => $size - $subdirStart,
+                Nesting  => $$dirInfo{Nesting} + 1,
+                RAF      => $raf,
+                Parent   => $$dirInfo{DirName},
+            );
+            #### eval Validate ($dirData, $subdirStart, $size)
+            if (defined $$subdir{Validate} and not eval $$subdir{Validate}) {
+                $exifTool->Warn("Invalid $name data");
+            } else {
+                $exifTool->ProcessDirectory(\%subdirInfo, $newTagTable, $$subdir{ProcessProc});
+            }
+        } else {
+            # convert to specified format if necessary
+            $format and $value = ReadValue(\$value, 0, $format, $count, $size);
+            # save the information
+            $exifTool->FoundTag($tagInfo, $value);
+            delete $$tagInfo{RawConv} if $delRawConv;
+        }
+    }
+    return 1;
+}
+
+#------------------------------------------------------------------------------
+# get information from raw file
+# Inputs: 0) ExifTool object reference, 1) dirInfo reference
+# Returns: 1 if this was a valid Canon RAW file
+sub ProcessCRW($$)
+{
+    my ($exifTool, $dirInfo) = @_;
+    my ($buff, $sig);
+    my $raf = $$dirInfo{RAF};
+    my $buildMakerNotes = $exifTool->Options('MakerNotes');
+
+    $raf->Read($buff,2) == 2      or return 0;
+    SetByteOrder($buff)           or return 0;
+    $raf->Read($buff,4) == 4      or return 0;
+    $raf->Read($sig,8) == 8       or return 0;  # get file signature
+    $sig =~ /^HEAP(CCDR|JPGM)/    or return 0;  # validate signature
+    my $hlen = Get32u(\$buff, 0);
+
+    $raf->Seek(0, 2)              or return 0;  # seek to end of file
+    my $filesize = $raf->Tell()   or return 0;
+
+    # initialize maker note data if building maker notes
+    $buildMakerNotes and InitMakerNotes($exifTool);
+
+    # set the FileType tag unless already done (ie. APP0 CIFF record in JPEG image)
+    $exifTool->SetFileType() unless $exifTool->GetValue('FileType');
+
+    # build directory information for main raw directory
+    my %dirInfo = (
+        DataLen  => 0,
+        DirStart => $hlen,
+        DirLen   => $filesize - $hlen,
+        Nesting  => 0,
+        RAF      => $raf,
+        Parent   => 'CRW',
+    );
+
+    # process the raw directory
+    my $rawTagTable = Image::ExifTool::GetTagTable('Image::ExifTool::CanonRaw::Main');
+    unless ($exifTool->ProcessDirectory(\%dirInfo, $rawTagTable)) {
+        $exifTool->Warn('CRW file format error');
+    }
+
+    # finish building maker notes if necessary
+    $buildMakerNotes and SaveMakerNotes($exifTool);
+
+    # process trailers if they exist
+    my $trailInfo = Image::ExifTool::IdentifyTrailer($raf);
+    $exifTool->ProcessTrailers($trailInfo) if $trailInfo;
+
+    return 1;
+}
+
+1;  # end
+
+__END__
+
+=head1 NAME
+
+Image::ExifTool::CanonRaw - Read Canon RAW (CRW) meta information
+
+=head1 SYNOPSIS
+
+This module is loaded automatically by Image::ExifTool when required.
+
+=head1 DESCRIPTION
+
+This module contains definitions required by Image::ExifTool to interpret
+meta information from Canon CRW raw files.  These files are written directly
+by some Canon cameras, and contain meta information similar to that found in
+the EXIF Canon maker notes.
+
+=head1 NOTES
+
+The CR2 format written by some Canon cameras is very different the CRW
+format processed by this module.  (CR2 is TIFF-based and uses standard EXIF
+tags.)
+
+=head1 AUTHOR
+
+Copyright 2003-2007, Phil Harvey (phil at owl.phy.queensu.ca)
+
+This library is free software; you can redistribute it and/or modify it
+under the same terms as Perl itself.
+
+=head1 REFERENCES
+
+=over 4
+
+=item L<http://www.cybercom.net/~dcoffin/dcraw/>
+
+=item L<http://www.wonderland.org/crw/>
+
+=item L<http://xyrion.org/ciff/>
+
+=item L<http://owl.phy.queensu.ca/~phil/exiftool/canon_raw.html>
+
+=back
+
+=head1 SEE ALSO
+
+L<Image::ExifTool::TagNames/CanonRaw Tags>,
+L<Image::ExifTool::Canon(3pm)|Image::ExifTool::Canon>,
+L<Image::ExifTool(3pm)|Image::ExifTool>
+
+=cut
+
Index: /gsdl/trunk/perllib/cpan/Image/ExifTool/CanonVRD.pm
===================================================================
--- /gsdl/trunk/perllib/cpan/Image/ExifTool/CanonVRD.pm	(revision 16842)
+++ /gsdl/trunk/perllib/cpan/Image/ExifTool/CanonVRD.pm	(revision 16842)
@@ -0,0 +1,703 @@
+#------------------------------------------------------------------------------
+# File:         CanonVRD.pm
+#
+# Description:  Read/write Canon VRD information
+#
+# Revisions:    10/30/2006 - P. Harvey Created
+#------------------------------------------------------------------------------
+
+package Image::ExifTool::CanonVRD;
+
+use strict;
+use vars qw($VERSION);
+use Image::ExifTool qw(:DataAccess :Utils);
+
+$VERSION = '1.05';
+
+sub ProcessCanonVRD($$);
+
+my $debug;  # set this to 1 for offsets relative to binary data start
+
+my %noYes = ( 0 => 'No', 1 => 'Yes' );
+
+# main tag table IPTC-format records in CanonVRD trailer
+%Image::ExifTool::CanonVRD::Main = (
+    VARS => { INDEX => 1 },
+    NOTES => q{
+        Canon Digital Photo Professional writes VRD (Recipe Data) information as a
+        trailer record to JPEG, TIFF, CRW and CR2 images, or as a stand-alone VRD
+        file. The tags listed below represent information found in this record.  The
+        complete VRD data record may be accessed as a block using the Extra
+        'CanonVRD' tag, but this tag is not extracted or copied unless specified
+        explicitly.
+    },
+    0 => {
+        Name => 'VRD1',
+        Size => 0x272,  # size of version 1.0 edit information in bytes
+        SubDirectory => { TagTable => 'Image::ExifTool::CanonVRD::Ver1' },
+    },
+    1 => {
+        Name => 'VRDStampTool',
+        # (size is variable, and obtained from int32u at end of last directory)
+        Size => 0,      # size is variable, and obtained from int32u at directory start
+        SubDirectory => { TagTable => 'Image::ExifTool::CanonVRD::StampTool' },
+    },
+    2 => {
+        Name => 'VRD2',
+        Size => undef,  # size is the remaining edit data
+        SubDirectory => { TagTable => 'Image::ExifTool::CanonVRD::Ver2' },
+    },
+);
+
+# VRD version 1 tags
+%Image::ExifTool::CanonVRD::Ver1 = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
+    CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
+    WRITABLE => 1,
+    GROUPS => { 2 => 'Image' },
+#
+# RAW image adjustment
+#
+    0x002 => {
+        Name => 'VRDVersion',
+        Format => 'int16u',
+        Writable => 0,
+        PrintConv => 'sprintf("%.2f", $val / 100)',
+    },
+    # 0x006 related somehow to RGB levels
+    0x008 => {
+        Name => 'WBAdjRGBLevels',
+        Format => 'int16u[3]',
+    },
+    0x018 => {
+        Name => 'WhiteBalanceAdj',
+        Format => 'int16u',
+        PrintConv => {
+            0 => 'Auto',
+            1 => 'Daylight',
+            2 => 'Cloudy',
+            3 => 'Tungsten',
+            4 => 'Fluorescent',
+            5 => 'Flash',
+            8 => 'Shade',
+            9 => 'Kelvin',
+            30 => 'Manual (Click)',
+            31 => 'Shot Settings',
+        },
+    },
+    0x01a => {
+        Name => 'WBAdjColorTemp',
+        Format => 'int16u',
+    },
+    # 0x01c similar to 0x006
+    # 0x01e similar to 0x008
+    0x024 => {
+        Name => 'WBFineTuneActive',
+        Format => 'int16u',
+        PrintConv => \%noYes,
+    },
+    0x028 => {
+        Name => 'WBFineTuneSaturation',
+        Format => 'int16u',
+    },
+    0x02c => {
+        Name => 'WBFineTuneTone',
+        Format => 'int16u',
+    },
+    0x02e => {
+        Name => 'RawColorAdj',
+        Format => 'int16u',
+        PrintConv => {
+            0 => 'Shot Settings',
+            1 => 'Faithful',
+            2 => 'Custom',
+        },
+    },
+    0x030 => {
+        Name => 'RawCustomSaturation',
+        Format => 'int32s',
+    },
+    0x034 => {
+        Name => 'RawCustomTone',
+        Format => 'int32s',
+    },
+    0x038 => {
+        Name => 'RawBrightnessAdj',
+        Format => 'int32s',
+        ValueConv => '$val / 6000',
+        ValueConvInv => 'int($val * 6000 + ($val < 0 ? -0.5 : 0.5))',
+        PrintConv => 'sprintf("%.2f",$val)',
+        PrintConvInv => '$val',
+    },
+    0x03c => {
+        Name => 'ToneCurveProperty',
+        Format => 'int16u',
+        PrintConv => {
+            0 => 'Shot Settings',
+            1 => 'Linear',
+            2 => 'Custom 1',
+            3 => 'Custom 2',
+            4 => 'Custom 3',
+            5 => 'Custom 4',
+            6 => 'Custom 5',
+        },
+    },
+    # 0x040 usually "10 9 2"
+    0x07a => {
+        Name => 'DynamicRangeMin',
+        Format => 'int16u',
+    },
+    0x07c => {
+        Name => 'DynamicRangeMax',
+        Format => 'int16u',
+    },
+    # 0x0c6 usually "10 9 2"
+#
+# RGB image adjustment
+#
+    0x110 => {
+        Name => 'ToneCurveActive',
+        Format => 'int16u',
+        PrintConv => \%noYes,
+    },
+    0x114 => {
+        Name => 'BrightnessAdj',
+        Format => 'int8s',
+    },
+    0x115 => {
+        Name => 'ContrastAdj',
+        Format => 'int8s',
+    },
+    0x116 => {
+        Name => 'SaturationAdj',
+        Format => 'int16s',
+    },
+    0x11e => {
+        Name => 'ColorToneAdj',
+        Notes => 'in degrees, so -1 is the same as 359',
+        Format => 'int32s',
+    },
+    0x160 => {
+        Name => 'RedCurvePoints',
+        Format => 'int16u[21]',
+        PrintConv => 'Image::ExifTool::CanonVRD::ToneCurvePrint($val)',
+        PrintConvInv => 'Image::ExifTool::CanonVRD::ToneCurvePrintInv($val)',
+    },
+    0x19a => {
+        Name => 'GreenCurvePoints',
+        Format => 'int16u[21]',
+        PrintConv => 'Image::ExifTool::CanonVRD::ToneCurvePrint($val)',
+        PrintConvInv => 'Image::ExifTool::CanonVRD::ToneCurvePrintInv($val)',
+    },
+    0x1d4 => {
+        Name => 'BlueCurvePoints',
+        Format => 'int16u[21]',
+        PrintConv => 'Image::ExifTool::CanonVRD::ToneCurvePrint($val)',
+        PrintConvInv => 'Image::ExifTool::CanonVRD::ToneCurvePrintInv($val)',
+    },
+    0x18a => {
+        Name => 'RedCurveLimits',
+        Notes => '4 numbers: input and output highlight and shadow points',
+        Format => 'int16u[4]',
+    },
+    0x1c4 => {
+        Name => 'GreenCurveLimits',
+        Format => 'int16u[4]',
+    },
+    0x1fe => {
+        Name => 'BlueCurveLimits',
+        Format => 'int16u[4]',
+    },
+    0x20e => {
+        Name => 'RGBCurvePoints',
+        Format => 'int16u[21]',
+        PrintConv => 'Image::ExifTool::CanonVRD::ToneCurvePrint($val)',
+        PrintConvInv => 'Image::ExifTool::CanonVRD::ToneCurvePrintInv($val)',
+    },
+    0x238 => {
+        Name => 'RGBCurveLimits',
+        Format => 'int16u[4]',
+    },
+    0x244 => {
+        Name => 'CropActive',
+        Format => 'int16u',
+        PrintConv => \%noYes,
+    },
+    0x246 => {
+        Name => 'CropLeft',
+        Notes => 'crop coordinates in original unrotated image',
+        Format => 'int16u',
+    },
+    0x248 => {
+        Name => 'CropTop',
+        Format => 'int16u',
+    },
+    0x24a => {
+        Name => 'CropWidth',
+        Format => 'int16u',
+    },
+    0x24c => {
+        Name => 'CropHeight',
+        Format => 'int16u',
+    },
+    0x260 => {
+        Name => 'CropAspectRatio',
+        Format => 'int16u',
+        PrintConv => {
+            0 => 'Free',
+            1 => '3:2',
+            2 => '2:3',
+            3 => '4:3',
+            4 => '3:4',
+            5 => 'A-size Landscape',
+            6 => 'A-size Portrait',
+            7 => 'Letter-size Landscape',
+            8 => 'Letter-size Portrait',
+            9 => '4:5',
+            10 => '5:4',
+            11 => '1:1',
+        },
+    },
+    0x262 => {
+        Name => 'ConstrainedCropWidth',
+        Format => 'float',
+        PrintConv => 'sprintf("%.7g",$val)',
+        PrintConvInv => '$val',
+    },
+    0x266 => {
+        Name => 'ConstrainedCropHeight',
+        Format => 'float',
+        PrintConv => 'sprintf("%.7g",$val)',
+        PrintConvInv => '$val',
+    },
+    0x26a => {
+        Name => 'CheckMark',
+        Format => 'int16u',
+        PrintConv => {
+            0 => 'Clear',
+            1 => 1,
+            2 => 2,
+            3 => 3,
+        },
+    },
+    0x26e => {
+        Name => 'Rotation',
+        Format => 'int16u',
+        PrintConv => {
+            0 => 0,
+            1 => 90,
+            2 => 180,
+            3 => 270,
+        },
+    },
+    0x270 => {
+        Name => 'WorkColorSpace',
+        Format => 'int16u',
+        PrintConv => {
+            0 => 'sRGB',
+            1 => 'Adobe RGB',
+            2 => 'Wide Gamut RGB',
+            3 => 'Apple RGB',
+            4 => 'ColorMatch RGB',
+        },
+    },
+    # (VRD 1.0 edit data ends here -- 0x272 bytes long)
+);
+
+# VRD Stamp Tool tags
+%Image::ExifTool::CanonVRD::StampTool = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    GROUPS => { 2 => 'Image' },
+    0x00 => {
+        Name => 'StampToolCount',
+        Format => 'int32u',
+    },
+);
+
+# VRD version 2 and 3 tags
+%Image::ExifTool::CanonVRD::Ver2 = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
+    CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
+    WRITABLE => 1,
+    GROUPS => { 2 => 'Image' },
+    NOTES => 'Tags added in DPP version 2.0 and later.',
+    0x04 => {
+        Name => 'PictureStyle',
+        Format => 'int16u',
+        PrintConv => {
+            0 => 'Standard',
+            1 => 'Portrait',
+            2 => 'Landscape',
+            3 => 'Neutral',
+            4 => 'Faithful',
+            5 => 'Monochrome',
+        },
+    },
+    0x1a => {
+        Name => 'RawColorToneAdj',
+        Format => 'int16s',
+    },
+    0x1c => {
+        Name => 'RawSaturationAdj',
+        Format => 'int16s',
+    },
+    0x1e => {
+        Name => 'RawContrastAdj',
+        Format => 'int16s',
+    },
+    0x20 => {
+        Name => 'RawLinear',
+        Format => 'int16u',
+        PrintConv => \%noYes,
+    },
+    0x22 => {
+        Name => 'RawSharpnessAdj',
+        Format => 'int16s',
+    },
+    0x24 => {
+        Name => 'RawHighlightPoint',
+        Format => 'int16s',
+    },
+    0x26 => {
+        Name => 'RawShadowPoint',
+        Format => 'int16s',
+    },
+    0x74 => {
+        Name => 'MonochromeFilterEffect',
+        Format => 'int16s',
+        PrintConv => {
+            -2 => 'None',
+            -1 => 'Yellow',
+            0 => 'Orange',
+            1 => 'Red',
+            2 => 'Green',
+        },
+    },
+    0x76 => {
+        Name => 'MonochromeToningEffect',
+        Format => 'int16s',
+        PrintConv => {
+            -2 => 'None',
+            -1 => 'Sepia',
+            0 => 'Blue',
+            1 => 'Purple',
+            2 => 'Green',
+        },
+    },
+    0x78 => {
+        Name => 'MonochromeContrast',
+        Format => 'int16s',
+    },
+    0x7a => {
+        Name => 'MonochromeLinear',
+        Format => 'int16u',
+        PrintConv => \%noYes,
+    },
+    0x7c => {
+        Name => 'MonochromeSharpness',
+        Format => 'int16s',
+    },
+    # (VRD 2.0 edit data ends here -- offset 0xb2)
+    0xbc => {
+        Name => 'ChrominanceNoiseReduction',
+        Format => 'int16u',
+        PrintConv => {
+            0   => 'Off',
+            58  => 'Low',
+            100 => 'High',
+        },
+    },
+    0xbe => {
+        Name => 'LuminanceNoiseReduction',
+        Format => 'int16u',
+        PrintConv => {
+            0   => 'Off',
+            65  => 'Low',
+            100 => 'High',
+        },
+    },
+    0xc0 => {
+        Name => 'ChrominanceNR_TIFF_JPEG',
+        Format => 'int16u',
+        PrintConv => {
+            0   => 'Off',
+            33  => 'Low',
+            100 => 'High',
+        },
+    }
+    # (VRD 3.0 edit data ends here -- offset 0xc4)
+);
+
+#------------------------------------------------------------------------------
+# Tone curve print conversion
+sub ToneCurvePrint($)
+{
+    my $val = shift;
+    my @vals = split ' ', $val;
+    return $val unless @vals == 21;
+    my $n = shift @vals;
+    return $val unless $n >= 2 and $n <= 10;
+    $val = '';
+    while ($n--) {
+        $val and $val .= ' ';
+        $val .= '(' . shift(@vals) . ',' . shift(@vals) . ')';
+    }
+    return $val;
+}
+
+#------------------------------------------------------------------------------
+# Inverse print conversion for tone curve
+sub ToneCurvePrintInv($)
+{
+    my $val = shift;
+    my @vals = ($val =~ /\((\d+),(\d+)\)/g);
+    return undef unless @vals >= 4 and @vals <= 20 and not @vals & 0x01;
+    unshift @vals, scalar(@vals) / 2;
+    while (@vals < 21) { push @vals, 0 }
+    return join(' ',@vals);
+}
+
+#------------------------------------------------------------------------------
+# Read/write Canon VRD file
+# Inputs: 0) ExifTool object reference, 1) dirInfo reference
+# Returns: 1 if this was a Canon VRD file, 0 otherwise, -1 on write error
+sub ProcessVRD($$)
+{
+    my ($exifTool, $dirInfo) = @_;
+    my $raf = $$dirInfo{RAF};
+    my $buff;
+    my $num = $raf->Read($buff, 0x1c);
+    if (not $num and $$dirInfo{OutFile}) {
+        # create new VRD file from scratch
+        my $newVal = $exifTool->GetNewValues('CanonVRD');
+        if ($newVal) {
+            $exifTool->VPrint(0, "  Writing CanonVRD as a block\n");
+            Write($$dirInfo{OutFile}, $newVal) or return -1;
+            ++$exifTool->{CHANGED};
+        } else {
+            $exifTool->Error('No CanonVRD information to write');
+        }
+    } else {
+        $num == 0x1c or return 0;
+        $buff =~ /^CANON OPTIONAL DATA\0/ or return 0;
+        $exifTool->SetFileType();
+        $$dirInfo{DirName} = 'CanonVRD';    # set directory name for verbose output
+        my $result = ProcessCanonVRD($exifTool, $dirInfo);
+        return $result if $result < 0;
+        $result or $exifTool->Warn('Format error in VRD file');
+    }
+    return 1;
+}
+
+#------------------------------------------------------------------------------
+# Read/write CanonVRD information
+# Inputs: 0) ExifTool object reference, 1) dirInfo reference
+# Returns: 1 on success, 0 not valid VRD, or -1 error writing
+# - updates DataPos to point to start of CanonVRD information
+# - updates DirLen to trailer length
+sub ProcessCanonVRD($$)
+{
+    my ($exifTool, $dirInfo) = @_;
+    my $raf = $$dirInfo{RAF};
+    my $offset = $$dirInfo{Offset} || 0;
+    my $outfile = $$dirInfo{OutFile};
+    my $verbose = $exifTool->Options('Verbose');
+    my $out = $exifTool->Options('TextOut');
+    my ($buff, $footer, $header, $err);
+    my ($blockLen, $blockType, $recLen, $size);
+
+    # read and validate the trailer footer
+    $raf->Seek(-64-$offset, 2)    or return 0;
+    $raf->Read($footer, 64) == 64 or return 0;
+    $footer =~ /^CANON OPTIONAL DATA\0(.{4})/s or return 0;
+    $size = unpack('N', $1);
+
+    # read and validate the header too
+    # (header is 0x1c bytes and footer is 0x40 bytes)
+    unless ($size > 12 and ($size & 0x80000000) == 0 and
+            $raf->Seek(-$size-0x5c, 1) and
+            $raf->Read($header, 0x1c) == 0x1c and
+            $header =~ /^CANON OPTIONAL DATA\0/ and
+            $raf->Read($buff, $size) == $size)
+    {
+        $exifTool->Warn('Bad CanonVRD trailer');
+        return 0;
+    }
+    # extract CanonVRD block if Binary option set, or if requested
+    if ($exifTool->{OPTIONS}->{Binary} or $exifTool->{REQ_TAG_LOOKUP}->{canonvrd}) {
+        $exifTool->FoundTag('CanonVRD', $header . $buff . $footer);
+    }
+    # set variables returned in dirInfo hash
+    $$dirInfo{DataPos} = $raf->Tell() - $size - 0x1c;
+    $$dirInfo{DirLen} = $size + 0x5c;
+
+    if ($outfile) {
+        # delete CanonVRD information if specified
+        if ($exifTool->{DEL_GROUP}->{CanonVRD} or $exifTool->{DEL_GROUP}->{Trailer} or
+            # also delete if writing as a block (will get added back again later)
+            $exifTool->{NEW_VALUE}->{$Image::ExifTool::Extra{CanonVRD}})
+        {
+            if ($exifTool->{FILE_TYPE} eq 'VRD') {
+                my $newVal = $exifTool->GetNewValues('CanonVRD');
+                if ($newVal) {
+                    $verbose and printf $out "  Writing CanonVRD as a block\n";
+                    Write($outfile, $newVal) or return -1;
+                    ++$exifTool->{CHANGED};
+                } else {
+                    $exifTool->Error("Can't delete all CanonVRD information from a VRD file");
+                }
+            } else {
+                $verbose and printf $out "  Deleting CanonVRD trailer\n";
+                ++$exifTool->{CHANGED};
+            }
+            return 1;
+        }
+        # write now and return if CanonVRD was set as a block
+        my $val = $exifTool->GetNewValues('CanonVRD');
+        if ($val) {
+            $verbose and print $out "  Writing CanonVRD as a block\n";
+            Write($outfile, $val) or return -1;
+            ++$exifTool->{CHANGED};
+            return 1;
+        }
+    } elsif ($verbose or $exifTool->{HTML_DUMP}) {
+        $exifTool->DumpTrailer($dirInfo);
+    }
+
+    # validate VRD trailer and get position and length of edit record
+    SetByteOrder('MM');
+    my $pos = 0;
+    my $vrdPos = $$dirInfo{DataPos} + length($header);
+Block: for (;;) {
+        if ($pos + 8 > $size) {
+            last if $pos == $size;
+            $blockLen = $size;  # mark as invalid
+        } else {
+            $blockType = Get32u(\$buff, $pos);
+            $blockLen = Get32u(\$buff, $pos + 4);
+        }
+        $pos += 8;          # move to start of block
+        if ($pos + $blockLen > $size) {
+            $exifTool->Warn('Possibly corrupt CanonVRD block');
+            last;
+        }
+        printf $out "  CanonVRD block %x ($blockLen bytes at offset 0x%x)\n",
+            $blockType, $pos + $vrdPos if $verbose > 1 and not $outfile;
+        # process edit-data block
+        if ($blockType == 0xffff00f4) {
+            my $blockEnd = $pos + $blockLen;
+            my $recNum;
+            for ($recNum=0;; ++$recNum, $pos+=$recLen) {
+                if ($pos + 4 > $blockEnd) {
+                    last Block if $pos == $blockEnd;  # all done if we arrived at end
+                    $recLen = $blockEnd;    # mark as invalid
+                } else {
+                    $recLen = Get32u(\$buff, $pos);
+                }
+                $pos += 4;      # move to start of record
+                if ($pos + $recLen > $blockEnd) {
+                    $exifTool->Warn('Possibly corrupt CanonVRD record');
+                    last Block;
+                }
+                printf $out "  CanonVRD record ($recLen bytes at offset 0x%x)\n",
+                    $pos + $vrdPos if $verbose > 1 and not $outfile;
+
+                # our edit information is the first record
+                # (don't process if simply deleting VRD)
+                next if $recNum;
+
+                # process VRD edit information
+                my $tagTablePtr = GetTagTable('Image::ExifTool::CanonVRD::Main');
+                my $index;
+                my $editData = substr($buff, $pos, $recLen);
+                my %subdirInfo = (
+                    DataPt => \$editData,
+                    DataPos => $debug ? 0 : $vrdPos + $pos,
+                );
+                my $start = 0;
+                for ($index=0; ; ++$index) {
+                    my $tagInfo = $$tagTablePtr{$index} or last;
+                    my $dirLen;
+                    my $maxLen = $recLen - $start;
+                    if ($$tagInfo{Size}) {
+                        $dirLen = $$tagInfo{Size};
+                    } elsif (defined $$tagInfo{Size}) {
+                        # get size from int32u at $start
+                        last unless $start + 4 <= $recLen;
+                        $dirLen = Get32u(\$editData, $start);
+                        $start += 4; # skip the length word
+                    } else {
+                        $dirLen = $maxLen;
+                    }
+                    $dirLen > $maxLen and $dirLen = $maxLen;
+                    if ($dirLen) {
+                        my $subTable = GetTagTable($tagInfo->{SubDirectory}->{TagTable});
+                        my $subName = $$tagInfo{Name};
+                        $subdirInfo{DirStart} = $start;
+                        $subdirInfo{DirLen} = $dirLen;
+                        $subdirInfo{DirName} = $subName;
+                        if ($outfile) {
+                            # rewrite CanonVRD information
+                            $verbose and print $out "  Rewriting Canon $subName\n";
+                            my $newVal = $exifTool->WriteDirectory(\%subdirInfo, $subTable);
+                            substr($buff, $pos+$start, $dirLen) = $newVal if $newVal;
+                        } else {
+                            Image::ExifTool::HexDump(\$editData, $dirLen,
+                                Start  => $start,
+                                Addr   => 0,
+                                Prefix => $$tagInfo{Name}
+                            ) if $debug;
+                            # extract CanonVRD information
+                            $exifTool->ProcessDirectory(\%subdirInfo, $subTable);
+                        }
+                    }
+                    # next directory starts at the end of this one
+                    $start += $dirLen;
+                }
+            }
+        } else {
+            $pos += $blockLen;  # skip other blocks
+        }
+    }
+    # write CanonVRD trailer
+    Write($outfile, $header, $buff, $footer) or $err = 1 if $outfile;
+
+    undef $buff;
+    return $err ? -1 : 1;
+}
+
+1;  # end
+
+__END__
+
+=head1 NAME
+
+Image::ExifTool::CanonVRD - Read/write Canon VRD information
+
+=head1 SYNOPSIS
+
+This module is used by Image::ExifTool
+
+=head1 DESCRIPTION
+
+This module contains definitions required by Image::ExifTool to read and
+write VRD Recipe Data information as written by the Canon Digital Photo
+Professional software.  This information is written to VRD files, and as a
+trailer in JPEG, CRW and CR2 images.
+
+=head1 AUTHOR
+
+Copyright 2003-2007, Phil Harvey (phil at owl.phy.queensu.ca)
+
+This library is free software; you can redistribute it and/or modify it
+under the same terms as Perl itself.
+
+=head1 SEE ALSO
+
+L<Image::ExifTool::TagNames/CanonVRD Tags>,
+L<Image::ExifTool(3pm)|Image::ExifTool>
+
+=cut
+
Index: /gsdl/trunk/perllib/cpan/Image/ExifTool/Casio.pm
===================================================================
--- /gsdl/trunk/perllib/cpan/Image/ExifTool/Casio.pm	(revision 16842)
+++ /gsdl/trunk/perllib/cpan/Image/ExifTool/Casio.pm	(revision 16842)
@@ -0,0 +1,583 @@
+#------------------------------------------------------------------------------
+# File:         Casio.pm
+#
+# Description:  Casio EXIF maker notes tags
+#
+# Revisions:    12/09/2003 - P. Harvey Created
+#               09/10/2004 - P. Harvey Added MakerNote2 (thanks to Joachim Loehr)
+#
+# References:   1) http://park2.wakwak.com/~tsuruzoh/Computer/Digicams/exif-e.html
+#               2) Joachim Loehr private communication
+#               3) http://homepage3.nifty.com/kamisaka/makernote/makernote_casio.htm
+#               4) http://www.gvsoft.homedns.org/exif/makernote-casio.html
+#------------------------------------------------------------------------------
+
+package Image::ExifTool::Casio;
+
+use strict;
+use vars qw($VERSION);
+use Image::ExifTool::Exif;
+
+$VERSION = '1.21';
+
+# older Casio maker notes (ref 1)
+%Image::ExifTool::Casio::Main = (
+    WRITE_PROC => \&Image::ExifTool::Exif::WriteExif,
+    CHECK_PROC => \&Image::ExifTool::Exif::CheckExif,
+    WRITABLE => 1,
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
+    0x0001 => {
+        Name => 'RecordingMode' ,
+        Writable => 'int16u',
+        PrintConv => {
+            1 => 'Single Shutter',
+            2 => 'Panorama',
+            3 => 'Night Scene',
+            4 => 'Portrait',
+            5 => 'Landscape',
+            7 => 'Panorama', #4
+            10 => 'Night Scene', #4
+            15 => 'Portrait', #4
+            16 => 'Landscape', #4
+        },
+    },
+    0x0002 => {
+        Name => 'Quality',
+        Writable => 'int16u',
+        PrintConv => { 1 => 'Economy', 2 => 'Normal', 3 => 'Fine' },
+    },
+    0x0003 => {
+        Name => 'FocusMode',
+        Writable => 'int16u',
+        PrintConv => {
+            2 => 'Macro',
+            3 => 'Auto',
+            4 => 'Manual',
+            5 => 'Infinity',
+            7 => 'Spot AF', #4
+        },
+    },
+    0x0004 => [
+        {
+            Name => 'FlashMode',
+            Condition => '$self->{CameraModel} =~ /^QV-(3500EX|8000SX)/',
+            Writable => 'int16u',
+            PrintConv => {
+                1 => 'Auto',
+                2 => 'On',
+                3 => 'Off',
+                4 => 'Off', #4
+                5 => 'Red-eye Reduction', #4
+            },
+        },
+        {
+            Name => 'FlashMode',
+            Writable => 'int16u',
+            PrintConv => {
+                1 => 'Auto',
+                2 => 'On',
+                3 => 'Off',
+                4 => 'Red-eye Reduction',
+            },
+        },
+    ],
+    0x0005 => {
+        Name => 'FlashIntensity',
+        Writable => 'int16u',
+        PrintConv => {
+            11 => 'Weak',
+            12 => 'Low', #4
+            13 => 'Normal',
+            14 => 'High', #4
+            15 => 'Strong',
+        },
+    },
+    0x0006 => {
+        Name => 'ObjectDistance',
+        Writable => 'int32u',
+        ValueConv => '$val / 1000', #4
+        ValueConvInv => '$val * 1000',
+        PrintConv => '"$val m"',
+        PrintConvInv => '$val=~s/\s*m$//;$val',
+    },
+    0x0007 => {
+        Name => 'WhiteBalance',
+        Writable => 'int16u',
+        PrintConv => {
+            1 => 'Auto',
+            2 => 'Tungsten',
+            3 => 'Daylight',
+            4 => 'Fluorescent',
+            5 => 'Shade',
+            129 => 'Manual',
+        },
+    },
+    # 0x0009 Bulb? (ref unknown)
+    0x000a => {
+        Name => 'DigitalZoom',
+        Writable => 'int32u',
+        PrintHex => 1,
+        PrintConv => {
+            0x10000 => 'Off',
+            0x10001 => '2x',
+            0x19999 => '1.6x', #4
+            0x20000 => '2x', #4
+            0x33333 => '3.2x', #4
+            0x40000 => '4x', #4
+        },
+    },
+    0x000b => {
+        Name => 'Sharpness',
+        Writable => 'int16u',
+        PrintConv => {
+            0 => 'Normal',
+            1 => 'Soft',
+            2 => 'Hard',
+            16 => 'Normal', #4
+            17 => '+1', #4
+            18 => '-1', #4
+         },
+    },
+    0x000c => {
+        Name => 'Contrast',
+        Writable => 'int16u',
+        PrintConv => {
+            0 => 'Normal',
+            1 => 'Low',
+            2 => 'High',
+            16 => 'Normal', #4
+            17 => '+1', #4
+            18 => '-1', #4
+        },
+    },
+    0x000d => {
+        Name => 'Saturation',
+        Writable => 'int16u',
+        PrintConv => {
+            0 => 'Normal',
+            1 => 'Low',
+            2 => 'High',
+            16 => 'Normal', #4
+            17 => '+1', #4
+            18 => '-1', #4
+        },
+    },
+    0x0014 => {
+        Name => 'ISO',
+        Writable => 'int16u',
+        Priority => 0,
+    },
+    0x0016 => { #4
+        Name => 'Enhancement',
+        Writable => 'int16u',
+        PrintConv => {
+            1 => 'Off',
+            2 => 'Red',
+            3 => 'Green',
+            4 => 'Blue',
+            5 => 'Flesh Tones',
+        },
+    },
+    0x0017 => { #4
+        Name => 'ColorFilter',
+        Writable => 'int16u',
+        PrintConv => {
+            1 => 'Off',
+            2 => 'Black & White',
+            3 => 'Sepia',
+            4 => 'Red',
+            5 => 'Green',
+            6 => 'Blue',
+            7 => 'Yellow',
+            8 => 'Pink',
+            9 => 'Purple',
+        },
+    },
+    0x0018 => { #4
+        Name => 'AFPoint',
+        Writable => 'int16u',
+        PrintConv => {
+            1 => 'Center',
+            2 => 'Upper Left',
+            3 => 'Upper Right',
+            4 => 'Near Left/Right of Center',
+            5 => 'Far Left/Right of Center',
+            6 => 'Far Left/Right of Center/Bottom',
+            7 => 'Top Near-Left',
+            8 => 'Near Upper/Left',
+            9 => 'Top Near-Right',
+            10 => 'Top Left',
+            11 => 'Top Center',
+            12 => 'Top Right',
+            13 => 'Center Left',
+            14 => 'Center Right',
+            15 => 'Bottom Left',
+            16 => 'Bottom Center',
+            17 => 'Bottom Right',
+        },
+    },
+    0x0019 => { #4
+        Name => 'FlashIntensity',
+        Writable => 'int16u',
+        PrintConv => {
+            1 => 'Normal',
+            2 => 'Weak',
+            3 => 'Strong',
+        },
+    },
+    0x0e00 => {
+        Name => 'PrintIM',
+        Description => 'Print Image Matching',
+        # crazy I know, but the offset for this value is entry-based
+        # (QV-2100, QV-2900UX, QV-3500EX and QV-4000) even though the
+        # offsets for other values isn't
+        EntryBased => 1,
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::PrintIM::Main',
+        },
+    },
+);
+
+# ref 2:
+%Image::ExifTool::Casio::Type2 = (
+    WRITE_PROC => \&Image::ExifTool::Exif::WriteExif,
+    CHECK_PROC => \&Image::ExifTool::Exif::CheckExif,
+    WRITABLE => 1,
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
+    0x0002 => {
+        Name => 'PreviewImageSize',
+        Groups => { 2 => 'Image' },
+        Writable => 'int16u',
+        Count => 2,
+        PrintConv => '$val =~ tr/ /x/; $val',
+        PrintConvInv => '$val =~ tr/x/ /; $val',
+    },
+    0x0003 => {
+        Name => 'PreviewImageLength',
+        Groups => { 2 => 'Image' },
+        OffsetPair => 0x0004, # point to associated offset
+        DataTag => 'PreviewImage',
+        Writable => 'int32u',
+        Protected => 2,
+    },
+    0x0004 => {
+        Name => 'PreviewImageStart',
+        Groups => { 2 => 'Image' },
+        Flags => 'IsOffset',
+        OffsetPair => 0x0003, # point to associated byte count
+        DataTag => 'PreviewImage',
+        Writable => 'int32u',
+        Protected => 2,
+    },
+    0x0008 => {
+        Name => 'QualityMode',
+        Writable => 'int16u',
+        PrintConv => {
+           0 => 'Economy',
+           1 => 'Normal',
+           2 => 'Fine',
+        },
+    },
+    0x0009 => {
+        Name => 'CasioImageSize',
+        Groups => { 2 => 'Image' },
+        Writable => 'int16u',
+        PrintConv => {
+            0 => '640x480',
+            4 => '1600x1200',
+            5 => '2048x1536',
+            20 => '2288x1712',
+            21 => '2592x1944',
+            22 => '2304x1728',
+            36 => '3008x2008',
+        },
+    },
+    0x000d => {
+        Name => 'FocusMode',
+        Writable => 'int16u',
+        PrintConv => {
+           0 => 'Normal',
+           1 => 'Macro',
+        },
+    },
+    0x0014 => {
+        Name => 'ISO',
+        Writable => 'int16u',
+        Priority => 0,
+        PrintConv => {
+           3 => 50,
+           4 => 64,
+           6 => 100,
+           9 => 200,
+        },
+    },
+    0x0019 => {
+        Name => 'WhiteBalance',
+        Writable => 'int16u',
+        PrintConv => {
+           0 => 'Auto',
+           1 => 'Daylight',
+           2 => 'Shade',
+           3 => 'Tungsten',
+           4 => 'Fluorescent',
+           5 => 'Manual',
+        },
+    },
+    0x001d => {
+        Name => 'FocalLength',
+        Writable => 'rational64u',
+        PrintConv => 'sprintf("%.1fmm",$val)',
+        PrintConvInv => '$val=~s/mm$//;$val',
+    },
+    0x001f => {
+        Name => 'Saturation',
+        Writable => 'int16u',
+        PrintConv => {
+           0 => 'Low',
+           1 => 'Normal',
+           2 => 'High',
+        },
+    },
+    0x0020 => {
+        Name => 'Contrast',
+        Writable => 'int16u',
+        PrintConv => {
+           0 => 'Low',
+           1 => 'Normal',
+           2 => 'High',
+        },
+    },
+    0x0021 => {
+        Name => 'Sharpness',
+        Writable => 'int16u',
+        PrintConv => {
+           0 => 'Soft',
+           1 => 'Normal',
+           2 => 'Hard',
+        },
+    },
+    0x0e00 => {
+        Name => 'PrintIM',
+        Description => 'Print Image Matching',
+        Writable => 0,
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::PrintIM::Main',
+        },
+    },
+    0x2000 => {
+        # this image data is also referenced by tags 3 and 4
+        # (nasty that they double-reference the image!)
+        %Image::ExifTool::previewImageTagInfo,
+    },
+    0x2001 => { #PH
+        # I downloaded images from 12 different EX-Z50 cameras, and they showed
+        # only 3 distinct dates here (2004:08:31 18:55, 2004:09:13 14:14, and
+        # 2004:11:26 17:07), so I'm guessing this is a firmware version date - PH
+        Name => 'FirmwareDate',
+        Writable => 'string',
+        Format => 'undef', # the 'string' contains nulls
+        Count => 18,
+        PrintConv => q{
+            $_ = $val;
+            if (/^(\d{2})(\d{2})\0\0(\d{2})(\d{2})\0\0(\d{2})\0{4}$/) {
+                my $yr = $1 + ($1 < 70 ? 2000 : 1900);
+                return "$yr:$2:$3 $4:$5";
+            }
+            tr/\0/./;  s/\.+$//;
+            return "Unknown ($_)";
+        },
+        PrintConvInv => q{
+            $_ = $val;
+            if (/^(19|20)(\d{2}):(\d{2}):(\d{2}) (\d{2}):(\d{2})$/) {
+                return "$2$3\0\0$4$5\0\0$6\0\0\0\0";
+            } elsif (/^Unknown\s*\((.*)\)$/i) {
+                $_ = $1;  tr/./\0/;
+                return $_;
+            } else {
+                return undef;
+            }
+        },
+    },
+    0x2011 => {
+        Name => 'WhiteBalanceBias',
+        Writable => 'int16u',
+        Count => 2,
+    },
+    0x2012 => {
+        Name => 'WhiteBalance',
+        Writable => 'int16u',
+        PrintConv => {
+           0 => 'Manual',
+           1 => 'Daylight', #3
+           3 => 'Shade', #3
+           4 => 'Flash?',
+           6 => 'Fluorescent', #3
+           10 => 'Tungsten', #3
+           12 => 'Flash',
+        },
+    },
+    0x2022 => {
+        Name => 'ObjectDistance',
+        Writable => 'int32u',
+        ValueConv => '$val >= 0x20000000 ? "inf" : $val / 1000',
+        ValueConvInv => '$val eq "inf" ? 0x20000000 : $val * 1000',
+        PrintConv => '$val eq "inf" ? $val : "$val m"',
+        PrintConvInv => '$val=~s/\s*m$//;$val',
+    },
+    # 0x2023 looks interesting (values 0,1,2,3,5 in samples) - PH
+    0x2034 => {
+        Name => 'FlashDistance',
+        Writable => 'int16u',
+    },
+    0x3000 => {
+        Name => 'RecordMode',
+        Writable => 'int16u',
+        PrintConv => {
+            2 => 'Program AE', #3
+            3 => 'Shutter Priority', #3
+            4 => 'Aperture Priority', #3
+            5 => 'Manual', #3
+            6 => 'Best Shot', #3
+        },
+    },
+    # 0x3001 is ShutterMode according to ref 3!
+    0x3001 => {
+        Name => 'SelfTimer',
+        Writable => 'int16u',
+        PrintConv => { 1 => 'Off' },
+    },
+    0x3002 => {
+        Name => 'Quality',
+        Writable => 'int16u',
+        PrintConv => {
+           1 => 'Economy',
+           2 => 'Normal',
+           3 => 'Fine',
+        },
+    },
+    0x3003 => {
+        Name => 'FocusMode',
+        Writable => 'int16u',
+        PrintConv => {
+           0 => 'Manual?',
+           1 => 'Fixation?',
+           2 => 'Macro', #3
+           3 => 'Single-Area Auto Focus',
+           6 => 'Multi-Area Auto Focus',
+        },
+    },
+    0x3006 => {
+        Name => 'HometownCity',
+        Writable => 'string',
+    },
+    0x3007 => {
+        Name => 'BestShotMode',
+        Writable => 'int16u',
+        PrintConv => {
+           0 => 'Off',
+           1 => 'On?',
+        },
+    },
+    0x3008 => { #3
+        Name => 'AutoISO',
+        Writable => 'int16u',
+        PrintConv => { 1 => 'On', 2 => 'Off' },
+    },
+    0x3011 => { #3
+        Name => 'Sharpness',
+        Format => 'int16s',
+        Writable => 'undef',
+    },
+    0x3012 => { #3
+        Name => 'Contrast',
+        Format => 'int16s',
+        Writable => 'undef',
+    },
+    0x3013 => { #3
+        Name => 'Saturation',
+        Format => 'int16s',
+        Writable => 'undef',
+    },
+    0x3014 => {
+        Name => 'ISO',
+        Writable => 'int16u',
+        Priority => 0,
+    },
+    0x3015 => {
+        Name => 'ColorMode',
+        Writable => 'int16u',
+        PrintConv => { 0 => 'Off' },
+    },
+    0x3016 => {
+        Name => 'Enhancement',
+        Writable => 'int16u',
+        PrintConv => { 0 => 'Off' },
+    },
+    0x3017 => {
+        Name => 'Filter',
+        Writable => 'int16u',
+        PrintConv => { 0 => 'Off' },
+    },
+    0x301c => { #3
+        Name => 'SequenceNumber', # for continuous shooting
+        Writable => 'int16u',
+    },
+    0x301d => { #3
+        Name => 'BracketSequence',
+        Writable => 'int16u',
+        Count => 2,
+    },
+    # 0x301e - MultiBracket ? (ref 3)
+    0x3020 => { #3
+        Name => 'ImageStabilization',
+        Writable => 'int16u',
+        PrintConv => {
+            0 => 'Off',
+            1 => 'On',
+            2 => 'Best Shot',
+        },
+    },
+);
+
+1;  # end
+
+__END__
+
+=head1 NAME
+
+Image::ExifTool::Casio - Casio EXIF maker notes tags
+
+=head1 SYNOPSIS
+
+This module is loaded automatically by Image::ExifTool when required.
+
+=head1 DESCRIPTION
+
+This module contains definitions required by Image::ExifTool to interpret
+Casio maker notes in EXIF information.
+
+=head1 AUTHOR
+
+Copyright 2003-2007, Phil Harvey (phil at owl.phy.queensu.ca)
+
+This library is free software; you can redistribute it and/or modify it
+under the same terms as Perl itself.
+
+=head1 REFERENCES
+
+=over 4
+
+=item L<http://park2.wakwak.com/~tsuruzoh/Computer/Digicams/exif-e.html>
+
+=back
+
+=head1 ACKNOWLEDGEMENTS
+
+Thanks to Joachim Loehr for adding support for the type 2 maker notes.
+
+=head1 SEE ALSO
+
+L<Image::ExifTool::TagNames/Casio Tags>,
+L<Image::ExifTool(3pm)|Image::ExifTool>
+
+=cut
Index: /gsdl/trunk/perllib/cpan/Image/ExifTool/DICOM.pm
===================================================================
--- /gsdl/trunk/perllib/cpan/Image/ExifTool/DICOM.pm	(revision 16842)
+++ /gsdl/trunk/perllib/cpan/Image/ExifTool/DICOM.pm	(revision 16842)
@@ -0,0 +1,2651 @@
+#------------------------------------------------------------------------------
+# File:         DICOM.pm
+#
+# Description:  Read DICOM and ACR-NEMA medical images
+#
+# Revisions:    11/09/2005 - P. Harvey Created
+#
+# References:   1) http://medical.nema.org/dicom/2004.html
+#               2) http://www.sph.sc.edu/comd/rorden/dicom.html
+#               3) http://www.dclunie.com/
+#------------------------------------------------------------------------------
+
+package Image::ExifTool::DICOM;
+
+use strict;
+use vars qw($VERSION);
+use Image::ExifTool qw(:DataAccess :Utils);
+
+$VERSION = '1.06';
+
+# DICOM VR (Value Representation) format conversions
+my %dicomFormat = (
+    FD => 'double',
+    FL => 'float',
+    OB => 'int8u',
+    OF => 'float',
+    OW => 'int16u',
+    SL => 'int32s',
+    SS => 'int16s',
+    UL => 'int32u',
+    US => 'int16u',
+);
+
+# VR elements with 32-bit length in explicit VR syntax
+my %vr32 = ( OB=>1, OW=>1, OF=>1, SQ=>1, UT=>1, UN=>1 );
+
+# data elements with implicit VR regardless of syntax
+my %implicitVR = (
+    'FFFE,E000' => 1,
+    'FFFE,E00D' => 1,
+    'FFFE,E0DD' => 1,
+);
+
+# DICOM tags
+# Note: "XxxGroupLength" tags are generated automatically if they don't exist
+%Image::ExifTool::DICOM::Main = (
+    GROUPS => { 2 => 'Image' },
+    PROCESS_PROC => 0,  # set this to zero to omit tags from lookup (way too many!)
+    NOTES => q{
+        The DICOM format is based on the ACR-NEMA specification, but adds a file
+        header and a number of new tags.  ExifTool will extract information from
+        either type of file.  The Tag ID's in the following table are the tag group
+        and element numbers in hexadecimal, as given in the DICOM specification.
+    },
+    # file meta information group (names end with VR)
+    '0002,0000' => { VR => 'UL', Name => 'FileMetaInfoGroupLength' },
+    '0002,0001' => { VR => 'OB', Name => 'FileMetaInfoVersion' },
+    '0002,0002' => { VR => 'UI', Name => 'MediaStorageSOPClassUID' },
+    '0002,0003' => { VR => 'UI', Name => 'MediaStorageSOPInstanceUID' },
+    '0002,0010' => { VR => 'UI', Name => 'TransferSyntaxUID' },
+    '0002,0012' => { VR => 'UI', Name => 'ImplementationClassUID' },
+    '0002,0013' => { VR => 'SH', Name => 'ImplementationVersionName' },
+    '0002,0016' => { VR => 'AE', Name => 'SourceApplicationEntityTitle' },
+    '0002,0100' => { VR => 'UI', Name => 'PrivateInformationCreatorUID' },
+    '0002,0102' => { VR => 'OB', Name => 'PrivateInformation' },
+    # directory structuring group
+    '0004,1130' => { VR => 'CS', Name => 'FileSetID' },
+    '0004,1141' => { VR => 'CS', Name => 'FileSetDescriptorFileID' },
+    '0004,1142' => { VR => 'CS', Name => 'SpecificCharacterSetOfFile' },
+    '0004,1200' => { VR => 'UL', Name => 'FirstDirectoryRecordOffset' },
+    '0004,1202' => { VR => 'UL', Name => 'LastDirectoryRecordOffset' },
+    '0004,1212' => { VR => 'US', Name => 'FileSetConsistencyFlag' },
+    '0004,1220' => { VR => 'SQ', Name => 'DirectoryRecordSequence' },
+    '0004,1400' => { VR => 'UL', Name => 'OffsetOfNextDirectoryRecord' },
+    '0004,1410' => { VR => 'US', Name => 'RecordInUseFlag' },
+    '0004,1420' => { VR => 'UL', Name => 'LowerLevelDirectoryEntityOffset' },
+    '0004,1430' => { VR => 'CS', Name => 'DirectoryRecordType' },
+    '0004,1432' => { VR => 'UI', Name => 'PrivateRecordUID' },
+    '0004,1500' => { VR => 'CS', Name => 'ReferencedFileID' },
+    '0004,1504' => { VR => 'UL', Name => 'MRDRDirectoryRecordOffset' },
+    '0004,1510' => { VR => 'UI', Name => 'ReferencedSOPClassUIDInFile' },
+    '0004,1511' => { VR => 'UI', Name => 'ReferencedSOPInstanceUIDInFile' },
+    '0004,1512' => { VR => 'UI', Name => 'ReferencedTransferSyntaxUIDInFile' },
+    '0004,151A' => { VR => 'UI', Name => 'ReferencedRelatedSOPClassUIDInFile' },
+    '0004,1600' => { VR => 'UL', Name => 'NumberOfReferences' },
+    # identifying group
+    '0008,0000' => { VR => 'UL', Name => 'IdentifyingGroupLength' },
+    '0008,0001' => { VR => 'RET',Name => 'LengthToEnd' },
+    '0008,0005' => { VR => 'CS', Name => 'SpecificCharacterSet' },
+    '0008,0008' => { VR => 'CS', Name => 'ImageType' },
+    '0008,0010' => { VR => 'RET',Name => 'RecognitionCode' },
+    '0008,0012' => { VR => 'DA', Name => 'InstanceCreationDate' },
+    '0008,0013' => { VR => 'TM', Name => 'InstanceCreationTime' },
+    '0008,0014' => { VR => 'UI', Name => 'InstanceCreatorUID' },
+    '0008,0016' => { VR => 'UI', Name => 'SOPClassUID' },
+    '0008,0018' => { VR => 'UI', Name => 'SOPInstanceUID' },
+    '0008,001A' => { VR => 'UI', Name => 'RelatedGeneralSOPClassUID' },
+    '0008,001B' => { VR => 'UI', Name => 'OriginalSpecializedSOPClassUID' },
+    '0008,0020' => { VR => 'DA', Name => 'StudyDate' },
+    '0008,0021' => { VR => 'DA', Name => 'SeriesDate' },
+    '0008,0022' => { VR => 'DA', Name => 'AcquisitionDate' },
+    '0008,0023' => { VR => 'DA', Name => 'ContentDate' },
+    '0008,0024' => { VR => 'DA', Name => 'OverlayDate' },
+    '0008,0025' => { VR => 'DA', Name => 'CurveDate' },
+    '0008,002A' => { VR => 'DT', Name => 'AcquisitionDatetime' },
+    '0008,0030' => { VR => 'TM', Name => 'StudyTime' },
+    '0008,0031' => { VR => 'TM', Name => 'SeriesTime' },
+    '0008,0032' => { VR => 'TM', Name => 'AcquisitionTime' },
+    '0008,0033' => { VR => 'TM', Name => 'ContentTime' },
+    '0008,0034' => { VR => 'TM', Name => 'OverlayTime' },
+    '0008,0035' => { VR => 'TM', Name => 'CurveTime' },
+    '0008,0040' => { VR => 'RET',Name => 'DataSetType' },
+    '0008,0041' => { VR => 'RET',Name => 'DataSetSubtype' },
+    '0008,0042' => { VR => 'RET',Name => 'NuclearMedicineSeriesType' },
+    '0008,0050' => { VR => 'SH', Name => 'AccessionNumber' },
+    '0008,0052' => { VR => 'CS', Name => 'Query-RetrieveLevel' },
+    '0008,0054' => { VR => 'AE', Name => 'RetrieveAETitle' },
+    '0008,0056' => { VR => 'CS', Name => 'InstanceAvailability' },
+    '0008,0058' => { VR => 'UI', Name => 'FailedSOPInstanceUIDList' },
+    '0008,0060' => { VR => 'CS', Name => 'Modality' },
+    '0008,0061' => { VR => 'CS', Name => 'ModalitiesInStudy' },
+    '0008,0062' => { VR => 'UI', Name => 'SOPClassesInStudy' },
+    '0008,0064' => { VR => 'CS', Name => 'ConversionType' },
+    '0008,0068' => { VR => 'CS', Name => 'PresentationIntentType' },
+    '0008,0070' => { VR => 'LO', Name => 'Manufacturer' },
+    '0008,0080' => { VR => 'LO', Name => 'InstitutionName' },
+    '0008,0081' => { VR => 'ST', Name => 'InstitutionAddress' },
+    '0008,0082' => { VR => 'SQ', Name => 'InstitutionCodeSequence' },
+    '0008,0090' => { VR => 'PN', Name => 'ReferringPhysiciansName' },
+    '0008,0092' => { VR => 'ST', Name => 'ReferringPhysiciansAddress' },
+    '0008,0094' => { VR => 'SH', Name => 'ReferringPhysiciansTelephoneNumber' },
+    '0008,0096' => { VR => 'SQ', Name => 'ReferringPhysicianIDSequence' },
+    '0008,0100' => { VR => 'SH', Name => 'CodeValue' },
+    '0008,0102' => { VR => 'SH', Name => 'CodingSchemeDesignator' },
+    '0008,0103' => { VR => 'SH', Name => 'CodingSchemeVersion' },
+    '0008,0104' => { VR => 'LO', Name => 'CodeMeaning' },
+    '0008,0105' => { VR => 'CS', Name => 'MappingResource' },
+    '0008,0106' => { VR => 'DT', Name => 'ContextGroupVersion' },
+    '0008,0107' => { VR => 'DT', Name => 'ContextGroupLocalVersion' },
+    '0008,010B' => { VR => 'CS', Name => 'ContextGroupExtensionFlag' },
+    '0008,010C' => { VR => 'UI', Name => 'CodingSchemeUID' },
+    '0008,010D' => { VR => 'UI', Name => 'ContextGroupExtensionCreatorUID' },
+    '0008,010F' => { VR => 'CS', Name => 'ContextIdentifier' },
+    '0008,0110' => { VR => 'SQ', Name => 'CodingSchemeIDSequence' },
+    '0008,0112' => { VR => 'LO', Name => 'CodingSchemeRegistry' },
+    '0008,0114' => { VR => 'ST', Name => 'CodingSchemeExternalID' },
+    '0008,0115' => { VR => 'ST', Name => 'CodingSchemeName' },
+    '0008,0116' => { VR => 'ST', Name => 'ResponsibleOrganization' },
+    '0008,0201' => { VR => 'SH', Name => 'TimezoneOffsetFromUTC' },
+    '0008,1000' => { VR => 'RET',Name => 'NetworkID' },
+    '0008,1010' => { VR => 'SH', Name => 'StationName' },
+    '0008,1030' => { VR => 'LO', Name => 'StudyDescription' },
+    '0008,1032' => { VR => 'SQ', Name => 'ProcedureCodeSequence' },
+    '0008,103E' => { VR => 'LO', Name => 'SeriesDescription' },
+    '0008,1040' => { VR => 'LO', Name => 'InstitutionalDepartmentName' },
+    '0008,1048' => { VR => 'PN', Name => 'PhysicianOfRecord' },
+    '0008,1049' => { VR => 'SQ', Name => 'PhysicianOfRecordIDSequence' },
+    '0008,1050' => { VR => 'PN', Name => 'PerformingPhysiciansName' },
+    '0008,1052' => { VR => 'SQ', Name => 'PerformingPhysicianIDSequence' },
+    '0008,1060' => { VR => 'PN', Name => 'NameOfPhysicianReadingStudy' },
+    '0008,1062' => { VR => 'SQ', Name => 'PhysicianReadingStudyIDSequence' },
+    '0008,1070' => { VR => 'PN', Name => 'OperatorsName' },
+    '0008,1072' => { VR => 'SQ', Name => 'OperatorIDSequence' },
+    '0008,1080' => { VR => 'LO', Name => 'AdmittingDiagnosesDescription' },
+    '0008,1084' => { VR => 'SQ', Name => 'AdmittingDiagnosesCodeSequence' },
+    '0008,1090' => { VR => 'LO', Name => 'ManufacturersModelName' },
+    '0008,1100' => { VR => 'SQ', Name => 'ReferencedResultsSequence' },
+    '0008,1110' => { VR => 'SQ', Name => 'ReferencedStudySequence' },
+    '0008,1111' => { VR => 'SQ', Name => 'ReferencedProcedureStepSequence' },
+    '0008,1115' => { VR => 'SQ', Name => 'ReferencedSeriesSequence' },
+    '0008,1120' => { VR => 'SQ', Name => 'ReferencedPatientSequence' },
+    '0008,1125' => { VR => 'SQ', Name => 'ReferencedVisitSequence' },
+    '0008,1130' => { VR => 'SQ', Name => 'ReferencedOverlaySequence' },
+    '0008,113A' => { VR => 'SQ', Name => 'ReferencedWaveformSequence' },
+    '0008,1140' => { VR => 'SQ', Name => 'ReferencedImageSequence' },
+    '0008,1145' => { VR => 'SQ', Name => 'ReferencedCurveSequence' },
+    '0008,114A' => { VR => 'SQ', Name => 'ReferencedInstanceSequence' },
+    '0008,1150' => { VR => 'UI', Name => 'ReferencedSOPClassUID' },
+    '0008,1155' => { VR => 'UI', Name => 'ReferencedSOPInstanceUID' },
+    '0008,115A' => { VR => 'UI', Name => 'SOPClassesSupported' },
+    '0008,1160' => { VR => 'IS', Name => 'ReferencedFrameNumber' },
+    '0008,1195' => { VR => 'UI', Name => 'TransactionUID' },
+    '0008,1197' => { VR => 'US', Name => 'FailureReason' },
+    '0008,1198' => { VR => 'SQ', Name => 'FailedSOPSequence' },
+    '0008,1199' => { VR => 'SQ', Name => 'ReferencedSOPSequence' },
+    '0008,1200' => { VR => 'SQ', Name => 'OtherReferencedStudiesSequence' },
+    '0008,1250' => { VR => 'SQ', Name => 'RelatedSeriesSequence' },
+    '0008,2110' => { VR => 'RET',Name => 'LossyImageCompression' },
+    '0008,2111' => { VR => 'ST', Name => 'DerivationDescription' },
+    '0008,2112' => { VR => 'SQ', Name => 'SourceImageSequence' },
+    '0008,2120' => { VR => 'SH', Name => 'StageName' },
+    '0008,2122' => { VR => 'IS', Name => 'StageNumber' },
+    '0008,2124' => { VR => 'IS', Name => 'NumberOfStages' },
+    '0008,2127' => { VR => 'SH', Name => 'ViewName' },
+    '0008,2128' => { VR => 'IS', Name => 'ViewNumber' },
+    '0008,2129' => { VR => 'IS', Name => 'NumberOfEventTimers' },
+    '0008,212A' => { VR => 'IS', Name => 'NumberOfViewsInStage' },
+    '0008,2130' => { VR => 'DS', Name => 'EventElapsedTime' },
+    '0008,2132' => { VR => 'LO', Name => 'EventTimerName' },
+    '0008,2142' => { VR => 'IS', Name => 'StartTrim' },
+    '0008,2143' => { VR => 'IS', Name => 'StopTrim' },
+    '0008,2144' => { VR => 'IS', Name => 'RecommendedDisplayFrameRate' },
+    '0008,2200' => { VR => 'RET',Name => 'TransducerPosition' },
+    '0008,2204' => { VR => 'RET',Name => 'TransducerOrientation' },
+    '0008,2208' => { VR => 'RET',Name => 'AnatomicStructure' },
+    '0008,2218' => { VR => 'SQ', Name => 'AnatomicRegionSequence' },
+    '0008,2220' => { VR => 'SQ', Name => 'AnatomicRegionModifierSequence' },
+    '0008,2228' => { VR => 'SQ', Name => 'PrimaryAnatomicStructureSequence' },
+    '0008,2229' => { VR => 'SQ', Name => 'AnatomicStructureOrRegionSequence' },
+    '0008,2230' => { VR => 'SQ', Name => 'AnatomicStructureModifierSequence' },
+    '0008,2240' => { VR => 'SQ', Name => 'TransducerPositionSequence' },
+    '0008,2242' => { VR => 'SQ', Name => 'TransducerPositionModifierSequence' },
+    '0008,2244' => { VR => 'SQ', Name => 'TransducerOrientationSequence' },
+    '0008,2246' => { VR => 'SQ', Name => 'TransducerOrientationModifierSeq' },
+    '0008,3001' => { VR => 'SQ', Name => 'AlternateRepresentationSequence' },
+    '0008,4000' => { VR => 'RET',Name => 'IdentifyingComments' },
+    '0008,9007' => { VR => 'CS', Name => 'FrameType' },
+    '0008,9092' => { VR => 'SQ', Name => 'ReferencedImageEvidenceSequence' },
+    '0008,9121' => { VR => 'SQ', Name => 'ReferencedRawDataSequence' },
+    '0008,9123' => { VR => 'UI', Name => 'CreatorVersionUID' },
+    '0008,9124' => { VR => 'SQ', Name => 'DerivationImageSequence' },
+    '0008,9154' => { VR => 'SQ', Name => 'SourceImageEvidenceSequence' },
+    '0008,9205' => { VR => 'CS', Name => 'PixelPresentation' },
+    '0008,9206' => { VR => 'CS', Name => 'VolumetricProperties' },
+    '0008,9207' => { VR => 'CS', Name => 'VolumeBasedCalculationTechnique' },
+    '0008,9208' => { VR => 'CS', Name => 'ComplexImageComponent' },
+    '0008,9209' => { VR => 'CS', Name => 'AcquisitionContrast' },
+    '0008,9215' => { VR => 'SQ', Name => 'DerivationCodeSequence' },
+    '0008,9237' => { VR => 'SQ', Name => 'GrayscalePresentationStateSequence' },
+    # patient group
+    '0010,0000' => { VR => 'UL', Name => 'PatientGroupLength' },
+    '0010,0010' => { VR => 'PN', Name => 'PatientsName' },
+    '0010,0020' => { VR => 'LO', Name => 'PatientID' },
+    '0010,0021' => { VR => 'LO', Name => 'IssuerOfPatientID' },
+    '0010,0030' => { VR => 'DA', Name => 'PatientsBirthDate' },
+    '0010,0032' => { VR => 'TM', Name => 'PatientsBirthTime' },
+    '0010,0040' => { VR => 'CS', Name => 'PatientsSex' },
+    '0010,0050' => { VR => 'SQ', Name => 'PatientsInsurancePlanCodeSequence' },
+    '0010,0101' => { VR => 'SQ', Name => 'PatientsPrimaryLanguageCodeSeq' },
+    '0010,0102' => { VR => 'SQ', Name => 'PatientsPrimaryLanguageCodeModSeq' },
+    '0010,1000' => { VR => 'LO', Name => 'OtherPatientIDs' },
+    '0010,1001' => { VR => 'PN', Name => 'OtherPatientNames' },
+    '0010,1005' => { VR => 'PN', Name => 'PatientsBirthName' },
+    '0010,1010' => { VR => 'AS', Name => 'PatientsAge' },
+    '0010,1020' => { VR => 'DS', Name => 'PatientsSize' },
+    '0010,1030' => { VR => 'DS', Name => 'PatientsWeight' },
+    '0010,1040' => { VR => 'LO', Name => 'PatientsAddress' },
+    '0010,1050' => { VR => 'RET',Name => 'InsurancePlanIdentification' },
+    '0010,1060' => { VR => 'PN', Name => 'PatientsMothersBirthName' },
+    '0010,1080' => { VR => 'LO', Name => 'MilitaryRank' },
+    '0010,1081' => { VR => 'LO', Name => 'BranchOfService' },
+    '0010,1090' => { VR => 'LO', Name => 'MedicalRecordLocator' },
+    '0010,2000' => { VR => 'LO', Name => 'MedicalAlerts' },
+    '0010,2110' => { VR => 'LO', Name => 'ContrastAllergies' },
+    '0010,2150' => { VR => 'LO', Name => 'CountryOfResidence' },
+    '0010,2152' => { VR => 'LO', Name => 'RegionOfResidence' },
+    '0010,2154' => { VR => 'SH', Name => 'PatientsTelephoneNumbers' },
+    '0010,2160' => { VR => 'SH', Name => 'EthnicGroup' },
+    '0010,2180' => { VR => 'SH', Name => 'Occupation' },
+    '0010,21A0' => { VR => 'CS', Name => 'SmokingStatus' },
+    '0010,21B0' => { VR => 'LT', Name => 'AdditionalPatientHistory' },
+    '0010,21C0' => { VR => 'US', Name => 'PregnancyStatus' },
+    '0010,21D0' => { VR => 'DA', Name => 'LastMenstrualDate' },
+    '0010,21F0' => { VR => 'LO', Name => 'PatientsReligiousPreference' },
+    '0010,4000' => { VR => 'LT', Name => 'PatientComments' },
+    '0012,0010' => { VR => 'LO', Name => 'ClinicalTrialSponsorName' },
+    '0012,0020' => { VR => 'LO', Name => 'ClinicalTrialProtocolID' },
+    '0012,0021' => { VR => 'LO', Name => 'ClinicalTrialProtocolName' },
+    '0012,0030' => { VR => 'LO', Name => 'ClinicalTrialSiteID' },
+    '0012,0031' => { VR => 'LO', Name => 'ClinicalTrialSiteName' },
+    '0012,0040' => { VR => 'LO', Name => 'ClinicalTrialSubjectID' },
+    '0012,0042' => { VR => 'LO', Name => 'ClinicalTrialSubjectReadingID' },
+    '0012,0050' => { VR => 'LO', Name => 'ClinicalTrialTimePointID' },
+    '0012,0051' => { VR => 'ST', Name => 'ClinicalTrialTimePointDescription' },
+    '0012,0060' => { VR => 'LO', Name => 'ClinicalTrialCoordinatingCenter' },
+    # acquisition group
+    '0018,0000' => { VR => 'UL', Name => 'AcquisitionGroupLength' },
+    '0018,0010' => { VR => 'LO', Name => 'Contrast-BolusAgent' },
+    '0018,0012' => { VR => 'SQ', Name => 'Contrast-BolusAgentSequence' },
+    '0018,0014' => { VR => 'SQ', Name => 'Contrast-BolusAdministrationRoute' },
+    '0018,0015' => { VR => 'CS', Name => 'BodyPartExamined' },
+    '0018,0020' => { VR => 'CS', Name => 'ScanningSequence' },
+    '0018,0021' => { VR => 'CS', Name => 'SequenceVariant' },
+    '0018,0022' => { VR => 'CS', Name => 'ScanOptions' },
+    '0018,0023' => { VR => 'CS', Name => 'MRAcquisitionType' },
+    '0018,0024' => { VR => 'SH', Name => 'SequenceName' },
+    '0018,0025' => { VR => 'CS', Name => 'AngioFlag' },
+    '0018,0026' => { VR => 'SQ', Name => 'InterventionDrugInformationSeq' },
+    '0018,0027' => { VR => 'TM', Name => 'InterventionDrugStopTime' },
+    '0018,0028' => { VR => 'DS', Name => 'InterventionDrugDose' },
+    '0018,0029' => { VR => 'SQ', Name => 'InterventionDrugSequence' },
+    '0018,002A' => { VR => 'SQ', Name => 'AdditionalDrugSequence' },
+    '0018,0030' => { VR => 'RET',Name => 'Radionuclide' },
+    '0018,0031' => { VR => 'LO', Name => 'Radiopharmaceutical' },
+    '0018,0032' => { VR => 'RET',Name => 'EnergyWindowCenterline' },
+    '0018,0033' => { VR => 'RET',Name => 'EnergyWindowTotalWidth' },
+    '0018,0034' => { VR => 'LO', Name => 'InterventionDrugName' },
+    '0018,0035' => { VR => 'TM', Name => 'InterventionDrugStartTime' },
+    '0018,0036' => { VR => 'SQ', Name => 'InterventionSequence' },
+    '0018,0037' => { VR => 'RET',Name => 'TherapyType' },
+    '0018,0038' => { VR => 'CS', Name => 'InterventionStatus' },
+    '0018,0039' => { VR => 'RET',Name => 'TherapyDescription' },
+    '0018,003A' => { VR => 'ST', Name => 'InterventionDescription' },
+    '0018,0040' => { VR => 'IS', Name => 'CineRate' },
+    '0018,0050' => { VR => 'DS', Name => 'SliceThickness' },
+    '0018,0060' => { VR => 'DS', Name => 'KVP' },
+    '0018,0070' => { VR => 'IS', Name => 'CountsAccumulated' },
+    '0018,0071' => { VR => 'CS', Name => 'AcquisitionTerminationCondition' },
+    '0018,0072' => { VR => 'DS', Name => 'EffectiveDuration' },
+    '0018,0073' => { VR => 'CS', Name => 'AcquisitionStartCondition' },
+    '0018,0074' => { VR => 'IS', Name => 'AcquisitionStartConditionData' },
+    '0018,0075' => { VR => 'IS', Name => 'AcquisitionEndConditionData' },
+    '0018,0080' => { VR => 'DS', Name => 'RepetitionTime' },
+    '0018,0081' => { VR => 'DS', Name => 'EchoTime' },
+    '0018,0082' => { VR => 'DS', Name => 'InversionTime' },
+    '0018,0083' => { VR => 'DS', Name => 'NumberOfAverages' },
+    '0018,0084' => { VR => 'DS', Name => 'ImagingFrequency' },
+    '0018,0085' => { VR => 'SH', Name => 'ImagedNucleus' },
+    '0018,0086' => { VR => 'IS', Name => 'EchoNumber' },
+    '0018,0087' => { VR => 'DS', Name => 'MagneticFieldStrength' },
+    '0018,0088' => { VR => 'DS', Name => 'SpacingBetweenSlices' },
+    '0018,0089' => { VR => 'IS', Name => 'NumberOfPhaseEncodingSteps' },
+    '0018,0090' => { VR => 'DS', Name => 'DataCollectionDiameter' },
+    '0018,0091' => { VR => 'IS', Name => 'EchoTrainLength' },
+    '0018,0093' => { VR => 'DS', Name => 'PercentSampling' },
+    '0018,0094' => { VR => 'DS', Name => 'PercentPhaseFieldOfView' },
+    '0018,0095' => { VR => 'DS', Name => 'PixelBandwidth' },
+    '0018,1000' => { VR => 'LO', Name => 'DeviceSerialNumber' },
+    '0018,1004' => { VR => 'LO', Name => 'PlateID' },
+    '0018,1010' => { VR => 'LO', Name => 'SecondaryCaptureDeviceID' },
+    '0018,1011' => { VR => 'LO', Name => 'HardcopyCreationDeviceID' },
+    '0018,1012' => { VR => 'DA', Name => 'DateOfSecondaryCapture' },
+    '0018,1014' => { VR => 'TM', Name => 'TimeOfSecondaryCapture' },
+    '0018,1016' => { VR => 'LO', Name => 'SecondaryCaptureDeviceManufacturer' },
+    '0018,1017' => { VR => 'LO', Name => 'HardcopyDeviceManufacturer' },
+    '0018,1018' => { VR => 'LO', Name => 'SecondaryCaptureDeviceModelName' },
+    '0018,1019' => { VR => 'LO', Name => 'SecondaryCaptureDeviceSoftwareVers' },
+    '0018,101A' => { VR => 'LO', Name => 'HardcopyDeviceSoftwareVersion' },
+    '0018,101B' => { VR => 'LO', Name => 'HardcopyDeviceModelName' },
+    '0018,1020' => { VR => 'LO', Name => 'SoftwareVersion' },
+    '0018,1022' => { VR => 'SH', Name => 'VideoImageFormatAcquired' },
+    '0018,1023' => { VR => 'LO', Name => 'DigitalImageFormatAcquired' },
+    '0018,1030' => { VR => 'LO', Name => 'ProtocolName' },
+    '0018,1040' => { VR => 'LO', Name => 'Contrast-BolusRoute' },
+    '0018,1041' => { VR => 'DS', Name => 'Contrast-BolusVolume' },
+    '0018,1042' => { VR => 'TM', Name => 'Contrast-BolusStartTime' },
+    '0018,1043' => { VR => 'TM', Name => 'Contrast-BolusStopTime' },
+    '0018,1044' => { VR => 'DS', Name => 'Contrast-BolusTotalDose' },
+    '0018,1045' => { VR => 'IS', Name => 'SyringeCounts' },
+    '0018,1046' => { VR => 'DS', Name => 'ContrastFlowRate' },
+    '0018,1047' => { VR => 'DS', Name => 'ContrastFlowDuration' },
+    '0018,1048' => { VR => 'CS', Name => 'Contrast-BolusIngredient' },
+    '0018,1049' => { VR => 'DS', Name => 'Contrast-BolusConcentration' },
+    '0018,1050' => { VR => 'DS', Name => 'SpatialResolution' },
+    '0018,1060' => { VR => 'DS', Name => 'TriggerTime' },
+    '0018,1061' => { VR => 'LO', Name => 'TriggerSourceOrType' },
+    '0018,1062' => { VR => 'IS', Name => 'NominalInterval' },
+    '0018,1063' => { VR => 'DS', Name => 'FrameTime' },
+    '0018,1064' => { VR => 'LO', Name => 'FramingType' },
+    '0018,1065' => { VR => 'DS', Name => 'FrameTimeVector' },
+    '0018,1066' => { VR => 'DS', Name => 'FrameDelay' },
+    '0018,1067' => { VR => 'DS', Name => 'ImageTriggerDelay' },
+    '0018,1068' => { VR => 'DS', Name => 'MultiplexGroupTimeOffset' },
+    '0018,1069' => { VR => 'DS', Name => 'TriggerTimeOffset' },
+    '0018,106A' => { VR => 'CS', Name => 'SynchronizationTrigger' },
+    '0018,106C' => { VR => 'US', Name => 'SynchronizationChannel' },
+    '0018,106E' => { VR => 'UL', Name => 'TriggerSamplePosition' },
+    '0018,1070' => { VR => 'LO', Name => 'RadiopharmaceuticalRoute' },
+    '0018,1071' => { VR => 'DS', Name => 'RadiopharmaceuticalVolume' },
+    '0018,1072' => { VR => 'TM', Name => 'RadiopharmaceuticalStartTime' },
+    '0018,1073' => { VR => 'TM', Name => 'RadiopharmaceuticalStopTime' },
+    '0018,1074' => { VR => 'DS', Name => 'RadionuclideTotalDose' },
+    '0018,1075' => { VR => 'DS', Name => 'RadionuclideHalfLife' },
+    '0018,1076' => { VR => 'DS', Name => 'RadionuclidePositronFraction' },
+    '0018,1077' => { VR => 'DS', Name => 'RadiopharmaceuticalSpecActivity' },
+    '0018,1080' => { VR => 'CS', Name => 'BeatRejectionFlag' },
+    '0018,1081' => { VR => 'IS', Name => 'LowRRValue' },
+    '0018,1082' => { VR => 'IS', Name => 'HighRRValue' },
+    '0018,1083' => { VR => 'IS', Name => 'IntervalsAcquired' },
+    '0018,1084' => { VR => 'IS', Name => 'IntervalsRejected' },
+    '0018,1085' => { VR => 'LO', Name => 'PVCRejection' },
+    '0018,1086' => { VR => 'IS', Name => 'SkipBeats' },
+    '0018,1088' => { VR => 'IS', Name => 'HeartRate' },
+    '0018,1090' => { VR => 'IS', Name => 'CardiacNumberOfImages' },
+    '0018,1094' => { VR => 'IS', Name => 'TriggerWindow' },
+    '0018,1100' => { VR => 'DS', Name => 'ReconstructionDiameter' },
+    '0018,1110' => { VR => 'DS', Name => 'DistanceSourceToDetector' },
+    '0018,1111' => { VR => 'DS', Name => 'DistanceSourceToPatient' },
+    '0018,1114' => { VR => 'DS', Name => 'EstimatedRadiographicMagnification' },
+    '0018,1120' => { VR => 'DS', Name => 'Gantry-DetectorTilt' },
+    '0018,1121' => { VR => 'DS', Name => 'Gantry-DetectorSlew' },
+    '0018,1130' => { VR => 'DS', Name => 'TableHeight' },
+    '0018,1131' => { VR => 'DS', Name => 'TableTraverse' },
+    '0018,1134' => { VR => 'CS', Name => 'TableMotion' },
+    '0018,1135' => { VR => 'DS', Name => 'TableVerticalIncrement' },
+    '0018,1136' => { VR => 'DS', Name => 'TableLateralIncrement' },
+    '0018,1137' => { VR => 'DS', Name => 'TableLongitudinalIncrement' },
+    '0018,1138' => { VR => 'DS', Name => 'TableAngle' },
+    '0018,113A' => { VR => 'CS', Name => 'TableType' },
+    '0018,1140' => { VR => 'CS', Name => 'RotationDirection' },
+    '0018,1141' => { VR => 'DS', Name => 'AngularPosition' },
+    '0018,1142' => { VR => 'DS', Name => 'RadialPosition' },
+    '0018,1143' => { VR => 'DS', Name => 'ScanArc' },
+    '0018,1144' => { VR => 'DS', Name => 'AngularStep' },
+    '0018,1145' => { VR => 'DS', Name => 'CenterOfRotationOffset' },
+    '0018,1146' => { VR => 'RET',Name => 'RotationOffset' },
+    '0018,1147' => { VR => 'CS', Name => 'FieldOfViewShape' },
+    '0018,1149' => { VR => 'IS', Name => 'FieldOfViewDimensions' },
+    '0018,1150' => { VR => 'IS', Name => 'ExposureTime' },
+    '0018,1151' => { VR => 'IS', Name => 'XRayTubeCurrent' },
+    '0018,1152' => { VR => 'IS', Name => 'Exposure' },
+    '0018,1153' => { VR => 'IS', Name => 'ExposureInMicroAmpSec' },
+    '0018,1154' => { VR => 'DS', Name => 'AveragePulseWidth' },
+    '0018,1155' => { VR => 'CS', Name => 'RadiationSetting' },
+    '0018,1156' => { VR => 'CS', Name => 'RectificationType' },
+    '0018,115A' => { VR => 'CS', Name => 'RadiationMode' },
+    '0018,115E' => { VR => 'DS', Name => 'ImageAreaDoseProduct' },
+    '0018,1160' => { VR => 'SH', Name => 'FilterType' },
+    '0018,1161' => { VR => 'LO', Name => 'TypeOfFilters' },
+    '0018,1162' => { VR => 'DS', Name => 'IntensifierSize' },
+    '0018,1164' => { VR => 'DS', Name => 'ImagerPixelSpacing' },
+    '0018,1166' => { VR => 'CS', Name => 'Grid' },
+    '0018,1170' => { VR => 'IS', Name => 'GeneratorPower' },
+    '0018,1180' => { VR => 'SH', Name => 'Collimator-GridName' },
+    '0018,1181' => { VR => 'CS', Name => 'CollimatorType' },
+    '0018,1182' => { VR => 'IS', Name => 'FocalDistance' },
+    '0018,1183' => { VR => 'DS', Name => 'XFocusCenter' },
+    '0018,1184' => { VR => 'DS', Name => 'YFocusCenter' },
+    '0018,1190' => { VR => 'DS', Name => 'FocalSpot' },
+    '0018,1191' => { VR => 'CS', Name => 'AnodeTargetMaterial' },
+    '0018,11A0' => { VR => 'DS', Name => 'BodyPartThickness' },
+    '0018,11A2' => { VR => 'DS', Name => 'CompressionForce' },
+    '0018,1200' => { VR => 'DA', Name => 'DateOfLastCalibration' },
+    '0018,1201' => { VR => 'TM', Name => 'TimeOfLastCalibration' },
+    '0018,1210' => { VR => 'SH', Name => 'ConvolutionKernel' },
+    '0018,1240' => { VR => 'RET',Name => 'Upper-LowerPixelValues' },
+    '0018,1242' => { VR => 'IS', Name => 'ActualFrameDuration' },
+    '0018,1243' => { VR => 'IS', Name => 'CountRate' },
+    '0018,1244' => { VR => 'US', Name => 'PreferredPlaybackSequencing' },
+    '0018,1250' => { VR => 'SH', Name => 'ReceiveCoilName' },
+    '0018,1251' => { VR => 'SH', Name => 'TransmitCoilName' },
+    '0018,1260' => { VR => 'SH', Name => 'PlateType' },
+    '0018,1261' => { VR => 'LO', Name => 'PhosphorType' },
+    '0018,1300' => { VR => 'DS', Name => 'ScanVelocity' },
+    '0018,1301' => { VR => 'CS', Name => 'WholeBodyTechnique' },
+    '0018,1302' => { VR => 'IS', Name => 'ScanLength' },
+    '0018,1310' => { VR => 'US', Name => 'AcquisitionMatrix' },
+    '0018,1312' => { VR => 'CS', Name => 'InPlanePhaseEncodingDirection' },
+    '0018,1314' => { VR => 'DS', Name => 'FlipAngle' },
+    '0018,1315' => { VR => 'CS', Name => 'VariableFlipAngleFlag' },
+    '0018,1316' => { VR => 'DS', Name => 'SAR' },
+    '0018,1318' => { VR => 'DS', Name => 'DB-Dt' },
+    '0018,1400' => { VR => 'LO', Name => 'AcquisitionDeviceProcessingDescr' },
+    '0018,1401' => { VR => 'LO', Name => 'AcquisitionDeviceProcessingCode' },
+    '0018,1402' => { VR => 'CS', Name => 'CassetteOrientation' },
+    '0018,1403' => { VR => 'CS', Name => 'CassetteSize' },
+    '0018,1404' => { VR => 'US', Name => 'ExposuresonPlate' },
+    '0018,1405' => { VR => 'IS', Name => 'RelativeXRayExposure' },
+    '0018,1450' => { VR => 'DS', Name => 'ColumnAngulation' },
+    '0018,1460' => { VR => 'DS', Name => 'TomoLayerHeight' },
+    '0018,1470' => { VR => 'DS', Name => 'TomoAngle' },
+    '0018,1480' => { VR => 'DS', Name => 'TomoTime' },
+    '0018,1490' => { VR => 'CS', Name => 'TomoType' },
+    '0018,1491' => { VR => 'CS', Name => 'TomoClass' },
+    '0018,1495' => { VR => 'IS', Name => 'NumberOfTomosynthesisSourceImages' },
+    '0018,1500' => { VR => 'CS', Name => 'PositionerMotion' },
+    '0018,1508' => { VR => 'CS', Name => 'PositionerType' },
+    '0018,1510' => { VR => 'DS', Name => 'PositionerPrimaryAngle' },
+    '0018,1511' => { VR => 'DS', Name => 'PositionerSecondaryAngle' },
+    '0018,1520' => { VR => 'DS', Name => 'PositionerPrimaryAngleIncrement' },
+    '0018,1521' => { VR => 'DS', Name => 'PositionerSecondaryAngleIncrement' },
+    '0018,1530' => { VR => 'DS', Name => 'DetectorPrimaryAngle' },
+    '0018,1531' => { VR => 'DS', Name => 'DetectorSecondaryAngle' },
+    '0018,1600' => { VR => 'CS', Name => 'ShutterShape' },
+    '0018,1602' => { VR => 'IS', Name => 'ShutterLeftVerticalEdge' },
+    '0018,1604' => { VR => 'IS', Name => 'ShutterRightVerticalEdge' },
+    '0018,1606' => { VR => 'IS', Name => 'ShutterUpperHorizontalEdge' },
+    '0018,1608' => { VR => 'IS', Name => 'ShutterLowerHorizontalEdge' },
+    '0018,1610' => { VR => 'IS', Name => 'CenterOfCircularShutter' },
+    '0018,1612' => { VR => 'IS', Name => 'RadiusOfCircularShutter' },
+    '0018,1620' => { VR => 'IS', Name => 'VerticesOfPolygonalShutter' },
+    '0018,1622' => { VR => 'US', Name => 'ShutterPresentationValue' },
+    '0018,1623' => { VR => 'US', Name => 'ShutterOverlayGroup' },
+    '0018,1700' => { VR => 'CS', Name => 'CollimatorShape' },
+    '0018,1702' => { VR => 'IS', Name => 'CollimatorLeftVerticalEdge' },
+    '0018,1704' => { VR => 'IS', Name => 'CollimatorRightVerticalEdge' },
+    '0018,1706' => { VR => 'IS', Name => 'CollimatorUpperHorizontalEdge' },
+    '0018,1708' => { VR => 'IS', Name => 'CollimatorLowerHorizontalEdge' },
+    '0018,1710' => { VR => 'IS', Name => 'CenterOfCircularCollimator' },
+    '0018,1712' => { VR => 'IS', Name => 'RadiusOfCircularCollimator' },
+    '0018,1720' => { VR => 'IS', Name => 'VerticesOfPolygonalCollimator' },
+    '0018,1800' => { VR => 'CS', Name => 'AcquisitionTimeSynchronized' },
+    '0018,1801' => { VR => 'SH', Name => 'TimeSource' },
+    '0018,1802' => { VR => 'CS', Name => 'TimeDistributionProtocol' },
+    '0018,1803' => { VR => 'LO', Name => 'NTPSourceAddress' },
+    '0018,2001' => { VR => 'IS', Name => 'PageNumberVector' },
+    '0018,2002' => { VR => 'SH', Name => 'FrameLabelVector' },
+    '0018,2003' => { VR => 'DS', Name => 'FramePrimaryAngleVector' },
+    '0018,2004' => { VR => 'DS', Name => 'FrameSecondaryAngleVector' },
+    '0018,2005' => { VR => 'DS', Name => 'SliceLocationVector' },
+    '0018,2006' => { VR => 'SH', Name => 'DisplayWindowLabelVector' },
+    '0018,2010' => { VR => 'DS', Name => 'NominalScannedPixelSpacing' },
+    '0018,2020' => { VR => 'CS', Name => 'DigitizingDeviceTransportDirection' },
+    '0018,2030' => { VR => 'DS', Name => 'RotationOfScannedFilm' },
+    '0018,3100' => { VR => 'CS', Name => 'IVUSAcquisition' },
+    '0018,3101' => { VR => 'DS', Name => 'IVUSPullbackRate' },
+    '0018,3102' => { VR => 'DS', Name => 'IVUSGatedRate' },
+    '0018,3103' => { VR => 'IS', Name => 'IVUSPullbackStartFrameNumber' },
+    '0018,3104' => { VR => 'IS', Name => 'IVUSPullbackStopFrameNumber' },
+    '0018,3105' => { VR => 'IS', Name => 'LesionNumber' },
+    '0018,4000' => { VR => 'RET',Name => 'AcquisitionComments' },
+    '0018,5000' => { VR => 'SH', Name => 'OutputPower' },
+    '0018,5010' => { VR => 'LO', Name => 'TransducerData' },
+    '0018,5012' => { VR => 'DS', Name => 'FocusDepth' },
+    '0018,5020' => { VR => 'LO', Name => 'ProcessingFunction' },
+    '0018,5021' => { VR => 'LO', Name => 'PostprocessingFunction' },
+    '0018,5022' => { VR => 'DS', Name => 'MechanicalIndex' },
+    '0018,5024' => { VR => 'DS', Name => 'BoneThermalIndex' },
+    '0018,5026' => { VR => 'DS', Name => 'CranialThermalIndex' },
+    '0018,5027' => { VR => 'DS', Name => 'SoftTissueThermalIndex' },
+    '0018,5028' => { VR => 'DS', Name => 'SoftTissueFocusThermalIndex' },
+    '0018,5029' => { VR => 'DS', Name => 'SoftTissueSurfaceThermalIndex' },
+    '0018,5030' => { VR => 'RET',Name => 'DynamicRange' },
+    '0018,5040' => { VR => 'RET',Name => 'TotalGain' },
+    '0018,5050' => { VR => 'IS', Name => 'DepthOfScanField' },
+    '0018,5100' => { VR => 'CS', Name => 'PatientPosition' },
+    '0018,5101' => { VR => 'CS', Name => 'ViewPosition' },
+    '0018,5104' => { VR => 'SQ', Name => 'ProjectionEponymousNameCodeSeq' },
+    '0018,5210' => { VR => 'RET',Name => 'ImageTransformationMatrix' },
+    '0018,5212' => { VR => 'RET',Name => 'ImageTranslationVector' },
+    '0018,6000' => { VR => 'DS', Name => 'Sensitivity' },
+    '0018,6011' => { VR => 'SQ', Name => 'SequenceOfUltrasoundRegions' },
+    '0018,6012' => { VR => 'US', Name => 'RegionSpatialFormat' },
+    '0018,6014' => { VR => 'US', Name => 'RegionDataType' },
+    '0018,6016' => { VR => 'UL', Name => 'RegionFlags' },
+    '0018,6018' => { VR => 'UL', Name => 'RegionLocationMinX0' },
+    '0018,601A' => { VR => 'UL', Name => 'RegionLocationMinY0' },
+    '0018,601C' => { VR => 'UL', Name => 'RegionLocationMaxX1' },
+    '0018,601E' => { VR => 'UL', Name => 'RegionLocationMaxY1' },
+    '0018,6020' => { VR => 'SL', Name => 'ReferencePixelX0' },
+    '0018,6022' => { VR => 'SL', Name => 'ReferencePixelY0' },
+    '0018,6024' => { VR => 'US', Name => 'PhysicalUnitsXDirection' },
+    '0018,6026' => { VR => 'US', Name => 'PhysicalUnitsYDirection' },
+    '0018,6028' => { VR => 'FD', Name => 'ReferencePixelPhysicalValueX' },
+    '0018,602A' => { VR => 'FD', Name => 'ReferencePixelPhysicalValueY' },
+    '0018,602C' => { VR => 'FD', Name => 'PhysicalDeltaX' },
+    '0018,602E' => { VR => 'FD', Name => 'PhysicalDeltaY' },
+    '0018,6030' => { VR => 'UL', Name => 'TransducerFrequency' },
+    '0018,6031' => { VR => 'CS', Name => 'TransducerType' },
+    '0018,6032' => { VR => 'UL', Name => 'PulseRepetitionFrequency' },
+    '0018,6034' => { VR => 'FD', Name => 'DopplerCorrectionAngle' },
+    '0018,6036' => { VR => 'FD', Name => 'SteeringAngle' },
+    '0018,6038' => { VR => 'RET',Name => 'DopplerSampleVolumeXPositionUL' },
+    '0018,6039' => { VR => 'SL', Name => 'DopplerSampleVolumeXPosition' },
+    '0018,603A' => { VR => 'RET',Name => 'DopplerSampleVolumeYPositionUL' },
+    '0018,603B' => { VR => 'SL', Name => 'DopplerSampleVolumeYPosition' },
+    '0018,603C' => { VR => 'RET',Name => 'TMLinePositionX0UL' },
+    '0018,603D' => { VR => 'SL', Name => 'TMLinePositionX0' },
+    '0018,603E' => { VR => 'RET',Name => 'TMLinePositionY0UL' },
+    '0018,603F' => { VR => 'SL', Name => 'TMLinePositionY0' },
+    '0018,6040' => { VR => 'RET',Name => 'TMLinePositionX1UL' },
+    '0018,6041' => { VR => 'SL', Name => 'TMLinePositionX1' },
+    '0018,6042' => { VR => 'RET',Name => 'TMLinePositionY1UL' },
+    '0018,6043' => { VR => 'SL', Name => 'TMLinePositionY1' },
+    '0018,6044' => { VR => 'US', Name => 'PixelComponentOrganization' },
+    '0018,6046' => { VR => 'UL', Name => 'PixelComponentMask' },
+    '0018,6048' => { VR => 'UL', Name => 'PixelComponentRangeStart' },
+    '0018,604A' => { VR => 'UL', Name => 'PixelComponentRangeStop' },
+    '0018,604C' => { VR => 'US', Name => 'PixelComponentPhysicalUnits' },
+    '0018,604E' => { VR => 'US', Name => 'PixelComponentDataType' },
+    '0018,6050' => { VR => 'UL', Name => 'NumberOfTableBreakPoints' },
+    '0018,6052' => { VR => 'UL', Name => 'TableOfXBreakPoints' },
+    '0018,6054' => { VR => 'FD', Name => 'TableOfYBreakPoints' },
+    '0018,6056' => { VR => 'UL', Name => 'NumberOfTableEntries' },
+    '0018,6058' => { VR => 'UL', Name => 'TableOfPixelValues' },
+    '0018,605A' => { VR => 'FL', Name => 'TableOfParameterValues' },
+    '0018,6060' => { VR => 'FL', Name => 'RWaveTimeVector' },
+    '0018,7000' => { VR => 'CS', Name => 'DetectorConditionsNominalFlag' },
+    '0018,7001' => { VR => 'DS', Name => 'DetectorTemperature' },
+    '0018,7004' => { VR => 'CS', Name => 'DetectorType' },
+    '0018,7005' => { VR => 'CS', Name => 'DetectorConfiguration' },
+    '0018,7006' => { VR => 'LT', Name => 'DetectorDescription' },
+    '0018,7008' => { VR => 'LT', Name => 'DetectorMode' },
+    '0018,700A' => { VR => 'SH', Name => 'DetectorID' },
+    '0018,700C' => { VR => 'DA', Name => 'DateOfLastDetectorCalibration' },
+    '0018,700E' => { VR => 'TM', Name => 'TimeOfLastDetectorCalibration' },
+    '0018,7010' => { VR => 'IS', Name => 'DetectorExposuresSinceCalibration' },
+    '0018,7011' => { VR => 'IS', Name => 'DetectorExposuresSinceManufactured' },
+    '0018,7012' => { VR => 'DS', Name => 'DetectorTimeSinceLastExposure' },
+    '0018,7014' => { VR => 'DS', Name => 'DetectorActiveTime' },
+    '0018,7016' => { VR => 'DS', Name => 'DetectorActiveOffsetFromExposure' },
+    '0018,701A' => { VR => 'DS', Name => 'DetectorBinning' },
+    '0018,7020' => { VR => 'DS', Name => 'DetectorElementPhysicalSize' },
+    '0018,7022' => { VR => 'DS', Name => 'DetectorElementSpacing' },
+    '0018,7024' => { VR => 'CS', Name => 'DetectorActiveShape' },
+    '0018,7026' => { VR => 'DS', Name => 'DetectorActiveDimensions' },
+    '0018,7028' => { VR => 'DS', Name => 'DetectorActiveOrigin' },
+    '0018,702A' => { VR => 'LO', Name => 'DetectorManufacturerName' },
+    '0018,702B' => { VR => 'LO', Name => 'DetectorManufacturersModelName' },
+    '0018,7030' => { VR => 'DS', Name => 'FieldOfViewOrigin' },
+    '0018,7032' => { VR => 'DS', Name => 'FieldOfViewRotation' },
+    '0018,7034' => { VR => 'CS', Name => 'FieldOfViewHorizontalFlip' },
+    '0018,7040' => { VR => 'LT', Name => 'GridAbsorbingMaterial' },
+    '0018,7041' => { VR => 'LT', Name => 'GridSpacingMaterial' },
+    '0018,7042' => { VR => 'DS', Name => 'GridThickness' },
+    '0018,7044' => { VR => 'DS', Name => 'GridPitch' },
+    '0018,7046' => { VR => 'IS', Name => 'GridAspectRatio' },
+    '0018,7048' => { VR => 'DS', Name => 'GridPeriod' },
+    '0018,704C' => { VR => 'DS', Name => 'GridFocalDistance' },
+    '0018,7050' => { VR => 'CS', Name => 'FilterMaterial' },
+    '0018,7052' => { VR => 'DS', Name => 'FilterThicknessMinimum' },
+    '0018,7054' => { VR => 'DS', Name => 'FilterThicknessMaximum' },
+    '0018,7060' => { VR => 'CS', Name => 'ExposureControlMode' },
+    '0018,7062' => { VR => 'LT', Name => 'ExposureControlModeDescription' },
+    '0018,7064' => { VR => 'CS', Name => 'ExposureStatus' },
+    '0018,7065' => { VR => 'DS', Name => 'PhototimerSetting' },
+    '0018,8150' => { VR => 'DS', Name => 'ExposureTimeInMicroSec' },
+    '0018,8151' => { VR => 'DS', Name => 'XRayTubeCurrentInMicroAmps' },
+    '0018,9004' => { VR => 'CS', Name => 'ContentQualification' },
+    '0018,9005' => { VR => 'SH', Name => 'PulseSequenceName' },
+    '0018,9006' => { VR => 'SQ', Name => 'MRImagingModifierSequence' },
+    '0018,9008' => { VR => 'CS', Name => 'EchoPulseSequence' },
+    '0018,9009' => { VR => 'CS', Name => 'InversionRecovery' },
+    '0018,9010' => { VR => 'CS', Name => 'FlowCompensation' },
+    '0018,9011' => { VR => 'CS', Name => 'MultipleSpinEcho' },
+    '0018,9012' => { VR => 'CS', Name => 'MultiPlanarExcitation' },
+    '0018,9014' => { VR => 'CS', Name => 'PhaseContrast' },
+    '0018,9015' => { VR => 'CS', Name => 'TimeOfFlightContrast' },
+    '0018,9016' => { VR => 'CS', Name => 'Spoiling' },
+    '0018,9017' => { VR => 'CS', Name => 'SteadyStatePulseSequence' },
+    '0018,9018' => { VR => 'CS', Name => 'EchoPlanarPulseSequence' },
+    '0018,9019' => { VR => 'FD', Name => 'TagAngleFirstAxis' },
+    '0018,9020' => { VR => 'CS', Name => 'MagnetizationTransfer' },
+    '0018,9021' => { VR => 'CS', Name => 'T2Preparation' },
+    '0018,9022' => { VR => 'CS', Name => 'BloodSignalNulling' },
+    '0018,9022' => { VR => 'CS', Name => 'BloodSignalNulling' },
+    '0018,9024' => { VR => 'CS', Name => 'SaturationRecovery' },
+    '0018,9025' => { VR => 'CS', Name => 'SpectrallySelectedSuppression' },
+    '0018,9026' => { VR => 'CS', Name => 'SpectrallySelectedExcitation' },
+    '0018,9027' => { VR => 'CS', Name => 'SpatialPreSaturation' },
+    '0018,9028' => { VR => 'CS', Name => 'Tagging' },
+    '0018,9029' => { VR => 'CS', Name => 'OversamplingPhase' },
+    '0018,9030' => { VR => 'FD', Name => 'TagSpacingFirstDimension' },
+    '0018,9032' => { VR => 'CS', Name => 'GeometryOfKSpaceTraversal' },
+    '0018,9033' => { VR => 'CS', Name => 'SegmentedKSpaceTraversal' },
+    '0018,9034' => { VR => 'CS', Name => 'RectilinearPhaseEncodeReordering' },
+    '0018,9035' => { VR => 'FD', Name => 'TagThickness' },
+    '0018,9036' => { VR => 'CS', Name => 'PartialFourierDirection' },
+    '0018,9037' => { VR => 'CS', Name => 'CardiacSynchronizationTechnique' },
+    '0018,9041' => { VR => 'LO', Name => 'ReceiveCoilManufacturerName' },
+    '0018,9042' => { VR => 'SQ', Name => 'MRReceiveCoilSequence' },
+    '0018,9043' => { VR => 'CS', Name => 'ReceiveCoilType' },
+    '0018,9044' => { VR => 'CS', Name => 'QuadratureReceiveCoil' },
+    '0018,9045' => { VR => 'SQ', Name => 'MultiCoilDefinitionSequence' },
+    '0018,9046' => { VR => 'LO', Name => 'MultiCoilConfiguration' },
+    '0018,9047' => { VR => 'SH', Name => 'MultiCoilElementName' },
+    '0018,9048' => { VR => 'CS', Name => 'MultiCoilElementUsed' },
+    '0018,9049' => { VR => 'SQ', Name => 'MRTransmitCoilSequence' },
+    '0018,9050' => { VR => 'LO', Name => 'TransmitCoilManufacturerName' },
+    '0018,9051' => { VR => 'CS', Name => 'TransmitCoilType' },
+    '0018,9052' => { VR => 'FD', Name => 'SpectralWidth' },
+    '0018,9053' => { VR => 'FD', Name => 'ChemicalShiftReference' },
+    '0018,9054' => { VR => 'CS', Name => 'VolumeLocalizationTechnique' },
+    '0018,9058' => { VR => 'US', Name => 'MRAcquisitionFrequencyEncodeSteps' },
+    '0018,9059' => { VR => 'CS', Name => 'Decoupling' },
+    '0018,9060' => { VR => 'CS', Name => 'DecoupledNucleus' },
+    '0018,9061' => { VR => 'FD', Name => 'DecouplingFrequency' },
+    '0018,9062' => { VR => 'CS', Name => 'DecouplingMethod' },
+    '0018,9063' => { VR => 'FD', Name => 'DecouplingChemicalShiftReference' },
+    '0018,9064' => { VR => 'CS', Name => 'KSpaceFiltering' },
+    '0018,9065' => { VR => 'CS', Name => 'TimeDomainFiltering' },
+    '0018,9066' => { VR => 'US', Name => 'NumberOfZeroFills' },
+    '0018,9067' => { VR => 'CS', Name => 'BaselineCorrection' },
+    '0018,9069' => { VR => 'FD', Name => 'ParallelReductionFactorInPlane' },
+    '0018,9070' => { VR => 'FD', Name => 'CardiacRRIntervalSpecified' },
+    '0018,9073' => { VR => 'FD', Name => 'AcquisitionDuration' },
+    '0018,9074' => { VR => 'DT', Name => 'FrameAcquisitionDatetime' },
+    '0018,9075' => { VR => 'CS', Name => 'DiffusionDirectionality' },
+    '0018,9076' => { VR => 'SQ', Name => 'DiffusionGradientDirectionSequence' },
+    '0018,9077' => { VR => 'CS', Name => 'ParallelAcquisition' },
+    '0018,9078' => { VR => 'CS', Name => 'ParallelAcquisitionTechnique' },
+    '0018,9079' => { VR => 'FD', Name => 'InversionTimes' },
+    '0018,9080' => { VR => 'ST', Name => 'MetaboliteMapDescription' },
+    '0018,9081' => { VR => 'CS', Name => 'PartialFourier' },
+    '0018,9082' => { VR => 'FD', Name => 'EffectiveEchoTime' },
+    '0018,9083' => { VR => 'SQ', Name => 'MetaboliteMapCodeSequence' },
+    '0018,9084' => { VR => 'SQ', Name => 'ChemicalShiftSequence' },
+    '0018,9085' => { VR => 'CS', Name => 'CardiacSignalSource' },
+    '0018,9087' => { VR => 'FD', Name => 'DiffusionBValue' },
+    '0018,9089' => { VR => 'FD', Name => 'DiffusionGradientOrientation' },
+    '0018,9090' => { VR => 'FD', Name => 'VelocityEncodingDirection' },
+    '0018,9091' => { VR => 'FD', Name => 'VelocityEncodingMinimumValue' },
+    '0018,9093' => { VR => 'US', Name => 'NumberOfKSpaceTrajectories' },
+    '0018,9094' => { VR => 'CS', Name => 'CoverageOfKSpace' },
+    '0018,9095' => { VR => 'UL', Name => 'SpectroscopyAcquisitionPhaseRows' },
+    '0018,9098' => { VR => 'FD', Name => 'TransmitterFrequency' },
+    '0018,9100' => { VR => 'CS', Name => 'ResonantNucleus' },
+    '0018,9101' => { VR => 'CS', Name => 'FrequencyCorrection' },
+    '0018,9103' => { VR => 'SQ', Name => 'MRSpectroscopyFOV-GeometrySequence' },
+    '0018,9104' => { VR => 'FD', Name => 'SlabThickness' },
+    '0018,9105' => { VR => 'FD', Name => 'SlabOrientation' },
+    '0018,9106' => { VR => 'FD', Name => 'MidSlabPosition' },
+    '0018,9107' => { VR => 'SQ', Name => 'MRSpatialSaturationSequence' },
+    '0018,9112' => { VR => 'SQ', Name => 'MRTimingAndRelatedParametersSeq' },
+    '0018,9114' => { VR => 'SQ', Name => 'MREchoSequence' },
+    '0018,9115' => { VR => 'SQ', Name => 'MRModifierSequence' },
+    '0018,9117' => { VR => 'SQ', Name => 'MRDiffusionSequence' },
+    '0018,9118' => { VR => 'SQ', Name => 'CardiacTriggerSequence' },
+    '0018,9119' => { VR => 'SQ', Name => 'MRAveragesSequence' },
+    '0018,9125' => { VR => 'SQ', Name => 'MRFOV-GeometrySequence' },
+    '0018,9126' => { VR => 'SQ', Name => 'VolumeLocalizationSequence' },
+    '0018,9127' => { VR => 'UL', Name => 'SpectroscopyAcquisitionDataColumns' },
+    '0018,9147' => { VR => 'CS', Name => 'DiffusionAnisotropyType' },
+    '0018,9151' => { VR => 'DT', Name => 'FrameReferenceDatetime' },
+    '0018,9152' => { VR => 'SQ', Name => 'MRMetaboliteMapSequence' },
+    '0018,9155' => { VR => 'FD', Name => 'ParallelReductionFactorOutOfPlane' },
+    '0018,9159' => { VR => 'UL', Name => 'SpectroscopyOutOfPlanePhaseSteps' },
+    '0018,9166' => { VR => 'CS', Name => 'BulkMotionStatus' },
+    '0018,9168' => { VR => 'FD', Name => 'ParallelReductionFactSecondInPlane' },
+    '0018,9169' => { VR => 'CS', Name => 'CardiacBeatRejectionTechnique' },
+    '0018,9170' => { VR => 'CS', Name => 'RespiratoryMotionCompTechnique' },
+    '0018,9171' => { VR => 'CS', Name => 'RespiratorySignalSource' },
+    '0018,9172' => { VR => 'CS', Name => 'BulkMotionCompensationTechnique' },
+    '0018,9173' => { VR => 'CS', Name => 'BulkMotionSignalSource' },
+    '0018,9174' => { VR => 'CS', Name => 'ApplicableSafetyStandardAgency' },
+    '0018,9175' => { VR => 'LO', Name => 'ApplicableSafetyStandardDescr' },
+    '0018,9176' => { VR => 'SQ', Name => 'OperatingModeSequence' },
+    '0018,9177' => { VR => 'CS', Name => 'OperatingModeType' },
+    '0018,9178' => { VR => 'CS', Name => 'OperatingMode' },
+    '0018,9179' => { VR => 'CS', Name => 'SpecificAbsorptionRateDefinition' },
+    '0018,9180' => { VR => 'CS', Name => 'GradientOutputType' },
+    '0018,9181' => { VR => 'FD', Name => 'SpecificAbsorptionRateValue' },
+    '0018,9182' => { VR => 'FD', Name => 'GradientOutput' },
+    '0018,9183' => { VR => 'CS', Name => 'FlowCompensationDirection' },
+    '0018,9184' => { VR => 'FD', Name => 'TaggingDelay' },
+    '0018,9195' => { VR => 'FD', Name => 'ChemicalShiftsMinIntegrateLimitHz' },
+    '0018,9196' => { VR => 'FD', Name => 'ChemicalShiftsMaxIntegrateLimitHz' },
+    '0018,9197' => { VR => 'SQ', Name => 'MRVelocityEncodingSequence' },
+    '0018,9198' => { VR => 'CS', Name => 'FirstOrderPhaseCorrection' },
+    '0018,9199' => { VR => 'CS', Name => 'WaterReferencedPhaseCorrection' },
+    '0018,9200' => { VR => 'CS', Name => 'MRSpectroscopyAcquisitionType' },
+    '0018,9214' => { VR => 'CS', Name => 'RespiratoryCyclePosition' },
+    '0018,9217' => { VR => 'FD', Name => 'VelocityEncodingMaximumValue' },
+    '0018,9218' => { VR => 'FD', Name => 'TagSpacingSecondDimension' },
+    '0018,9219' => { VR => 'SS', Name => 'TagAngleSecondAxis' },
+    '0018,9220' => { VR => 'FD', Name => 'FrameAcquisitionDuration' },
+    '0018,9226' => { VR => 'SQ', Name => 'MRImageFrameTypeSequence' },
+    '0018,9227' => { VR => 'SQ', Name => 'MRSpectroscopyFrameTypeSequence' },
+    '0018,9231' => { VR => 'US', Name => 'MRAcqPhaseEncodingStepsInPlane' },
+    '0018,9232' => { VR => 'US', Name => 'MRAcqPhaseEncodingStepsOutOfPlane' },
+    '0018,9234' => { VR => 'UL', Name => 'SpectroscopyAcqPhaseColumns' },
+    '0018,9236' => { VR => 'CS', Name => 'CardiacCyclePosition' },
+    '0018,9239' => { VR => 'SQ', Name => 'SpecificAbsorptionRateSequence' },
+    '0018,9240' => { VR => 'US', Name => 'RFEchoTrainLength' },
+    '0018,9241' => { VR => 'US', Name => 'GradientEchoTrainLength' },
+    '0018,9295' => { VR => 'FD', Name => 'ChemicalShiftsMinIntegrateLimitPPM' },
+    '0018,9296' => { VR => 'FD', Name => 'ChemicalShiftsMaxIntegrateLimitPPM' },
+    '0018,9301' => { VR => 'SQ', Name => 'CTAcquisitionTypeSequence' },
+    '0018,9302' => { VR => 'CS', Name => 'AcquisitionType' },
+    '0018,9303' => { VR => 'FD', Name => 'TubeAngle' },
+    '0018,9304' => { VR => 'SQ', Name => 'CTAcquisitionDetailsSequence' },
+    '0018,9305' => { VR => 'FD', Name => 'RevolutionTime' },
+    '0018,9306' => { VR => 'FD', Name => 'SingleCollimationWidth' },
+    '0018,9307' => { VR => 'FD', Name => 'TotalCollimationWidth' },
+    '0018,9308' => { VR => 'SQ', Name => 'CTTableDynamicsSequence' },
+    '0018,9309' => { VR => 'FD', Name => 'TableSpeed' },
+    '0018,9310' => { VR => 'FD', Name => 'TableFeedPerRotation' },
+    '0018,9311' => { VR => 'FD', Name => 'SpiralPitchFactor' },
+    '0018,9312' => { VR => 'SQ', Name => 'CTGeometrySequence' },
+    '0018,9313' => { VR => 'FD', Name => 'DataCollectionCenterPatient' },
+    '0018,9314' => { VR => 'SQ', Name => 'CTReconstructionSequence' },
+    '0018,9315' => { VR => 'CS', Name => 'ReconstructionAlgorithm' },
+    '0018,9316' => { VR => 'CS', Name => 'ConvolutionKernelGroup' },
+    '0018,9317' => { VR => 'FD', Name => 'ReconstructionFieldOfView' },
+    '0018,9318' => { VR => 'FD', Name => 'ReconstructionTargetCenterPatient' },
+    '0018,9319' => { VR => 'FD', Name => 'ReconstructionAngle' },
+    '0018,9320' => { VR => 'SH', Name => 'ImageFilter' },
+    '0018,9321' => { VR => 'SQ', Name => 'CTExposureSequence' },
+    '0018,9322' => { VR => 'FD', Name => 'ReconstructionPixelSpacing' },
+    '0018,9323' => { VR => 'CS', Name => 'ExposureModulationType' },
+    '0018,9324' => { VR => 'FD', Name => 'EstimatedDoseSaving' },
+    '0018,9325' => { VR => 'SQ', Name => 'CTXRayDetailsSequence' },
+    '0018,9326' => { VR => 'SQ', Name => 'CTPositionSequence' },
+    '0018,9327' => { VR => 'FD', Name => 'TablePosition' },
+    '0018,9328' => { VR => 'FD', Name => 'ExposureTimeInMilliSec' },
+    '0018,9329' => { VR => 'SQ', Name => 'CTImageFrameTypeSequence' },
+    '0018,9330' => { VR => 'FD', Name => 'XRayTubeCurrentInMilliAmps' },
+    '0018,9332' => { VR => 'FD', Name => 'ExposureInMilliAmpSec' },
+    '0018,9333' => { VR => 'CS', Name => 'ConstantVolumeFlag' },
+    '0018,9334' => { VR => 'CS', Name => 'FluoroscopyFlag' },
+    '0018,9335' => { VR => 'FD', Name => 'SourceToDataCollectionCenterDist' },
+    '0018,9337' => { VR => 'US', Name => 'Contrast-BolusAgentNumber' },
+    '0018,9338' => { VR => 'SQ', Name => 'Contrast-BolusIngredientCodeSeq' },
+    '0018,9340' => { VR => 'SQ', Name => 'ContrastAdministrationProfileSeq' },
+    '0018,9341' => { VR => 'SQ', Name => 'Contrast-BolusUsageSequence' },
+    '0018,9342' => { VR => 'CS', Name => 'Contrast-BolusAgentAdministered' },
+    '0018,9343' => { VR => 'CS', Name => 'Contrast-BolusAgentDetected' },
+    '0018,9344' => { VR => 'CS', Name => 'Contrast-BolusAgentPhase' },
+    '0018,9345' => { VR => 'FD', Name => 'CTDIvol' },
+    '0018,A001' => { VR => 'SQ', Name => 'ContributingEquipmentSequence' },
+    '0018,A002' => { VR => 'DT', Name => 'ContributionDateTime' },
+    '0018,A003' => { VR => 'ST', Name => 'ContributionDescription' },
+    # relationship group
+    '0020,0000' => { VR => 'UL', Name => 'RelationshipGroupLength' },
+    '0020,000D' => { VR => 'UI', Name => 'StudyInstanceUID' },
+    '0020,000E' => { VR => 'UI', Name => 'SeriesInstanceUID' },
+    '0020,0010' => { VR => 'SH', Name => 'StudyID' },
+    '0020,0011' => { VR => 'IS', Name => 'SeriesNumber' },
+    '0020,0012' => { VR => 'IS', Name => 'AcquisitionNumber' },
+    '0020,0013' => { VR => 'IS', Name => 'InstanceNumber' },
+    '0020,0014' => { VR => 'RET',Name => 'IsotopeNumber' },
+    '0020,0015' => { VR => 'RET',Name => 'PhaseNumber' },
+    '0020,0016' => { VR => 'RET',Name => 'IntervalNumber' },
+    '0020,0017' => { VR => 'RET',Name => 'TimeSlotNumber' },
+    '0020,0018' => { VR => 'RET',Name => 'AngleNumber' },
+    '0020,0019' => { VR => 'IS', Name => 'ItemNumber' },
+    '0020,0020' => { VR => 'CS', Name => 'PatientOrientation' },
+    '0020,0022' => { VR => 'IS', Name => 'OverlayNumber' },
+    '0020,0024' => { VR => 'IS', Name => 'CurveNumber' },
+    '0020,0026' => { VR => 'IS', Name => 'LookupTableNumber' },
+    '0020,0030' => { VR => 'DS', Name => 'ImagePosition' },
+    '0020,0032' => { VR => 'DS', Name => 'ImagePositionPatient' },
+    '0020,0035' => { VR => 'DS', Name => 'ImageOrientation' },
+    '0020,0037' => { VR => 'DS', Name => 'ImageOrientationPatient' },
+    '0020,0050' => { VR => 'RET',Name => 'Location' },
+    '0020,0052' => { VR => 'UI', Name => 'FrameOfReferenceUID' },
+    '0020,0060' => { VR => 'CS', Name => 'Laterality' },
+    '0020,0062' => { VR => 'CS', Name => 'ImageLaterality' },
+    '0020,0070' => { VR => 'RET',Name => 'ImageGeometryType' },
+    '0020,0080' => { VR => 'RET',Name => 'MaskingImage' },
+    '0020,0100' => { VR => 'IS', Name => 'TemporalPositionIdentifier' },
+    '0020,0105' => { VR => 'IS', Name => 'NumberOfTemporalPositions' },
+    '0020,0110' => { VR => 'DS', Name => 'TemporalResolution' },
+    '0020,0200' => { VR => 'UI', Name => 'SynchronizationFrameOfReferenceUID' },
+    '0020,1000' => { VR => 'IS', Name => 'SeriesInStudy' },
+    '0020,1001' => { VR => 'RET',Name => 'AcquisitionsInSeries' },
+    '0020,1002' => { VR => 'IS', Name => 'ImagesInAcquisition' },
+    '0020,1004' => { VR => 'IS', Name => 'AcquisitionsInStudy' },
+    '0020,1020' => { VR => 'RET',Name => 'Reference' },
+    '0020,1040' => { VR => 'LO', Name => 'PositionReferenceIndicator' },
+    '0020,1041' => { VR => 'DS', Name => 'SliceLocation' },
+    '0020,1070' => { VR => 'IS', Name => 'OtherStudyNumbers' },
+    '0020,1200' => { VR => 'IS', Name => 'NumberOfPatientRelatedStudies' },
+    '0020,1202' => { VR => 'IS', Name => 'NumberOfPatientRelatedSeries' },
+    '0020,1204' => { VR => 'IS', Name => 'NumberOfPatientRelatedInstances' },
+    '0020,1206' => { VR => 'IS', Name => 'NumberOfStudyRelatedSeries' },
+    '0020,1208' => { VR => 'IS', Name => 'NumberOfStudyRelatedInstances' },
+    '0020,1209' => { VR => 'RET',Name => 'NumberOfSeriesRelatedInstances' },
+    '0020,31xx' => { VR => 'RET',Name => 'SourceImageIDs' },
+    '0020,3401' => { VR => 'RET',Name => 'ModifyingDeviceID' },
+    '0020,3402' => { VR => 'RET',Name => 'ModifiedImageID' },
+    '0020,3403' => { VR => 'RET',Name => 'ModifiedImageDate' },
+    '0020,3404' => { VR => 'RET',Name => 'ModifyingDeviceManufacturer' },
+    '0020,3405' => { VR => 'RET',Name => 'ModifiedImageTime' },
+    '0020,3406' => { VR => 'RET',Name => 'ModifiedImageDescription' },
+    '0020,4000' => { VR => 'LT', Name => 'ImageComments' },
+    '0020,5000' => { VR => 'US', Name => 'OriginalImageIdentification' },
+    '0020,5002' => { VR => 'RET',Name => 'OriginalImageIdentNomenclature' },
+    '0020,9056' => { VR => 'SH', Name => 'StackID' },
+    '0020,9057' => { VR => 'UL', Name => 'InStackPositionNumber' },
+    '0020,9071' => { VR => 'SQ', Name => 'FrameAnatomySequence' },
+    '0020,9072' => { VR => 'CS', Name => 'FrameLaterality' },
+    '0020,9111' => { VR => 'SQ', Name => 'FrameContentSequence' },
+    '0020,9113' => { VR => 'SQ', Name => 'PlanePositionSequence' },
+    '0020,9116' => { VR => 'SQ', Name => 'PlaneOrientationSequence' },
+    '0020,9128' => { VR => 'UL', Name => 'TemporalPositionIndex' },
+    '0020,9153' => { VR => 'FD', Name => 'TriggerDelayTime' },
+    '0020,9156' => { VR => 'US', Name => 'FrameAcquisitionNumber' },
+    '0020,9157' => { VR => 'UL', Name => 'DimensionIndexValues' },
+    '0020,9158' => { VR => 'LT', Name => 'FrameComments' },
+    '0020,9161' => { VR => 'UI', Name => 'ConcatenationUID' },
+    '0020,9162' => { VR => 'US', Name => 'InconcatenationNumber' },
+    '0020,9163' => { VR => 'US', Name => 'InconcatenationTotalNumber' },
+    '0020,9164' => { VR => 'UI', Name => 'DimensionOrganizationUID' },
+    '0020,9165' => { VR => 'AT', Name => 'DimensionIndexPointer' },
+    '0020,9167' => { VR => 'AT', Name => 'FunctionalGroupPointer' },
+    '0020,9213' => { VR => 'LO', Name => 'DimensionIndexPrivateCreator' },
+    '0020,9221' => { VR => 'SQ', Name => 'DimensionOrganizationSequence' },
+    '0020,9222' => { VR => 'SQ', Name => 'DimensionIndexSequence' },
+    '0020,9228' => { VR => 'UL', Name => 'ConcatenationFrameOffsetNumber' },
+    '0020,9238' => { VR => 'LO', Name => 'FunctionalGroupPrivateCreator' },
+    '0022,0001' => { VR => 'US', Name => 'LightPathFilterPassThroughWavelen' },
+    '0022,0002' => { VR => 'US', Name => 'LightPathFilterPassBand' },
+    '0022,0003' => { VR => 'US', Name => 'ImagePathFilterPassThroughWavelen' },
+    '0022,0004' => { VR => 'US', Name => 'ImagePathFilterPassBand' },
+    '0022,0005' => { VR => 'CS', Name => 'PatientEyeMovementCommanded' },
+    '0022,0006' => { VR => 'SQ', Name => 'PatientEyeMovementCommandCodeSeq' },
+    '0022,0007' => { VR => 'FL', Name => 'SphericalLensPower' },
+    '0022,0008' => { VR => 'FL', Name => 'CylinderLensPower' },
+    '0022,0009' => { VR => 'FL', Name => 'CylinderAxis' },
+    '0022,000A' => { VR => 'FL', Name => 'EmmetropicMagnification' },
+    '0022,000B' => { VR => 'FL', Name => 'IntraOcularPressure' },
+    '0022,000C' => { VR => 'FL', Name => 'HorizontalFieldOfView' },
+    '0022,000D' => { VR => 'CS', Name => 'PupilDilated' },
+    '0022,000E' => { VR => 'FL', Name => 'DegreeOfDilation' },
+    '0022,0010' => { VR => 'FL', Name => 'StereoBaselineAngle' },
+    '0022,0011' => { VR => 'FL', Name => 'StereoBaselineDisplacement' },
+    '0022,0012' => { VR => 'FL', Name => 'StereoHorizontalPixelOffset' },
+    '0022,0013' => { VR => 'FL', Name => 'StereoVerticalPixelOffset' },
+    '0022,0014' => { VR => 'FL', Name => 'StereoRotation' },
+    '0022,0015' => { VR => 'SQ', Name => 'AcquisitionDeviceTypeCodeSequence' },
+    '0022,0016' => { VR => 'SQ', Name => 'IlluminationTypeCodeSequence' },
+    '0022,0017' => { VR => 'SQ', Name => 'LightPathFilterTypeStackCodeSeq' },
+    '0022,0018' => { VR => 'SQ', Name => 'ImagePathFilterTypeStackCodeSeq' },
+    '0022,0019' => { VR => 'SQ', Name => 'LensesCodeSequence' },
+    '0022,001A' => { VR => 'SQ', Name => 'ChannelDescriptionCodeSequence' },
+    '0022,001B' => { VR => 'SQ', Name => 'RefractiveStateSequence' },
+    '0022,001C' => { VR => 'SQ', Name => 'MydriaticAgentCodeSequence' },
+    '0022,001D' => { VR => 'SQ', Name => 'RelativeImagePositionCodeSequence' },
+    '0022,0020' => { VR => 'SQ', Name => 'StereoPairsSequence' },
+    '0022,0021' => { VR => 'SQ', Name => 'LeftImageSequence' },
+    '0022,0022' => { VR => 'SQ', Name => 'RightImageSequence' },
+    # image presentation group
+    '0028,0000' => { VR => 'UL', Name => 'ImagePresentationGroupLength' },
+    '0028,0002' => { VR => 'US', Name => 'SamplesPerPixel' },
+    '0028,0003' => { VR => 'US', Name => 'SamplesPerPixelUsed' },
+    '0028,0004' => { VR => 'CS', Name => 'PhotometricInterpretation' },
+    '0028,0005' => { VR => 'US', Name => 'ImageDimensions' },
+    '0028,0006' => { VR => 'US', Name => 'PlanarConfiguration' },
+    '0028,0008' => { VR => 'IS', Name => 'NumberOfFrames' },
+    '0028,0009' => { VR => 'AT', Name => 'FrameIncrementPointer' },
+    '0028,000A' => { VR => 'AT', Name => 'FrameDimensionPointer' },
+    '0028,0010' => { VR => 'US', Name => 'Rows' },
+    '0028,0011' => { VR => 'US', Name => 'Columns' },
+    '0028,0012' => { VR => 'US', Name => 'Planes' },
+    '0028,0014' => { VR => 'US', Name => 'UltrasoundColorDataPresent' },
+    '0028,0030' => { VR => 'DS', Name => 'PixelSpacing' },
+    '0028,0031' => { VR => 'DS', Name => 'ZoomFactor' },
+    '0028,0032' => { VR => 'DS', Name => 'ZoomCenter' },
+    '0028,0034' => { VR => 'IS', Name => 'PixelAspectRatio' },
+    '0028,0040' => { VR => 'RET',Name => 'ImageFormat' },
+    '0028,0050' => { VR => 'RET',Name => 'ManipulatedImage' },
+    '0028,0051' => { VR => 'CS', Name => 'CorrectedImage' },
+    '0028,0060' => { VR => 'RET',Name => 'CompressionCode' },
+    '0028,0100' => { VR => 'US', Name => 'BitsAllocated' },
+    '0028,0101' => { VR => 'US', Name => 'BitsStored' },
+    '0028,0102' => { VR => 'US', Name => 'HighBit' },
+    '0028,0103' => { VR => 'US', Name => 'PixelRepresentation' },
+    '0028,0104' => { VR => 'RET',Name => 'SmallestValidPixelValue' },
+    '0028,0105' => { VR => 'RET',Name => 'LargestValidPixelValue' },
+    '0028,0106' => { VR => 'SS', Name => 'SmallestImagePixelValue' },
+    '0028,0107' => { VR => 'SS', Name => 'LargestImagePixelValue' },
+    '0028,0108' => { VR => 'SS', Name => 'SmallestPixelValueInSeries' },
+    '0028,0109' => { VR => 'SS', Name => 'LargestPixelValueInSeries' },
+    '0028,0110' => { VR => 'SS', Name => 'SmallestImagePixelValueInPlane' },
+    '0028,0111' => { VR => 'SS', Name => 'LargestImagePixelValueInPlane' },
+    '0028,0120' => { VR => 'SS', Name => 'PixelPaddingValue' },
+    '0028,0200' => { VR => 'SS', Name => 'ImageLocation' },
+    '0028,0300' => { VR => 'CS', Name => 'QualityControlImage' },
+    '0028,0301' => { VR => 'CS', Name => 'BurnedInAnnotation' },
+    '0028,1040' => { VR => 'CS', Name => 'PixelIntensityRelationship' },
+    '0028,1041' => { VR => 'SS', Name => 'PixelIntensityRelationshipSign' },
+    '0028,1050' => { VR => 'DS', Name => 'WindowCenter' },
+    '0028,1051' => { VR => 'DS', Name => 'WindowWidth' },
+    '0028,1052' => { VR => 'DS', Name => 'RescaleIntercept' },
+    '0028,1053' => { VR => 'DS', Name => 'RescaleSlope' },
+    '0028,1054' => { VR => 'LO', Name => 'RescaleType' },
+    '0028,1055' => { VR => 'LO', Name => 'WindowCenterAndWidthExplanation' },
+    '0028,1080' => { VR => 'RET',Name => 'GrayScale' },
+    '0028,1090' => { VR => 'CS', Name => 'RecommendedViewingMode' },
+    '0028,1100' => { VR => 'RET',Name => 'GrayLookupTableDescriptor' },
+    '0028,1101' => { VR => 'SS', Name => 'RedPaletteColorTableDescriptor' },
+    '0028,1102' => { VR => 'SS', Name => 'GreenPaletteColorTableDescriptor' },
+    '0028,1103' => { VR => 'SS', Name => 'BluePaletteColorTableDescriptor' },
+    '0028,1199' => { VR => 'UI', Name => 'PaletteColorTableUID' },
+    '0028,1200' => { VR => 'RET',Name => 'GrayLookupTableData' },
+    '0028,1201' => { VR => 'OW', Name => 'RedPaletteColorTableData' },
+    '0028,1202' => { VR => 'OW', Name => 'GreenPaletteColorTableData' },
+    '0028,1203' => { VR => 'OW', Name => 'BluePaletteColorTableData' },
+    '0028,1221' => { VR => 'OW', Name => 'SegmentedRedColorTableData' },
+    '0028,1222' => { VR => 'OW', Name => 'SegmentedGreenColorTableData' },
+    '0028,1223' => { VR => 'OW', Name => 'SegmentedBlueColorTableData' },
+    '0028,1300' => { VR => 'CS', Name => 'ImplantPresent' },
+    '0028,1350' => { VR => 'CS', Name => 'PartialView' },
+    '0028,1351' => { VR => 'ST', Name => 'PartialViewDescription' },
+    '0028,2110' => { VR => 'CS', Name => 'LossyImageCompression' },
+    '0028,2112' => { VR => 'DS', Name => 'LossyImageCompressionRatio' },
+    '0028,2114' => { VR => 'CS', Name => 'LossyImageCompressionMethod' },
+    '0028,3000' => { VR => 'SQ', Name => 'ModalityLUTSequence' },
+    '0028,3002' => { VR => 'SS', Name => 'LUTDescriptor' },
+    '0028,3003' => { VR => 'LO', Name => 'LUTExplanation' },
+    '0028,3004' => { VR => 'LO', Name => 'ModalityLUTType' },
+    '0028,3006' => { VR => 'SS', Name => 'LUTData' },
+    '0028,3010' => { VR => 'SQ', Name => 'VOILUTSequence' },
+    '0028,3110' => { VR => 'SQ', Name => 'SoftcopyVOILUTSequence' },
+    '0028,4000' => { VR => 'RET',Name => 'ImagePresentationComments' },
+    '0028,5000' => { VR => 'SQ', Name => 'BiPlaneAcquisitionSequence' },
+    '0028,6010' => { VR => 'US', Name => 'RepresentativeFrameNumber' },
+    '0028,6020' => { VR => 'US', Name => 'FrameNumbersOfInterestFOI' },
+    '0028,6022' => { VR => 'LO', Name => 'FrameOfInterestDescription' },
+    '0028,6023' => { VR => 'CS', Name => 'FrameOfInterestType' },
+    '0028,6030' => { VR => 'RET',Name => 'MaskPointers' },
+    '0028,6040' => { VR => 'US', Name => 'RWavePointer' },
+    '0028,6100' => { VR => 'SQ', Name => 'MaskSubtractionSequence' },
+    '0028,6101' => { VR => 'CS', Name => 'MaskOperation' },
+    '0028,6102' => { VR => 'US', Name => 'ApplicableFrameRange' },
+    '0028,6110' => { VR => 'US', Name => 'MaskFrameNumbers' },
+    '0028,6112' => { VR => 'US', Name => 'ContrastFrameAveraging' },
+    '0028,6114' => { VR => 'FL', Name => 'MaskSubPixelShift' },
+    '0028,6120' => { VR => 'SS', Name => 'TIDOffset' },
+    '0028,6190' => { VR => 'ST', Name => 'MaskOperationExplanation' },
+    '0028,9001' => { VR => 'UL', Name => 'DataPointRows' },
+    '0028,9002' => { VR => 'UL', Name => 'DataPointColumns' },
+    '0028,9003' => { VR => 'CS', Name => 'SignalDomainColumns' },
+    '0028,9099' => { VR => 'US', Name => 'LargestMonochromePixelValue' },
+    '0028,9108' => { VR => 'CS', Name => 'DataRepresentation' },
+    '0028,9110' => { VR => 'SQ', Name => 'PixelMeasuresSequence' },
+    '0028,9132' => { VR => 'SQ', Name => 'FrameVOILUTSequence' },
+    '0028,9145' => { VR => 'SQ', Name => 'PixelValueTransformationSequence' },
+    '0028,9235' => { VR => 'CS', Name => 'SignalDomainRows' },
+    # study group
+    '0032,0000' => { VR => 'UL', Name => 'StudyGroupLength' },
+    '0032,000A' => { VR => 'CS', Name => 'StudyStatusID' },
+    '0032,000C' => { VR => 'CS', Name => 'StudyPriorityID' },
+    '0032,0012' => { VR => 'LO', Name => 'StudyIDIssuer' },
+    '0032,0032' => { VR => 'DA', Name => 'StudyVerifiedDate' },
+    '0032,0033' => { VR => 'TM', Name => 'StudyVerifiedTime' },
+    '0032,0034' => { VR => 'DA', Name => 'StudyReadDate' },
+    '0032,0035' => { VR => 'TM', Name => 'StudyReadTime' },
+    '0032,1000' => { VR => 'DA', Name => 'ScheduledStudyStartDate' },
+    '0032,1001' => { VR => 'TM', Name => 'ScheduledStudyStartTime' },
+    '0032,1010' => { VR => 'DA', Name => 'ScheduledStudyStopDate' },
+    '0032,1011' => { VR => 'TM', Name => 'ScheduledStudyStopTime' },
+    '0032,1020' => { VR => 'LO', Name => 'ScheduledStudyLocation' },
+    '0032,1021' => { VR => 'AE', Name => 'ScheduledStudyLocationAETitle' },
+    '0032,1030' => { VR => 'LO', Name => 'ReasonForStudy' },
+    '0032,1031' => { VR => 'SQ', Name => 'RequestingPhysicianIDSequence' },
+    '0032,1032' => { VR => 'PN', Name => 'RequestingPhysician' },
+    '0032,1033' => { VR => 'LO', Name => 'RequestingService' },
+    '0032,1040' => { VR => 'DA', Name => 'StudyArrivalDate' },
+    '0032,1041' => { VR => 'TM', Name => 'StudyArrivalTime' },
+    '0032,1050' => { VR => 'DA', Name => 'StudyCompletionDate' },
+    '0032,1051' => { VR => 'TM', Name => 'StudyCompletionTime' },
+    '0032,1055' => { VR => 'CS', Name => 'StudyComponentStatusID' },
+    '0032,1060' => { VR => 'LO', Name => 'RequestedProcedureDescription' },
+    '0032,1064' => { VR => 'SQ', Name => 'RequestedProcedureCodeSequence' },
+    '0032,1070' => { VR => 'LO', Name => 'RequestedContrastAgent' },
+    '0032,4000' => { VR => 'LT', Name => 'StudyComments' },
+    # visit group
+    '0038,0004' => { VR => 'SQ', Name => 'ReferencedPatientAliasSequence' },
+    '0038,0008' => { VR => 'CS', Name => 'VisitStatusID' },
+    '0038,0010' => { VR => 'LO', Name => 'AdmissionID' },
+    '0038,0011' => { VR => 'LO', Name => 'IssuerOfAdmissionID' },
+    '0038,0016' => { VR => 'LO', Name => 'RouteOfAdmissions' },
+    '0038,001A' => { VR => 'DA', Name => 'ScheduledAdmissionDate' },
+    '0038,001B' => { VR => 'TM', Name => 'ScheduledAdmissionTime' },
+    '0038,001C' => { VR => 'DA', Name => 'ScheduledDischargeDate' },
+    '0038,001D' => { VR => 'TM', Name => 'ScheduledDischargeTime' },
+    '0038,001E' => { VR => 'LO', Name => 'ScheduledPatientInstitResidence' },
+    '0038,0020' => { VR => 'DA', Name => 'AdmittingDate' },
+    '0038,0021' => { VR => 'TM', Name => 'AdmittingTime' },
+    '0038,0030' => { VR => 'DA', Name => 'DischargeDate' },
+    '0038,0032' => { VR => 'TM', Name => 'DischargeTime' },
+    '0038,0040' => { VR => 'LO', Name => 'DischargeDiagnosisDescription' },
+    '0038,0044' => { VR => 'SQ', Name => 'DischargeDiagnosisCodeSequence' },
+    '0038,0050' => { VR => 'LO', Name => 'SpecialNeeds' },
+    '0038,0300' => { VR => 'LO', Name => 'CurrentPatientLocation' },
+    '0038,0400' => { VR => 'LO', Name => 'PatientsInstitutionResidence' },
+    '0038,0500' => { VR => 'LO', Name => 'PatientState' },
+    '0038,4000' => { VR => 'LT', Name => 'VisitComments' },
+    '003A,0004' => { VR => 'CS', Name => 'WaveformOriginality' },
+    '003A,0005' => { VR => 'US', Name => 'NumberOfWaveformChannels' },
+    '003A,0010' => { VR => 'UL', Name => 'NumberOfWaveformSamples' },
+    '003A,001A' => { VR => 'DS', Name => 'SamplingFrequency' },
+    '003A,0020' => { VR => 'SH', Name => 'MultiplexGroupLabel' },
+    '003A,0200' => { VR => 'SQ', Name => 'ChannelDefinitionSequence' },
+    '003A,0202' => { VR => 'IS', Name => 'WaveformChannelNumber' },
+    '003A,0203' => { VR => 'SH', Name => 'ChannelLabel' },
+    '003A,0205' => { VR => 'CS', Name => 'ChannelStatus' },
+    '003A,0208' => { VR => 'SQ', Name => 'ChannelSourceSequence' },
+    '003A,0209' => { VR => 'SQ', Name => 'ChannelSourceModifiersSequence' },
+    '003A,020A' => { VR => 'SQ', Name => 'SourceWaveformSequence' },
+    '003A,020C' => { VR => 'LO', Name => 'ChannelDerivationDescription' },
+    '003A,0210' => { VR => 'DS', Name => 'ChannelSensitivity' },
+    '003A,0211' => { VR => 'SQ', Name => 'ChannelSensitivityUnitsSequence' },
+    '003A,0212' => { VR => 'DS', Name => 'ChannelSensitivityCorrectionFactor' },
+    '003A,0213' => { VR => 'DS', Name => 'ChannelBaseline' },
+    '003A,0214' => { VR => 'DS', Name => 'ChannelTimeSkew' },
+    '003A,0215' => { VR => 'DS', Name => 'ChannelSampleSkew' },
+    '003A,0218' => { VR => 'DS', Name => 'ChannelOffset' },
+    '003A,021A' => { VR => 'US', Name => 'WaveformBitsStored' },
+    '003A,0220' => { VR => 'DS', Name => 'FilterLowFrequency' },
+    '003A,0221' => { VR => 'DS', Name => 'FilterHighFrequency' },
+    '003A,0222' => { VR => 'DS', Name => 'NotchFilterFrequency' },
+    '003A,0223' => { VR => 'DS', Name => 'NotchFilterBandwidth' },
+    '003A,0300' => { VR => 'SQ', Name => 'MultiplexAudioChannelsDescrCodeSeq' },
+    '003A,0301' => { VR => 'IS', Name => 'ChannelIdentificationCode' },
+    '003A,0302' => { VR => 'CS', Name => 'ChannelMode' },
+    '0040,0001' => { VR => 'AE', Name => 'ScheduledStationAETitle' },
+    '0040,0002' => { VR => 'DA', Name => 'ScheduledProcedureStepStartDate' },
+    '0040,0003' => { VR => 'TM', Name => 'ScheduledProcedureStepStartTime' },
+    '0040,0004' => { VR => 'DA', Name => 'ScheduledProcedureStepEndDate' },
+    '0040,0005' => { VR => 'TM', Name => 'ScheduledProcedureStepEndTime' },
+    '0040,0006' => { VR => 'PN', Name => 'ScheduledPerformingPhysiciansName' },
+    '0040,0007' => { VR => 'LO', Name => 'ScheduledProcedureStepDescription' },
+    '0040,0008' => { VR => 'SQ', Name => 'ScheduledProtocolCodeSequence' },
+    '0040,0009' => { VR => 'SH', Name => 'ScheduledProcedureStepID' },
+    '0040,000A' => { VR => 'SQ', Name => 'StageCodeSequence' },
+    '0040,000B' => { VR => 'SQ', Name => 'ScheduledPerformingPhysicianIDSeq' },
+    '0040,0010' => { VR => 'SH', Name => 'ScheduledStationName' },
+    '0040,0011' => { VR => 'SH', Name => 'ScheduledProcedureStepLocation' },
+    '0040,0012' => { VR => 'LO', Name => 'PreMedication' },
+    '0040,0020' => { VR => 'CS', Name => 'ScheduledProcedureStepStatus' },
+    '0040,0100' => { VR => 'SQ', Name => 'ScheduledProcedureStepSequence' },
+    '0040,0220' => { VR => 'SQ', Name => 'ReferencedNonImageCompositeSOPSeq' },
+    '0040,0241' => { VR => 'AE', Name => 'PerformedStationAETitle' },
+    '0040,0242' => { VR => 'SH', Name => 'PerformedStationName' },
+    '0040,0243' => { VR => 'SH', Name => 'PerformedLocation' },
+    '0040,0244' => { VR => 'DA', Name => 'PerformedProcedureStepStartDate' },
+    '0040,0245' => { VR => 'TM', Name => 'PerformedProcedureStepStartTime' },
+    '0040,0250' => { VR => 'DA', Name => 'PerformedProcedureStepEndDate' },
+    '0040,0251' => { VR => 'TM', Name => 'PerformedProcedureStepEndTime' },
+    '0040,0252' => { VR => 'CS', Name => 'PerformedProcedureStepStatus' },
+    '0040,0253' => { VR => 'SH', Name => 'PerformedProcedureStepID' },
+    '0040,0254' => { VR => 'LO', Name => 'PerformedProcedureStepDescription' },
+    '0040,0255' => { VR => 'LO', Name => 'PerformedProcedureTypeDescription' },
+    '0040,0260' => { VR => 'SQ', Name => 'PerformedProtocolCodeSequence' },
+    '0040,0270' => { VR => 'SQ', Name => 'ScheduledStepAttributesSequence' },
+    '0040,0275' => { VR => 'SQ', Name => 'RequestAttributesSequence' },
+    '0040,0280' => { VR => 'ST', Name => 'CommentsOnPerformedProcedureStep' },
+    '0040,0281' => { VR => 'SQ', Name => 'ProcStepDiscontinueReasonCodeSeq' },
+    '0040,0293' => { VR => 'SQ', Name => 'QuantitySequence' },
+    '0040,0294' => { VR => 'DS', Name => 'Quantity' },
+    '0040,0295' => { VR => 'SQ', Name => 'MeasuringUnitsSequence' },
+    '0040,0296' => { VR => 'SQ', Name => 'BillingItemSequence' },
+    '0040,0300' => { VR => 'US', Name => 'TotalTimeOfFluoroscopy' },
+    '0040,0301' => { VR => 'US', Name => 'TotalNumberOfExposures' },
+    '0040,0302' => { VR => 'US', Name => 'EntranceDose' },
+    '0040,0303' => { VR => 'US', Name => 'ExposedArea' },
+    '0040,0306' => { VR => 'DS', Name => 'DistanceSourceToEntrance' },
+    '0040,0307' => { VR => 'RET',Name => 'DistanceSourceToSupport' },
+    '0040,030E' => { VR => 'SQ', Name => 'ExposureDoseSequence' },
+    '0040,0310' => { VR => 'ST', Name => 'CommentsOnRadiationDose' },
+    '0040,0312' => { VR => 'DS', Name => 'XRayOutput' },
+    '0040,0314' => { VR => 'DS', Name => 'HalfValueLayer' },
+    '0040,0316' => { VR => 'DS', Name => 'OrganDose' },
+    '0040,0318' => { VR => 'CS', Name => 'OrganExposed' },
+    '0040,0320' => { VR => 'SQ', Name => 'BillingProcedureStepSequence' },
+    '0040,0321' => { VR => 'SQ', Name => 'FilmConsumptionSequence' },
+    '0040,0324' => { VR => 'SQ', Name => 'BillingSuppliesAndDevicesSequence' },
+    '0040,0330' => { VR => 'RET',Name => 'ReferencedProcedureStepSequence' },
+    '0040,0340' => { VR => 'SQ', Name => 'PerformedSeriesSequence' },
+    '0040,0400' => { VR => 'LT', Name => 'CommentsOnScheduledProcedureStep' },
+    '0040,0440' => { VR => 'SQ', Name => 'ProtocolContextSequence' },
+    '0040,0441' => { VR => 'SQ', Name => 'ContentItemModifierSequence' },
+    '0040,050A' => { VR => 'LO', Name => 'SpecimenAccessionNumber' },
+    '0040,0550' => { VR => 'SQ', Name => 'SpecimenSequence' },
+    '0040,0551' => { VR => 'LO', Name => 'SpecimenIdentifier' },
+    '0040,0555' => { VR => 'SQ', Name => 'AcquisitionContextSequence' },
+    '0040,0556' => { VR => 'ST', Name => 'AcquisitionContextDescription' },
+    '0040,059A' => { VR => 'SQ', Name => 'SpecimenTypeCodeSequence' },
+    '0040,06FA' => { VR => 'LO', Name => 'SlideIdentifier' },
+    '0040,071A' => { VR => 'SQ', Name => 'ImageCenterPointCoordinatesSeq' },
+    '0040,072A' => { VR => 'DS', Name => 'XOffsetInSlideCoordinateSystem' },
+    '0040,073A' => { VR => 'DS', Name => 'YOffsetInSlideCoordinateSystem' },
+    '0040,074A' => { VR => 'DS', Name => 'ZOffsetInSlideCoordinateSystem' },
+    '0040,08D8' => { VR => 'SQ', Name => 'PixelSpacingSequence' },
+    '0040,08DA' => { VR => 'SQ', Name => 'CoordinateSystemAxisCodeSequence' },
+    '0040,08EA' => { VR => 'SQ', Name => 'MeasurementUnitsCodeSequence' },
+    '0040,1001' => { VR => 'SH', Name => 'RequestedProcedureID' },
+    '0040,1002' => { VR => 'LO', Name => 'ReasonForRequestedProcedure' },
+    '0040,1003' => { VR => 'SH', Name => 'RequestedProcedurePriority' },
+    '0040,1004' => { VR => 'LO', Name => 'PatientTransportArrangements' },
+    '0040,1005' => { VR => 'LO', Name => 'RequestedProcedureLocation' },
+    '0040,1006' => { VR => 'RET',Name => 'PlacerOrderNumber-Procedure' },
+    '0040,1007' => { VR => 'RET',Name => 'FillerOrderNumber-Procedure' },
+    '0040,1008' => { VR => 'LO', Name => 'ConfidentialityCode' },
+    '0040,1009' => { VR => 'SH', Name => 'ReportingPriority' },
+    '0040,100A' => { VR => 'SQ', Name => 'ReasonForRequestedProcedureCodeSeq' },
+    '0040,1010' => { VR => 'PN', Name => 'NamesOfIntendedRecipientsOfResults' },
+    '0040,1011' => { VR => 'SQ', Name => 'IntendedRecipientsOfResultsIDSeq' },
+    '0040,1101' => { VR => 'SQ', Name => 'PersonIdentificationCodeSequence' },
+    '0040,1102' => { VR => 'ST', Name => 'PersonsAddress' },
+    '0040,1103' => { VR => 'LO', Name => 'PersonsTelephoneNumbers' },
+    '0040,1400' => { VR => 'LT', Name => 'RequestedProcedureComments' },
+    '0040,2001' => { VR => 'RET',Name => 'ReasonForImagingServiceRequest' },
+    '0040,2004' => { VR => 'DA', Name => 'IssueDateOfImagingServiceRequest' },
+    '0040,2005' => { VR => 'TM', Name => 'IssueTimeOfImagingServiceRequest' },
+    '0040,2006' => { VR => 'RET',Name => 'PlacerOrderNum-ImagingServiceReq' },
+    '0040,2007' => { VR => 'RET',Name => 'FillerOrderNum-ImagingServiceReq' },
+    '0040,2008' => { VR => 'PN', Name => 'OrderEnteredBy' },
+    '0040,2009' => { VR => 'SH', Name => 'OrderEnterersLocation' },
+    '0040,2010' => { VR => 'SH', Name => 'OrderCallbackPhoneNumber' },
+    '0040,2016' => { VR => 'LO', Name => 'PlacerOrderNum-ImagingServiceReq' },
+    '0040,2017' => { VR => 'LO', Name => 'FillerOrderNum-ImagingServiceReq' },
+    '0040,2400' => { VR => 'LT', Name => 'ImagingServiceRequestComments' },
+    '0040,3001' => { VR => 'LO', Name => 'ConfidentialityOnPatientDataDescr' },
+    '0040,4001' => { VR => 'CS', Name => 'GenPurposeScheduledProcStepStatus' },
+    '0040,4002' => { VR => 'CS', Name => 'GenPurposePerformedProcStepStatus' },
+    '0040,4003' => { VR => 'CS', Name => 'GenPurposeSchedProcStepPriority' },
+    '0040,4004' => { VR => 'SQ', Name => 'SchedProcessingApplicationsCodeSeq' },
+    '0040,4005' => { VR => 'DT', Name => 'SchedProcedureStepStartDateAndTime' },
+    '0040,4006' => { VR => 'CS', Name => 'MultipleCopiesFlag' },
+    '0040,4007' => { VR => 'SQ', Name => 'PerformedProcessingAppsCodeSeq' },
+    '0040,4009' => { VR => 'SQ', Name => 'HumanPerformerCodeSequence' },
+    '0040,4010' => { VR => 'DT', Name => 'SchedProcStepModificationDateTime' },
+    '0040,4011' => { VR => 'DT', Name => 'ExpectedCompletionDateAndTime' },
+    '0040,4015' => { VR => 'SQ', Name => 'ResultingGenPurposePerfProcStepSeq' },
+    '0040,4016' => { VR => 'SQ', Name => 'RefGenPurposeSchedProcStepSeq' },
+    '0040,4018' => { VR => 'SQ', Name => 'ScheduledWorkitemCodeSequence' },
+    '0040,4019' => { VR => 'SQ', Name => 'PerformedWorkitemCodeSequence' },
+    '0040,4020' => { VR => 'CS', Name => 'InputAvailabilityFlag' },
+    '0040,4021' => { VR => 'SQ', Name => 'InputInformationSequence' },
+    '0040,4022' => { VR => 'SQ', Name => 'RelevantInformationSequence' },
+    '0040,4023' => { VR => 'UI', Name => 'RefGenPurSchedProcStepTransUID' },
+    '0040,4025' => { VR => 'SQ', Name => 'ScheduledStationNameCodeSequence' },
+    '0040,4026' => { VR => 'SQ', Name => 'ScheduledStationClassCodeSequence' },
+    '0040,4027' => { VR => 'SQ', Name => 'SchedStationGeographicLocCodeSeq' },
+    '0040,4028' => { VR => 'SQ', Name => 'PerformedStationNameCodeSequence' },
+    '0040,4029' => { VR => 'SQ', Name => 'PerformedStationClassCodeSequence' },
+    '0040,4030' => { VR => 'SQ', Name => 'PerformedStationGeogLocCodeSeq' },
+    '0040,4031' => { VR => 'SQ', Name => 'RequestedSubsequentWorkItemCodeSeq' },
+    '0040,4032' => { VR => 'SQ', Name => 'NonDICOMOutputCodeSequence' },
+    '0040,4033' => { VR => 'SQ', Name => 'OutputInformationSequence' },
+    '0040,4034' => { VR => 'SQ', Name => 'ScheduledHumanPerformersSequence' },
+    '0040,4035' => { VR => 'SQ', Name => 'ActualHumanPerformersSequence' },
+    '0040,4036' => { VR => 'LO', Name => 'HumanPerformersOrganization' },
+    '0040,4037' => { VR => 'PN', Name => 'HumanPerformersName' },
+    '0040,8302' => { VR => 'DS', Name => 'EntranceDoseInMilliGy' },
+    '0040,9096' => { VR => 'SQ', Name => 'RealWorldValueMappingSequence' },
+    '0040,9210' => { VR => 'SH', Name => 'LUTLabel' },
+    '0040,9211' => { VR => 'SS', Name => 'RealWorldValueLastValueMapped' },
+    '0040,9212' => { VR => 'FD', Name => 'RealWorldValueLUTData' },
+    '0040,9216' => { VR => 'SS', Name => 'RealWorldValueFirstValueMapped' },
+    '0040,9224' => { VR => 'FD', Name => 'RealWorldValueIntercept' },
+    '0040,9225' => { VR => 'FD', Name => 'RealWorldValueSlope' },
+    '0040,A010' => { VR => 'CS', Name => 'RelationshipType' },
+    '0040,A027' => { VR => 'LO', Name => 'VerifyingOrganization' },
+    '0040,A030' => { VR => 'DT', Name => 'VerificationDateTime' },
+    '0040,A032' => { VR => 'DT', Name => 'ObservationDateTime' },
+    '0040,A040' => { VR => 'CS', Name => 'ValueType' },
+    '0040,A043' => { VR => 'SQ', Name => 'ConceptNameCodeSequence' },
+    '0040,A050' => { VR => 'CS', Name => 'ContinuityOfContent' },
+    '0040,A073' => { VR => 'SQ', Name => 'VerifyingObserverSequence' },
+    '0040,A075' => { VR => 'PN', Name => 'VerifyingObserverName' },
+    '0040,A088' => { VR => 'SQ', Name => 'VerifyingObserverIdentCodeSequence' },
+    '0040,A0B0' => { VR => 'US', Name => 'ReferencedWaveformChannels' },
+    '0040,A120' => { VR => 'DT', Name => 'DateTime' },
+    '0040,A121' => { VR => 'DA', Name => 'Date' },
+    '0040,A122' => { VR => 'TM', Name => 'Time' },
+    '0040,A123' => { VR => 'PN', Name => 'PersonName' },
+    '0040,A124' => { VR => 'UI', Name => 'UID' },
+    '0040,A130' => { VR => 'CS', Name => 'TemporalRangeType' },
+    '0040,A132' => { VR => 'UL', Name => 'ReferencedSamplePositions' },
+    '0040,A136' => { VR => 'US', Name => 'ReferencedFrameNumbers' },
+    '0040,A138' => { VR => 'DS', Name => 'ReferencedTimeOffsets' },
+    '0040,A13A' => { VR => 'DT', Name => 'ReferencedDatetime' },
+    '0040,A160' => { VR => 'UT', Name => 'TextValue' },
+    '0040,A168' => { VR => 'SQ', Name => 'ConceptCodeSequence' },
+    '0040,A170' => { VR => 'SQ', Name => 'PurposeOfReferenceCodeSequence' },
+    '0040,A180' => { VR => 'US', Name => 'AnnotationGroupNumber' },
+    '0040,A195' => { VR => 'SQ', Name => 'ModifierCodeSequence' },
+    '0040,A300' => { VR => 'SQ', Name => 'MeasuredValueSequence' },
+    '0040,A301' => { VR => 'SQ', Name => 'NumericValueQualifierCodeSequence' },
+    '0040,A30A' => { VR => 'DS', Name => 'NumericValue' },
+    '0040,A360' => { VR => 'SQ', Name => 'PredecessorDocumentsSequence' },
+    '0040,A370' => { VR => 'SQ', Name => 'ReferencedRequestSequence' },
+    '0040,A372' => { VR => 'SQ', Name => 'PerformedProcedureCodeSequence' },
+    '0040,A375' => { VR => 'SQ', Name => 'CurrentRequestedProcEvidenceSeq' },
+    '0040,A385' => { VR => 'SQ', Name => 'PertinentOtherEvidenceSequence' },
+    '0040,A491' => { VR => 'CS', Name => 'CompletionFlag' },
+    '0040,A492' => { VR => 'LO', Name => 'CompletionFlagDescription' },
+    '0040,A493' => { VR => 'CS', Name => 'VerificationFlag' },
+    '0040,A504' => { VR => 'SQ', Name => 'ContentTemplateSequence' },
+    '0040,A525' => { VR => 'SQ', Name => 'IdenticalDocumentsSequence' },
+    '0040,A730' => { VR => 'SQ', Name => 'ContentSequence' },
+    '0040,B020' => { VR => 'SQ', Name => 'AnnotationSequence' },
+    '0040,DB00' => { VR => 'CS', Name => 'TemplateIdentifier' },
+    '0040,DB06' => { VR => 'RET',Name => 'TemplateVersion' },
+    '0040,DB07' => { VR => 'RET',Name => 'TemplateLocalVersion' },
+    '0040,DB0B' => { VR => 'RET',Name => 'TemplateExtensionFlag' },
+    '0040,DB0C' => { VR => 'RET',Name => 'TemplateExtensionOrganizationUID' },
+    '0040,DB0D' => { VR => 'RET',Name => 'TemplateExtensionCreatorUID' },
+    '0040,DB73' => { VR => 'UL', Name => 'ReferencedContentItemIdentifier' },
+    # calibration group
+    '0050,0004' => { VR => 'CS', Name => 'CalibrationImage' },
+    '0050,0010' => { VR => 'SQ', Name => 'DeviceSequence' },
+    '0050,0014' => { VR => 'DS', Name => 'DeviceLength' },
+    '0050,0016' => { VR => 'DS', Name => 'DeviceDiameter' },
+    '0050,0017' => { VR => 'CS', Name => 'DeviceDiameterUnits' },
+    '0050,0018' => { VR => 'DS', Name => 'DeviceVolume' },
+    '0050,0019' => { VR => 'DS', Name => 'InterMarkerDistance' },
+    '0050,0020' => { VR => 'LO', Name => 'DeviceDescription' },
+    # nuclear acquisition group
+    '0054,0010' => { VR => 'US', Name => 'EnergyWindowVector' },
+    '0054,0011' => { VR => 'US', Name => 'NumberOfEnergyWindows' },
+    '0054,0012' => { VR => 'SQ', Name => 'EnergyWindowInformationSequence' },
+    '0054,0013' => { VR => 'SQ', Name => 'EnergyWindowRangeSequence' },
+    '0054,0014' => { VR => 'DS', Name => 'EnergyWindowLowerLimit' },
+    '0054,0015' => { VR => 'DS', Name => 'EnergyWindowUpperLimit' },
+    '0054,0016' => { VR => 'SQ', Name => 'RadiopharmaceuticalInformationSeq' },
+    '0054,0017' => { VR => 'IS', Name => 'ResidualSyringeCounts' },
+    '0054,0018' => { VR => 'SH', Name => 'EnergyWindowName' },
+    '0054,0020' => { VR => 'US', Name => 'DetectorVector' },
+    '0054,0021' => { VR => 'US', Name => 'NumberOfDetectors' },
+    '0054,0022' => { VR => 'SQ', Name => 'DetectorInformationSequence' },
+    '0054,0030' => { VR => 'US', Name => 'PhaseVector' },
+    '0054,0031' => { VR => 'US', Name => 'NumberOfPhases' },
+    '0054,0032' => { VR => 'SQ', Name => 'PhaseInformationSequence' },
+    '0054,0033' => { VR => 'US', Name => 'NumberOfFramesInPhase' },
+    '0054,0036' => { VR => 'IS', Name => 'PhaseDelay' },
+    '0054,0038' => { VR => 'IS', Name => 'PauseBetweenFrames' },
+    '0054,0039' => { VR => 'CS', Name => 'PhaseDescription' },
+    '0054,0050' => { VR => 'US', Name => 'RotationVector' },
+    '0054,0051' => { VR => 'US', Name => 'NumberOfRotations' },
+    '0054,0052' => { VR => 'SQ', Name => 'RotationInformationSequence' },
+    '0054,0053' => { VR => 'US', Name => 'NumberOfFramesInRotation' },
+    '0054,0060' => { VR => 'US', Name => 'RRIntervalVector' },
+    '0054,0061' => { VR => 'US', Name => 'NumberOfRRIntervals' },
+    '0054,0062' => { VR => 'SQ', Name => 'GatedInformationSequence' },
+    '0054,0063' => { VR => 'SQ', Name => 'DataInformationSequence' },
+    '0054,0070' => { VR => 'US', Name => 'TimeSlotVector' },
+    '0054,0071' => { VR => 'US', Name => 'NumberOfTimeSlots' },
+    '0054,0072' => { VR => 'SQ', Name => 'TimeSlotInformationSequence' },
+    '0054,0073' => { VR => 'DS', Name => 'TimeSlotTime' },
+    '0054,0080' => { VR => 'US', Name => 'SliceVector' },
+    '0054,0081' => { VR => 'US', Name => 'NumberOfSlices' },
+    '0054,0090' => { VR => 'US', Name => 'AngularViewVector' },
+    '0054,0100' => { VR => 'US', Name => 'TimeSliceVector' },
+    '0054,0101' => { VR => 'US', Name => 'NumberOfTimeSlices' },
+    '0054,0200' => { VR => 'DS', Name => 'StartAngle' },
+    '0054,0202' => { VR => 'CS', Name => 'TypeOfDetectorMotion' },
+    '0054,0210' => { VR => 'IS', Name => 'TriggerVector' },
+    '0054,0211' => { VR => 'US', Name => 'NumberOfTriggersInPhase' },
+    '0054,0220' => { VR => 'SQ', Name => 'ViewCodeSequence' },
+    '0054,0222' => { VR => 'SQ', Name => 'ViewModifierCodeSequence' },
+    '0054,0300' => { VR => 'SQ', Name => 'RadionuclideCodeSequence' },
+    '0054,0302' => { VR => 'SQ', Name => 'AdministrationRouteCodeSequence' },
+    '0054,0304' => { VR => 'SQ', Name => 'RadiopharmaceuticalCodeSequence' },
+    '0054,0306' => { VR => 'SQ', Name => 'CalibrationDataSequence' },
+    '0054,0308' => { VR => 'US', Name => 'EnergyWindowNumber' },
+    '0054,0400' => { VR => 'SH', Name => 'ImageID' },
+    '0054,0410' => { VR => 'SQ', Name => 'PatientOrientationCodeSequence' },
+    '0054,0412' => { VR => 'SQ', Name => 'PatientOrientationModifierCodeSeq' },
+    '0054,0414' => { VR => 'SQ', Name => 'PatientGantryRelationshipCodeSeq' },
+    '0054,0500' => { VR => 'CS', Name => 'SliceProgressionDirection' },
+    '0054,1000' => { VR => 'CS', Name => 'SeriesType' },
+    '0054,1001' => { VR => 'CS', Name => 'Units' },
+    '0054,1002' => { VR => 'CS', Name => 'CountsSource' },
+    '0054,1004' => { VR => 'CS', Name => 'ReprojectionMethod' },
+    '0054,1100' => { VR => 'CS', Name => 'RandomsCorrectionMethod' },
+    '0054,1101' => { VR => 'LO', Name => 'AttenuationCorrectionMethod' },
+    '0054,1102' => { VR => 'CS', Name => 'DecayCorrection' },
+    '0054,1103' => { VR => 'LO', Name => 'ReconstructionMethod' },
+    '0054,1104' => { VR => 'LO', Name => 'DetectorLinesOfResponseUsed' },
+    '0054,1105' => { VR => 'LO', Name => 'ScatterCorrectionMethod' },
+    '0054,1200' => { VR => 'DS', Name => 'AxialAcceptance' },
+    '0054,1201' => { VR => 'IS', Name => 'AxialMash' },
+    '0054,1202' => { VR => 'IS', Name => 'TransverseMash' },
+    '0054,1203' => { VR => 'DS', Name => 'DetectorElementSize' },
+    '0054,1210' => { VR => 'DS', Name => 'CoincidenceWindowWidth' },
+    '0054,1220' => { VR => 'CS', Name => 'SecondaryCountsType' },
+    '0054,1300' => { VR => 'DS', Name => 'FrameReferenceTime' },
+    '0054,1310' => { VR => 'IS', Name => 'PrimaryCountsAccumulated' },
+    '0054,1311' => { VR => 'IS', Name => 'SecondaryCountsAccumulated' },
+    '0054,1320' => { VR => 'DS', Name => 'SliceSensitivityFactor' },
+    '0054,1321' => { VR => 'DS', Name => 'DecayFactor' },
+    '0054,1322' => { VR => 'DS', Name => 'DoseCalibrationFactor' },
+    '0054,1323' => { VR => 'DS', Name => 'ScatterFractionFactor' },
+    '0054,1324' => { VR => 'DS', Name => 'DeadTimeFactor' },
+    '0054,1330' => { VR => 'US', Name => 'ImageIndex' },
+    '0054,1400' => { VR => 'CS', Name => 'CountsIncluded' },
+    '0054,1401' => { VR => 'CS', Name => 'DeadTimeCorrectionFlag' },
+    '0060,3000' => { VR => 'SQ', Name => 'HistogramSequence' },
+    '0060,3002' => { VR => 'US', Name => 'HistogramNumberOfBins' },
+    '0060,3004' => { VR => 'SS', Name => 'HistogramFirstBinValue' },
+    '0060,3006' => { VR => 'SS', Name => 'HistogramLastBinValue' },
+    '0060,3008' => { VR => 'US', Name => 'HistogramBinWidth' },
+    '0060,3010' => { VR => 'LO', Name => 'HistogramExplanation' },
+    '0060,3020' => { VR => 'UL', Name => 'HistogramData' },
+    '0070,0001' => { VR => 'SQ', Name => 'GraphicAnnotationSequence' },
+    '0070,0002' => { VR => 'CS', Name => 'GraphicLayer' },
+    '0070,0003' => { VR => 'CS', Name => 'BoundingBoxAnnotationUnits' },
+    '0070,0004' => { VR => 'CS', Name => 'AnchorPointAnnotationUnits' },
+    '0070,0005' => { VR => 'CS', Name => 'GraphicAnnotationUnits' },
+    '0070,0006' => { VR => 'ST', Name => 'UnformattedTextValue' },
+    '0070,0008' => { VR => 'SQ', Name => 'TextObjectSequence' },
+    '0070,0009' => { VR => 'SQ', Name => 'GraphicObjectSequence' },
+    '0070,0010' => { VR => 'FL', Name => 'BoundingBoxTopLeftHandCorner' },
+    '0070,0011' => { VR => 'FL', Name => 'BoundingBoxBottomRightHandCorner' },
+    '0070,0012' => { VR => 'CS', Name => 'BoundingBoxTextHorizJustification' },
+    '0070,0014' => { VR => 'FL', Name => 'AnchorPoint' },
+    '0070,0015' => { VR => 'CS', Name => 'AnchorPointVisibility' },
+    '0070,0020' => { VR => 'US', Name => 'GraphicDimensions' },
+    '0070,0021' => { VR => 'US', Name => 'NumberOfGraphicPoints' },
+    '0070,0022' => { VR => 'FL', Name => 'GraphicData' },
+    '0070,0023' => { VR => 'CS', Name => 'GraphicType' },
+    '0070,0024' => { VR => 'CS', Name => 'GraphicFilled' },
+    '0070,0041' => { VR => 'CS', Name => 'ImageHorizontalFlip' },
+    '0070,0042' => { VR => 'US', Name => 'ImageRotation' },
+    '0070,0052' => { VR => 'SL', Name => 'DisplayedAreaTopLeftHandCorner' },
+    '0070,0053' => { VR => 'SL', Name => 'DisplayedAreaBottomRightHandCorner' },
+    '0070,005A' => { VR => 'SQ', Name => 'DisplayedAreaSelectionSequence' },
+    '0070,0060' => { VR => 'SQ', Name => 'GraphicLayerSequence' },
+    '0070,0062' => { VR => 'IS', Name => 'GraphicLayerOrder' },
+    '0070,0066' => { VR => 'US', Name => 'GraphicLayerRecDisplayGraysclValue' },
+    '0070,0067' => { VR => 'US', Name => 'GraphicLayerRecDisplayRGBValue' },
+    '0070,0068' => { VR => 'LO', Name => 'GraphicLayerDescription' },
+    '0070,0080' => { VR => 'CS', Name => 'ContentLabel' },
+    '0070,0081' => { VR => 'LO', Name => 'ContentDescription' },
+    '0070,0082' => { VR => 'DA', Name => 'PresentationCreationDate' },
+    '0070,0083' => { VR => 'TM', Name => 'PresentationCreationTime' },
+    '0070,0084' => { VR => 'PN', Name => 'ContentCreatorsName' },
+    '0070,0100' => { VR => 'CS', Name => 'PresentationSizeMode' },
+    '0070,0101' => { VR => 'DS', Name => 'PresentationPixelSpacing' },
+    '0070,0102' => { VR => 'IS', Name => 'PresentationPixelAspectRatio' },
+    '0070,0103' => { VR => 'FL', Name => 'PresentationPixelMagRatio' },
+    '0070,0306' => { VR => 'CS', Name => 'ShapeType' },
+    '0070,0308' => { VR => 'SQ', Name => 'RegistrationSequence' },
+    '0070,0309' => { VR => 'SQ', Name => 'MatrixRegistrationSequence' },
+    '0070,030A' => { VR => 'SQ', Name => 'MatrixSequence' },
+    '0070,030C' => { VR => 'CS', Name => 'FrameOfRefTransformationMatrixType' },
+    '0070,030D' => { VR => 'SQ', Name => 'RegistrationTypeCodeSequence' },
+    '0070,030F' => { VR => 'ST', Name => 'FiducialDescription' },
+    '0070,0310' => { VR => 'SH', Name => 'FiducialIdentifier' },
+    '0070,0311' => { VR => 'SQ', Name => 'FiducialIdentifierCodeSequence' },
+    '0070,0312' => { VR => 'FD', Name => 'ContourUncertaintyRadius' },
+    '0070,0314' => { VR => 'SQ', Name => 'UsedFiducialsSequence' },
+    '0070,0318' => { VR => 'SQ', Name => 'GraphicCoordinatesDataSequence' },
+    '0070,031A' => { VR => 'UI', Name => 'FiducialUID' },
+    '0070,031C' => { VR => 'SQ', Name => 'FiducialSetSequence' },
+    '0070,031E' => { VR => 'SQ', Name => 'FiducialSequence' },
+    # storage group
+    '0088,0130' => { VR => 'SH', Name => 'StorageMediaFileSetID' },
+    '0088,0140' => { VR => 'UI', Name => 'StorageMediaFileSetUID' },
+    '0088,0200' => { VR => 'SQ', Name => 'IconImageSequence' },
+    '0088,0904' => { VR => 'LO', Name => 'TopicTitle' },
+    '0088,0906' => { VR => 'ST', Name => 'TopicSubject' },
+    '0088,0910' => { VR => 'LO', Name => 'TopicAuthor' },
+    '0088,0912' => { VR => 'LO', Name => 'TopicKeyWords' },
+    '0100,0410' => { VR => 'CS', Name => 'SOPInstanceStatus' },
+    '0100,0420' => { VR => 'DT', Name => 'SOPAuthorizationDateAndTime' },
+    '0100,0424' => { VR => 'LT', Name => 'SOPAuthorizationComment' },
+    '0100,0426' => { VR => 'LO', Name => 'AuthorizationEquipmentCertNumber' },
+    '0400,0005' => { VR => 'US', Name => 'MACIDNumber' },
+    '0400,0010' => { VR => 'UI', Name => 'MACCalculationTransferSyntaxUID' },
+    '0400,0015' => { VR => 'CS', Name => 'MACAlgorithm' },
+    '0400,0020' => { VR => 'AT', Name => 'DataElementsSigned' },
+    '0400,0100' => { VR => 'UI', Name => 'DigitalSignatureUID' },
+    '0400,0105' => { VR => 'DT', Name => 'DigitalSignatureDateTime' },
+    '0400,0110' => { VR => 'CS', Name => 'CertificateType' },
+    '0400,0115' => { VR => 'OB', Name => 'CertificateOfSigner' },
+    '0400,0120' => { VR => 'OB', Name => 'Signature' },
+    '0400,0305' => { VR => 'CS', Name => 'CertifiedTimestampType' },
+    '0400,0310' => { VR => 'OB', Name => 'CertifiedTimestamp' },
+    '0400,0500' => { VR => 'SQ', Name => 'EncryptedAttributesSequence' },
+    '0400,0510' => { VR => 'UI', Name => 'EncryptedContentTransferSyntaxUID' },
+    '0400,0520' => { VR => 'OB', Name => 'EncryptedContent' },
+    '0400,0550' => { VR => 'SQ', Name => 'ModifiedAttributesSequence' },
+    '2000,0010' => { VR => 'IS', Name => 'NumberOfCopies' },
+    '2000,001E' => { VR => 'SQ', Name => 'PrinterConfigurationSequence' },
+    '2000,0020' => { VR => 'CS', Name => 'PrintPriority' },
+    '2000,0030' => { VR => 'CS', Name => 'MediumType' },
+    '2000,0040' => { VR => 'CS', Name => 'FilmDestination' },
+    '2000,0050' => { VR => 'LO', Name => 'FilmSessionLabel' },
+    '2000,0060' => { VR => 'IS', Name => 'MemoryAllocation' },
+    '2000,0061' => { VR => 'IS', Name => 'MaximumMemoryAllocation' },
+    '2000,0062' => { VR => 'CS', Name => 'ColorImagePrintingFlag' },
+    '2000,0063' => { VR => 'CS', Name => 'CollationFlag' },
+    '2000,0065' => { VR => 'CS', Name => 'AnnotationFlag' },
+    '2000,0067' => { VR => 'CS', Name => 'ImageOverlayFlag' },
+    '2000,0069' => { VR => 'CS', Name => 'PresentationLUTFlag' },
+    '2000,006A' => { VR => 'CS', Name => 'ImageBoxPresentationLUTFlag' },
+    '2000,00A0' => { VR => 'US', Name => 'MemoryBitDepth' },
+    '2000,00A1' => { VR => 'US', Name => 'PrintingBitDepth' },
+    '2000,00A2' => { VR => 'SQ', Name => 'MediaInstalledSequence' },
+    '2000,00A4' => { VR => 'SQ', Name => 'OtherMediaAvailableSequence' },
+    '2000,00A8' => { VR => 'SQ', Name => 'SupportedImageDisplayFormatSeq' },
+    # film box group
+    '2000,0500' => { VR => 'SQ', Name => 'ReferencedFilmBoxSequence' },
+    '2000,0510' => { VR => 'SQ', Name => 'ReferencedStoredPrintSequence' },
+    '2010,0010' => { VR => 'ST', Name => 'ImageDisplayFormat' },
+    '2010,0030' => { VR => 'CS', Name => 'AnnotationDisplayFormatID' },
+    '2010,0040' => { VR => 'CS', Name => 'FilmOrientation' },
+    '2010,0050' => { VR => 'CS', Name => 'FilmSizeID' },
+    '2010,0052' => { VR => 'CS', Name => 'PrinterResolutionID' },
+    '2010,0054' => { VR => 'CS', Name => 'DefaultPrinterResolutionID' },
+    '2010,0060' => { VR => 'CS', Name => 'MagnificationType' },
+    '2010,0080' => { VR => 'CS', Name => 'SmoothingType' },
+    '2010,00A6' => { VR => 'CS', Name => 'DefaultMagnificationType' },
+    '2010,00A7' => { VR => 'CS', Name => 'OtherMagnificationTypesAvailable' },
+    '2010,00A8' => { VR => 'CS', Name => 'DefaultSmoothingType' },
+    '2010,00A9' => { VR => 'CS', Name => 'OtherSmoothingTypesAvailable' },
+    '2010,0100' => { VR => 'CS', Name => 'BorderDensity' },
+    '2010,0110' => { VR => 'CS', Name => 'EmptyImageDensity' },
+    '2010,0120' => { VR => 'US', Name => 'MinDensity' },
+    '2010,0130' => { VR => 'US', Name => 'MaxDensity' },
+    '2010,0140' => { VR => 'CS', Name => 'Trim' },
+    '2010,0150' => { VR => 'ST', Name => 'ConfigurationInformation' },
+    '2010,0152' => { VR => 'LT', Name => 'ConfigurationInformationDescr' },
+    '2010,0154' => { VR => 'IS', Name => 'MaximumCollatedFilms' },
+    '2010,015E' => { VR => 'US', Name => 'Illumination' },
+    '2010,0160' => { VR => 'US', Name => 'ReflectedAmbientLight' },
+    '2010,0376' => { VR => 'DS', Name => 'PrinterPixelSpacing' },
+    '2010,0500' => { VR => 'SQ', Name => 'ReferencedFilmSessionSequence' },
+    '2010,0510' => { VR => 'SQ', Name => 'ReferencedImageBoxSequence' },
+    '2010,0520' => { VR => 'SQ', Name => 'ReferencedBasicAnnotationBoxSeq' },
+    # image box group
+    '2020,0010' => { VR => 'US', Name => 'ImagePosition' },
+    '2020,0020' => { VR => 'CS', Name => 'Polarity' },
+    '2020,0030' => { VR => 'DS', Name => 'RequestedImageSize' },
+    '2020,0040' => { VR => 'CS', Name => 'RequestedDecimate-CropBehavior' },
+    '2020,0050' => { VR => 'CS', Name => 'RequestedResolutionID' },
+    '2020,00A0' => { VR => 'CS', Name => 'RequestedImageSizeFlag' },
+    '2020,00A2' => { VR => 'CS', Name => 'Decimate-CropResult' },
+    '2020,0110' => { VR => 'SQ', Name => 'BasicGrayscaleImageSequence' },
+    '2020,0111' => { VR => 'SQ', Name => 'BasicColorImageSequence' },
+    '2020,0130' => { VR => 'RET',Name => 'ReferencedImageOverlayBoxSequence' },
+    '2020,0140' => { VR => 'RET',Name => 'ReferencedVOILUTBoxSequence' },
+    # annotation group
+    '2030,0010' => { VR => 'US', Name => 'AnnotationPosition' },
+    '2030,0020' => { VR => 'LO', Name => 'TextString' },
+    # overlay box group
+    '2040,0010' => { VR => 'SQ', Name => 'ReferencedOverlayPlaneSequence' },
+    '2040,0011' => { VR => 'US', Name => 'ReferencedOverlayPlaneGroups' },
+    '2040,0020' => { VR => 'SQ', Name => 'OverlayPixelDataSequence' },
+    '2040,0060' => { VR => 'CS', Name => 'OverlayMagnificationType' },
+    '2040,0070' => { VR => 'CS', Name => 'OverlaySmoothingType' },
+    '2040,0072' => { VR => 'CS', Name => 'OverlayOrImageMagnification' },
+    '2040,0074' => { VR => 'US', Name => 'MagnifyToNumberOfColumns' },
+    '2040,0080' => { VR => 'CS', Name => 'OverlayForegroundDensity' },
+    '2040,0082' => { VR => 'CS', Name => 'OverlayBackgroundDensity' },
+    '2040,0090' => { VR => 'RET',Name => 'OverlayMode' },
+    '2040,0100' => { VR => 'RET',Name => 'ThresholdDensity' },
+    '2040,0500' => { VR => 'RET',Name => 'ReferencedImageBoxSequence' },
+    '2050,0010' => { VR => 'SQ', Name => 'PresentationLUTSequence' },
+    '2050,0020' => { VR => 'CS', Name => 'PresentationLUTShape' },
+    '2050,0500' => { VR => 'SQ', Name => 'ReferencedPresentationLUTSequence' },
+    '2100,0010' => { VR => 'SH', Name => 'PrintJobID' },
+    '2100,0020' => { VR => 'CS', Name => 'ExecutionStatus' },
+    '2100,0030' => { VR => 'CS', Name => 'ExecutionStatusInfo' },
+    '2100,0040' => { VR => 'DA', Name => 'CreationDate' },
+    '2100,0050' => { VR => 'TM', Name => 'CreationTime' },
+    '2100,0070' => { VR => 'AE', Name => 'Originator' },
+    '2100,0140' => { VR => 'AE', Name => 'Destination' },
+    '2100,0160' => { VR => 'SH', Name => 'OwnerID' },
+    '2100,0170' => { VR => 'IS', Name => 'NumberOfFilms' },
+    '2100,0500' => { VR => 'SQ', Name => 'ReferencedPrintJobSequence' },
+    # printer group
+    '2110,0010' => { VR => 'CS', Name => 'PrinterStatus' },
+    '2110,0020' => { VR => 'CS', Name => 'PrinterStatusInfo' },
+    '2110,0030' => { VR => 'LO', Name => 'PrinterName' },
+    '2110,0099' => { VR => 'SH', Name => 'PrintQueueID' },
+    '2120,0010' => { VR => 'CS', Name => 'QueueStatus' },
+    # print job group
+    '2120,0050' => { VR => 'SQ', Name => 'PrintJobDescriptionSequence' },
+    '2120,0070' => { VR => 'SQ', Name => 'ReferencedPrintJobSequence' },
+    '2130,0010' => { VR => 'SQ', Name => 'PrintManagementCapabilitiesSeq' },
+    '2130,0015' => { VR => 'SQ', Name => 'PrinterCharacteristicsSequence' },
+    '2130,0030' => { VR => 'SQ', Name => 'FilmBoxContentSequence' },
+    '2130,0040' => { VR => 'SQ', Name => 'ImageBoxContentSequence' },
+    '2130,0050' => { VR => 'SQ', Name => 'AnnotationContentSequence' },
+    '2130,0060' => { VR => 'SQ', Name => 'ImageOverlayBoxContentSequence' },
+    '2130,0080' => { VR => 'SQ', Name => 'PresentationLUTContentSequence' },
+    '2130,00A0' => { VR => 'SQ', Name => 'ProposedStudySequence' },
+    '2130,00C0' => { VR => 'SQ', Name => 'OriginalImageSequence' },
+    '2200,0001' => { VR => 'CS', Name => 'LabelFromInfoExtractedFromInstance' },
+    '2200,0002' => { VR => 'UT', Name => 'LabelText' },
+    '2200,0003' => { VR => 'CS', Name => 'LabelStyleSelection' },
+    '2200,0004' => { VR => 'LT', Name => 'MediaDisposition' },
+    '2200,0005' => { VR => 'LT', Name => 'BarcodeValue' },
+    '2200,0006' => { VR => 'CS', Name => 'BarcodeSymbology' },
+    '2200,0007' => { VR => 'CS', Name => 'AllowMediaSplitting' },
+    '2200,0008' => { VR => 'CS', Name => 'IncludeNonDICOMObjects' },
+    '2200,0009' => { VR => 'CS', Name => 'IncludeDisplayApplication' },
+    '2200,000A' => { VR => 'CS', Name => 'SaveCompInstancesAfterMediaCreate' },
+    '2200,000B' => { VR => 'US', Name => 'TotalNumberMediaPiecesCreated' },
+    '2200,000C' => { VR => 'LO', Name => 'RequestedMediaApplicationProfile' },
+    '2200,000D' => { VR => 'SQ', Name => 'ReferencedStorageMediaSequence' },
+    '2200,000E' => { VR => 'AT', Name => 'FailureAttributes' },
+    '2200,000F' => { VR => 'CS', Name => 'AllowLossyCompression' },
+    '2200,0020' => { VR => 'CS', Name => 'RequestPriority' },
+    '3002,0002' => { VR => 'SH', Name => 'RTImageLabel' },
+    '3002,0003' => { VR => 'LO', Name => 'RTImageName' },
+    '3002,0004' => { VR => 'ST', Name => 'RTImageDescription' },
+    '3002,000A' => { VR => 'CS', Name => 'ReportedValuesOrigin' },
+    '3002,000C' => { VR => 'CS', Name => 'RTImagePlane' },
+    '3002,000D' => { VR => 'DS', Name => 'XRayImageReceptorTranslation' },
+    '3002,000E' => { VR => 'DS', Name => 'XRayImageReceptorAngle' },
+    '3002,0010' => { VR => 'DS', Name => 'RTImageOrientation' },
+    '3002,0011' => { VR => 'DS', Name => 'ImagePlanePixelSpacing' },
+    '3002,0012' => { VR => 'DS', Name => 'RTImagePosition' },
+    '3002,0020' => { VR => 'SH', Name => 'RadiationMachineName' },
+    '3002,0022' => { VR => 'DS', Name => 'RadiationMachineSAD' },
+    '3002,0024' => { VR => 'DS', Name => 'RadiationMachineSSD' },
+    '3002,0026' => { VR => 'DS', Name => 'RTImageSID' },
+    '3002,0028' => { VR => 'DS', Name => 'SourceToReferenceObjectDistance' },
+    '3002,0029' => { VR => 'IS', Name => 'FractionNumber' },
+    '3002,0030' => { VR => 'SQ', Name => 'ExposureSequence' },
+    '3002,0032' => { VR => 'DS', Name => 'MetersetExposure' },
+    '3002,0034' => { VR => 'DS', Name => 'DiaphragmPosition' },
+    '3002,0040' => { VR => 'SQ', Name => 'FluenceMapSequence' },
+    '3002,0041' => { VR => 'CS', Name => 'FluenceDataSource' },
+    '3002,0042' => { VR => 'DS', Name => 'FluenceDataScale' },
+    '3004,0001' => { VR => 'CS', Name => 'DVHType' },
+    '3004,0002' => { VR => 'CS', Name => 'DoseUnits' },
+    '3004,0004' => { VR => 'CS', Name => 'DoseType' },
+    '3004,0006' => { VR => 'LO', Name => 'DoseComment' },
+    '3004,0008' => { VR => 'DS', Name => 'NormalizationPoint' },
+    '3004,000A' => { VR => 'CS', Name => 'DoseSummationType' },
+    '3004,000C' => { VR => 'DS', Name => 'GridFrameOffsetVector' },
+    '3004,000E' => { VR => 'DS', Name => 'DoseGridScaling' },
+    '3004,0010' => { VR => 'SQ', Name => 'RTDoseROISequence' },
+    '3004,0012' => { VR => 'DS', Name => 'DoseValue' },
+    '3004,0014' => { VR => 'CS', Name => 'TissueHeterogeneityCorrection' },
+    '3004,0040' => { VR => 'DS', Name => 'DVHNormalizationPoint' },
+    '3004,0042' => { VR => 'DS', Name => 'DVHNormalizationDoseValue' },
+    '3004,0050' => { VR => 'SQ', Name => 'DVHSequence' },
+    '3004,0052' => { VR => 'DS', Name => 'DVHDoseScaling' },
+    '3004,0054' => { VR => 'CS', Name => 'DVHVolumeUnits' },
+    '3004,0056' => { VR => 'IS', Name => 'DVHNumberOfBins' },
+    '3004,0058' => { VR => 'DS', Name => 'DVHData' },
+    '3004,0060' => { VR => 'SQ', Name => 'DVHReferencedROISequence' },
+    '3004,0062' => { VR => 'CS', Name => 'DVHROIContributionType' },
+    '3004,0070' => { VR => 'DS', Name => 'DVHMinimumDose' },
+    '3004,0072' => { VR => 'DS', Name => 'DVHMaximumDose' },
+    '3004,0074' => { VR => 'DS', Name => 'DVHMeanDose' },
+    '3006,0002' => { VR => 'SH', Name => 'StructureSetLabel' },
+    '3006,0004' => { VR => 'LO', Name => 'StructureSetName' },
+    '3006,0006' => { VR => 'ST', Name => 'StructureSetDescription' },
+    '3006,0008' => { VR => 'DA', Name => 'StructureSetDate' },
+    '3006,0009' => { VR => 'TM', Name => 'StructureSetTime' },
+    '3006,0010' => { VR => 'SQ', Name => 'ReferencedFrameOfReferenceSequence' },
+    '3006,0012' => { VR => 'SQ', Name => 'RTReferencedStudySequence' },
+    '3006,0014' => { VR => 'SQ', Name => 'RTReferencedSeriesSequence' },
+    '3006,0016' => { VR => 'SQ', Name => 'ContourImageSequence' },
+    '3006,0020' => { VR => 'SQ', Name => 'StructureSetROISequence' },
+    '3006,0022' => { VR => 'IS', Name => 'ROINumber' },
+    '3006,0024' => { VR => 'UI', Name => 'ReferencedFrameOfReferenceUID' },
+    '3006,0026' => { VR => 'LO', Name => 'ROIName' },
+    '3006,0028' => { VR => 'ST', Name => 'ROIDescription' },
+    '3006,002A' => { VR => 'IS', Name => 'ROIDisplayColor' },
+    '3006,002C' => { VR => 'DS', Name => 'ROIVolume' },
+    '3006,0030' => { VR => 'SQ', Name => 'RTRelatedROISequence' },
+    '3006,0033' => { VR => 'CS', Name => 'RTROIRelationship' },
+    '3006,0036' => { VR => 'CS', Name => 'ROIGenerationAlgorithm' },
+    '3006,0038' => { VR => 'LO', Name => 'ROIGenerationDescription' },
+    '3006,0039' => { VR => 'SQ', Name => 'ROIContourSequence' },
+    '3006,0040' => { VR => 'SQ', Name => 'ContourSequence' },
+    '3006,0042' => { VR => 'CS', Name => 'ContourGeometricType' },
+    '3006,0044' => { VR => 'DS', Name => 'ContourSlabThickness' },
+    '3006,0045' => { VR => 'DS', Name => 'ContourOffsetVector' },
+    '3006,0046' => { VR => 'IS', Name => 'NumberOfContourPoints' },
+    '3006,0048' => { VR => 'IS', Name => 'ContourNumber' },
+    '3006,0049' => { VR => 'IS', Name => 'AttachedContours' },
+    '3006,0050' => { VR => 'DS', Name => 'ContourData' },
+    '3006,0080' => { VR => 'SQ', Name => 'RTROIObservationsSequence' },
+    '3006,0082' => { VR => 'IS', Name => 'ObservationNumber' },
+    '3006,0084' => { VR => 'IS', Name => 'ReferencedROINumber' },
+    '3006,0085' => { VR => 'SH', Name => 'ROIObservationLabel' },
+    '3006,0086' => { VR => 'SQ', Name => 'RTROIIdentificationCodeSequence' },
+    '3006,0088' => { VR => 'ST', Name => 'ROIObservationDescription' },
+    '3006,00A0' => { VR => 'SQ', Name => 'RelatedRTROIObservationsSequence' },
+    '3006,00A4' => { VR => 'CS', Name => 'RTROIInterpretedType' },
+    '3006,00A6' => { VR => 'PN', Name => 'ROIInterpreter' },
+    '3006,00B0' => { VR => 'SQ', Name => 'ROIPhysicalPropertiesSequence' },
+    '3006,00B2' => { VR => 'CS', Name => 'ROIPhysicalProperty' },
+    '3006,00B4' => { VR => 'DS', Name => 'ROIPhysicalPropertyValue' },
+    '3006,00C0' => { VR => 'SQ', Name => 'FrameOfReferenceRelationshipSeq' },
+    '3006,00C2' => { VR => 'UI', Name => 'RelatedFrameOfReferenceUID' },
+    '3006,00C4' => { VR => 'CS', Name => 'FrameOfReferenceTransformType' },
+    '3006,00C6' => { VR => 'DS', Name => 'FrameOfReferenceTransformMatrix' },
+    '3006,00C8' => { VR => 'LO', Name => 'FrameOfReferenceTransformComment' },
+    '3008,0010' => { VR => 'SQ', Name => 'MeasuredDoseReferenceSequence' },
+    '3008,0012' => { VR => 'ST', Name => 'MeasuredDoseDescription' },
+    '3008,0014' => { VR => 'CS', Name => 'MeasuredDoseType' },
+    '3008,0016' => { VR => 'DS', Name => 'MeasuredDoseValue' },
+    '3008,0020' => { VR => 'SQ', Name => 'TreatmentSessionBeamSequence' },
+    '3008,0022' => { VR => 'IS', Name => 'CurrentFractionNumber' },
+    '3008,0024' => { VR => 'DA', Name => 'TreatmentControlPointDate' },
+    '3008,0025' => { VR => 'TM', Name => 'TreatmentControlPointTime' },
+    '3008,002A' => { VR => 'CS', Name => 'TreatmentTerminationStatus' },
+    '3008,002B' => { VR => 'SH', Name => 'TreatmentTerminationCode' },
+    '3008,002C' => { VR => 'CS', Name => 'TreatmentVerificationStatus' },
+    '3008,0030' => { VR => 'SQ', Name => 'ReferencedTreatmentRecordSequence' },
+    '3008,0032' => { VR => 'DS', Name => 'SpecifiedPrimaryMeterset' },
+    '3008,0033' => { VR => 'DS', Name => 'SpecifiedSecondaryMeterset' },
+    '3008,0036' => { VR => 'DS', Name => 'DeliveredPrimaryMeterset' },
+    '3008,0037' => { VR => 'DS', Name => 'DeliveredSecondaryMeterset' },
+    '3008,003A' => { VR => 'DS', Name => 'SpecifiedTreatmentTime' },
+    '3008,003B' => { VR => 'DS', Name => 'DeliveredTreatmentTime' },
+    '3008,0040' => { VR => 'SQ', Name => 'ControlPointDeliverySequence' },
+    '3008,0042' => { VR => 'DS', Name => 'SpecifiedMeterset' },
+    '3008,0044' => { VR => 'DS', Name => 'DeliveredMeterset' },
+    '3008,0048' => { VR => 'DS', Name => 'DoseRateDelivered' },
+    '3008,0050' => { VR => 'SQ', Name => 'TreatmentSummaryCalcDoseRefSeq' },
+    '3008,0052' => { VR => 'DS', Name => 'CumulativeDoseToDoseReference' },
+    '3008,0054' => { VR => 'DA', Name => 'FirstTreatmentDate' },
+    '3008,0056' => { VR => 'DA', Name => 'MostRecentTreatmentDate' },
+    '3008,005A' => { VR => 'IS', Name => 'NumberOfFractionsDelivered' },
+    '3008,0060' => { VR => 'SQ', Name => 'OverrideSequence' },
+    '3008,0062' => { VR => 'AT', Name => 'OverrideParameterPointer' },
+    '3008,0064' => { VR => 'IS', Name => 'MeasuredDoseReferenceNumber' },
+    '3008,0066' => { VR => 'ST', Name => 'OverrideReason' },
+    '3008,0070' => { VR => 'SQ', Name => 'CalculatedDoseReferenceSequence' },
+    '3008,0072' => { VR => 'IS', Name => 'CalculatedDoseReferenceNumber' },
+    '3008,0074' => { VR => 'ST', Name => 'CalculatedDoseReferenceDescription' },
+    '3008,0076' => { VR => 'DS', Name => 'CalculatedDoseReferenceDoseValue' },
+    '3008,0078' => { VR => 'DS', Name => 'StartMeterset' },
+    '3008,007A' => { VR => 'DS', Name => 'EndMeterset' },
+    '3008,0080' => { VR => 'SQ', Name => 'ReferencedMeasuredDoseReferenceSeq' },
+    '3008,0082' => { VR => 'IS', Name => 'ReferencedMeasuredDoseReferenceNum' },
+    '3008,0090' => { VR => 'SQ', Name => 'ReferencedCalculatedDoseRefSeq' },
+    '3008,0092' => { VR => 'IS', Name => 'ReferencedCalculatedDoseRefNumber' },
+    '3008,00A0' => { VR => 'SQ', Name => 'BeamLimitingDeviceLeafPairsSeq' },
+    '3008,00B0' => { VR => 'SQ', Name => 'RecordedWedgeSequence' },
+    '3008,00C0' => { VR => 'SQ', Name => 'RecordedCompensatorSequence' },
+    '3008,00D0' => { VR => 'SQ', Name => 'RecordedBlockSequence' },
+    '3008,00E0' => { VR => 'SQ', Name => 'TreatmentSummaryMeasuredDoseRefSeq' },
+    '3008,0100' => { VR => 'SQ', Name => 'RecordedSourceSequence' },
+    '3008,0105' => { VR => 'LO', Name => 'SourceSerialNumber' },
+    '3008,0110' => { VR => 'SQ', Name => 'TreatmentSessionAppSetupSeq' },
+    '3008,0116' => { VR => 'CS', Name => 'ApplicationSetupCheck' },
+    '3008,0120' => { VR => 'SQ', Name => 'RecordedBrachyAccessoryDeviceSeq' },
+    '3008,0122' => { VR => 'IS', Name => 'ReferencedBrachyAccessoryDeviceNum' },
+    '3008,0130' => { VR => 'SQ', Name => 'RecordedChannelSequence' },
+    '3008,0132' => { VR => 'DS', Name => 'SpecifiedChannelTotalTime' },
+    '3008,0134' => { VR => 'DS', Name => 'DeliveredChannelTotalTime' },
+    '3008,0136' => { VR => 'IS', Name => 'SpecifiedNumberOfPulses' },
+    '3008,0138' => { VR => 'IS', Name => 'DeliveredNumberOfPulses' },
+    '3008,013A' => { VR => 'DS', Name => 'SpecifiedPulseRepetitionInterval' },
+    '3008,013C' => { VR => 'DS', Name => 'DeliveredPulseRepetitionInterval' },
+    '3008,0140' => { VR => 'SQ', Name => 'RecordedSourceApplicatorSequence' },
+    '3008,0142' => { VR => 'IS', Name => 'ReferencedSourceApplicatorNumber' },
+    '3008,0150' => { VR => 'SQ', Name => 'RecordedChannelShieldSequence' },
+    '3008,0152' => { VR => 'IS', Name => 'ReferencedChannelShieldNumber' },
+    '3008,0160' => { VR => 'SQ', Name => 'BrachyControlPointDeliveredSeq' },
+    '3008,0162' => { VR => 'DA', Name => 'SafePositionExitDate' },
+    '3008,0164' => { VR => 'TM', Name => 'SafePositionExitTime' },
+    '3008,0166' => { VR => 'DA', Name => 'SafePositionReturnDate' },
+    '3008,0168' => { VR => 'TM', Name => 'SafePositionReturnTime' },
+    '3008,0200' => { VR => 'CS', Name => 'CurrentTreatmentStatus' },
+    '3008,0202' => { VR => 'ST', Name => 'TreatmentStatusComment' },
+    '3008,0220' => { VR => 'SQ', Name => 'FractionGroupSummarySequence' },
+    '3008,0223' => { VR => 'IS', Name => 'ReferencedFractionNumber' },
+    '3008,0224' => { VR => 'CS', Name => 'FractionGroupType' },
+    '3008,0230' => { VR => 'CS', Name => 'BeamStopperPosition' },
+    '3008,0240' => { VR => 'SQ', Name => 'FractionStatusSummarySequence' },
+    '3008,0250' => { VR => 'DA', Name => 'TreatmentDate' },
+    '3008,0251' => { VR => 'TM', Name => 'TreatmentTime' },
+    '300A,0002' => { VR => 'SH', Name => 'RTPlanLabel' },
+    '300A,0003' => { VR => 'LO', Name => 'RTPlanName' },
+    '300A,0004' => { VR => 'ST', Name => 'RTPlanDescription' },
+    '300A,0006' => { VR => 'DA', Name => 'RTPlanDate' },
+    '300A,0007' => { VR => 'TM', Name => 'RTPlanTime' },
+    '300A,0009' => { VR => 'LO', Name => 'TreatmentProtocols' },
+    '300A,000A' => { VR => 'CS', Name => 'TreatmentIntent' },
+    '300A,000B' => { VR => 'LO', Name => 'TreatmentSites' },
+    '300A,000C' => { VR => 'CS', Name => 'RTPlanGeometry' },
+    '300A,000E' => { VR => 'ST', Name => 'PrescriptionDescription' },
+    '300A,0010' => { VR => 'SQ', Name => 'DoseReferenceSequence' },
+    '300A,0012' => { VR => 'IS', Name => 'DoseReferenceNumber' },
+    '300A,0013' => { VR => 'UI', Name => 'DoseReferenceUID' },
+    '300A,0014' => { VR => 'CS', Name => 'DoseReferenceStructureType' },
+    '300A,0015' => { VR => 'CS', Name => 'NominalBeamEnergyUnit' },
+    '300A,0016' => { VR => 'LO', Name => 'DoseReferenceDescription' },
+    '300A,0018' => { VR => 'DS', Name => 'DoseReferencePointCoordinates' },
+    '300A,001A' => { VR => 'DS', Name => 'NominalPriorDose' },
+    '300A,0020' => { VR => 'CS', Name => 'DoseReferenceType' },
+    '300A,0021' => { VR => 'DS', Name => 'ConstraintWeight' },
+    '300A,0022' => { VR => 'DS', Name => 'DeliveryWarningDose' },
+    '300A,0023' => { VR => 'DS', Name => 'DeliveryMaximumDose' },
+    '300A,0025' => { VR => 'DS', Name => 'TargetMinimumDose' },
+    '300A,0026' => { VR => 'DS', Name => 'TargetPrescriptionDose' },
+    '300A,0027' => { VR => 'DS', Name => 'TargetMaximumDose' },
+    '300A,0028' => { VR => 'DS', Name => 'TargetUnderdoseVolumeFraction' },
+    '300A,002A' => { VR => 'DS', Name => 'OrganAtRiskFullVolumeDose' },
+    '300A,002B' => { VR => 'DS', Name => 'OrganAtRiskLimitDose' },
+    '300A,002C' => { VR => 'DS', Name => 'OrganAtRiskMaximumDose' },
+    '300A,002D' => { VR => 'DS', Name => 'OrganAtRiskOverdoseVolumeFraction' },
+    '300A,0040' => { VR => 'SQ', Name => 'ToleranceTableSequence' },
+    '300A,0042' => { VR => 'IS', Name => 'ToleranceTableNumber' },
+    '300A,0043' => { VR => 'SH', Name => 'ToleranceTableLabel' },
+    '300A,0044' => { VR => 'DS', Name => 'GantryAngleTolerance' },
+    '300A,0046' => { VR => 'DS', Name => 'BeamLimitingDeviceAngleTolerance' },
+    '300A,0048' => { VR => 'SQ', Name => 'BeamLimitingDeviceToleranceSeq' },
+    '300A,004A' => { VR => 'DS', Name => 'BeamLimitingDevicePositionTol' },
+    '300A,004C' => { VR => 'DS', Name => 'PatientSupportAngleTolerance' },
+    '300A,004E' => { VR => 'DS', Name => 'TableTopEccentricAngleTolerance' },
+    '300A,0051' => { VR => 'DS', Name => 'TableTopVerticalPositionTolerance' },
+    '300A,0052' => { VR => 'DS', Name => 'TableTopLongitudinalPositionTol' },
+    '300A,0053' => { VR => 'DS', Name => 'TableTopLateralPositionTolerance' },
+    '300A,0055' => { VR => 'CS', Name => 'RTPlanRelationship' },
+    '300A,0070' => { VR => 'SQ', Name => 'FractionGroupSequence' },
+    '300A,0071' => { VR => 'IS', Name => 'FractionGroupNumber' },
+    '300A,0072' => { VR => 'LO', Name => 'FractionGroupDescription' },
+    '300A,0078' => { VR => 'IS', Name => 'NumberOfFractionsPlanned' },
+    '300A,0079' => { VR => 'IS', Name => 'NumberFractionPatternDigitsPerDay' },
+    '300A,007A' => { VR => 'IS', Name => 'RepeatFractionCycleLength' },
+    '300A,007B' => { VR => 'LT', Name => 'FractionPattern' },
+    '300A,0080' => { VR => 'IS', Name => 'NumberOfBeams' },
+    '300A,0082' => { VR => 'DS', Name => 'BeamDoseSpecificationPoint' },
+    '300A,0084' => { VR => 'DS', Name => 'BeamDose' },
+    '300A,0086' => { VR => 'DS', Name => 'BeamMeterset' },
+    '300A,00A0' => { VR => 'IS', Name => 'NumberOfBrachyApplicationSetups' },
+    '300A,00A2' => { VR => 'DS', Name => 'BrachyAppSetupDoseSpecPoint' },
+    '300A,00A4' => { VR => 'DS', Name => 'BrachyApplicationSetupDose' },
+    '300A,00B0' => { VR => 'SQ', Name => 'BeamSequence' },
+    '300A,00B2' => { VR => 'SH', Name => 'TreatmentMachineName' },
+    '300A,00B3' => { VR => 'CS', Name => 'PrimaryDosimeterUnit' },
+    '300A,00B4' => { VR => 'DS', Name => 'SourceAxisDistance' },
+    '300A,00B6' => { VR => 'SQ', Name => 'BeamLimitingDeviceSequence' },
+    '300A,00B8' => { VR => 'CS', Name => 'RTBeamLimitingDeviceType' },
+    '300A,00BA' => { VR => 'DS', Name => 'SourceToBeamLimitingDeviceDistance' },
+    '300A,00BC' => { VR => 'IS', Name => 'NumberOfLeaf-JawPairs' },
+    '300A,00BE' => { VR => 'DS', Name => 'LeafPositionBoundaries' },
+    '300A,00C0' => { VR => 'IS', Name => 'BeamNumber' },
+    '300A,00C2' => { VR => 'LO', Name => 'BeamName' },
+    '300A,00C3' => { VR => 'ST', Name => 'BeamDescription' },
+    '300A,00C4' => { VR => 'CS', Name => 'BeamType' },
+    '300A,00C6' => { VR => 'CS', Name => 'RadiationType' },
+    '300A,00C7' => { VR => 'CS', Name => 'HighDoseTechniqueType' },
+    '300A,00C8' => { VR => 'IS', Name => 'ReferenceImageNumber' },
+    '300A,00CA' => { VR => 'SQ', Name => 'PlannedVerificationImageSequence' },
+    '300A,00CC' => { VR => 'LO', Name => 'ImagingDeviceSpecificAcqParams' },
+    '300A,00CE' => { VR => 'CS', Name => 'TreatmentDeliveryType' },
+    '300A,00D0' => { VR => 'IS', Name => 'NumberOfWedges' },
+    '300A,00D1' => { VR => 'SQ', Name => 'WedgeSequence' },
+    '300A,00D2' => { VR => 'IS', Name => 'WedgeNumber' },
+    '300A,00D3' => { VR => 'CS', Name => 'WedgeType' },
+    '300A,00D4' => { VR => 'SH', Name => 'WedgeID' },
+    '300A,00D5' => { VR => 'IS', Name => 'WedgeAngle' },
+    '300A,00D6' => { VR => 'DS', Name => 'WedgeFactor' },
+    '300A,00D8' => { VR => 'DS', Name => 'WedgeOrientation' },
+    '300A,00DA' => { VR => 'DS', Name => 'SourceToWedgeTrayDistance' },
+    '300A,00E0' => { VR => 'IS', Name => 'NumberOfCompensators' },
+    '300A,00E1' => { VR => 'SH', Name => 'MaterialID' },
+    '300A,00E2' => { VR => 'DS', Name => 'TotalCompensatorTrayFactor' },
+    '300A,00E3' => { VR => 'SQ', Name => 'CompensatorSequence' },
+    '300A,00E4' => { VR => 'IS', Name => 'CompensatorNumber' },
+    '300A,00E5' => { VR => 'SH', Name => 'CompensatorID' },
+    '300A,00E6' => { VR => 'DS', Name => 'SourceToCompensatorTrayDistance' },
+    '300A,00E7' => { VR => 'IS', Name => 'CompensatorRows' },
+    '300A,00E8' => { VR => 'IS', Name => 'CompensatorColumns' },
+    '300A,00E9' => { VR => 'DS', Name => 'CompensatorPixelSpacing' },
+    '300A,00EA' => { VR => 'DS', Name => 'CompensatorPosition' },
+    '300A,00EB' => { VR => 'DS', Name => 'CompensatorTransmissionData' },
+    '300A,00EC' => { VR => 'DS', Name => 'CompensatorThicknessData' },
+    '300A,00ED' => { VR => 'IS', Name => 'NumberOfBoli' },
+    '300A,00EE' => { VR => 'CS', Name => 'CompensatorType' },
+    '300A,00F0' => { VR => 'IS', Name => 'NumberOfBlocks' },
+    '300A,00F2' => { VR => 'DS', Name => 'TotalBlockTrayFactor' },
+    '300A,00F4' => { VR => 'SQ', Name => 'BlockSequence' },
+    '300A,00F5' => { VR => 'SH', Name => 'BlockTrayID' },
+    '300A,00F6' => { VR => 'DS', Name => 'SourceToBlockTrayDistance' },
+    '300A,00F8' => { VR => 'CS', Name => 'BlockType' },
+    '300A,00F9' => { VR => 'LO', Name => 'AccessoryCode' },
+    '300A,00FA' => { VR => 'CS', Name => 'BlockDivergence' },
+    '300A,00FB' => { VR => 'CS', Name => 'BlockMountingPosition' },
+    '300A,00FC' => { VR => 'IS', Name => 'BlockNumber' },
+    '300A,00FE' => { VR => 'LO', Name => 'BlockName' },
+    '300A,0100' => { VR => 'DS', Name => 'BlockThickness' },
+    '300A,0102' => { VR => 'DS', Name => 'BlockTransmission' },
+    '300A,0104' => { VR => 'IS', Name => 'BlockNumberOfPoints' },
+    '300A,0106' => { VR => 'DS', Name => 'BlockData' },
+    '300A,0107' => { VR => 'SQ', Name => 'ApplicatorSequence' },
+    '300A,0108' => { VR => 'SH', Name => 'ApplicatorID' },
+    '300A,0109' => { VR => 'CS', Name => 'ApplicatorType' },
+    '300A,010A' => { VR => 'LO', Name => 'ApplicatorDescription' },
+    '300A,010C' => { VR => 'DS', Name => 'CumulativeDoseReferenceCoefficient' },
+    '300A,010E' => { VR => 'DS', Name => 'FinalCumulativeMetersetWeight' },
+    '300A,0110' => { VR => 'IS', Name => 'NumberOfControlPoints' },
+    '300A,0111' => { VR => 'SQ', Name => 'ControlPointSequence' },
+    '300A,0112' => { VR => 'IS', Name => 'ControlPointIndex' },
+    '300A,0114' => { VR => 'DS', Name => 'NominalBeamEnergy' },
+    '300A,0115' => { VR => 'DS', Name => 'DoseRateSet' },
+    '300A,0116' => { VR => 'SQ', Name => 'WedgePositionSequence' },
+    '300A,0118' => { VR => 'CS', Name => 'WedgePosition' },
+    '300A,011A' => { VR => 'SQ', Name => 'BeamLimitingDevicePositionSequence' },
+    '300A,011C' => { VR => 'DS', Name => 'Leaf-JawPositions' },
+    '300A,011E' => { VR => 'DS', Name => 'GantryAngle' },
+    '300A,011F' => { VR => 'CS', Name => 'GantryRotationDirection' },
+    '300A,0120' => { VR => 'DS', Name => 'BeamLimitingDeviceAngle' },
+    '300A,0121' => { VR => 'CS', Name => 'BeamLimitingDeviceRotateDirection' },
+    '300A,0122' => { VR => 'DS', Name => 'PatientSupportAngle' },
+    '300A,0123' => { VR => 'CS', Name => 'PatientSupportRotationDirection' },
+    '300A,0124' => { VR => 'DS', Name => 'TableTopEccentricAxisDistance' },
+    '300A,0125' => { VR => 'DS', Name => 'TableTopEccentricAngle' },
+    '300A,0126' => { VR => 'CS', Name => 'TableTopEccentricRotateDirection' },
+    '300A,0128' => { VR => 'DS', Name => 'TableTopVerticalPosition' },
+    '300A,0129' => { VR => 'DS', Name => 'TableTopLongitudinalPosition' },
+    '300A,012A' => { VR => 'DS', Name => 'TableTopLateralPosition' },
+    '300A,012C' => { VR => 'DS', Name => 'IsocenterPosition' },
+    '300A,012E' => { VR => 'DS', Name => 'SurfaceEntryPoint' },
+    '300A,0130' => { VR => 'DS', Name => 'SourceToSurfaceDistance' },
+    '300A,0134' => { VR => 'DS', Name => 'CumulativeMetersetWeight' },
+    '300A,0180' => { VR => 'SQ', Name => 'PatientSetupSequence' },
+    '300A,0182' => { VR => 'IS', Name => 'PatientSetupNumber' },
+    '300A,0184' => { VR => 'LO', Name => 'PatientAdditionalPosition' },
+    '300A,0190' => { VR => 'SQ', Name => 'FixationDeviceSequence' },
+    '300A,0192' => { VR => 'CS', Name => 'FixationDeviceType' },
+    '300A,0194' => { VR => 'SH', Name => 'FixationDeviceLabel' },
+    '300A,0196' => { VR => 'ST', Name => 'FixationDeviceDescription' },
+    '300A,0198' => { VR => 'SH', Name => 'FixationDevicePosition' },
+    '300A,01A0' => { VR => 'SQ', Name => 'ShieldingDeviceSequence' },
+    '300A,01A2' => { VR => 'CS', Name => 'ShieldingDeviceType' },
+    '300A,01A4' => { VR => 'SH', Name => 'ShieldingDeviceLabel' },
+    '300A,01A6' => { VR => 'ST', Name => 'ShieldingDeviceDescription' },
+    '300A,01A8' => { VR => 'SH', Name => 'ShieldingDevicePosition' },
+    '300A,01B0' => { VR => 'CS', Name => 'SetupTechnique' },
+    '300A,01B2' => { VR => 'ST', Name => 'SetupTechniqueDescription' },
+    '300A,01B4' => { VR => 'SQ', Name => 'SetupDeviceSequence' },
+    '300A,01B6' => { VR => 'CS', Name => 'SetupDeviceType' },
+    '300A,01B8' => { VR => 'SH', Name => 'SetupDeviceLabel' },
+    '300A,01BA' => { VR => 'ST', Name => 'SetupDeviceDescription' },
+    '300A,01BC' => { VR => 'DS', Name => 'SetupDeviceParameter' },
+    '300A,01D0' => { VR => 'ST', Name => 'SetupReferenceDescription' },
+    '300A,01D2' => { VR => 'DS', Name => 'TableTopVerticalSetupDisplacement' },
+    '300A,01D4' => { VR => 'DS', Name => 'TableTopLongitudinalSetupDisplace' },
+    '300A,01D6' => { VR => 'DS', Name => 'TableTopLateralSetupDisplacement' },
+    '300A,0200' => { VR => 'CS', Name => 'BrachyTreatmentTechnique' },
+    '300A,0202' => { VR => 'CS', Name => 'BrachyTreatmentType' },
+    '300A,0206' => { VR => 'SQ', Name => 'TreatmentMachineSequence' },
+    '300A,0210' => { VR => 'SQ', Name => 'SourceSequence' },
+    '300A,0212' => { VR => 'IS', Name => 'SourceNumber' },
+    '300A,0214' => { VR => 'CS', Name => 'SourceType' },
+    '300A,0216' => { VR => 'LO', Name => 'SourceManufacturer' },
+    '300A,0218' => { VR => 'DS', Name => 'ActiveSourceDiameter' },
+    '300A,021A' => { VR => 'DS', Name => 'ActiveSourceLength' },
+    '300A,0222' => { VR => 'DS', Name => 'SourceEncapsulationNomThickness' },
+    '300A,0224' => { VR => 'DS', Name => 'SourceEncapsulationNomTransmission' },
+    '300A,0226' => { VR => 'LO', Name => 'SourceIsotopeName' },
+    '300A,0228' => { VR => 'DS', Name => 'SourceIsotopeHalfLife' },
+    '300A,022A' => { VR => 'DS', Name => 'ReferenceAirKermaRate' },
+    '300A,022C' => { VR => 'DA', Name => 'AirKermaRateReferenceDate' },
+    '300A,022E' => { VR => 'TM', Name => 'AirKermaRateReferenceTime' },
+    '300A,0230' => { VR => 'SQ', Name => 'ApplicationSetupSequence' },
+    '300A,0232' => { VR => 'CS', Name => 'ApplicationSetupType' },
+    '300A,0234' => { VR => 'IS', Name => 'ApplicationSetupNumber' },
+    '300A,0236' => { VR => 'LO', Name => 'ApplicationSetupName' },
+    '300A,0238' => { VR => 'LO', Name => 'ApplicationSetupManufacturer' },
+    '300A,0240' => { VR => 'IS', Name => 'TemplateNumber' },
+    '300A,0242' => { VR => 'SH', Name => 'TemplateType' },
+    '300A,0244' => { VR => 'LO', Name => 'TemplateName' },
+    '300A,0250' => { VR => 'DS', Name => 'TotalReferenceAirKerma' },
+    '300A,0260' => { VR => 'SQ', Name => 'BrachyAccessoryDeviceSequence' },
+    '300A,0262' => { VR => 'IS', Name => 'BrachyAccessoryDeviceNumber' },
+    '300A,0263' => { VR => 'SH', Name => 'BrachyAccessoryDeviceID' },
+    '300A,0264' => { VR => 'CS', Name => 'BrachyAccessoryDeviceType' },
+    '300A,0266' => { VR => 'LO', Name => 'BrachyAccessoryDeviceName' },
+    '300A,026A' => { VR => 'DS', Name => 'BrachyAccessoryDeviceNomThickness' },
+    '300A,026C' => { VR => 'DS', Name => 'BrachyAccessoryDevNomTransmission' },
+    '300A,0280' => { VR => 'SQ', Name => 'ChannelSequence' },
+    '300A,0282' => { VR => 'IS', Name => 'ChannelNumber' },
+    '300A,0284' => { VR => 'DS', Name => 'ChannelLength' },
+    '300A,0286' => { VR => 'DS', Name => 'ChannelTotalTime' },
+    '300A,0288' => { VR => 'CS', Name => 'SourceMovementType' },
+    '300A,028A' => { VR => 'IS', Name => 'NumberOfPulses' },
+    '300A,028C' => { VR => 'DS', Name => 'PulseRepetitionInterval' },
+    '300A,0290' => { VR => 'IS', Name => 'SourceApplicatorNumber' },
+    '300A,0291' => { VR => 'SH', Name => 'SourceApplicatorID' },
+    '300A,0292' => { VR => 'CS', Name => 'SourceApplicatorType' },
+    '300A,0294' => { VR => 'LO', Name => 'SourceApplicatorName' },
+    '300A,0296' => { VR => 'DS', Name => 'SourceApplicatorLength' },
+    '300A,0298' => { VR => 'LO', Name => 'SourceApplicatorManufacturer' },
+    '300A,029C' => { VR => 'DS', Name => 'SourceApplicatorWallNomThickness' },
+    '300A,029E' => { VR => 'DS', Name => 'SourceApplicatorWallNomTrans' },
+    '300A,02A0' => { VR => 'DS', Name => 'SourceApplicatorStepSize' },
+    '300A,02A2' => { VR => 'IS', Name => 'TransferTubeNumber' },
+    '300A,02A4' => { VR => 'DS', Name => 'TransferTubeLength' },
+    '300A,02B0' => { VR => 'SQ', Name => 'ChannelShieldSequence' },
+    '300A,02B2' => { VR => 'IS', Name => 'ChannelShieldNumber' },
+    '300A,02B3' => { VR => 'SH', Name => 'ChannelShieldID' },
+    '300A,02B4' => { VR => 'LO', Name => 'ChannelShieldName' },
+    '300A,02B8' => { VR => 'DS', Name => 'ChannelShieldNominalThickness' },
+    '300A,02BA' => { VR => 'DS', Name => 'ChannelShieldNominalTransmission' },
+    '300A,02C8' => { VR => 'DS', Name => 'FinalCumulativeTimeWeight' },
+    '300A,02D0' => { VR => 'SQ', Name => 'BrachyControlPointSequence' },
+    '300A,02D2' => { VR => 'DS', Name => 'ControlPointRelativePosition' },
+    '300A,02D4' => { VR => 'DS', Name => 'ControlPoint3DPosition' },
+    '300A,02D6' => { VR => 'DS', Name => 'CumulativeTimeWeight' },
+    '300A,02E0' => { VR => 'CS', Name => 'CompensatorDivergence' },
+    '300A,02E1' => { VR => 'CS', Name => 'CompensatorMountingPosition' },
+    '300A,02E2' => { VR => 'DS', Name => 'SourceToCompensatorDistance' },
+    '300C,0002' => { VR => 'SQ', Name => 'ReferencedRTPlanSequence' },
+    '300C,0004' => { VR => 'SQ', Name => 'ReferencedBeamSequence' },
+    '300C,0006' => { VR => 'IS', Name => 'ReferencedBeamNumber' },
+    '300C,0007' => { VR => 'IS', Name => 'ReferencedReferenceImageNumber' },
+    '300C,0008' => { VR => 'DS', Name => 'StartCumulativeMetersetWeight' },
+    '300C,0009' => { VR => 'DS', Name => 'EndCumulativeMetersetWeight' },
+    '300C,000A' => { VR => 'SQ', Name => 'ReferencedBrachyAppSetupSeq' },
+    '300C,000C' => { VR => 'IS', Name => 'ReferencedBrachyAppSetupNumber' },
+    '300C,000E' => { VR => 'IS', Name => 'ReferencedSourceNumber' },
+    '300C,0020' => { VR => 'SQ', Name => 'ReferencedFractionGroupSequence' },
+    '300C,0022' => { VR => 'IS', Name => 'ReferencedFractionGroupNumber' },
+    '300C,0040' => { VR => 'SQ', Name => 'ReferencedVerificationImageSeq' },
+    '300C,0042' => { VR => 'SQ', Name => 'ReferencedReferenceImageSequence' },
+    '300C,0050' => { VR => 'SQ', Name => 'ReferencedDoseReferenceSequence' },
+    '300C,0051' => { VR => 'IS', Name => 'ReferencedDoseReferenceNumber' },
+    '300C,0055' => { VR => 'SQ', Name => 'BrachyReferencedDoseReferenceSeq' },
+    '300C,0060' => { VR => 'SQ', Name => 'ReferencedStructureSetSequence' },
+    '300C,006A' => { VR => 'IS', Name => 'ReferencedPatientSetupNumber' },
+    '300C,0080' => { VR => 'SQ', Name => 'ReferencedDoseSequence' },
+    '300C,00A0' => { VR => 'IS', Name => 'ReferencedToleranceTableNumber' },
+    '300C,00B0' => { VR => 'SQ', Name => 'ReferencedBolusSequence' },
+    '300C,00C0' => { VR => 'IS', Name => 'ReferencedWedgeNumber' },
+    '300C,00D0' => { VR => 'IS', Name => 'ReferencedCompensatorNumber' },
+    '300C,00E0' => { VR => 'IS', Name => 'ReferencedBlockNumber' },
+    '300C,00F0' => { VR => 'IS', Name => 'ReferencedControlPointIndex' },
+    '300E,0002' => { VR => 'CS', Name => 'ApprovalStatus' },
+    '300E,0004' => { VR => 'DA', Name => 'ReviewDate' },
+    '300E,0005' => { VR => 'TM', Name => 'ReviewTime' },
+    '300E,0008' => { VR => 'PN', Name => 'ReviewerName' },
+    # text group
+    '4000,0000' => { VR => 'UL', Name => 'TextGroupLength' },
+    '4000,0010' => { VR => 'RET',Name => 'Arbitrary' },
+    '4000,4000' => { VR => 'RET',Name => 'TextComments' },
+    # results group
+    '4008,0040' => { VR => 'SH', Name => 'ResultsID' },
+    '4008,0042' => { VR => 'LO', Name => 'ResultsIDIssuer' },
+    '4008,0050' => { VR => 'SQ', Name => 'ReferencedInterpretationSequence' },
+    '4008,0100' => { VR => 'DA', Name => 'InterpretationRecordedDate' },
+    '4008,0101' => { VR => 'TM', Name => 'InterpretationRecordedTime' },
+    '4008,0102' => { VR => 'PN', Name => 'InterpretationRecorder' },
+    '4008,0103' => { VR => 'LO', Name => 'ReferenceToRecordedSound' },
+    '4008,0108' => { VR => 'DA', Name => 'InterpretationTranscriptionDate' },
+    '4008,0109' => { VR => 'TM', Name => 'InterpretationTranscriptionTime' },
+    '4008,010A' => { VR => 'PN', Name => 'InterpretationTranscriber' },
+    '4008,010B' => { VR => 'ST', Name => 'InterpretationText' },
+    '4008,010C' => { VR => 'PN', Name => 'InterpretationAuthor' },
+    '4008,0111' => { VR => 'SQ', Name => 'InterpretationApproverSequence' },
+    '4008,0112' => { VR => 'DA', Name => 'InterpretationApprovalDate' },
+    '4008,0113' => { VR => 'TM', Name => 'InterpretationApprovalTime' },
+    '4008,0114' => { VR => 'PN', Name => 'PhysicianApprovingInterpretation' },
+    '4008,0115' => { VR => 'LT', Name => 'InterpretationDiagnosisDescription' },
+    '4008,0117' => { VR => 'SQ', Name => 'InterpretationDiagnosisCodeSeq' },
+    '4008,0118' => { VR => 'SQ', Name => 'ResultsDistributionListSequence' },
+    '4008,0119' => { VR => 'PN', Name => 'DistributionName' },
+    '4008,011A' => { VR => 'LO', Name => 'DistributionAddress' },
+    '4008,0200' => { VR => 'SH', Name => 'InterpretationID' },
+    '4008,0202' => { VR => 'LO', Name => 'InterpretationIDIssuer' },
+    '4008,0210' => { VR => 'CS', Name => 'InterpretationTypeID' },
+    '4008,0212' => { VR => 'CS', Name => 'InterpretationStatusID' },
+    '4008,0300' => { VR => 'ST', Name => 'Impressions' },
+    '4008,4000' => { VR => 'ST', Name => 'ResultsComments' },
+    '4FFE,0001' => { VR => 'SQ', Name => 'MACParametersSequence' },
+    # curve group
+    '50xx,0005' => { VR => 'US', Name => 'CurveDimensions' },
+    '50xx,0010' => { VR => 'US', Name => 'NumberOfPoints' },
+    '50xx,0020' => { VR => 'CS', Name => 'TypeOfData' },
+    '50xx,0022' => { VR => 'LO', Name => 'CurveDescription' },
+    '50xx,0030' => { VR => 'SH', Name => 'AxisUnits' },
+    '50xx,0040' => { VR => 'SH', Name => 'AxisLabels' },
+    '50xx,0103' => { VR => 'US', Name => 'DataValueRepresentation' },
+    '50xx,0104' => { VR => 'US', Name => 'MinimumCoordinateValue' },
+    '50xx,0105' => { VR => 'US', Name => 'MaximumCoordinateValue' },
+    '50xx,0106' => { VR => 'SH', Name => 'CurveRange' },
+    '50xx,0110' => { VR => 'US', Name => 'CurveDataDescriptor' },
+    '50xx,0112' => { VR => 'US', Name => 'CoordinateStartValue' },
+    '50xx,0114' => { VR => 'US', Name => 'CoordinateStepValue' },
+    '50xx,1001' => { VR => 'CS', Name => 'CurveActivationLayer' },
+    '50xx,2000' => { VR => 'US', Name => 'AudioType' },
+    '50xx,2002' => { VR => 'US', Name => 'AudioSampleFormat' },
+    '50xx,2004' => { VR => 'US', Name => 'NumberOfChannels' },
+    '50xx,2006' => { VR => 'UL', Name => 'NumberOfSamples' },
+    '50xx,2008' => { VR => 'UL', Name => 'SampleRate' },
+    '50xx,200A' => { VR => 'UL', Name => 'TotalTime' },
+    '50xx,200C' => { VR => 'OB', Name => 'AudioSampleData' },
+    '50xx,200E' => { VR => 'LT', Name => 'AudioComments' },
+    '50xx,2500' => { VR => 'LO', Name => 'CurveLabel' },
+    '50xx,2600' => { VR => 'SQ', Name => 'ReferencedOverlaySequence' },
+    '50xx,2610' => { VR => 'US', Name => 'ReferencedOverlayGroup' },
+    '50xx,3000' => { VR => 'OB', Name => 'CurveData' },
+    '5200,9229' => { VR => 'SQ', Name => 'SharedFunctionalGroupsSequence' },
+    '5200,9230' => { VR => 'SQ', Name => 'PerFrameFunctionalGroupsSequence' },
+    '5400,0100' => { VR => 'SQ', Name => 'WaveformSequence' },
+    '5400,0110' => { VR => 'OW', Name => 'ChannelMinimumValue' },
+    '5400,0112' => { VR => 'OW', Name => 'ChannelMaximumValue' },
+    '5400,1004' => { VR => 'US', Name => 'WaveformBitsAllocated' },
+    '5400,1006' => { VR => 'CS', Name => 'WaveformSampleInterpretation' },
+    '5400,100A' => { VR => 'OW', Name => 'WaveformPaddingValue' },
+    '5400,1010' => { VR => 'OW', Name => 'WaveformData' },
+    '5600,0010' => { VR => 'OF', Name => 'FirstOrderPhaseCorrectionAngle' },
+    '5600,0020' => { VR => 'OF', Name => 'SpectroscopyData' },
+    # overlay group
+    '6000,0000' => { VR => 'UL', Name => 'OverlayGroupLength' },
+    '60xx,0010' => { VR => 'US', Name => 'OverlayRows' },
+    '60xx,0011' => { VR => 'US', Name => 'OverlayColumns' },
+    '60xx,0012' => { VR => 'US', Name => 'OverlayPlanes' },
+    '60xx,0015' => { VR => 'IS', Name => 'NumberOfFramesInOverlay' },
+    '60xx,0022' => { VR => 'LO', Name => 'OverlayDescription' },
+    '60xx,0040' => { VR => 'CS', Name => 'OverlayType' },
+    '60xx,0045' => { VR => 'LO', Name => 'OverlaySubtype' },
+    '60xx,0050' => { VR => 'SS', Name => 'OverlayOrigin' },
+    '60xx,0051' => { VR => 'US', Name => 'ImageFrameOrigin' },
+    '60xx,0052' => { VR => 'US', Name => 'OverlayPlaneOrigin' },
+    '60xx,0060' => { VR => 'RET',Name => 'CompressionCode' },
+    '60xx,0100' => { VR => 'US', Name => 'OverlayBitsAllocated' },
+    '60xx,0102' => { VR => 'US', Name => 'OverlayBitPosition' },
+    '60xx,0110' => { VR => 'RET',Name => 'OverlayFormat' },
+    '60xx,0200' => { VR => 'RET',Name => 'OverlayLocation' },
+    '60xx,1001' => { VR => 'CS', Name => 'OverlayActivationLayer' },
+    '60xx,1100' => { VR => 'RET',Name => 'OverlayDescriptorGray' },
+    '60xx,1101' => { VR => 'RET',Name => 'OverlayDescriptorRed' },
+    '60xx,1102' => { VR => 'RET',Name => 'OverlayDescriptorGreen' },
+    '60xx,1103' => { VR => 'RET',Name => 'OverlayDescriptorBlue' },
+    '60xx,1200' => { VR => 'RET',Name => 'OverlaysGray' },
+    '60xx,1201' => { VR => 'RET',Name => 'OverlaysRed' },
+    '60xx,1202' => { VR => 'RET',Name => 'OverlaysGreen' },
+    '60xx,1203' => { VR => 'RET',Name => 'OverlaysBlue' },
+    '60xx,1301' => { VR => 'IS', Name => 'ROIArea' },
+    '60xx,1302' => { VR => 'DS', Name => 'ROIMean' },
+    '60xx,1303' => { VR => 'DS', Name => 'ROIStandardDeviation' },
+    '60xx,1500' => { VR => 'LO', Name => 'OverlayLabel' },
+    '60xx,3000' => { VR => 'OW', Name => 'OverlayData' },
+    '60xx,4000' => { VR => 'RET',Name => 'OverlayComments' },
+    # pixel data group
+    '7FE0,0000' => { VR => 'UL', Name => 'PixelDataGroupLength' },
+    '7FE0,0010' => { VR => 'OB', Name => 'PixelData' },
+    'FFFA,FFFA' => { VR => 'SQ', Name => 'DigitalSignaturesSequence' },
+    'FFFC,FFFC' => { VR => 'OB', Name => 'DataSetTrailingPadding' },
+    # the sequence delimiters have no VR:
+    'FFFE,E000' => 'StartOfItem',
+    'FFFE,E00D' => 'EndOfItems',
+    'FFFE,E0DD' => 'EndOfSequence',
+);
+
+# table to translate registered UID values to readable strings
+my %uid = (
+    '1.2.840.10008.1.1' => 'Verification SOP Class',
+    '1.2.840.10008.1.2' => 'Implicit VR Little Endian',
+    '1.2.840.10008.1.2.1' => 'Explicit VR Little Endian',
+    '1.2.840.10008.1.2.1.99' => 'Deflated Explicit VR Little Endian',
+    '1.2.840.10008.1.2.2' => 'Explicit VR Big Endian',
+    '1.2.840.10008.1.2.4.50' => 'JPEG Baseline (Process 1)',
+    '1.2.840.10008.1.2.4.51' => 'JPEG Extended (Process 2 & 4)',
+    '1.2.840.10008.1.2.4.52' => 'JPEG Extended (Process 3 & 5)',
+    '1.2.840.10008.1.2.4.53' => 'JPEG Spectral Selection, Non-Hierarchical (Process 6 & 8)',
+    '1.2.840.10008.1.2.4.54' => 'JPEG Spectral Selection, Non-Hierarchical (Process 7 & 9)',
+    '1.2.840.10008.1.2.4.55' => 'JPEG Full Progression, Non-Hierarchical (Process 10 & 12)',
+    '1.2.840.10008.1.2.4.56' => 'JPEG Full Progression, Non-Hierarchical (Process 11 & 13)',
+    '1.2.840.10008.1.2.4.57' => 'JPEG Lossless, Non-Hierarchical (Process 14)',
+    '1.2.840.10008.1.2.4.58' => 'JPEG Lossless, Non-Hierarchical (Process 15) ',
+    '1.2.840.10008.1.2.4.59' => 'JPEG Extended, Hierarchical (Process 16 & 18) ',
+    '1.2.840.10008.1.2.4.60' => 'JPEG Extended, Hierarchical (Process 17 & 19) ',
+    '1.2.840.10008.1.2.4.61' => 'JPEG Spectral Selection, Hierarchical (Process 20 & 22)',
+    '1.2.840.10008.1.2.4.62' => 'JPEG Spectral Selection, Hierarchical (Process 21 & 23)',
+    '1.2.840.10008.1.2.4.63' => 'JPEG Full Progression, Hierarchical (Process 24 & 26)',
+    '1.2.840.10008.1.2.4.64' => 'JPEG Full Progression, Hierarchical (Process 25 & 27)',
+    '1.2.840.10008.1.2.4.65' => 'JPEG Lossless, Hierarchical (Process 28) ',
+    '1.2.840.10008.1.2.4.66' => 'JPEG Lossless, Hierarchical (Process 29) ',
+    '1.2.840.10008.1.2.4.70' => 'JPEG Lossless, Non-Hierarchical, First-Order Prediction (Process 14-1)',
+    '1.2.840.10008.1.2.4.80' => 'JPEG-LS Lossless Image Compression',
+    '1.2.840.10008.1.2.4.81' => 'JPEG-LS Lossy (Near-Lossless) Image Compression',
+    '1.2.840.10008.1.2.4.90' => 'JPEG 2000 Image Compression (Lossless Only)',
+    '1.2.840.10008.1.2.4.91' => 'JPEG 2000 Image Compression',
+    '1.2.840.10008.1.2.4.100' => 'MPEG2 Main Profile @ Main Level',
+    '1.2.840.10008.1.2.5' => 'RLE Lossless',
+    '1.2.840.10008.1.3.10' => 'Media Storage Directory Storage',
+    '1.2.840.10008.1.4.1.1' => 'Talairach Brain Atlas Frame of Reference',
+    '1.2.840.10008.1.4.1.2' => 'SPM2 T1 Frame of Reference',
+    '1.2.840.10008.1.4.1.3' => 'SPM2 T2 Frame of Reference',
+    '1.2.840.10008.1.4.1.4' => 'SPM2 PD Frame of Reference',
+    '1.2.840.10008.1.4.1.5' => 'SPM2 EPI Frame of Reference',
+    '1.2.840.10008.1.4.1.6' => 'SPM2 FIL T1 Frame of Reference',
+    '1.2.840.10008.1.4.1.7' => 'SPM2 PET Frame of Reference',
+    '1.2.840.10008.1.4.1.8' => 'SPM2 TRANSM Frame of Reference',
+    '1.2.840.10008.1.4.1.9' => 'SPM2 SPECT Frame of Reference',
+    '1.2.840.10008.1.4.1.10' => 'SPM2 GRAY Frame of Reference',
+    '1.2.840.10008.1.4.1.11' => 'SPM2 WHITE Frame of Reference',
+    '1.2.840.10008.1.4.1.12' => 'SPM2 CSF Frame of Reference',
+    '1.2.840.10008.1.4.1.13' => 'SPM2 BRAINMASK Frame of Reference',
+    '1.2.840.10008.1.4.1.14' => 'SPM2 AVG305T1 Frame of Reference',
+    '1.2.840.10008.1.4.1.15' => 'SPM2 AVG152T1 Frame of Reference',
+    '1.2.840.10008.1.4.1.16' => 'SPM2 AVG152T2 Frame of Reference',
+    '1.2.840.10008.1.4.1.17' => 'SPM2 AVG152PD Frame of Reference',
+    '1.2.840.10008.1.4.1.18' => 'SPM2 SINGLESUBJT1 Frame of Reference',
+    '1.2.840.10008.1.4.2.1' => 'ICBM 452 T1 Frame of Reference',
+    '1.2.840.10008.1.4.2.2' => 'ICBM Single Subject MRI Frame of Reference',
+    '1.2.840.10008.1.9' => 'Basic Study Content Notification SOP Class',
+    '1.2.840.10008.1.20.1' => 'Storage Commitment Push Model SOP Class',
+    '1.2.840.10008.1.20.1.1' => 'Storage Commitment Push Model SOP Instance',
+    '1.2.840.10008.1.20.2' => 'Storage Commitment Pull Model SOP Class ',
+    '1.2.840.10008.1.20.2.1' => 'Storage Commitment Pull Model SOP Instance ',
+    '1.2.840.10008.1.40' => 'Procedural Event Logging SOP Class',
+    '1.2.840.10008.1.40.1' => 'Procedural Event Logging SOP Instance',
+    '1.2.840.10008.2.16.4' => 'DICOM Controlled Terminology Coding Scheme PS 3.16',
+    '1.2.840.10008.3.1.1.1' => 'DICOM Application Context Name',
+    '1.2.840.10008.3.1.2.1.1' => 'Detached Patient Management SOP Class',
+    '1.2.840.10008.3.1.2.1.4' => 'Detached Patient Management Meta SOP Class',
+    '1.2.840.10008.3.1.2.2.1' => 'Detached Visit Management SOP Class',
+    '1.2.840.10008.3.1.2.3.1' => 'Detached Study Management SOP Class',
+    '1.2.840.10008.3.1.2.3.2' => 'Study Component Management SOP Class',
+    '1.2.840.10008.3.1.2.3.3' => 'Modality Performed Procedure Step SOP Class',
+    '1.2.840.10008.3.1.2.3.4' => 'Modality Performed Procedure Step Retrieve SOP Class',
+    '1.2.840.10008.3.1.2.3.5' => 'Modality Performed Procedure Step Notification SOP Class',
+    '1.2.840.10008.3.1.2.5.1' => 'Detached Results Management SOP Class',
+    '1.2.840.10008.3.1.2.5.4' => 'Detached Results Management Meta SOP Class',
+    '1.2.840.10008.3.1.2.5.5' => 'Detached Study Management Meta SOP Class',
+    '1.2.840.10008.3.1.2.6.1' => 'Detached Interpretation Management SOP Class',
+    '1.2.840.10008.4.2' => 'Storage Service Class Service Class PS 3.4',
+    '1.2.840.10008.5.1.1.1' => 'Basic Film Session SOP Class',
+    '1.2.840.10008.5.1.1.2' => 'Basic Film Box SOP Class',
+    '1.2.840.10008.5.1.1.4' => 'Basic Grayscale Image Box SOP Class',
+    '1.2.840.10008.5.1.1.4.1' => 'Basic Color Image Box SOP Class',
+    '1.2.840.10008.5.1.1.4.2' => 'Referenced Image Box SOP Class',
+    '1.2.840.10008.5.1.1.9' => 'Basic Grayscale Print ManagementMeta SOP Class',
+    '1.2.840.10008.5.1.1.9.1' => 'Referenced Grayscale Print Management Meta SOP Class',
+    '1.2.840.10008.5.1.1.14' => 'Print Job SOP Class',
+    '1.2.840.10008.5.1.1.15' => 'Basic Annotation Box SOP Class',
+    '1.2.840.10008.5.1.1.16' => 'Printer SOP Class',
+    '1.2.840.10008.5.1.1.16.376' => 'Printer Configuration Retrieval SOP Class',
+    '1.2.840.10008.5.1.1.17' => 'Printer SOP Instance',
+    '1.2.840.10008.5.1.1.17.376' => 'Printer Configuration RetrievalSOP Instance',
+    '1.2.840.10008.5.1.1.18' => 'Basic Color Print Management Meta SOP Class',
+    '1.2.840.10008.5.1.1.18.1' => 'Referenced Color Print Management Meta SOP Class',
+    '1.2.840.10008.5.1.1.22' => 'VOI LUT Box SOP Class',
+    '1.2.840.10008.5.1.1.23' => 'Presentation LUT SOP Class',
+    '1.2.840.10008.5.1.1.24' => 'Image Overlay Box SOP Class',
+    '1.2.840.10008.5.1.1.24.1' => 'Basic Print Image Overlay Box SOP Class',
+    '1.2.840.10008.5.1.1.25' => 'Print Queue SOP Instance',
+    '1.2.840.10008.5.1.1.26' => 'Print Queue Management SOP Class',
+    '1.2.840.10008.5.1.1.27' => 'Stored Print Storage SOP Class',
+    '1.2.840.10008.5.1.1.29' => 'Hardcopy Grayscale Image',
+    '1.2.840.10008.5.1.1.30' => 'Hardcopy Color Image Storage SOP Class',
+    '1.2.840.10008.5.1.1.31' => 'Pull Print Request SOP Class',
+    '1.2.840.10008.5.1.1.32' => 'Pull Stored Print Management Meta SOP Class',
+    '1.2.840.10008.5.1.1.33' => 'Media Creation Management SOP Class',
+    '1.2.840.10008.5.1.4.1.1.1' => 'Computed Radiography Image Storage',
+    '1.2.840.10008.5.1.4.1.1.1.1' => 'Digital X-Ray Image Storage Ð For Presentation',
+    '1.2.840.10008.5.1.4.1.1.1.1.1' => 'Digital X-Ray Image Storage Ð For Processing',
+    '1.2.840.10008.5.1.4.1.1.1.2' => 'Digital Mammography X-Ray Image Storage Ð For Presentation',
+    '1.2.840.10008.5.1.4.1.1.1.2.1' => 'Digital Mammography X-Ray Image Storage Ð For Processing',
+    '1.2.840.10008.5.1.4.1.1.1.3' => 'Digital Intra-oral X-Ray Image Storage Ð For Presentation',
+    '1.2.840.10008.5.1.4.1.1.1.3.1' => 'Digital Intra-oral X-Ray Image Storage Ð For Processing',
+    '1.2.840.10008.5.1.4.1.1.2' => 'CT Image Storage',
+    '1.2.840.10008.5.1.4.1.1.2.1' => 'Enhanced CT Image Storage',
+    '1.2.840.10008.5.1.4.1.1.3' => 'Ultrasound Multi-frame Image Storage ',
+    '1.2.840.10008.5.1.4.1.1.3.1' => 'Ultrasound Multi-frame Image Storage',
+    '1.2.840.10008.5.1.4.1.1.4' => 'MR Image Storage',
+    '1.2.840.10008.5.1.4.1.1.4.1' => 'Enhanced MR Image Storage',
+    '1.2.840.10008.5.1.4.1.1.4.2' => 'MR Spectroscopy Storage',
+    '1.2.840.10008.5.1.4.1.1.5' => 'Nuclear Medicine Image Storage',
+    '1.2.840.10008.5.1.4.1.1.6' => 'Ultrasound Image Storage',
+    '1.2.840.10008.5.1.4.1.1.6.1' => 'Ultrasound Image Storage',
+    '1.2.840.10008.5.1.4.1.1.7' => 'Secondary Capture Image Storage',
+    '1.2.840.10008.5.1.4.1.1.7.1' => 'Multi-frame Single Bit Secondary',
+    '1.2.840.10008.5.1.4.1.1.7.2' => 'Multi-frame Grayscale Byte Secondary Capture Image Storage',
+    '1.2.840.10008.5.1.4.1.1.7.3' => 'Multi-frame Grayscale Word Secondary Capture Image Storage',
+    '1.2.840.10008.5.1.4.1.1.7.4' => 'Multi-frame True Color Secondary Capture Image Storage',
+    '1.2.840.10008.5.1.4.1.1.8' => 'Standalone Overlay Storage',
+    '1.2.840.10008.5.1.4.1.1.9' => 'Standalone Curve Storage',
+    '1.2.840.10008.5.1.4.1.1.9.1.1' => '12-lead ECG Waveform Storage',
+    '1.2.840.10008.5.1.4.1.1.9.1.2' => 'General ECG Waveform Storage',
+    '1.2.840.10008.5.1.4.1.1.9.1.3' => 'Ambulatory ECG Waveform Storage',
+    '1.2.840.10008.5.1.4.1.1.9.2.1' => 'Hemodynamic Waveform Storage',
+    '1.2.840.10008.5.1.4.1.1.9.3.1' => 'Cardiac Electrophysiology Waveform Storage',
+    '1.2.840.10008.5.1.4.1.1.9.4.1' => 'Basic Voice Audio Waveform Storage',
+    '1.2.840.10008.5.1.4.1.1.10' => 'Standalone Modality LUT Storage',
+    '1.2.840.10008.5.1.4.1.1.11' => 'Standalone VOI LUT Storage',
+    '1.2.840.10008.5.1.4.1.1.11.1' => 'Grayscale Softcopy Presentation State Storage SOP Class',
+    '1.2.840.10008.5.1.4.1.1.12.1' => 'X-Ray Angiographic Image Storage',
+    '1.2.840.10008.5.1.4.1.1.12.2' => 'X-Ray Radiofluoroscopic Image Storage',
+    '1.2.840.10008.5.1.4.1.1.12.3' => 'X-Ray Angiographic Bi-Plane Image Storage ',
+    '1.2.840.10008.5.1.4.1.1.20' => 'Nuclear Medicine Image Storage',
+    '1.2.840.10008.5.1.4.1.1.66' => 'Raw Data Storage',
+    '1.2.840.10008.5.1.4.1.1.66.1' => 'Spatial Registration Storage',
+    '1.2.840.10008.5.1.4.1.1.66.2' => 'Spatial Fiducials Storage',
+    '1.2.840.10008.5.1.4.1.1.77.1' => 'VL Image Storage ',
+    '1.2.840.10008.5.1.4.1.1.77.2' => 'VL Multi-frame Image Storage',
+    '1.2.840.10008.5.1.4.1.1.77.1.1' => 'VL Endoscopic Image Storage',
+    '1.2.840.10008.5.1.4.1.1.77.1.1.1' => 'Video Endoscopic Image Storage',
+    '1.2.840.10008.5.1.4.1.1.77.1.2' => 'VL Microscopic Image Storage',
+    '1.2.840.10008.5.1.4.1.1.77.1.2.1' => 'Video Microscopic Image Storage',
+    '1.2.840.10008.5.1.4.1.1.77.1.3' => 'VL Slide-Coordinates Microscopic Image Storage',
+    '1.2.840.10008.5.1.4.1.1.77.1.4' => 'VL Photographic Image Storage',
+    '1.2.840.10008.5.1.4.1.1.77.1.4.1' => 'Video Photographic Image Storage',
+    '1.2.840.10008.5.1.4.1.1.77.1.5.1' => 'Ophthalmic Photography 8 Bit Image Storage',
+    '1.2.840.10008.5.1.4.1.1.77.1.5.2' => 'Ophthalmic Photography 16 Bit Image Storage',
+    '1.2.840.10008.5.1.4.1.1.77.1.5.3' => 'Stereometric Relationship Storage',
+    '1.2.840.10008.5.1.4.1.1.88.11' => 'Basic Text SR',
+    '1.2.840.10008.5.1.4.1.1.88.22' => 'Enhanced SR',
+    '1.2.840.10008.5.1.4.1.1.88.33' => 'Comprehensive SR',
+    '1.2.840.10008.5.1.4.1.1.88.40' => 'Procedure Log Storage',
+    '1.2.840.10008.5.1.4.1.1.88.50' => 'Mammography CAD SR',
+    '1.2.840.10008.5.1.4.1.1.88.59' => 'Key Object Selection Document',
+    '1.2.840.10008.5.1.4.1.1.88.65' => 'Chest CAD SR',
+    '1.2.840.10008.5.1.4.1.1.128' => 'Positron Emission Tomography Image Storage',
+    '1.2.840.10008.5.1.4.1.1.129' => 'Standalone PET Curve Storage',
+    '1.2.840.10008.5.1.4.1.1.481.1' => 'RT Image Storage',
+    '1.2.840.10008.5.1.4.1.1.481.2' => 'RT Dose Storage',
+    '1.2.840.10008.5.1.4.1.1.481.3' => 'RT Structure Set Storage',
+    '1.2.840.10008.5.1.4.1.1.481.4' => 'RT Beams Treatment Record Storage',
+    '1.2.840.10008.5.1.4.1.1.481.5' => 'RT Plan Storage',
+    '1.2.840.10008.5.1.4.1.1.481.6' => 'RT Brachy Treatment Record Storage',
+    '1.2.840.10008.5.1.4.1.1.481.7' => 'RT Treatment Summary Record Storage',
+    '1.2.840.10008.5.1.4.1.2.1.1' => 'Patient Root Query/Retrieve Information Model Ð FIND',
+    '1.2.840.10008.5.1.4.1.2.1.2' => 'Patient Root Query/Retrieve Information Model Ð MOVE',
+    '1.2.840.10008.5.1.4.1.2.1.3' => 'Patient Root Query/Retrieve Information Model Ð GET',
+    '1.2.840.10008.5.1.4.1.2.2.1' => 'Study Root Query/Retrieve Information Model Ð FIND',
+    '1.2.840.10008.5.1.4.1.2.2.2' => 'Study Root Query/Retrieve Information Model Ð MOVE',
+    '1.2.840.10008.5.1.4.1.2.2.3' => 'Study Root Query/Retrieve Information Model Ð GET',
+    '1.2.840.10008.5.1.4.1.2.3.1' => 'Patient/Study Only Query/Retrieve Information Model - FIND',
+    '1.2.840.10008.5.1.4.1.2.3.2' => 'Patient/Study Only Query/Retrieve Information Model - MOVE',
+    '1.2.840.10008.5.1.4.1.2.3.3' => 'Patient/Study Only Query/Retrieve Information Model - GET',
+    '1.2.840.10008.5.1.4.31' => 'Modality Worklist Information Model Ð FIND',
+    '1.2.840.10008.5.1.4.32.1' => 'General Purpose Worklist Information Model Ð FIND',
+    '1.2.840.10008.5.1.4.32.2' => 'General Purpose Scheduled Procedure Step SOP Class',
+    '1.2.840.10008.5.1.4.32.3' => 'General Purpose Performed Procedure Step SOP Class',
+    '1.2.840.10008.5.1.4.32' => 'General Purpose Worklist Management Meta SOP Class',
+    '1.2.840.10008.5.1.4.33' => 'Instance Availability Notification SOP Class',
+    '1.2.840.10008.5.1.4.37.1' => 'General Relevant Patient Information Query',
+    '1.2.840.10008.5.1.4.37.2' => 'Breast Imaging Relevant Patient Information Query',
+    '1.2.840.10008.5.1.4.37.3' => 'Cardiac Relevant Patient Information Query',
+    '1.2.840.10008.15.0.3.1' => 'dicomDeviceName',
+    '1.2.840.10008.15.0.3.2' => 'dicomDescription',
+    '1.2.840.10008.15.0.3.3' => 'dicomManufacturer',
+    '1.2.840.10008.15.0.3.4' => 'dicomManufacturerModelName',
+    '1.2.840.10008.15.0.3.5' => 'dicomSoftwareVersion',
+    '1.2.840.10008.15.0.3.6' => 'dicomVendorData',
+    '1.2.840.10008.15.0.3.7' => 'dicomAETitle',
+    '1.2.840.10008.15.0.3.8' => 'dicomNetworkConnectionReference',
+    '1.2.840.10008.15.0.3.9' => 'dicomApplicationCluster',
+    '1.2.840.10008.15.0.3.10' => 'dicomAssociationInitiator',
+    '1.2.840.10008.15.0.3.11' => 'dicomAssociationAcceptor',
+    '1.2.840.10008.15.0.3.12' => 'dicomHostname',
+    '1.2.840.10008.15.0.3.13' => 'dicomPort',
+    '1.2.840.10008.15.0.3.14' => 'dicomSOPClass',
+    '1.2.840.10008.15.0.3.15' => 'dicomTransferRole',
+    '1.2.840.10008.15.0.3.16' => 'dicomTransferSyntax',
+    '1.2.840.10008.15.0.3.17' => 'dicomPrimaryDeviceType',
+    '1.2.840.10008.15.0.3.18' => 'dicomRelatedDeviceReference',
+    '1.2.840.10008.15.0.3.19' => 'dicomPreferredCalledAETitle',
+    '1.2.840.10008.15.0.3.20' => 'dicomTLSCyphersuite',
+    '1.2.840.10008.15.0.3.21' => 'dicomAuthorizedNodeCertificateReference',
+    '1.2.840.10008.15.0.3.22' => 'dicomThisNodeCertificateReference',
+    '1.2.840.10008.15.0.3.23' => 'dicomInstalled',
+    '1.2.840.10008.15.0.3.24' => 'dicomStationName',
+    '1.2.840.10008.15.0.3.25' => 'dicomDeviceSerialNumber',
+    '1.2.840.10008.15.0.3.26' => 'dicomInstitutionName',
+    '1.2.840.10008.15.0.3.27' => 'dicomInstitutionAddress',
+    '1.2.840.10008.15.0.3.28' => 'dicomInstitutionDepartmentName',
+    '1.2.840.10008.15.0.3.29' => 'dicomIssuerOfPatientID',
+    '1.2.840.10008.15.0.3.30' => 'dicomPreferredCallingAETitle',
+    '1.2.840.10008.15.0.3.31' => 'dicomSupportedCharacterSet',
+    '1.2.840.10008.15.0.4.1' => 'dicomConfigurationRoot',
+    '1.2.840.10008.15.0.4.2' => 'dicomDevicesRoot',
+    '1.2.840.10008.15.0.4.3' => 'dicomUniqueAETitlesRegistryRoot',
+    '1.2.840.10008.15.0.4.4' => 'dicomDevice',
+    '1.2.840.10008.15.0.4.5' => 'dicomNetworkAE',
+    '1.2.840.10008.15.0.4.6' => 'dicomNetworkConnection',
+    '1.2.840.10008.15.0.4.7' => 'dicomUniqueAETitle',
+    '1.2.840.10008.15.0.4.8' => 'dicomTransferCapability',
+);
+
+#------------------------------------------------------------------------------
+# Extract information from a DICOM (DCM) image
+# Inputs: 0) ExifTool object reference, 1) DirInfo reference
+# Returns: 1 on success, 0 if this wasn't a valid DICOM file
+sub ProcessDICM($$)
+{
+    my ($exifTool, $dirInfo) = @_;
+    my $raf = $$dirInfo{RAF};
+    my $unknown = $exifTool->Options('Unknown');
+    my $verbose = $exifTool->Options('Verbose');
+    my ($hdr, $buff, $implicit, $vr, $len);
+#
+# identify the DICOM or ACR-NEMA file
+#
+    $raf->Read($hdr, 12) == 12 or return 0; # save for ACR identification later
+    $raf->Seek(128, 0) or return 0;         # skip to end of DICM header
+    $raf->Read($buff, 4) == 4 or return 0;  # read signature
+    if ($buff eq 'DICM') {
+        # file meta information transfer syntax is explicit little endian
+        SetByteOrder('II');
+        $exifTool->SetFileType('DICOM');
+    } else {
+        # test for a RAW DCM image (ACR-NEMA format, ie. no header)
+        foreach ('II','MM','') {
+            return 0 unless $_; # no luck identifying the syntax
+            SetByteOrder($_);
+            my $g = Get16u(\$hdr, 0);
+            # expect group number to be small and even
+            next if $g < 2 or $g > 8 or $g & 0x01;
+            my $e = Get16u(\$hdr, 2);
+            next if $e > 0x20;          # expect a low element number at start
+            $vr = substr($hdr, 4, 2);   # look for explicit VR
+            if ($vr =~ /^[A-Z]{2}$/) {
+                $implicit = 0;
+                if ($vr32{$vr}) {
+                    next unless Get16u(\$hdr, 6) == 0;  # must be 2 zero bytes
+                    $len = Get32u(\$hdr, 8);
+                } else {
+                    next if $e == 0 and $vr ne 'UL';    # group length must be UL
+                    $len = Get16u(\$hdr, 6);
+                }
+            } else {
+                $implicit = 1;
+                $len = Get32u(\$hdr, 4);
+            }
+            next if $e == 0 and $len != 4;  # group length value must be 4 bytes
+            next if $len > 64;      # first element shouldn't be too long
+            last;   # success!
+        }
+        $raf->Seek(0, 0) or return 0;   # rewind to start of file
+        $exifTool->SetFileType('ACR');
+    }
+#
+# process the meta information
+#
+    my $tagTablePtr = GetTagTable('Image::ExifTool::DICOM::Main');
+    my $pos = $raf->Tell();
+    my $err = 1;
+    my ($transferSyntax, $group2end);
+    for (;;) {
+        $raf->Read($buff, 8) == 8 or $err = 0, last;
+        $pos += 8;
+        my $group = Get16u(\$buff, 0);
+        # implement the transfer syntax at the end of the group 2 data
+        if ($transferSyntax and ($group != 0x0002 or
+            ($group2end and $pos > $group2end)))
+        {
+            # 1.2.840.10008.1.2   = implicit VR little endian
+            # 1.2.840.10008.1.2.2 = explicit VR big endian
+            # 1.2.840.10008.1.2.x = explicit VR little endian
+            # 1.2.840.10008.1.2.1.99 = deflated
+            unless ($transferSyntax =~ /^1\.2\.840\.10008\.1\.2(\.\d+)?(\.\d+)?/) {
+                $exifTool->Warn("Unrecognized transfer syntax $transferSyntax");
+                last;
+            }
+            if (not $1) {
+                $implicit = 1;
+            } elsif ($1 eq '.2') {
+                SetByteOrder('MM');
+                $group = Get16u(\$buff, 0); # must get group again
+            } elsif ($1 eq '.1' and $2 and $2 eq '.99') {
+                # inflate compressed data stream
+                if (eval 'require Compress::Zlib') {
+                    # must use undocumented zlib feature to disable zlib header information
+                    # because DICOM deflated data doesn't have the zlib header (ref 3)
+                    my $wbits = -Compress::Zlib::MAX_WBITS();
+                    my $inflate = Compress::Zlib::inflateInit(-WindowBits => $wbits);
+                    if ($inflate) {
+                        $raf->Seek(-8, 1) or last;
+                        my $data = '';
+                        while ($raf->Read($buff, 65536)) {
+                            my ($buf, $stat) = $inflate->inflate($buff);
+                            if ($stat == Compress::Zlib::Z_OK() or
+                                $stat == Compress::Zlib::Z_STREAM_END())
+                            {
+                                $data .= $buf;
+                                last if $stat == Compress::Zlib::Z_STREAM_END();
+                            } else {
+                                $exifTool->Warn('Error inflating compressed data stream');
+                                return 1;
+                            }
+                        }
+                        last if length $data < 8;
+                        # create new RAF object from inflated data stream
+                        $raf = new File::RandomAccess(\$data);
+                        # re-read start of stream (now decompressed)
+                        $raf->Read($buff, 8) == 8 or last;
+                        $group = Get16u(\$buff, 0);
+                    } else {
+                        $exifTool->Warn('Error initializing inflation');
+                        return 1;
+                    }
+                } else {
+                    $exifTool->Warn('Install Compress::Zlib to decode compressed data stream');
+                    return 1;
+                }
+            }
+            undef $transferSyntax;
+        }
+        my $element = Get16u(\$buff,2);
+        my $tag = sprintf('%.4X,%.4X', $group, $element);
+
+        if ($implicit or $implicitVR{$tag}) {
+            # treat everything as string if implicit VR because it
+            # isn't worth it to generate the necessary VR lookup tables
+            # for the thousands of defined data elements
+            $vr = '';       # no VR (treat everything as string)
+            $len = Get32u(\$buff, 4);
+        } else {
+            $vr = substr($buff,4,2);
+            last unless $vr =~ /^[A-Z]{2}$/;
+            if ($vr32{$vr}) {
+                $raf->Read($buff, 4) == 4 or last;
+                $pos += 4;
+                $len = Get32u(\$buff, 0);
+                $len = 0 if $vr eq 'SQ';    # just recurse into sequences
+            } else {
+                $len = Get16u(\$buff, 6);
+            }
+        }
+        if ($len == 0xffffffff) {
+            $len = 0;   # don't read value if undefined length
+            if ($verbose) {
+                # start list of items in verbose output
+                $exifTool->VPrint(0, "$exifTool->{INDENT}+ [List of items]\n");
+                $exifTool->{INDENT} .= '| ';
+            }
+        }
+        # read the element value
+        if ($len) {
+            $raf->Read($buff, $len) == $len or last;
+            $pos += $len;
+        } else {
+            $buff = '';
+        }
+
+        # handle tags not found in the table
+        my $tagInfo = $$tagTablePtr{$tag};
+        unless ($tagInfo) {
+            # look for a tag like '60xx,1203' or '0020,31xx' in table
+            my $xx;
+            if ((($xx = $tag) =~ s/^(..)../$1xx/ and $$tagTablePtr{$xx}) or
+                (($xx = $tag) =~ s/(..)$/xx/ and $$tagTablePtr{$xx}))
+            {
+                $tag = $xx;
+                $tagInfo = $$tagTablePtr{$xx};
+            } elsif ($unknown) {
+                # create tag info hash for unknown elements
+                if ($element == 0) {    # element zero is group length
+                    $tagInfo = {
+                        Name => sprintf("Group%.4X_Length", $group),
+                        Description => sprintf("Group %.4X Length", $group),
+                    };
+                } else {
+                    $tagInfo = {
+                        Name => sprintf("DICOM_%.4X_%.4X", $group, $element),
+                        Description => sprintf("DICOM %.4X,%.4X", $group, $element),
+                    };
+                }
+                $$tagInfo{Unknown} = 1;
+                Image::ExifTool::AddTagToTable($tagTablePtr, $tag, $tagInfo);
+            }
+        }
+        # get VR from our tag information if implicit
+        $vr = $$tagInfo{VR} || '  ' if $tagInfo and not $vr;
+
+        if ($element == 0) {
+            $vr = 'UL'; # group length element is always unsigned long
+        }
+        my $val;
+        my $format = $dicomFormat{$vr};
+        if ($len > 1024) {
+            # treat large data elements as binary data
+            my $binData;
+            if ($exifTool->Options('Binary') or ($tagInfo and
+                $exifTool->{REQ_TAG_LOOKUP}->{lc($$tagInfo{Name})}))
+            {
+                $binData = $buff;   # must make a copy
+            } else {
+                $binData = "Binary data $len bytes";
+            }
+            $val = \$binData;
+        } elsif ($format) {
+            $val = ReadValue(\$buff, 0, $format, undef, $len);
+        } else {
+            $val = $buff;
+            $format = 'string';
+            if ($vr eq 'DA') {
+                # format date values
+                $val =~ s/^(\d{4})(\d{2})(\d{2})/$1:$2:$3/;
+            } elsif ($vr eq 'TM') {
+                # format time values
+                $val =~ s/^(\d{2})(\d{2})(\d{2}.*)/$1:$2:$3/;
+            } elsif ($vr eq 'DT') {
+                # format date/time values
+                $val =~ s/^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2}.*)/$1:$2:$3 $4:$5:$6/;
+            } elsif ($vr eq 'AT' and $len == 4) {
+                # convert attribute tag ID to hex format
+                my ($g, $e) = (Get16u(\$buff,0), Get16u(\$buff,2));
+                $val = sprintf('%.4X,%.4X', $g, $e);
+            } elsif ($vr eq 'UI') {
+                # add PrintConv to translate registered UID's
+                $val =~ s/\0.*//; # truncate at null
+                $$tagInfo{PrintConv} = \%uid if $uid{$val} and $tagInfo;
+            }
+        }
+        # save the group 2 end position and transfer syntax
+        if ($group == 0x0002) {
+            $element == 0x0000 and $group2end = $pos + $val;
+            $element == 0x0010 and $transferSyntax = $val;
+        }
+
+        # handle the new tag information
+        $exifTool->HandleTag($tagTablePtr, $tag, $val,
+            DataPt => \$buff,
+            DataPos => $pos - $len,
+            Format => $format,
+            Start => 0,
+            Size => $len,
+            Extra => " ($vr)",
+        );
+
+        # stop indenting for list if we reached EndOfItems tag
+        $exifTool->{INDENT} =~ s/..$// if $verbose and $tag eq 'FFFE,E00D';
+    }
+    $err and $exifTool->Warn('Error reading DICOM file (corrupted?)');
+    return 1;
+}
+
+1;  # end
+
+__END__
+
+=head1 NAME
+
+Image::ExifTool::DICOM - Read DICOM and ACR-NEMA medical images
+
+=head1 SYNOPSIS
+
+This module is used by Image::ExifTool
+
+=head1 DESCRIPTION
+
+This module contains routines required by Image::ExifTool to extract meta
+information from DICOM (Digital Imaging and Communications in Medicine) DCM
+and ACR-NEMA (American College of Radiology - National Electrical
+Manufacturer's Association) ACR medical images.
+
+=head1 NOTES
+
+Values of retired elements in implicit VR format files are intepreted as
+strings, hence they may not be displayed properly.  This is because the
+specification no longer lists these VR's, but simply lists 'RET' for these
+elements.  (Doh. Who's idea was that? :P)
+
+Images compressed using the DICOM deflated transfer syntax will be decoded
+if Compress::Zlib is installed.
+
+No translation of special characters sets is done.
+
+=head1 AUTHOR
+
+Copyright 2003-2007, Phil Harvey (phil at owl.phy.queensu.ca)
+
+This library is free software; you can redistribute it and/or modify it
+under the same terms as Perl itself.
+
+=head1 REFERENCES
+
+=over 4
+
+=item L<http://medical.nema.org/dicom/2004.html>
+
+=item L<http://www.sph.sc.edu/comd/rorden/dicom.html>
+
+=item L<http://www.dclunie.com/>
+
+=back
+
+=head1 SEE ALSO
+
+L<Image::ExifTool::TagNames/DICOM Tags>,
+L<Image::ExifTool(3pm)|Image::ExifTool>
+
+=cut
+
Index: /gsdl/trunk/perllib/cpan/Image/ExifTool/DNG.pm
===================================================================
--- /gsdl/trunk/perllib/cpan/Image/ExifTool/DNG.pm	(revision 16842)
+++ /gsdl/trunk/perllib/cpan/Image/ExifTool/DNG.pm	(revision 16842)
@@ -0,0 +1,694 @@
+#------------------------------------------------------------------------------
+# File:         DNG.pm
+#
+# Description:  Read DNG-specific information
+#
+# Revisions:    01/09/2006 - P. Harvey Created
+#
+# References:   1) http://www.adobe.com/products/dng/pdfs/dng_spec.pdf
+#------------------------------------------------------------------------------
+
+package Image::ExifTool::DNG;
+
+use strict;
+use vars qw($VERSION);
+use Image::ExifTool qw(:DataAccess :Utils);
+use Image::ExifTool::Exif;
+use Image::ExifTool::MakerNotes;
+use Image::ExifTool::CanonRaw;
+
+$VERSION = '1.07';
+
+sub ProcessOriginalRaw($$$);
+sub ProcessAdobeData($$$);
+sub ProcessAdobeMakN($$$);
+sub ProcessAdobeCRW($$$);
+sub ProcessAdobeMRW($$$);
+sub ProcessAdobeSR2($$$);
+sub WriteAdobeStuff($$$);
+
+# data in OriginalRawFileData
+%Image::ExifTool::DNG::OriginalRaw = (
+    GROUPS => { 2 => 'Image' },
+    PROCESS_PROC => \&ProcessOriginalRaw,
+    NOTES => q{
+        This table defines tags extracted from the DNG OriginalRawFileData
+        information.
+    },
+    0 => { Name => 'OriginalRawImage',    Binary => 1 },
+    1 => { Name => 'OriginalRawResource', Binary => 1 },
+    2 => 'OriginalRawFileType',
+    3 => 'OriginalRawCreator',
+    4 => { Name => 'OriginalTHMImage',    Binary => 1 },
+    5 => { Name => 'OriginalTHMResource', Binary => 1 },
+    6 => 'OriginalTHMFileType',
+    7 => 'OriginalTHMCreator',
+);
+
+%Image::ExifTool::DNG::AdobeData = ( #PH
+    GROUPS => { 0 => 'MakerNotes', 1 => 'Adobe', 2 => 'Image' },
+    PROCESS_PROC => \&ProcessAdobeData,
+    WRITE_PROC => \&WriteAdobeStuff,
+    NOTES => q{
+        This information is found in the "Adobe" DNGPrivateData.
+
+        The maker notes ('MakN') are processed by ExifTool, but some information may
+        have been lost by the Adobe DNG Converter.  This is because the Adobe DNG
+        Converter (as of version 3.3) doesn't properly handle information referenced
+        from inside the maker notes that lies outside the original maker notes
+        block, and this information is lost when only the maker note block is copied
+        to the DNG image.   While this isn't a big problem for most camera makes, it
+        is serious for some makes like Olympus.
+
+        The CRW data is from DNG-converted CRW images, but it has been restructured
+        to a proprietary Adobe format (which results in a loss of the original
+        hierarchical information structure, and creates extra work for everyone else
+        who wants to use this information).
+    },
+    MakN  => [ ],   # (filled in later)
+   'CRW ' => {
+        Name => 'AdobeCRW',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::CanonRaw::Main',
+            ProcessProc => \&ProcessAdobeCRW,
+            WriteProc => \&WriteAdobeStuff,
+        },
+    },
+   'MRW ' => {
+        Name => 'AdobeMRW',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::MinoltaRaw::Main',
+            ProcessProc => \&ProcessAdobeMRW,
+            WriteProc => \&WriteAdobeStuff,
+        },
+    },
+   'SR2 ' => {
+        Name => 'AdobeSR2',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::Sony::SR2Private',
+            ProcessProc => \&ProcessAdobeSR2,
+        },
+    },
+   'RAF ' => {
+        Name => 'AdobeRAF',
+        Flags => [qw(Binary Unknown)],
+    },
+);
+
+# fill in maker notes
+{
+    my $tagInfo;
+    my $list = $Image::ExifTool::DNG::AdobeData{MakN};
+    foreach $tagInfo (@Image::ExifTool::MakerNotes::Main) {
+        unless (ref $tagInfo eq 'HASH') {
+            push @$list, $tagInfo;
+            next;
+        }
+        my %copy = %$tagInfo;
+        delete $copy{Groups};
+        delete $copy{Table};
+        push @$list, \%copy;
+    }
+}
+
+#------------------------------------------------------------------------------
+# Process DNG OriginalRawFileData information
+# Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
+# Returns: 1 on success, otherwise returns 0 and sets a Warning
+sub ProcessOriginalRaw($$$)
+{
+    my ($exifTool, $dirInfo, $tagTablePtr) = @_;
+    my $dataPt = $$dirInfo{DataPt};
+    my $start = $$dirInfo{DirStart};
+    my $end = $start + $$dirInfo{DirLen};
+    my $pos = $start;
+    my ($index, $err);
+    SetByteOrder('MM'); # pointers are always big-endian in this structure
+    for ($index=0; $index<8; ++$index) {
+        last if $pos + 4 > $end;
+        my $val = Get32u($dataPt, $pos);
+        $val or $pos += 4, next; # ignore zero values
+        my $tagInfo = $exifTool->GetTagInfo($tagTablePtr, $index);
+        $tagInfo or $err = "Missing DNG tag $index", last;
+        if ($index & 0x02) {
+            # extract a simple file type (tags 2, 3, 6 and 7)
+            $val = substr($$dataPt, $pos, 4);
+            $pos += 4;
+        } else {
+            # extract a compressed data block (tags 0, 1, 4 and 5)
+            my $n = int(($val + 65535) / 65536);
+            my $hdrLen = 4 * ($n + 2);
+            $pos + $hdrLen > $end and $err = '', last;
+            my $tag = $$tagInfo{Name};
+            # only extract this information if requested (because it takes time)
+            if ($exifTool->{OPTIONS}->{Binary} or
+                $exifTool->{REQ_TAG_LOOKUP}->{lc($tag)})
+            {
+                unless (eval 'require Compress::Zlib') {
+                    $err = 'Install Compress::Zlib to extract compressed images';
+                    last;
+                }
+                my $i;
+                $val = '';
+                my $p2 = $pos + Get32u($dataPt, $pos + 4);
+                for ($i=0; $i<$n; ++$i) {
+                    # inflate this compressed block
+                    my $p1 = $p2;
+                    $p2 = $pos + Get32u($dataPt, $pos + ($i + 2) * 4);
+                    if ($p1 >= $p2 or $p2 > $end) {
+                        $err = 'Bad compressed RAW image';
+                        last;
+                    }
+                    my $buff = substr($$dataPt, $p1, $p2 - $p1);
+                    my ($v2, $stat);
+                    my $inflate = Compress::Zlib::inflateInit();
+                    $inflate and ($v2, $stat) = $inflate->inflate($buff);
+                    if ($inflate and $stat == Compress::Zlib::Z_STREAM_END()) {
+                        $val .= $v2;
+                    } else {
+                        $err = 'Error inflating compressed RAW image';
+                        last;
+                    }
+                }
+                $pos = $p2;
+            } else {
+                $pos + $hdrLen > $end and $err = '', last;
+                my $len = Get32u($dataPt, $pos + $hdrLen - 4);
+                $pos + $len > $end and $err = '', last;
+                $val = substr($$dataPt, $pos + $hdrLen, $len - $hdrLen);
+                $val = "Binary data $len bytes";
+                $pos += $len;   # skip over this block
+            }
+        }
+        $exifTool->FoundTag($tagInfo, $val);
+    }
+    $exifTool->Warn($err || 'Bad OriginalRawFileData') if defined $err;
+    return 1;
+}
+
+#------------------------------------------------------------------------------
+# Process Adobe DNGPrivateData directory
+# Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
+# Returns: 1 on success
+sub ProcessAdobeData($$$)
+{
+    my ($exifTool, $dirInfo, $tagTablePtr) = @_;
+    my $dataPt = $$dirInfo{DataPt};
+    my $dataPos = $$dirInfo{DataPos};
+    my $pos = $$dirInfo{DirStart};
+    my $end = $$dirInfo{DirLen} + $pos;
+    my $outfile = $$dirInfo{OutFile};
+    my $verbose = $exifTool->Options('Verbose');
+    my $htmlDump = $exifTool->Options('HtmlDump');
+
+    return 0 unless $$dataPt =~ /^Adobe\0/;
+    unless ($outfile) {
+        $exifTool->VerboseDir($dirInfo);
+    }
+    $htmlDump and $exifTool->HtmlDump($dataPos, 6, 'Adobe DNGPrivateData header');
+    SetByteOrder('MM'); # always big endian
+    $pos += 6;
+    while ($pos + 8 <= $end) {
+        my ($tag, $size) = unpack("x${pos}a4N", $$dataPt);
+        $pos += 8;
+        last if $pos + $size > $end;
+        my $tagInfo = $$tagTablePtr{$tag};
+        if ($htmlDump) {
+            my $name = "Adobe$tag";
+            $name =~ tr/ //d;
+            $exifTool->HtmlDump($dataPos + $pos - 8, 8, "$name header", "Data Size: $size bytes");
+            # dump non-EXIF format data
+            unless ($tag =~ /^(MakN|SR2 )$/) {
+                $exifTool->HtmlDump($dataPos + $pos, $size, "$name data");
+            }
+        }
+        $verbose and not $outfile and $exifTool->VerboseInfo($tag,
+            ref $tagInfo eq 'HASH' ? $tagInfo : undef,
+            DataPt => $dataPt,
+            DataPos => $dataPos,
+            Start => $pos,
+            Size => $size,
+        );
+        my $value;
+        while ($tagInfo) {
+            my ($subTable, $subName, $processProc);
+            if (ref $tagInfo eq 'HASH') {
+                unless ($$tagInfo{SubDirectory}) {
+                    if ($outfile) {
+                        # copy value across to outfile
+                        $value = substr($$dataPt, $pos, $size);
+                    } else {
+                        $exifTool->HandleTag($tagTablePtr, $tag, substr($$dataPt, $pos, $size));
+                    }
+                    last;
+                }
+                $subTable = GetTagTable($tagInfo->{SubDirectory}->{TagTable});
+                $subName = $$tagInfo{Name};
+                $processProc = $tagInfo->{SubDirectory}->{ProcessProc};
+            } else {
+                $subTable = $tagTablePtr;
+                $subName = 'AdobeMakN';
+                $processProc = \&ProcessAdobeMakN;
+            }
+            my %dirInfo = (
+                Base     => $$dirInfo{Base},
+                DataPt   => $dataPt,
+                DataPos  => $dataPos,
+                DataLen  => $$dirInfo{DataLen},
+                DirStart => $pos,
+                DirLen   => $size,
+                DirName  => $subName,
+            );
+            if ($outfile) {
+                $dirInfo{Proc} = $processProc;  # WriteAdobeStuff() calls this to do the actual writing
+                $value = $exifTool->WriteDirectory(\%dirInfo, $subTable, \&WriteAdobeStuff);
+                # use old directory if an error occurred
+                defined $value or $value = substr($$dataPt, $pos, $size);
+            } else {
+                # override process proc for MakN
+                $exifTool->ProcessDirectory(\%dirInfo, $subTable, $processProc);
+            }
+            last;
+        }
+        if (defined $value and length $value) {
+            # add "Adobe" header if necessary
+            $$outfile = "Adobe\0" unless $$outfile and length $$outfile;
+            $$outfile .= $tag . pack('N', length $value) . $value;
+            $$outfile .= "\0" if length($value) & 0x01; # pad if necessary
+        }
+        $pos += $size;
+        ++$pos if $size & 0x01; # (darn padding)
+    }
+    $pos == $end or $exifTool->Warn("$pos $end Adobe private data is corrupt");
+    return 1;
+}
+
+#------------------------------------------------------------------------------
+# Process Adobe CRW directory
+# Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
+# Returns: 1 on success, otherwise returns 0 and sets a Warning
+# Notes: data has 4 byte header (2 for byte order and 2 for entry count)
+# - this routine would be as simple as ProcessAdobeMRW() below if Adobe hadn't
+#   pulled the bonehead move of reformatting the CRW information
+sub ProcessAdobeCRW($$$)
+{
+    my ($exifTool, $dirInfo, $tagTablePtr) = @_;
+    my $dataPt = $$dirInfo{DataPt};
+    my $start = $$dirInfo{DirStart};
+    my $end = $start + $$dirInfo{DirLen};
+    my $verbose = $exifTool->Options('Verbose');
+    my $buildMakerNotes = $exifTool->Options('MakerNotes');
+    my $outfile = $$dirInfo{OutFile};
+    my ($newTags, $oldChanged);
+
+    SetByteOrder('MM'); # always big endian
+    return 0 if $$dirInfo{DirLen} < 4;
+    my $byteOrder = substr($$dataPt, $start, 2);
+    return 0 unless $byteOrder =~ /^(II|MM)$/;
+
+    # initialize maker note data if building maker notes
+    $buildMakerNotes and Image::ExifTool::CanonRaw::InitMakerNotes($exifTool);
+
+    my $entries = Get16u($dataPt, $start + 2);
+    my $pos = $start + 4;
+    $exifTool->VerboseDir($dirInfo, $entries) unless $outfile;
+    if ($outfile) {
+        # get hash of new tags
+        $newTags = $exifTool->GetNewTagInfoHash($tagTablePtr);
+        $$outfile = substr($$dataPt, $start, 4);
+        $oldChanged = $exifTool->{CHANGED};
+    }
+    # loop through entries in Adobe CRW information
+    my $index;
+    for ($index=0; $index<$entries; ++$index) {
+        last if $pos + 6 > $end;
+        my $tag = Get16u($dataPt, $pos);
+        my $size = Get32u($dataPt, $pos + 2);
+        $pos += 6;
+        last if $pos + $size > $end;
+        my $value = substr($$dataPt, $pos, $size);
+        my $tagID = $tag & 0x3fff;
+        my $tagType = ($tag >> 8) & 0x38;   # get tag type
+        my $format = $Image::ExifTool::CanonRaw::crwTagFormat{$tagType};
+        my $count;
+        my $tagInfo = $exifTool->GetTagInfo($tagTablePtr, $tagID, \$value);
+        if ($tagInfo) {
+            $format = $$tagInfo{Format} if $$tagInfo{Format};
+            $count = $$tagInfo{Count};
+        }
+        # set count to 1 by default for values that were in the directory entry
+        if (not defined $count and $tag & 0x4000 and $format and $format ne 'string') {
+            $count = 1;
+        }
+        # set count from tagInfo count if necessary
+        if ($format and not $count) {
+            # set count according to format and size
+            my $fnum = $Image::ExifTool::Exif::formatNumber{$format};
+            my $fsiz = $Image::ExifTool::Exif::formatSize[$fnum];
+            $count = int($size / $fsiz);
+        }
+        $format or $format = 'undef';
+        SetByteOrder($byteOrder);
+        my $val = ReadValue(\$value, 0, $format, $count, $size);
+        if ($outfile) {
+            if ($tagInfo) {
+                my $subdir = $$tagInfo{SubDirectory};
+                if ($subdir and $$subdir{TagTable}) {
+                    my $name = $$tagInfo{Name};
+                    my $newTagTable = Image::ExifTool::GetTagTable($$subdir{TagTable});
+                    return 0 unless $newTagTable;
+                    my $subdirStart = 0;
+                    #### eval Start ()
+                    $subdirStart = eval $$subdir{Start} if $$subdir{Start};
+                    my $dirData = \$value;
+                    my %subdirInfo = (
+                        Name     => $name,
+                        DataPt   => $dirData,
+                        DataLen  => $size,
+                        DirStart => $subdirStart,
+                        DirLen   => $size - $subdirStart,
+                        Parent   => $$dirInfo{DirName},
+                    );
+                    #### eval Validate ($dirData, $subdirStart, $size)
+                    if (defined $$subdir{Validate} and not eval $$subdir{Validate}) {
+                        $exifTool->Warn("Invalid $name data");
+                    } else {
+                        $subdir = $exifTool->WriteDirectory(\%subdirInfo, $newTagTable);
+                        if (defined $subdir and length $subdir) {
+                            if ($subdirStart) {
+                                # add header before data directory
+                                $value = substr($value, 0, $subdirStart) . $subdir;
+                            } else {
+                                $value = $subdir;
+                            }
+                        }
+                    }
+                } elsif ($$newTags{$tagID}) {
+                    my $newValueHash = $exifTool->GetNewValueHash($tagInfo);
+                    if (Image::ExifTool::IsOverwriting($newValueHash, $val)) {
+                        my $newVal = Image::ExifTool::GetNewValues($newValueHash);
+                        my $verboseVal;
+                        $verboseVal = $newVal if $verbose > 1;
+                        # convert to specified format if necessary
+                        if (defined $newVal and $format) {
+                            $newVal = WriteValue($newVal, $format, $count);
+                        }
+                        if (defined $newVal) {
+                            if ($verbose > 1) {
+                                my $oldStr = $exifTool->Printable($value);
+                                my $newStr = $exifTool->Printable($verboseVal);
+                                my $out = $exifTool->Options('TextOut');
+                                print $out "    - CanonRaw:$$tagInfo{Name} = '$oldStr'\n";
+                                print $out "    + CanonRaw:$$tagInfo{Name} = '$newStr'\n";
+                            }
+                            $value = $newVal;
+                            ++$exifTool->{CHANGED};
+                        }
+                    }
+                }
+            }
+            # write out new value (always big-endian)
+            SetByteOrder('MM');
+            # (verified that there is no padding here)
+            $$outfile .= Set16u($tag) . Set32u(length($value)) . $value;
+        } else {
+            $exifTool->HandleTag($tagTablePtr, $tagID, $val,
+                Index   => $index,
+                DataPt  => $dataPt,
+                DataPos => $$dirInfo{DataPos},
+                Start   => $pos,
+                Size    => $size,
+                TagInfo => $tagInfo,
+            );
+            if ($buildMakerNotes) {
+                # build maker notes information if requested
+                Image::ExifTool::CanonRaw::BuildMakerNotes($exifTool, $tagID, $tagInfo,
+                                                           \$value, $format, $count);
+            }
+        }
+        # (we lost the directory structure, but the second tag 0x0805
+        # should be in the ImageDescription directory)
+        $exifTool->{DIR_NAME} = 'ImageDescription' if $tagID == 0x0805;
+        SetByteOrder('MM');
+        $pos += $size;
+    }
+    if ($outfile and (not defined $$outfile or $index != $entries or
+        $exifTool->{CHANGED} ==  $oldChanged))
+    {
+        $exifTool->{CHANGED} = $oldChanged; # nothing changed
+        undef $$outfile;    # rewrite old directory
+    }
+    if ($index != $entries) {
+        $exifTool->Warn('Truncated CRW notes');
+    } elsif ($pos < $end) {
+        $exifTool->Warn($end-$pos . ' extra bytes at end of CRW notes');
+    }
+    # finish building maker notes if necessary
+    if ($buildMakerNotes) {
+        SetByteOrder($byteOrder);
+        Image::ExifTool::CanonRaw::SaveMakerNotes($exifTool);
+    }
+    return 1;
+}
+
+#------------------------------------------------------------------------------
+# Process Adobe MRW directory
+# Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
+# Returns: 1 on success, otherwise returns 0 and sets a Warning
+# Notes: data has 4 byte header (2 for byte order and 2 for entry count)
+sub ProcessAdobeMRW($$$)
+{
+    my ($exifTool, $dirInfo, $tagTablePtr) = @_;
+    my $dataPt = $$dirInfo{DataPt};
+    my $dirLen = $$dirInfo{DirLen};
+    my $dirStart = $$dirInfo{DirStart};
+    my $outfile = $$dirInfo{OutFile};
+    # construct fake MRW file
+    my $buff = "\0MRM" . pack('N', $dirLen - 4);
+    # ignore leading byte order and directory count words
+    $buff .= substr($$dataPt, $dirStart + 4, $dirLen - 4);
+    my $raf = new File::RandomAccess(\$buff);
+    my %dirInfo = ( RAF => $raf, OutFile => $outfile );
+    my $rtnVal = Image::ExifTool::MinoltaRaw::ProcessMRW($exifTool, \%dirInfo);
+    if ($outfile and defined $$outfile and length $$outfile) {
+        # remove MRW header and add Adobe header
+        $$outfile = substr($$dataPt, $dirStart, 4) . substr($$outfile, 8);
+    }
+    return $rtnVal;
+}
+
+#------------------------------------------------------------------------------
+# Process Adobe SR2 directory
+# Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
+# Returns: 1 on success, otherwise returns 0 and sets a Warning
+# Notes: data has 6 byte header (2 for byte order and 4 for original offset)
+sub ProcessAdobeSR2($$$)
+{
+    my ($exifTool, $dirInfo, $tagTablePtr) = @_;
+    my $dataPt = $$dirInfo{DataPt};
+    my $start = $$dirInfo{DirStart};
+    my $len = $$dirInfo{DirLen};
+    return 0 if $$dirInfo{OutFile}; # (can't write this yet)
+
+    return 0 if $len < 6;
+    SetByteOrder('MM');
+    my $originalPos = Get32u($dataPt, $start + 2);
+    return 0 unless SetByteOrder(substr($$dataPt, $start, 2));
+
+    $exifTool->VerboseDir($dirInfo);
+    my $dataPos = $$dirInfo{DataPos};
+    my $dirStart = $start + 6;  # pointer to maker note directory
+    my $dirLen = $len - 6;
+
+    # initialize subdirectory information
+    my $fix = $dataPos + $dirStart - $originalPos;
+    my %subdirInfo = (
+        DirName   => 'AdobeSR2',
+        Base      => $$dirInfo{Base} + $fix,
+        DataPt    => $dataPt,
+        DataPos   => $dataPos - $fix,
+        DataLen   => $$dirInfo{DataLen},
+        DirStart  => $dirStart,
+        DirLen    => $dirLen,
+        Parent    => $$dirInfo{DirName},
+    );
+    if ($exifTool->Options('HtmlDump')) {
+        $exifTool->HtmlDump($dataPos + $start, 6, 'Adobe SR2 data');
+    }
+    # parse the SR2 directory
+    $exifTool->ProcessDirectory(\%subdirInfo, $tagTablePtr);
+    return 1;
+}
+
+#------------------------------------------------------------------------------
+# Process Adobe MakerNotes directory
+# Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
+# Returns: 1 on success, otherwise returns 0 and sets a Warning
+# Notes: data has 6 byte header (2 for byte order and 4 for original offset)
+sub ProcessAdobeMakN($$$)
+{
+    my ($exifTool, $dirInfo, $tagTablePtr) = @_;
+    my $dataPt = $$dirInfo{DataPt};
+    my $start = $$dirInfo{DirStart};
+    my $len = $$dirInfo{DirLen};
+    my $outfile = $$dirInfo{OutFile};
+
+    return 0 if $len < 6;
+    SetByteOrder('MM');
+    my $originalPos = Get32u($dataPt, $start + 2);
+    return 0 unless SetByteOrder(substr($$dataPt, $start, 2));
+
+    $exifTool->VerboseDir($dirInfo) unless $outfile;
+    my $dataPos = $$dirInfo{DataPos};
+    my $dirStart = $start + 6;  # pointer to maker note directory
+    my $dirLen = $len - 6;
+
+    my $hdr = substr($$dataPt, $dirStart, $dirLen < 48 ? $dirLen : 48);
+    my $tagInfo = $exifTool->GetTagInfo($tagTablePtr, 'MakN', \$hdr);
+    return 0 unless $tagInfo and $$tagInfo{SubDirectory};
+    my $subdir = $$tagInfo{SubDirectory};
+    my $subTable = GetTagTable($$subdir{TagTable});
+    # initialize subdirectory information
+    my %subdirInfo = (
+        DirName   => 'MakerNotes',
+        Name      => $$tagInfo{Name},   # needed for maker notes verbose dump
+        Base      => $$dirInfo{Base},
+        DataPt    => $dataPt,
+        DataPos   => $dataPos,
+        DataLen   => $$dirInfo{DataLen},
+        DirStart  => $dirStart,
+        DirLen    => $dirLen,
+        TagInfo   => $tagInfo,
+        FixBase   => $$subdir{FixBase},
+        EntryBased=> $$subdir{EntryBased},
+        Parent    => $$dirInfo{DirName},
+    );
+    # look for start of maker notes IFD
+    my $loc = Image::ExifTool::MakerNotes::LocateIFD($exifTool,\%subdirInfo);
+    unless (defined $loc) {
+        $exifTool->Warn('Maker notes could not be parsed');
+        return 0;
+    }
+    if ($exifTool->Options('HtmlDump')) {
+        $exifTool->HtmlDump($dataPos + $start, 6, 'Adobe MakN data');
+        $exifTool->HtmlDump($dataPos + $dirStart, $loc, "$$tagInfo{Name} header") if $loc;
+    }
+
+    my $fix = 0;
+    unless ($$subdir{Base}) {
+        # adjust base offset for current maker note position
+        $fix = $dataPos + $dirStart - $originalPos;
+        $subdirInfo{Base} += $fix;
+        $subdirInfo{DataPos} -= $fix;
+    }
+    if ($outfile) {
+        # rewrite the maker notes directory
+        my $fixup = $subdirInfo{Fixup} = new Image::ExifTool::Fixup;
+        my $oldChanged = $$exifTool{CHANGED};
+        my $buff = $exifTool->WriteDirectory(\%subdirInfo, $subTable);
+        # nothing to do if error writing directory or nothing changed
+        unless (defined $buff and $exifTool->{CHANGED} != $oldChanged) {
+            $exifTool->{CHANGED} = $oldChanged;
+            return 1;
+        }
+        # deleting maker notes if directory is empty
+        unless (length $buff) {
+            $$outfile = '';
+            return 1;
+        }
+        # apply a one-time fixup to offsets
+        if ($subdirInfo{Relative}) {
+            # shift all offsets to be relative to new base
+            my $baseShift = $dataPos + $dirStart + $$dirInfo{Base} - $subdirInfo{Base};
+            $fixup->{Shift} += $baseShift;
+        } else {
+            # shift offsets to position of original maker notes
+            $fixup->{Shift} += $originalPos;
+        }
+        $fixup->{Shift} += $loc;    # adjust for makernotes header
+        $fixup->ApplyFixup(\$buff); # fix up pointer offsets
+        # get copy of original Adobe header (6) and makernotes header ($loc)
+        my $header = substr($$dataPt, $start, 6 + $loc);
+        # add Adobe and makernotes headers to new directory
+        $$outfile = $header . $buff;
+    } else {
+        # parse the maker notes directory
+        $exifTool->ProcessDirectory(\%subdirInfo, $subTable, $$subdir{ProcessProc});
+        # extract maker notes as a block if specified
+        if ($exifTool->Options('MakerNotes') or
+            $exifTool->{REQ_TAG_LOOKUP}->{lc($$tagInfo{Name})})
+        {
+            my $val;
+            if ($$tagInfo{MakerNotes}) {
+                $subdirInfo{Base}     = $$dirInfo{Base} + $fix;
+                $subdirInfo{DataPos}  = $dataPos - $fix;
+                $subdirInfo{DirStart} = $dirStart;
+                $subdirInfo{DirLen}   = $dirLen;
+                # rebuild the maker notes to identify all offsets that require fixing up
+                $val = Image::ExifTool::Exif::RebuildMakerNotes($exifTool, $subTable, \%subdirInfo);
+                defined $val or $exifTool->Warn('Error rebuilding maker notes (may be corrupt)');
+            } else {
+                # extract this directory as a block if specified
+                return 1 unless $$tagInfo{Writable};
+            }
+            $val = substr($$dataPt, 20) unless defined $val;
+            $exifTool->FoundTag($tagInfo, $val);
+        }
+    }
+    return 1;
+}
+
+#------------------------------------------------------------------------------
+# Write Adobe information (calls appropriate ProcessProc to do the actual work)
+# Inputs: 0) ExifTool object ref, 1) source dirInfo ref, 2) tag table ref
+# Returns: new data block (may be empty if directory is deleted) or undef on error
+sub WriteAdobeStuff($$$)
+{
+    my ($exifTool, $dirInfo, $tagTablePtr) = @_;
+    $exifTool or return 1;    # allow dummy access
+    my $proc = $$dirInfo{Proc} || \&ProcessAdobeData;
+    my $buff;
+    $$dirInfo{OutFile} = \$buff;
+    &$proc($exifTool, $dirInfo, $tagTablePtr) or undef $buff;
+    return $buff;
+}
+
+1; # end
+
+__END__
+
+=head1 NAME
+
+Image::ExifTool::DNG.pm - Read DNG-specific information
+
+=head1 SYNOPSIS
+
+This module is used by Image::ExifTool
+
+=head1 DESCRIPTION
+
+This module contains routines required by Image::ExifTool to process
+information in DNG (Digital Negative) images.
+
+=head1 AUTHOR
+
+Copyright 2003-2007, Phil Harvey (phil at owl.phy.queensu.ca)
+
+This library is free software; you can redistribute it and/or modify it
+under the same terms as Perl itself.
+
+=head1 REFERENCES
+
+=over 4
+
+=item L<http://www.adobe.com/products/dng/pdfs/dng_spec.pdf>
+
+=back
+
+=head1 SEE ALSO
+
+L<Image::ExifTool::TagNames/DNG Tags>,
+L<Image::ExifTool::TagNames/EXIF Tags>,
+L<Image::ExifTool(3pm)|Image::ExifTool>
+
+=cut
Index: /gsdl/trunk/perllib/cpan/Image/ExifTool/Exif.pm
===================================================================
--- /gsdl/trunk/perllib/cpan/Image/ExifTool/Exif.pm	(revision 16842)
+++ /gsdl/trunk/perllib/cpan/Image/ExifTool/Exif.pm	(revision 16842)
@@ -0,0 +1,2952 @@
+#------------------------------------------------------------------------------
+# File:         Exif.pm
+#
+# Description:  Read EXIF meta information
+#
+# Revisions:    11/25/2003 - P. Harvey Created
+#               02/06/2004 - P. Harvey Moved processing functions from ExifTool
+#               03/19/2004 - P. Harvey Check PreviewImage for validity
+#               11/11/2004 - P. Harvey Split off maker notes into MakerNotes.pm
+#               12/13/2004 - P. Harvey Added AUTOLOAD to load write routines
+#
+# References:   0) http://www.exif.org/Exif2-2.PDF
+#               1) http://partners.adobe.com/asn/developer/pdfs/tn/TIFF6.pdf
+#               2) http://www.adobe.com/products/dng/pdfs/dng_spec.pdf
+#               3) http://www.awaresystems.be/imaging/tiff/tifftags.html
+#               4) http://www.remotesensing.org/libtiff/TIFFTechNote2.html
+#               5) http://www.asmail.be/msg0054681802.html
+#               6) http://park2.wakwak.com/~tsuruzoh/Computer/Digicams/exif-e.html
+#               7) http://www.fine-view.com/jp/lab/doc/ps6ffspecsv2.pdf
+#               8) http://www.ozhiker.com/electronics/pjmt/jpeg_info/meta.html
+#               9) http://hul.harvard.edu/jhove/tiff-tags.html
+#              10) http://partners.adobe.com/public/developer/en/tiff/TIFFPM6.pdf
+#              11) Robert Mucke private communication
+#              12) http://www.broomscloset.com/closet/photo/exif/TAG2000-22_DIS12234-2.PDF
+#              13) http://www.microsoft.com/whdc/xps/wmphoto.mspx
+#------------------------------------------------------------------------------
+
+package Image::ExifTool::Exif;
+
+use strict;
+use vars qw($VERSION $AUTOLOAD @formatSize @formatName %formatNumber
+            %lightSource %compression %photometricInterpretation %orientation);
+use Image::ExifTool qw(:DataAccess :Utils);
+use Image::ExifTool::MakerNotes;
+
+$VERSION = '2.42';
+
+sub ProcessExif($$$);
+sub WriteExif($$$);
+sub CheckExif($$$);
+sub RebuildMakerNotes($$$);
+sub EncodeExifText($$);
+sub ValidateIFD($;$);
+
+# byte sizes for the various EXIF format types below
+@formatSize = (undef,1,1,2,4,8,1,1,2,4,8,4,8,4,undef,undef,8,8,8);
+
+@formatName = (
+    undef, 'int8u', 'string', 'int16u',
+    'int32u', 'rational64u', 'int8s', 'undef',
+    'int16s', 'int32s', 'rational64s', 'float',
+    'double', 'ifd', undef, undef, # (14 is unicode, 15 is complex? http://remotesensing.org/libtiff/bigtiffdesign.html)
+    'int64u', 'int64s', 'ifd8', # (new BigTIFF formats)
+);
+
+# hash to look up EXIF format numbers by name
+# (format types are all lower case)
+%formatNumber = (
+    'int8u'         => 1,   # BYTE
+    'string'        => 2,   # ASCII
+    'int16u'        => 3,   # SHORT
+    'int32u'        => 4,   # LONG
+    'rational64u'   => 5,   # RATIONAL
+    'int8s'         => 6,   # SBYTE
+    'undef'         => 7,   # UNDEFINED
+    'binary'        => 7,   # (treat binary data as undef)
+    'int16s'        => 8,   # SSHORT
+    'int32s'        => 9,   # SLONG
+    'rational64s'   => 10,  # SRATIONAL
+    'float'         => 11,  # FLOAT
+    'double'        => 12,  # DOUBLE
+    'ifd'           => 13,  # IFD (with int32u format)
+    'int64u'        => 16,  # LONG8 [BigTIFF]
+    'int64s'        => 17,  # SLONG8 [BigTIFF]
+    'ifd8'          => 18,  # IFD8 (with int64u format) [BigTIFF]
+);
+
+# EXIF LightSource PrintConv values
+%lightSource = (
+    1 => 'Daylight',
+    2 => 'Fluorescent',
+    3 => 'Tungsten',
+    4 => 'Flash',
+    9 => 'Fine Weather',
+    10 => 'Cloudy',
+    11 => 'Shade',
+    12 => 'Daylight Fluorescent',
+    13 => 'Day White Fluorescent',
+    14 => 'Cool White Fluorescent',
+    15 => 'White Fluorescent',
+    17 => 'Standard Light A',
+    18 => 'Standard Light B',
+    19 => 'Standard Light C',
+    20 => 'D55',
+    21 => 'D65',
+    22 => 'D75',
+    23 => 'D50',
+    24 => 'ISO Studio Tungsten',
+    255 => 'Other',
+);
+
+%compression = (
+    1 => 'Uncompressed',
+    2 => 'CCITT 1D',
+    3 => 'T4/Group 3 Fax',
+    4 => 'T6/Group 4 Fax',
+    5 => 'LZW',
+    6 => 'JPEG (old-style)', #3
+    7 => 'JPEG', #4
+    8 => 'Adobe Deflate', #3
+    9 => 'JBIG B&W', #3
+    10 => 'JBIG Color', #3
+    32766 => 'Next', #3
+    32769 => 'Epson ERF Compressed', #PH
+    32771 => 'CCIRLEW', #3
+    32773 => 'PackBits',
+    32809 => 'Thunderscan', #3
+    32895 => 'IT8CTPAD', #3
+    32896 => 'IT8LW', #3
+    32897 => 'IT8MP', #3
+    32898 => 'IT8BL', #3
+    32908 => 'PixarFilm', #3
+    32909 => 'PixarLog', #3
+    32946 => 'Deflate', #3
+    32947 => 'DCS', #3
+    34661 => 'JBIG', #3
+    34676 => 'SGILog', #3
+    34677 => 'SGILog24', #3
+    34712 => 'JPEG 2000', #3
+    34713 => 'Nikon NEF Compressed', #PH
+    65000 => 'Kodak DCR Compressed', #PH
+    65535 => 'Pentax PEF Compressed', #Jens
+    
+);
+
+%photometricInterpretation = (
+    0 => 'WhiteIsZero',
+    1 => 'BlackIsZero',
+    2 => 'RGB',
+    3 => 'RGB Palette',
+    4 => 'Transparency Mask',
+    5 => 'CMYK',
+    6 => 'YCbCr',
+    8 => 'CIELab',
+    9 => 'ICCLab', #3
+    10 => 'ITULab', #3
+    32803 => 'Color Filter Array', #2
+    32844 => 'Pixar LogL', #3
+    32845 => 'Pixar LogLuv', #3
+    34892 => 'Linear Raw', #2
+);
+
+%orientation = (
+    1 => 'Horizontal (normal)',
+    2 => 'Mirror horizontal',
+    3 => 'Rotate 180',
+    4 => 'Mirror vertical',
+    5 => 'Mirror horizontal and rotate 270 CW',
+    6 => 'Rotate 90 CW',
+    7 => 'Mirror horizontal and rotate 90 CW',
+    8 => 'Rotate 270 CW',
+);
+
+# ValueConv that makes long values binary type
+my %longBin = (
+    ValueConv => 'length($val) > 64 ? \$val : $val',
+    ValueConvInv => '$val',
+);
+
+# main EXIF tag table
+%Image::ExifTool::Exif::Main = (
+    GROUPS => { 0 => 'EXIF', 1 => 'IFD0', 2 => 'Image'},
+    WRITE_PROC => \&WriteExif,
+    WRITE_GROUP => 'ExifIFD',   # default write group
+    0x1 => {
+        Name => 'InteropIndex',
+        Description => 'Interoperability Index',
+        PrintConv => {
+            R98 => 'R98 - DCF basic file (sRGB)',
+            R03 => 'R03 - DCF option file (Adobe RGB)',
+            THM => 'THM - DCF thumbnail file',
+        },
+    },
+    0x2 => {
+        Name => 'InteropVersion',
+        Description => 'Interoperability Version',
+    },
+    0x0b => { #PH
+        Name => 'ProcessingSoftware',
+        Notes => 'used by ACD Systems Digital Imaging',
+    },
+    0xfe => {
+        Name => 'SubfileType',
+        # set priority directory if this is the full resolution image
+        RawConv => '$self->SetPriorityDir() if $val == 0; $val',
+        PrintConv => {
+            0 => 'Full-resolution Image',
+            1 => 'Reduced-resolution image',
+            2 => 'Single page of multi-page image',
+            3 => 'Single page of multi-page reduced-resolution image',
+            4 => 'Transparency mask',
+            5 => 'Transparency mask of reduced-resolution image',
+            6 => 'Transparency mask of multi-page image',
+            7 => 'Transparency mask of reduced-resolution multi-page image',
+        },
+    },
+    0xff => {
+        Name => 'OldSubfileType',
+        # set priority directory if this is the full resolution image
+        RawConv => '$self->SetPriorityDir() if $val == 1; $val',
+        PrintConv => {
+            1 => 'Full-resolution image',
+            2 => 'Reduced-resolution image',
+            3 => 'Single page of multi-page image',
+        },
+    },
+    0x100 => {
+        Name => 'ImageWidth',
+        # even though Group 1 is set dynamically we need to register IFD1 once
+        # so it will show up in the group lists
+        Groups => { 1 => 'IFD1' },
+        # set Priority to zero so the value found in the first IFD (IFD0) doesn't
+        # get overwritten by subsequent IFD's (same for ImageHeight below)
+        Priority => 0,
+    },
+    0x101 => {
+        Name => 'ImageHeight',
+        Priority => 0,
+    },
+    0x102 => {
+        Name => 'BitsPerSample',
+        Priority => 0,
+    },
+    0x103 => {
+        Name => 'Compression',
+        PrintConv => \%compression,
+        Priority => 0,
+    },
+    0x106 => {
+        Name => 'PhotometricInterpretation',
+        PrintConv => \%photometricInterpretation,
+        Priority => 0,
+    },
+    0x107 => {
+        Name => 'Thresholding',
+        PrintConv => {
+            1 => 'No dithering or halftoning',
+            2 => 'Ordered dither or halftone',
+            3 => 'Randomized dither',
+        },
+    },
+    0x108 => 'CellWidth',
+    0x109 => 'CellLength',
+    0x10a => {
+        Name => 'FillOrder',
+        PrintConv => {
+            1 => 'Normal',
+            2 => 'Reversed',
+        },
+    },
+    0x10d => 'DocumentName',
+    0x10e => {
+        Name => 'ImageDescription',
+        Priority => 0,
+    },
+    0x10f => {
+        Name => 'Make',
+        Groups => { 2 => 'Camera' },
+        DataMember => 'CameraMake',
+        # save this value as an ExifTool member variable
+        RawConv => '$self->{CameraMake} = $val',
+    },
+    0x110 => {
+        Name => 'Model',
+        Description => 'Camera Model Name',
+        Groups => { 2 => 'Camera' },
+        DataMember => 'CameraModel',
+        # save this value as an ExifTool member variable
+        RawConv => '$self->{CameraModel} = $val',
+    },
+    0x111 => [
+        {
+            Condition => q[
+                ($self->{TIFF_TYPE} ne "CR2" or $self->{DIR_NAME} ne "IFD0") and
+                ($self->{TIFF_TYPE} ne "DNG" or $self->{DIR_NAME} !~ /^SubIFD[12]$/)
+            ],
+            Name => 'StripOffsets',
+            IsOffset => 1,
+            OffsetPair => 0x117,  # point to associated byte counts
+            ValueConv => 'length($val) > 32 ? \$val : $val',
+        },
+        {
+            Condition => '$self->{DIR_NAME} eq "IFD0"',
+            Name => 'PreviewImageStart',
+            IsOffset => 1,
+            OffsetPair => 0x117,
+            Notes => q{
+                PreviewImageStart in IFD0 of CR2 images and SubIFD1 of DNG images, and
+                JpgFromRawStart in SubIFD2 of DNG images
+            },
+            DataTag => 'PreviewImage',
+            Writable => 'int32u',
+            WriteGroup => 'IFD0',
+            WriteCondition => '$self->{TIFF_TYPE} eq "CR2"',
+            Protected => 2,
+        },
+        {
+            Condition => '$self->{DIR_NAME} eq "SubIFD1"',
+            Name => 'PreviewImageStart',
+            IsOffset => 1,
+            OffsetPair => 0x117,
+            DataTag => 'PreviewImage',
+            Writable => 'int32u',
+            WriteGroup => 'SubIFD1',
+            WriteCondition => '$self->{TIFF_TYPE} eq "DNG"',
+            Protected => 2,
+        },
+        {
+            Name => 'JpgFromRawStart',
+            IsOffset => 1,
+            OffsetPair => 0x117,
+            DataTag => 'JpgFromRaw',
+            Writable => 'int32u',
+            WriteGroup => 'SubIFD2',
+            WriteCondition => '$self->{TIFF_TYPE} eq "DNG"',
+            Protected => 2,
+        },
+    ],
+    0x112 => {
+        Name => 'Orientation',
+        PrintConv => \%orientation,
+        Priority => 0,  # so IFD1 doesn't take precedence
+    },
+    0x115 => {
+        Name => 'SamplesPerPixel',
+        Priority => 0,
+    },
+    0x116 => {
+        Name => 'RowsPerStrip',
+        Priority => 0,
+    },
+    0x117 => [
+        {
+            Condition => q[
+                ($self->{TIFF_TYPE} ne "CR2" or $self->{DIR_NAME} ne "IFD0") and
+                ($self->{TIFF_TYPE} ne "DNG" or $self->{DIR_NAME} !~ /^SubIFD[12]$/)
+            ],
+            Name => 'StripByteCounts',
+            OffsetPair => 0x111,   # point to associated offset
+            ValueConv => 'length($val) > 32 ? \$val : $val',
+        },
+        {
+            Condition => '$self->{DIR_NAME} eq "IFD0"',
+            Name => 'PreviewImageLength',
+            OffsetPair => 0x111,
+            Notes => q{
+                PreviewImageLength in IFD0 of CR2 images and SubIFD1 of DNG images, and
+                JpgFromRawLength in SubIFD2 of DNG images
+            },
+            DataTag => 'PreviewImage',
+            Writable => 'int32u',
+            WriteGroup => 'IFD0',
+            WriteCondition => '$self->{TIFF_TYPE} eq "CR2"',
+            Protected => 2,
+        },
+        {
+            Condition => '$self->{DIR_NAME} eq "SubIFD1"',
+            Name => 'PreviewImageLength',
+            OffsetPair => 0x111,
+            DataTag => 'PreviewImage',
+            Writable => 'int32u',
+            WriteGroup => 'SubIFD1',
+            WriteCondition => '$self->{TIFF_TYPE} eq "DNG"',
+            Protected => 2,
+        },
+        {
+            Name => 'JpgFromRawLength',
+            OffsetPair => 0x111,
+            DataTag => 'JpgFromRaw',
+            Writable => 'int32u',
+            WriteGroup => 'SubIFD2',
+            WriteCondition => '$self->{TIFF_TYPE} eq "DNG"',
+            Protected => 2,
+        },
+    ],
+    0x118 => 'MinSampleValue',
+    0x119 => 'MaxSampleValue',
+    0x11a => {
+        Name => 'XResolution',
+        Priority => 0,  # so IFD0 takes priority over IFD1
+    },
+    0x11b => {
+        Name => 'YResolution',
+        Priority => 0,
+    },
+    0x11c => {
+        Name => 'PlanarConfiguration',
+        PrintConv => {
+            1 => 'Chunky',
+            2 => 'Planar',
+        },
+        Priority => 0,
+    },
+    0x11d => 'PageName',
+    0x11e => 'XPosition',
+    0x11f => 'YPosition',
+    0x120 => {
+        Name => 'FreeOffsets',
+        IsOffset => 1,
+        OffsetPair => 0x121,
+        ValueConv => 'length($val) > 32 ? \$val : $val',
+    },
+    0x121 => {
+        Name => 'FreeByteCounts',
+        OffsetPair => 0x120,
+        ValueConv => 'length($val) > 32 ? \$val : $val',
+    },
+    0x122 => {
+        Name => 'GrayResponseUnit',
+        PrintConv => { #3
+            1 => 0.1,
+            2 => 0.001,
+            3 => 0.0001,
+            4 => 0.00001,
+            5 => 0.000001,
+        },
+    },
+    0x123 => {
+        Name => 'GrayResponseCurve',
+        Binary => 1,
+    },
+    0x124 => {
+        Name => 'T4Options',
+        PrintConv => { BITMASK => {
+            0 => '2-Dimensional encoding',
+            1 => 'Uncompressed',
+            2 => 'Fill bits added',
+        } }, #3
+    },
+    0x125 => {
+        Name => 'T6Options',
+        PrintConv => { BITMASK => {
+            1 => 'Uncompressed',
+        } }, #3
+    },
+    0x128 => {
+        Name => 'ResolutionUnit',
+        PrintConv => {
+            1 => 'None',
+            2 => 'inches',
+            3 => 'cm',
+        },
+        Priority => 0,
+    },
+    0x129 => 'PageNumber',
+    0x12c => 'ColorResponseUnit', #9
+    0x12d => {
+        Name => 'TransferFunction',
+        Binary => 1,
+    },
+    0x131 => {
+        Name => 'Software',
+    },
+    0x132 => {
+        Name => 'ModifyDate',
+        Groups => { 2 => 'Time' },
+        PrintConv => '$self->ConvertDateTime($val)',
+    },
+    0x13b => {
+        Name => 'Artist',
+        Groups => { 2 => 'Author' },
+    },
+    0x13c => 'HostComputer',
+    0x13d => {
+        Name => 'Predictor',
+        PrintConv => {
+            1 => 'None',
+            2 => 'Horizontal differencing',
+        },
+    },
+    0x13e => {
+        Name => 'WhitePoint',
+        Groups => { 2 => 'Camera' },
+    },
+    0x13f => {
+        Name => 'PrimaryChromaticities',
+        Priority => 0,
+    },
+    0x140 => {
+        Name => 'ColorMap',
+        Format => 'binary',
+        Binary => 1,
+    },
+    0x141 => 'HalftoneHints',
+    0x142 => 'TileWidth',
+    0x143 => 'TileLength',
+    0x144 => {
+        Name => 'TileOffsets',
+        IsOffset => 1,
+        OffsetPair => 0x145,
+        ValueConv => 'length($val) > 32 ? \$val : $val',
+    },
+    0x145 => {
+        Name => 'TileByteCounts',
+        OffsetPair => 0x144,
+        ValueConv => 'length($val) > 32 ? \$val : $val',
+    },
+    0x146 => 'BadFaxLines', #3
+    0x147 => { #3
+        Name => 'CleanFaxData',
+        PrintConv => {
+            0 => 'Clean',
+            1 => 'Regenerated',
+            2 => 'Unclean',
+        },
+    },
+    0x148 => 'ConsecutiveBadFaxLines', #3
+    0x14a => {
+        Name => 'SubIFD',
+        Groups => { 1 => 'SubIFD' },
+        Flags => 'SubIFD',
+        SubDirectory => {
+            Start => '$val',
+            MaxSubdirs => 3,
+        },
+    },
+    0x14c => {
+        Name => 'InkSet',
+        PrintConv => { #3
+            1 => 'CMYK',
+            2 => 'Not CMYK',
+        },
+    },
+    0x14d => 'InkNames', #3
+    0x14e => 'NumberofInks', #3
+    0x150 => 'DotRange',
+    0x151 => 'TargetPrinter',
+    0x152 => 'ExtraSamples',
+    0x153 => {
+        Name => 'SampleFormat',
+        PrintConv => {
+            1 => 'Unsigned integer',
+            2 => "Two's complement signed integer",
+            3 => 'IEEE floating point',
+            4 => 'Undefined',
+            5 => 'Complex integer', #3
+            6 => 'IEEE floating point', #3
+        },
+    },
+    0x154 => 'SMinSampleValue',
+    0x155 => 'SMaxSampleValue',
+    0x156 => 'TransferRange',
+    0x157 => 'ClipPath', #3
+    0x158 => 'XClipPathUnits', #3
+    0x159 => 'YClipPathUnits', #3
+    0x15a => { #3
+        Name => 'Indexed',
+        PrintConv => { 0 => 'Not indexed', 1 => 'Indexed' },
+    },
+    0x15b => {
+        Name => 'JPEGTables',
+        Binary => 1,
+    },
+    0x15f => { #10
+        Name => 'OPIProxy',
+        PrintConv => {
+            0 => 'Higher resolution image does not exist',
+            1 => 'Higher resolution image exists',
+        },
+    },
+    0x190 => { #3
+        Name => 'GlobalParametersIFD',
+        Groups => { 1 => 'GlobParamIFD' },
+        Flags => 'SubIFD',
+        SubDirectory => {
+            DirName => 'GlobParamIFD',
+            Start => '$val',
+        },
+    },
+    0x191 => { #3
+        Name => 'ProfileType',
+        PrintConv => { 0 => 'Unspecified', 1 => 'Group 3 FAX' },
+    },
+    0x192 => { #3
+        Name => 'FaxProfile',
+        PrintConv => {
+            0 => 'Unknown',
+            1 => 'Minimal B&W lossless, S',
+            2 => 'Extended B&W lossless, F',
+            3 => 'Lossless JBIG B&W, J',
+            4 => 'Lossy color and grayscale, C',
+            5 => 'Lossless color and grayscale, L',
+            6 => 'Mixed raster content, M',
+        },
+    },
+    0x193 => { #3
+        Name => 'CodingMethods',
+        PrintConv => { BITMASK => {
+            0 => 'Unspecified compression',
+            1 => 'Modified Huffman',
+            2 => 'Modified Read',
+            3 => 'Modified MR',
+            4 => 'JBIG',
+            5 => 'Baseline JPEG',
+            6 => 'JBIG color',
+        } },
+    },
+    0x194 => 'VersionYear', #3
+    0x195 => 'ModeNumber', #3
+    0x1b1 => 'Decode', #3
+    0x1b2 => 'DefaultImageColor', #3
+    0x200 => {
+        Name => 'JPEGProc',
+        PrintConv => {
+            1 => 'Baseline',
+            14 => 'Lossless',
+        },
+    },
+    0x201 => [
+        {
+            Name => 'ThumbnailOffset',
+            # thumbnail is found in IFD1 of JPEG and TIFF images, and
+            # IFD0 of EXIF information in FujiFilm AVI (RIFF) videos
+            Condition => q{
+                $self->{DIR_NAME} eq 'IFD1' or
+                ($self->{FILE_TYPE} eq 'RIFF' and $self->{DIR_NAME} eq 'IFD0')
+            },
+            IsOffset => 1,
+            OffsetPair => 0x202,
+            DataTag => 'ThumbnailImage',
+            Writable => 'int32u',
+            WriteGroup => 'IFD1',
+            WriteCondition => '$$self{FILE_TYPE} ne "TIFF"',
+            Protected => 2,
+        },
+        {
+            Name => 'PreviewImageStart',
+            Condition => '$self->{DIR_NAME} eq "MakerNotes"',
+            IsOffset => 1,
+            OffsetPair => 0x202,
+            DataTag => 'PreviewImage',
+            Writable => 'int32u',
+            WriteGroup => 'MakerNotes',
+            Protected => 2,
+        },
+        {
+            Name => 'JpgFromRawStart',
+            Condition => '$self->{DIR_NAME} eq "SubIFD"',
+            IsOffset => 1,
+            OffsetPair => 0x202,
+            DataTag => 'JpgFromRaw',
+            Writable => 'int32u',
+            WriteGroup => 'SubIFD',
+            # JpgFromRaw is in SubIFD of NEF files
+            WriteCondition => '$self->{TIFF_TYPE} eq "NEF"',
+            Protected => 2,
+        },
+        {
+            Name => 'JpgFromRawStart',
+            Condition => '$self->{DIR_NAME} eq "IFD2"',
+            IsOffset => 1,
+            OffsetPair => 0x202,
+            DataTag => 'JpgFromRaw',
+            Writable => 'int32u',
+            WriteGroup => 'IFD2',
+            # JpgFromRaw is in IFD2 of PEF files
+            WriteCondition => '$self->{TIFF_TYPE} eq "PEF"',
+            Protected => 2,
+        },
+        {
+            Name => 'OtherImageStart',
+            IsOffset => 1,
+            OffsetPair => 0x202,
+        },
+    ],
+    0x202 => [
+        {
+            Name => 'ThumbnailLength',
+            Condition => q{
+                $self->{DIR_NAME} eq 'IFD1' or
+                ($self->{FILE_TYPE} eq 'RIFF' and $self->{DIR_NAME} eq 'IFD0')
+            },
+            OffsetPair => 0x201,
+            DataTag => 'ThumbnailImage',
+            Writable => 'int32u',
+            WriteGroup => 'IFD1',
+            WriteCondition => '$$self{FILE_TYPE} ne "TIFF"',
+            Protected => 2,
+        },
+        {
+            Name => 'PreviewImageLength',
+            Condition => '$self->{DIR_NAME} eq "MakerNotes"',
+            OffsetPair => 0x201,
+            DataTag => 'PreviewImage',
+            Writable => 'int32u',
+            WriteGroup => 'MakerNotes',
+            Protected => 2,
+        },
+        {
+            Name => 'JpgFromRawLength',
+            Condition => '$self->{DIR_NAME} eq "SubIFD"',
+            OffsetPair => 0x201,
+            DataTag => 'JpgFromRaw',
+            Writable => 'int32u',
+            WriteGroup => 'SubIFD',
+            WriteCondition => '$self->{TIFF_TYPE} eq "NEF"',
+            Protected => 2,
+        },
+        {
+            Name => 'JpgFromRawLength',
+            Condition => '$self->{DIR_NAME} eq "IFD2"',
+            OffsetPair => 0x201,
+            DataTag => 'JpgFromRaw',
+            Writable => 'int32u',
+            WriteGroup => 'IFD2',
+            WriteCondition => '$self->{TIFF_TYPE} eq "PEF"',
+            Protected => 2,
+        },
+        {
+            Name => 'OtherImageLength',
+            OffsetPair => 0x201,
+        },
+    ],
+    0x203 => 'JPEGRestartInterval',
+    0x205 => 'JPEGLosslessPredictors',
+    0x206 => 'JPEGPointTransforms',
+    0x207 => 'JPEGQTables',
+    0x208 => 'JPEGDCTables',
+    0x209 => 'JPEGACTables',
+    0x211 => {
+        Name => 'YCbCrCoefficients',
+        Priority => 0,
+    },
+    0x212 => {
+        Name => 'YCbCrSubSampling',
+        PrintConv => {
+            '1 1' => 'YCbCr4:4:4 (1 1)', #PH
+            '2 1' => 'YCbCr4:2:2 (2 1)', #6
+            '2 2' => 'YCbCr4:2:0 (2 2)', #6
+            '4 1' => 'YCbCr4:1:1 (4 1)', #6
+            '4 2' => 'YCbCr4:1:0 (4 2)', #PH
+            '1 2' => 'YCbCr4:4:0 (1 2)', #PH
+        },
+        Priority => 0,
+    },
+    0x213 => {
+        Name => 'YCbCrPositioning',
+        PrintConv => {
+            1 => 'Centered',
+            2 => 'Co-sited',
+        },
+        Priority => 0,
+    },
+    0x214 => {
+        Name => 'ReferenceBlackWhite',
+        Priority => 0,
+    },
+    0x22f => 'StripRowCounts',
+    0x2bc => {
+        Name => 'ApplicationNotes',
+        # this could be an XMP block
+        SubDirectory => {
+            DirName => 'XMP',
+            TagTable => 'Image::ExifTool::XMP::Main',
+        },
+    },
+    0x1000 => 'RelatedImageFileFormat',
+    0x1001 => 'RelatedImageWidth',
+    0x1002 => 'RelatedImageLength',
+    # (0x474x tags written by MicrosoftPhoto)
+    0x4746 => 'Rating', #PH
+    0x4749 => 'RatingPercent', #PH
+    0x800d => 'ImageID', #10
+    0x80a4 => 'WangAnnotation',
+    0x80e3 => 'Matteing', #9
+    0x80e4 => 'DataType', #9
+    0x80e5 => 'ImageDepth', #9
+    0x80e6 => 'TileDepth', #9
+    0x827d => 'Model2',
+    0x828d => 'CFARepeatPatternDim',
+    0x828e => 'CFAPattern2',
+    0x828f => {
+        Name => 'BatteryLevel',
+        Groups => { 2 => 'Camera' },
+    },
+    0x8298 => {
+        Name => 'Copyright',
+        Groups => { 2 => 'Author' },
+    },
+    0x829a => {
+        Name => 'ExposureTime',
+        PrintConv => 'Image::ExifTool::Exif::PrintExposureTime($val)',
+    },
+    0x829d => {
+        Name => 'FNumber',
+        PrintConv => 'sprintf("%.1f",$val)',
+    },
+    0x82a5 => { #3
+        Name => 'MDFileTag',
+        Notes => 'tags 0x82a5-0x82ac are used in Molecular Dynamics GEL files',
+    },
+    0x82a6 => 'MDScalePixel', #3
+    0x82a7 => 'MDColorTable', #3
+    0x82a8 => 'MDLabName', #3
+    0x82a9 => 'MDSampleInfo', #3
+    0x82aa => 'MDPrepDate', #3
+    0x82ab => 'MDPrepTime', #3
+    0x82ac => 'MDFileUnits', #3
+    0x830e => 'PixelScale',
+    0x83bb => {
+        Name => 'IPTC-NAA',
+        SubDirectory => {
+            DirName => 'IPTC',
+            TagTable => 'Image::ExifTool::IPTC::Main',
+        },
+    },
+    0x847e => 'IntergraphPacketData', #3
+    0x847f => 'IntergraphFlagRegisters', #3
+    0x8480 => 'IntergraphMatrix',
+    0x8482 => {
+        Name => 'ModelTiePoint',
+        Groups => { 2 => 'Location' },
+    },
+    0x84e0 => 'Site', #9
+    0x84e1 => 'ColorSequence', #9
+    0x84e2 => 'IT8Header', #9
+    0x84e3 => 'RasterPadding', #9
+    0x84e4 => 'BitsPerRunLength', #9
+    0x84e5 => 'BitsPerExtendedRunLength', #9
+    0x84e6 => 'ColorTable', #9
+    0x84e7 => 'ImageColorIndicator', #9
+    0x84e8 => 'BackgroundColorIndicator', #9
+    0x84e9 => 'ImageColorValue', #9
+    0x84ea => 'BackgroundColorValue', #9
+    0x84eb => 'PixelIntensityRange', #9
+    0x84ec => 'TransparencyIndicator', #9
+    0x84ed => 'ColorCharacterization', #9
+    0x84ee => 'HCUsage', #9
+    0x8546 => { #11
+        Name => 'SEMInfo',
+        Notes => 'found in some scanning electron microscope images',
+    },
+    0x8568 => {
+        Name => 'AFCP_IPTC',
+        SubDirectory => {
+            DirName => 'IPTC2', # change name because this isn't the IPTC we want to write
+            TagTable => 'Image::ExifTool::IPTC::Main',
+        },
+    },
+    0x85d8 => {
+        Name => 'ModelTransform',
+        Groups => { 2 => 'Location' },
+    },
+    0x8606 => {
+        Name => 'LeafData',
+        Format => 'undef',    # avoid converting huge block to string of int8u's!
+        SubDirectory => {
+            DirName => 'LeafIFD',
+            TagTable => 'Image::ExifTool::Leaf::Main',
+        },
+    },
+    0x8649 => {
+        Name => 'PhotoshopSettings',
+        Format => 'binary',
+        SubDirectory => {
+            DirName => 'Photoshop',
+            TagTable => 'Image::ExifTool::Photoshop::Main',
+        },
+    },
+    0x8769 => {
+        Name => 'ExifOffset',
+        Groups => { 1 => 'ExifIFD' },
+        Flags => 'SubIFD',
+        SubDirectory => {
+            DirName => 'ExifIFD',
+            Start => '$val',
+        },
+    },
+    0x8773 => {
+        Name => 'ICC_Profile',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::ICC_Profile::Main',
+        },
+    },
+    0x87ac => 'ImageLayer',
+    0x87af => {
+        Name => 'GeoTiffDirectory',
+        Format => 'binary',
+        Binary => 1,
+    },
+    0x87b0 => {
+        Name => 'GeoTiffDoubleParams',
+        Format => 'binary',
+        Binary => 1,
+    },
+    0x87b1 => {
+        Name => 'GeoTiffAsciiParams',
+        Binary => 1,
+    },
+    0x8822 => {
+        Name => 'ExposureProgram',
+        Groups => { 2 => 'Camera' },
+        PrintConv => {
+            1 => 'Manual',
+            2 => 'Program AE',
+            3 => 'Aperture-priority AE',
+            4 => 'Shutter speed priority AE',
+            5 => 'Creative (Slow speed)',
+            6 => 'Action (High speed)',
+            7 => 'Portrait',
+            8 => 'Landscape',
+        },
+    },
+    0x8824 => {
+        Name => 'SpectralSensitivity',
+        Groups => { 2 => 'Camera' },
+    },
+    0x8825 => {
+        Name => 'GPSInfo',
+        Groups => { 1 => 'GPS' },
+        Flags => 'SubIFD',
+        SubDirectory => {
+            DirName => 'GPS',
+            TagTable => 'Image::ExifTool::GPS::Main',
+            Start => '$val',
+        },
+    },
+    0x8827 => 'ISO',
+    0x8828 => {
+        Name => 'Opto-ElectricConvFactor',
+        Binary => 1,
+    },
+    0x8829 => 'Interlace',
+    0x882a => 'TimeZoneOffset',
+    0x882b => 'SelfTimerMode',
+    0x885c => 'FaxRecvParams', #9
+    0x885d => 'FaxSubAddress', #9
+    0x885e => 'FaxRecvTime', #9
+    0x888a => { #PH
+        Name => 'LeafSubIFD',
+        Format => 'int32u',     # Leaf incorrectly uses 'undef' format!
+        Groups => { 1 => 'LeafSubIFD' },
+        Flags => 'SubIFD',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::Leaf::SubIFD',
+            Start => '$val',
+        },
+    },
+    0x9000 => 'ExifVersion',
+    0x9003 => {
+        Name => 'DateTimeOriginal',
+        Description => 'Date/Time Original',
+        Groups => { 2 => 'Time' },
+        PrintConv => '$self->ConvertDateTime($val)',
+    },
+    0x9004 => {
+        Name => 'CreateDate',
+        Groups => { 2 => 'Time' },
+        PrintConv => '$self->ConvertDateTime($val)',
+    },
+    0x9101 => {
+        Name => 'ComponentsConfiguration',
+        PrintConv => '$_=$val;s/\0.*//s;tr/\x01-\x06/YbrRGB/;s/b/Cb/g;s/r/Cr/g;$_',
+    },
+    0x9102 => 'CompressedBitsPerPixel',
+    0x9201 => {
+        Name => 'ShutterSpeedValue',
+        ValueConv => 'abs($val)<100 ? 1/(2**$val) : 0',
+        PrintConv => 'Image::ExifTool::Exif::PrintExposureTime($val)',
+    },
+    0x9202 => {
+        Name => 'ApertureValue',
+        ValueConv => '2 ** ($val / 2)',
+        PrintConv => 'sprintf("%.1f",$val)',
+    },
+    0x9203 => 'BrightnessValue',
+    0x9204 => {
+        Name => 'ExposureCompensation',
+        PrintConv => 'Image::ExifTool::Exif::ConvertFraction($val)',
+    },
+    0x9205 => {
+        Name => 'MaxApertureValue',
+        Groups => { 2 => 'Camera' },
+        ValueConv => '2 ** ($val / 2)',
+        PrintConv => 'sprintf("%.1f",$val)',
+    },
+    0x9206 => {
+        Name => 'SubjectDistance',
+        Groups => { 2 => 'Camera' },
+        PrintConv => '$val eq "inf" ? $val : "${val} m"',
+    },
+    0x9207 => {
+        Name => 'MeteringMode',
+        Groups => { 2 => 'Camera' },
+        PrintConv => {
+            1 => 'Average',
+            2 => 'Center-weighted average',
+            3 => 'Spot',
+            4 => 'Multi-spot',
+            5 => 'Multi-segment',
+            6 => 'Partial',
+            255 => 'Other',
+        },
+    },
+    0x9208 => {
+        Name => 'LightSource',
+        Groups => { 2 => 'Camera' },
+        PrintConv => \%lightSource,
+    },
+    0x9209 => {
+        Name => 'Flash',
+        Groups => { 2 => 'Camera' },
+        Flags => 'PrintHex',
+        PrintConv => {
+            0x00 => 'No Flash',
+            0x01 => 'Fired',
+            0x05 => 'Fired, Return not detected',
+            0x07 => 'Fired, Return detected',
+            0x08 => 'On, Did not fire', # not charged up?
+            0x09 => 'On',
+            0x0d => 'On, Return not detected',
+            0x0f => 'On, Return detected',
+            0x10 => 'Off',
+            0x14 => 'Off, Did not fire, Return not detected',
+            0x18 => 'Auto, Did not fire',
+            0x19 => 'Auto, Fired',
+            0x1d => 'Auto, Fired, Return not detected',
+            0x1f => 'Auto, Fired, Return detected',
+            0x20 => 'No flash function',
+            0x30 => 'Off, No flash function',
+            0x41 => 'Fired, Red-eye reduction',
+            0x45 => 'Fired, Red-eye reduction, Return not detected',
+            0x47 => 'Fired, Red-eye reduction, Return detected',
+            0x49 => 'On, Red-eye reduction',
+            0x4d => 'On, Red-eye reduction, Return not detected',
+            0x4f => 'On, Red-eye reduction, Return detected',
+            0x50 => 'Off, Red-eye reduction',
+            0x58 => 'Auto, Did not fire, Red-eye reduction',
+            0x59 => 'Auto, Fired, Red-eye reduction',
+            0x5d => 'Auto, Fired, Red-eye reduction, Return not detected',
+            0x5f => 'Auto, Fired, Red-eye reduction, Return detected',
+        },
+    },
+    0x920a => {
+        Name => 'FocalLength',
+        Groups => { 2 => 'Camera' },
+        PrintConv => 'sprintf("%.1fmm",$val)',
+    },
+    # Note: tags 0x920b-0x9217 are duplicates of 0xa20b-0xa217
+    # (The TIFF standard uses 0xa2xx, but you'll find both in images)
+    0x920b => {
+        Name => 'FlashEnergy',
+        Groups => { 2 => 'Camera' },
+    },
+    0x920c => 'SpatialFrequencyResponse',   # ?? (not in Fuji images - PH)
+    0x920d => 'Noise',
+    0x920e => 'FocalPlaneXResolution',
+    0x920f => 'FocalPlaneYResolution',
+    0x9210 => {
+        Name => 'FocalPlaneResolutionUnit',
+        Groups => { 2 => 'Camera' },
+        PrintConv => {
+            1 => 'None',
+            2 => 'inches',
+            3 => 'cm',
+            4 => 'mm',
+            5 => 'um',
+        },
+    },
+    0x9211 => 'ImageNumber',
+    0x9212 => { #12
+        Name => 'SecurityClassification',
+        PrintConv => {
+            T => 'Top Secret',
+            S => 'Secret',
+            C => 'Confidential',
+            R => 'Restricted',
+            U => 'Unclassified',
+        },
+    },
+    0x9213 => 'ImageHistory',
+    0x9214 => {
+        Name => 'SubjectLocation',
+        Groups => { 2 => 'Camera' },
+    },
+    0x9215 => 'ExposureIndex',
+    0x9216 => 'TIFF-EPStandardID',
+    0x9217 => {
+        Name => 'SensingMethod',
+        Groups => { 2 => 'Camera' },
+        PrintConv => {
+            1 => 'Monochrome area', #12
+            2 => 'One-chip color area',
+            3 => 'Two-chip color area',
+            4 => 'Three-chip color area',
+            5 => 'Color sequential area',
+            6 => 'Monochrome linear', #12
+            7 => 'Trilinear',
+            8 => 'Color sequential linear',
+        },
+    },
+    0x9213 => 'ImageHistory',
+    0x923f => 'StoNits', #9
+    # handle maker notes as a conditional list
+    0x927c => \@Image::ExifTool::MakerNotes::Main,
+    0x9286 => {
+        Name => 'UserComment',
+        PrintConv => 'Image::ExifTool::Exif::ConvertExifText($self,$val)',
+    },
+    0x9290 => {
+        Name => 'SubSecTime',
+        Groups => { 2 => 'Time' },
+    },
+    0x9291 => {
+        Name => 'SubSecTimeOriginal',
+        Groups => { 2 => 'Time' },
+    },
+    0x9292 => {
+        Name => 'SubSecTimeDigitized',
+        Groups => { 2 => 'Time' },
+    },
+    0x935c => { #3
+        Name => 'ImageSourceData',
+        Binary => 1,
+    },
+    0x9c9b => {
+        Name => 'XPTitle',
+        Format => 'undef',
+        ValueConv => '$self->Unicode2Charset($val,"II")',
+    },
+    0x9c9c => {
+        Name => 'XPComment',
+        Format => 'undef',
+        ValueConv => '$self->Unicode2Charset($val,"II")',
+    },
+    0x9c9d => {
+        Name => 'XPAuthor',
+        Groups => { 2 => 'Author' },
+        Format => 'undef',
+        ValueConv => '$self->Unicode2Charset($val,"II")',
+    },
+    0x9c9e => {
+        Name => 'XPKeywords',
+        Format => 'undef',
+        ValueConv => '$self->Unicode2Charset($val,"II")',
+    },
+    0x9c9f => {
+        Name => 'XPSubject',
+        Format => 'undef',
+        ValueConv => '$self->Unicode2Charset($val,"II")',
+    },
+    0xa000 => 'FlashpixVersion',
+    0xa001 => {
+        Name => 'ColorSpace',
+        Notes => q{
+            the value of 2 is not standard EXIF.  Instead, an Adobe RGB image is
+            indicated by "Uncalibrated" with an InteropIndex of "R03"'
+        },
+        PrintConv => {
+            1 => 'sRGB',
+            2 => 'Adobe RGB',
+            0xffff => 'Uncalibrated',
+        },
+    },
+    0xa002 => 'ExifImageWidth',
+    0xa003 => 'ExifImageLength',
+    0xa004 => 'RelatedSoundFile',
+    0xa005 => {
+        Name => 'InteropOffset',
+        Groups => { 1 => 'InteropIFD' },
+        Flags => 'SubIFD',
+        Description => 'Interoperability Offset',
+        SubDirectory => {
+            DirName => 'InteropIFD',
+            Start => '$val',
+        },
+    },
+    0xa20b => {
+        Name => 'FlashEnergy',
+        Groups => { 2 => 'Camera' },
+    },
+    0xa20c => {
+        Name => 'SpatialFrequencyResponse',
+        PrintConv => 'Image::ExifTool::Exif::PrintSFR($val)',
+    },
+    0xa20d => 'Noise',
+    0xa20e => { Name => 'FocalPlaneXResolution', Groups => { 2 => 'Camera' } },
+    0xa20f => { Name => 'FocalPlaneYResolution', Groups => { 2 => 'Camera' } },
+    0xa210 => {
+        Name => 'FocalPlaneResolutionUnit',
+        Groups => { 2 => 'Camera' },
+        PrintConv => {
+            1 => 'None', # (not standard EXIF)
+            2 => 'inches',
+            3 => 'cm',
+            4 => 'mm',   # (not standard EXIF)
+            5 => 'um',   # (not standard EXIF)
+        },
+    },
+    0xa211 => 'ImageNumber',
+    0xa212 => 'SecurityClassification',
+    0xa213 => 'ImageHistory',
+    0xa214 => {
+        Name => 'SubjectLocation',
+        Groups => { 2 => 'Camera' },
+    },
+    0xa215 => 'ExposureIndex',
+    0xa216 => 'TIFF-EPStandardID',
+    0xa217 => {
+        Name => 'SensingMethod',
+        Groups => { 2 => 'Camera' },
+        PrintConv => {
+            1 => 'Not defined',
+            2 => 'One-chip color area',
+            3 => 'Two-chip color area',
+            4 => 'Three-chip color area',
+            5 => 'Color sequential area',
+            7 => 'Trilinear',
+            8 => 'Color sequential linear',
+        },
+    },
+    0xa300 => {
+        Name => 'FileSource',
+        PrintConv => {
+            1 => 'Film Scanner',
+            2 => 'Reflection Print Scanner',
+            3 => 'Digital Camera',
+            # handle the case where Sigma incorrectly gives this tag a count of 4
+            "\3\0\0\0" => 'Sigma Digital Camera',
+        },
+    },
+    0xa301 => {
+        Name => 'SceneType',
+        PrintConv => {
+            1 => 'Directly photographed',
+        },
+    },
+    0xa302 => {
+        Name => 'CFAPattern',
+        PrintConv => 'Image::ExifTool::Exif::PrintCFAPattern($val)',
+    },
+    0xa401 => {
+        Name => 'CustomRendered',
+        PrintConv => {
+            0 => 'Normal',
+            1 => 'Custom',
+        },
+    },
+    0xa402 => {
+        Name => 'ExposureMode',
+        Groups => { 2 => 'Camera' },
+        PrintConv => {
+            0 => 'Auto',
+            1 => 'Manual',
+            2 => 'Auto bracket',
+        },
+    },
+    0xa403 => {
+        Name => 'WhiteBalance',
+        Groups => { 2 => 'Camera' },
+        # set Priority to zero to keep this WhiteBalance from overriding the
+        # MakerNotes WhiteBalance, since the MakerNotes WhiteBalance and is more
+        # accurate and contains more information (if it exists)
+        Priority => 0,
+        PrintConv => {
+            0 => 'Auto',
+            1 => 'Manual',
+        },
+    },
+    0xa404 => {
+        Name => 'DigitalZoomRatio',
+        Groups => { 2 => 'Camera' },
+    },
+    0xa405 => {
+        Name => 'FocalLengthIn35mmFormat',
+        Groups => { 2 => 'Camera' },
+        PrintConv => '"${val}mm"',
+    },
+    0xa406 => {
+        Name => 'SceneCaptureType',
+        Groups => { 2 => 'Camera' },
+        PrintConv => {
+            0 => 'Standard',
+            1 => 'Landscape',
+            2 => 'Portrait',
+            3 => 'Night',
+        },
+    },
+    0xa407 => {
+        Name => 'GainControl',
+        Groups => { 2 => 'Camera' },
+        PrintConv => {
+            0 => 'None',
+            1 => 'Low gain up',
+            2 => 'High gain up',
+            3 => 'Low gain down',
+            4 => 'High gain down',
+        },
+    },
+    0xa408 => {
+        Name => 'Contrast',
+        Groups => { 2 => 'Camera' },
+        PrintConv => {
+            0 => 'Normal',
+            1 => 'Low',
+            2 => 'High',
+        },
+    },
+    0xa409 => {
+        Name => 'Saturation',
+        Groups => { 2 => 'Camera' },
+        PrintConv => {
+            0 => 'Normal',
+            1 => 'Low',
+            2 => 'High',
+        },
+    },
+    0xa40a => {
+        Name => 'Sharpness',
+        Groups => { 2 => 'Camera' },
+        PrintConv => {
+            0 => 'Normal',
+            1 => 'Soft',
+            2 => 'Hard',
+        },
+    },
+    0xa40b => {
+        Name => 'DeviceSettingDescription',
+        Groups => { 2 => 'Camera' },
+    },
+    0xa40c => {
+        Name => 'SubjectDistanceRange',
+        Groups => { 2 => 'Camera' },
+        PrintConv => {
+            1 => 'Macro',
+            2 => 'Close',
+            3 => 'Distant',
+        },
+    },
+    0xa420 => 'ImageUniqueID',
+    0xa480 => 'GDALMetadata', #3
+    0xa481 => 'GDALNoData', #3
+    0xa500 => 'Gamma',
+#
+# Windows Media Photo WDP tags
+#
+    0xbc01 => { #13
+        Name => 'PixelFormat',
+        PrintHex => 1,
+        Format => 'undef',
+        Notes => q{
+            tags 0xbc** are used in Windows Media Photo WDP images. The actual
+            PixelFormat values are 16-byte GUID's but the leading 15 bytes,
+            '6fddc324-4e03-4bfe-b1853-d77768dc9', have been removed below to avoid
+            unnecessary clutter
+        },
+        ValueConv => q{
+            require Image::ExifTool::ASF;
+            $val = Image::ExifTool::ASF::GetGUID($val);
+            # GUID's are too long, so remove redundant information
+            $val =~ s/^6fddc324-4e03-4bfe-b185-3d77768dc9//i and $val = hex($val);
+            return $val;
+        },
+        PrintConv => {
+            0x0d => '24-bit RGB',
+            0x0c => '24-bit BGR',
+            0x0e => '32-bit BGR',
+            0x15 => '48-bit RGB',
+            0x12 => '48-bit RGB Fixed Point',
+            0x3b => '48-bit RGB Half',
+            0x18 => '96-bit RGB Fixed Point',
+            0x1b => '128-bit RGB Float',
+            0x0f => '32-bit BGRA',
+            0x16 => '64-bit RGBA',
+            0x1d => '64-bit RGBA Fixed Point',
+            0x3a => '64-bit RGBA Half',
+            0x1e => '128-bit RGBA Fixed Point',
+            0x19 => '128-bit RGBA Float',
+            0x10 => '32-bit PBGRA',
+            0x17 => '64-bit PRGBA',
+            0x1a => '128-bit PRGBA Float',
+            0x1c => '32-bit CMYK',
+            0x2c => '40-bit CMYK Alpha',
+            0x1f => '64-bit CMYK',
+            0x2d => '80-bit CMYK Alpha',
+            0x20 => '24-bit 3 Channels',
+            0x21 => '32-bit 4 Channels',
+            0x22 => '40-bit 5 Channels',
+            0x23 => '48-bit 6 Channels',
+            0x24 => '56-bit 7 Channels',
+            0x25 => '64-bit 8 Channels',
+            0x2e => '32-bit 3 Channels Alpha',
+            0x2f => '40-bit 4 Channels Alpha',
+            0x30 => '48-bit 5 Channels Alpha',
+            0x31 => '56-bit 6 Channels Alpha',
+            0x32 => '64-bit 7 Channels Alpha',
+            0x33 => '72-bit 8 Channels Alpha',
+            0x26 => '48-bit 3 Channels',
+            0x27 => '64-bit 4 Channels',
+            0x28 => '80-bit 5 Channels',
+            0x29 => '96-bit 6 Channels',
+            0x2a => '112-bit 7 Channels',
+            0x2b => '128-bit 8 Channels',
+            0x34 => '64-bit 3 Channels Alpha',
+            0x35 => '80-bit 4 Channels Alpha',
+            0x36 => '96-bit 5 Channels Alpha',
+            0x37 => '112-bit 6 Channels Alpha',
+            0x38 => '128-bit 7 Channels Alpha',
+            0x39 => '144-bit 8 Channels Alpha',
+            0x08 => '8-bit Gray',
+            0x0b => '16-bit Gray',
+            0x13 => '16-bit Gray Fixed Point',
+            0x3e => '16-bit Gray Half',
+            0x3f => '32-bit Gray Fixed Point',
+            0x11 => '32-bit Gray Float',
+            0x05 => 'Black & White',
+            0x09 => '16-bit BGR555',
+            0x0a => '16-bit BGR565',
+            0x13 => '32-bit BGR101010',
+            0x3d => '32-bit RGBE',
+        },
+    },
+    0xbc04 => { #13
+        Name => 'ImageType',
+        PrintConv => { BITMASK => {
+            0 => 'Preview',
+            1 => 'Page',
+        } },
+    },
+    0xbc02 => { #13
+        Name => 'Transfomation',
+        PrintConv => {
+            0 => 'Horizontal (normal)',
+            1 => 'Mirror vertical',
+            2 => 'Mirror horizontal',
+            3 => 'Rotate 180',
+            4 => 'Rotate 90 CW',
+            5 => 'Mirror horizontal and rotate 90 CW',
+            6 => 'Mirror horizontal and rotate 270 CW',
+            7 => 'Rotate 270 CW',
+        },
+    },
+    0xbc03 => { #13
+        Name => 'Uncompressed',
+        PrintConv => { 0 => 'No', 1 => 'Yes' },
+    },
+    0xbcc0 => { #13
+        Name => 'ImageOffset',
+        IsOffset => 1,
+        OffsetPair => 0xbcc1,  # point to associated byte count
+    },
+    0xbcc1 => { #13
+        Name => 'ImageByteCount',
+        OffsetPair => 0xbcc0,  # point to associated offset
+    },
+    0xbcc2 => { #13
+        Name => 'AlphaOffset',
+        IsOffset => 1,
+        OffsetPair => 0xbcc3,  # point to associated byte count
+    },
+    0xbcc3 => { #13
+        Name => 'AlphaByteCount',
+        OffsetPair => 0xbcc2,  # point to associated offset
+    },
+    0xbcc4 => { #13
+        Name => 'ImageDataDiscard',
+        PrintConv => {
+            0 => 'Full Resolution',
+            1 => 'Flexbits Discarded',
+            2 => 'HighPass Frequency Data Discarded',
+            3 => 'Highpass and LowPass Frequency Data Discarded',
+        },
+    },
+    0xbcc5 => { #13
+        Name => 'AlphaDataDiscard',
+        PrintConv => {
+            0 => 'Full Resolution',
+            1 => 'Flexbits Discarded',
+            2 => 'HighPass Frequency Data Discarded',
+            3 => 'Highpass and LowPass Frequency Data Discarded',
+        },
+    },
+    0xbc80 => 'ImageWidth', #13
+    0xbc81 => 'ImageHeight', #13
+    0xbc82 => 'WidthResolution', #13
+    0xbc83 => 'HeightResolution', #13
+#
+    0xc427 => 'OceScanjobDesc', #3
+    0xc428 => 'OceApplicationSelector', #3
+    0xc429 => 'OceIDNumber', #3
+    0xc42a => 'OceImageLogic', #3
+    0xc44f => 'Annotations', #7
+    0xc4a5 => {
+        Name => 'PrintIM',
+        # must set Writable here so this tag will be saved with MakerNotes option
+        Writable => 'undef',
+        Description => 'Print Image Matching',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::PrintIM::Main',
+        },
+    },
+#
+# DNG tags 0xc6XX (ref 2 unless otherwise stated)
+#
+    0xc612 => {
+        Name => 'DNGVersion',
+        Notes => 'tags 0xc612-0xc65d are used in DNG images',
+    },
+    0xc613 => 'DNGBackwardVersion',
+    0xc614 => 'UniqueCameraModel',
+    0xc615 => {
+        Name => 'LocalizedCameraModel',
+        Format => 'string',
+        PrintConv => '$self->Printable($val)',
+    },
+    0xc616 => {
+        Name => 'CFAPlaneColor',
+        PrintConv => q{
+            my @cols = qw(Red Green Blue Cyan Magenta Yellow White);
+            my @vals = map { $cols[$_] || "Unknown($_)" } split(' ', $val);
+            return join(',', @vals);
+        },
+    },
+    0xc617 => {
+        Name => 'CFALayout',
+        PrintConv => {
+            1 => 'Rectangular',
+            2 => 'Even columns offset down 1/2 row',
+            3 => 'Even columns offset up 1/2 row',
+            4 => 'Even rows offset right 1/2 column',
+            5 => 'Even rows offset left 1/2 column',
+        },
+    },
+    0xc618 => {
+        Name => 'LinearizationTable',
+        Binary => 1,
+    },
+    0xc619 => 'BlackLevelRepeatDim',
+    0xc61a => 'BlackLevel',
+    0xc61b => { Name => 'BlackLevelDeltaH', %longBin },
+    0xc61c => { Name => 'BlackLevelDeltaV', %longBin },
+    0xc61d => 'WhiteLevel',
+    0xc61e => 'DefaultScale',
+    0xc61f => 'DefaultCropOrigin',
+    0xc620 => 'DefaultCropSize',
+    0xc621 => 'ColorMatrix1',
+    0xc622 => 'ColorMatrix2',
+    0xc623 => 'CameraCalibration1',
+    0xc624 => 'CameraCalibration2',
+    0xc625 => 'ReductionMatrix1',
+    0xc626 => 'ReductionMatrix2',
+    0xc627 => 'AnalogBalance',
+    0xc628 => 'AsShotNeutral',
+    0xc629 => 'AsShotWhiteXY',
+    0xc62a => 'BaselineExposure',
+    0xc62b => 'BaselineNoise',
+    0xc62c => 'BaselineSharpness',
+    0xc62d => 'BayerGreenSplit',
+    0xc62e => 'LinearResponseLimit',
+    0xc62f => {
+        Name => 'CameraSerialNumber',
+        Groups => { 2 => 'Camera' },
+    },
+    0xc630 => {
+        Name => 'DNGLensInfo',
+        Groups => { 2 => 'Camera' },
+        PrintConv => '$_=$val;s/(\S+) (\S+) (\S+) (\S+)/$1-$2mm f\/$3-$4/;$_',
+    },
+    0xc631 => 'ChromaBlurRadius',
+    0xc632 => 'AntiAliasStrength',
+    0xc633 => 'ShadowScale',
+    0xc634 => [
+        {
+            Condition => '$self->{TIFF_TYPE} eq "SR2"',
+            Name => 'SR2Private',
+            Groups => { 1 => 'SR2' },
+            Flags => 'SubIFD',
+            Format => 'int32u',
+            SubDirectory => {
+                DirName => 'SR2Private',
+                TagTable => 'Image::ExifTool::Sony::SR2Private',
+                Start => '$val',
+            },
+        },
+        {
+            Condition => '$$valPt =~ /^Adobe\0/',
+            Name => 'DNGAdobeData',
+            DNGMakerNotes => 1,
+            SubDirectory => { TagTable => 'Image::ExifTool::DNG::AdobeData' },
+            Format => 'undef',  # written incorrectly as int8u (change to undef for speed)
+        },
+        {
+            Condition => '$$valPt =~ /^PENTAX \0/',
+            Name => 'DNGPentaxData',
+            DNGMakerNotes => 1,
+            SubDirectory => {
+                TagTable => 'Image::ExifTool::Pentax::Main',
+                Start => '$valuePtr + 10',
+                Base => '$start - 10',
+                ByteOrder => 'Unknown', # easier to do this than read byteorder word
+            },
+            Format => 'undef',  # written incorrectly as int8u (change to undef for speed)
+        },
+        {
+            Name => 'DNGPrivateData',
+            Binary => 1,
+            Format => 'undef',
+        },
+    ],
+    0xc635 => {
+        Name => 'MakerNoteSafety',
+        PrintConv => {
+            0 => 'Unsafe',
+            1 => 'Safe',
+        },
+    },
+    0xc65a => {
+        Name => 'CalibrationIlluminant1',
+        PrintConv => \%lightSource,
+    },
+    0xc65b => {
+        Name => 'CalibrationIlluminant2',
+        PrintConv => \%lightSource,
+    },
+    0xc65c => 'BestQualityScale',
+    0xc65d => {
+        Name => 'RawDataUniqueID',
+        Format => 'undef',
+        ValueConv => 'uc(unpack("H*",$val))',
+    },
+    0xc660 => { #3
+        Name => 'AliasLayerMetadata',
+        Notes => 'used by Alias Sketchbook Pro, not a DNG tag',
+    },
+    0xc68b => {
+        Name => 'OriginalRawFileName',
+        Format => 'string', # sometimes written as int8u
+    },
+    0xc68c => {
+        Name => 'OriginalRawFileData',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::DNG::OriginalRaw',
+        },
+    },
+    0xc68d => 'ActiveArea',
+    0xc68e => 'MaskedAreas',
+    0xc68f => {
+        Name => 'AsShotICCProfile',
+        Binary => 1,
+        Writable => 'undef', # must be defined here so tag will be extracted if specified
+        SubDirectory => {
+            DirName => 'AsShotICCProfile',
+            TagTable => 'Image::ExifTool::ICC_Profile::Main',
+        },
+    },
+    0xc690 => 'AsShotPreProfileMatrix',
+    0xc691 => {
+        Name => 'CurrentICCProfile',
+        Binary => 1,
+        Writable => 'undef', # must be defined here so tag will be extracted if specified
+        SubDirectory => {
+            DirName => 'CurrentICCProfile',
+            TagTable => 'Image::ExifTool::ICC_Profile::Main',
+        },
+    },
+    0xc692 => 'CurrentPreProfileMatrix',
+    # 0xea1c - unknown tag written by MicrosoftPhoto (undef)
+    #  observed: 1720 bytes in IFD0 or 1988 bytes in ExifIFD, in both cases
+    #  the data starts with "1c ea 00 00 00 08" followed by all 00's
+    0xea1d => {
+        Name => 'OffsetSchema',
+        Notes => "Microsoft's ill-conceived maker note offset difference",
+    },
+
+    # tags in the range 0xfde8-0xfe58 have been observed in PS7 files
+    # generated from RAW images.  They are all strings with the
+    # tag name at the start of the string.  To accomodate these types
+    # of tags, all tags with values above 0xf000 are handled specially
+    # by ProcessExif().
+);
+
+# the Composite tags are evaluated last, and are used
+# to calculate values based on the other tags
+# (the main script looks for the special 'Composite' hash)
+%Image::ExifTool::Exif::Composite = (
+    GROUPS => { 2 => 'Image' },
+    ImageSize => {
+        Require => {
+            0 => 'ImageWidth',
+            1 => 'ImageHeight',
+        },
+        ValueConv => '"$val[0]x$val[1]"',
+    },
+    # pick the best shutter speed value
+    ShutterSpeed => {
+        Desire => {
+            0 => 'ExposureTime',
+            1 => 'ShutterSpeedValue',
+            2 => 'BulbDuration',
+        },
+        ValueConv => '($val[2] and $val[2]>0) ? $val[2] : (defined($val[0]) ? $val[0] : $val[1])',
+        PrintConv => 'Image::ExifTool::Exif::PrintExposureTime($val)',
+    },
+    Aperture => {
+        Desire => {
+            0 => 'FNumber',
+            1 => 'ApertureValue',
+        },
+        RawConv => '($val[0] || $val[1]) ? $val : undef',
+        ValueConv => '$val[0] || $val[1]',
+        PrintConv => 'sprintf("%.1f", $val)',
+    },
+    LightValue => {
+        Notes => 'calculated LV -- similar to exposure value but includes ISO speed',
+        Require => {
+            0 => 'Aperture',
+            1 => 'ShutterSpeed',
+            2 => 'ISO',
+        },
+        ValueConv => 'Image::ExifTool::Exif::CalculateLV($val[0],$val[1],$prt[2])',
+        PrintConv => 'sprintf("%.1f",$val)',
+    },
+    FocalLength35efl => {
+        Description => 'Focal Length',
+        Notes => 'this value may be incorrect if the image has been resized',
+        Groups => { 2 => 'Camera' },
+        Require => {
+            0 => 'FocalLength',
+        },
+        Desire => {
+            1 => 'ScaleFactor35efl',
+        },
+        ValueConv => '$val[0] * ($val[1] ? $val[1] : 1)',
+        PrintConv => '$val[1] ? sprintf("%.1fmm (35mm equivalent: %.1fmm)", $val[0], $val) : sprintf("%.1fmm", $val)',
+    },
+    ScaleFactor35efl => {
+        Description => 'Scale Factor To 35mm Equivalent',
+        Notes => 'this value and any derived values may be incorrect if image has been resized',
+        Groups => { 2 => 'Camera' },
+        Desire => {
+            0 => 'FocalLength',
+            1 => 'FocalLengthIn35mmFormat',
+            2 => 'Composite:DigitalZoom',
+            3 => 'FocalPlaneDiagonal',
+            4 => 'FocalPlaneXSize',
+            5 => 'FocalPlaneYSize',
+            6 => 'FocalPlaneResolutionUnit',
+            7 => 'FocalPlaneXResolution',
+            8 => 'FocalPlaneYResolution',
+            9 => 'CanonImageWidth',
+           10 => 'CanonImageHeight',
+           11 => 'ExifImageWidth',
+           12 => 'ExifImageLength',
+           13 => 'ImageWidth',
+           14 => 'ImageHeight',
+        },
+        ValueConv => 'Image::ExifTool::Exif::CalcScaleFactor35efl(@val)',
+        PrintConv => 'sprintf("%.1f", $val)',
+    },
+    CircleOfConfusion => {
+        Notes => q{
+            this value may be incorrect if the image has been resized.  Calculated as
+            D/1440, where D is the focal plane diagonal in mm
+        },
+        Groups => { 2 => 'Camera' },
+        Require => 'ScaleFactor35efl',
+        ValueConv => 'sqrt(24*24+36*36) / ($val * 1440)',
+        PrintConv => 'sprintf("%.3f mm",$val)',
+    },
+    HyperfocalDistance => {
+        Notes => 'this value may be incorrect if the image has been resized',
+        Groups => { 2 => 'Camera' },
+        Require => {
+            0 => 'FocalLength',
+            1 => 'Aperture',
+            2 => 'CircleOfConfusion',
+        },
+        ValueConv => q{
+            return undef unless $val[1] and $val[2];
+            return $val[0] * $val[0] / ($val[1] * $val[2] * 1000);
+        },
+        PrintConv => 'sprintf("%.2f m", $val)',
+    },
+    DOF => {
+        Description => 'Depth of Field',
+        Notes => 'this value may be incorrect if the image has been resized',
+        Require => {
+            0 => 'FocusDistance', # FocusDistance in meters, 0 means 'inf'
+            1 => 'FocalLength',
+            2 => 'Aperture',
+            3 => 'CircleOfConfusion',
+        },
+        ValueConv => q{
+            return undef unless $val[1] and $val[3];
+            $val[0] or $val[0] = 1e10;  # use a large number for 'inf'
+            my ($s, $f) = ($val[0], $val[1]);
+            my $t = $val[2] * $val[3] * ($s * 1000 - $f) / ($f * $f);
+            my @v = ($s / (1 + $t), $s / (1 - $t));
+            $v[1] < 0 and $v[1] = 0; # 0 means 'inf'
+            return join(' ',@v);
+        },
+        PrintConv => q{
+            $val =~ tr/,/./;    # in case locale is whacky
+            my @v = split ' ', $val;
+            $v[1] or return sprintf("inf (%.2f m - inf)", $v[0]);
+            return sprintf("%.2f m (%.2f - %.2f)",$v[1]-$v[0],$v[0],$v[1]);
+        },
+    },
+    DateTimeCreated => { # used by IPTC, XMP, WAV, etc
+        Description => 'Date/Time Created',
+        Groups => { 2 => 'Time' },
+        Require => {
+            0 => 'DateCreated',
+            1 => 'TimeCreated',
+        },
+        ValueConv => '"$val[0] $val[1]"',
+        PrintConv => '$self->ConvertDateTime($val)',
+    },
+    # create DateTimeOriginal from DateTimeCreated if not set already
+    DateTimeOriginal => {
+        Condition => 'not defined($oldVal)',
+        Description => 'Date/Time Original',
+        Groups => { 2 => 'Time' },
+        Require => 'DateTimeCreated',
+        ValueConv => '$val[0]',
+        PrintConv => '$prt[0]',
+    },
+    ThumbnailImage => {
+        Writable => 1,
+        WriteCheck => '$self->CheckImage(\$val)',
+        WriteAlso => {
+            # (the 0xfeedfeed values are translated in the Exif write routine)
+            ThumbnailOffset => 'defined $val ? 0xfeedfeed : undef',
+            ThumbnailLength => 'defined $val ? 0xfeedfeed : undef',
+        },
+        Require => {
+            0 => 'ThumbnailOffset',
+            1 => 'ThumbnailLength',
+        },
+        # retrieve the thumbnail from our EXIF data
+        RawConv => 'Image::ExifTool::Exif::ExtractImage($self,$val[0],$val[1],"ThumbnailImage")',
+    },
+    PreviewImage => {
+        Writable => 1,
+        WriteCheck => '$self->CheckImage(\$val)',
+        WriteAlso => {
+            PreviewImageStart  => 'defined $val ? 0xfeedfeed : undef',
+            PreviewImageLength => 'defined $val ? 0xfeedfeed : undef',
+            PreviewImageValid  => 'defined $val and length $val ? 1 : 0',
+        },
+        Require => {
+            0 => 'PreviewImageStart',
+            1 => 'PreviewImageLength',
+        },
+        Desire => {
+            2 => 'PreviewImageValid',
+            # (DNG may be have 2 preview images)
+            3 => 'PreviewImageStart (1)',
+            4 => 'PreviewImageLength (1)',
+        },
+        RawConv => q{
+            if ($val[3] and $val[4]) {
+                my %val = (
+                    0 => 'PreviewImageStart (1)',
+                    1 => 'PreviewImageLength (1)',
+                    2 => 'PreviewImageValid',
+                );
+                $self->FoundTag($tagInfo, \%val);
+            }
+            return undef if defined $val[2] and not $val[2];
+            return Image::ExifTool::Exif::ExtractImage($self,$val[0],$val[1],'PreviewImage');
+        },
+    },
+    JpgFromRaw => {
+        Writable => 1,
+        WriteCheck => '$self->CheckImage(\$val)',
+        WriteAlso => {
+            JpgFromRawStart  => 'defined $val ? 0xfeedfeed : undef',
+            JpgFromRawLength => 'defined $val ? 0xfeedfeed : undef',
+        },
+        Require => {
+            0 => 'JpgFromRawStart',
+            1 => 'JpgFromRawLength',
+        },
+        RawConv => 'Image::ExifTool::Exif::ExtractImage($self,$val[0],$val[1],"JpgFromRaw")',
+    },
+    OtherImage => {
+        Require => {
+            0 => 'OtherImageStart',
+            1 => 'OtherImageLength',
+        },
+        # retrieve the thumbnail from our EXIF data
+        RawConv => 'Image::ExifTool::Exif::ExtractImage($self,$val[0],$val[1],"OtherImage")',
+    },
+    PreviewImageSize => {
+        Require => {
+            0 => 'PreviewImageWidth',
+            1 => 'PreviewImageHeight',
+        },
+        ValueConv => '"$val[0]x$val[1]"',
+    },
+    SubSecDateTimeOriginal => {
+        Description => 'Date/Time Original',
+        Require => {
+            0 => 'DateTimeOriginal',
+            1 => 'SubSecTimeOriginal',
+        },
+        # be careful here in case there is a timezone following the seconds
+        ValueConv => '$_=$val[0];s/(.*:\d{2})/$1\.$val[1]/;$_',
+        PrintConv => '$self->ConvertDateTime($val)',
+    },
+    CFAPattern => {
+        Require => {
+            0 => 'CFARepeatPatternDim',
+            1 => 'CFAPattern2',
+        },
+        # generate CFAPattern
+        ValueConv => q{
+            my @a = split / /, $val[0];
+            my @b = split / /, $val[1];
+            return undef unless @a==2 and @b==$a[0]*$a[1];
+            return Set16u($a[0]) . Set16u($a[1]) . pack('C*', @b);
+        },
+        PrintConv => 'Image::ExifTool::Exif::PrintCFAPattern($val)',
+    },
+    RedBalance => {
+        Groups => { 2 => 'Camera' },
+        Desire => {
+            0 => 'WB_RGGBLevels',
+            1 => 'WB_RGBGLevels',
+            2 => 'WB_RBGGLevels',
+            3 => 'WB_GRBGLevels',
+            4 => 'WB_GRGBLevels',
+            5 => 'WB_RBLevels',
+            6 => 'WBRedLevel',
+            7 => 'WBGreenLevel',
+        },
+        ValueConv => 'Image::ExifTool::Exif::RedBlueBalance(0,@val)',
+        PrintConv => 'int($val * 1e6 + 0.5) * 1e-6',
+    },
+    BlueBalance => {
+        Groups => { 2 => 'Camera' },
+        Desire => {
+            0 => 'WB_RGGBLevels',
+            1 => 'WB_RGBGLevels',
+            2 => 'WB_RBGGLevels',
+            3 => 'WB_GRBGLevels',
+            4 => 'WB_GRGBLevels',
+            5 => 'WB_RBLevels',
+            6 => 'WBBlueLevel',
+            7 => 'WBGreenLevel',
+        },
+        ValueConv => 'Image::ExifTool::Exif::RedBlueBalance(1,@val)',
+        PrintConv => 'int($val * 1e6 + 0.5) * 1e-6',
+    },
+    GPSPosition => {
+        Require => {
+            0 => 'GPSLatitude',
+            1 => 'GPSLongitude',
+        },
+        ValueConv => '"$val[0] $val[1]"',
+        PrintConv => '"$prt[0], $prt[1]"',
+    },
+);
+
+# table for unknown IFD entries
+%Image::ExifTool::Exif::Unknown = (
+    GROUPS => { 0 => 'EXIF', 1 => 'UnknownIFD', 2 => 'Image'},
+    WRITE_PROC => \&WriteExif,
+);
+
+# add our composite tags
+Image::ExifTool::AddCompositeTags('Image::ExifTool::Exif');
+
+
+#------------------------------------------------------------------------------
+# AutoLoad our writer routines when necessary
+#
+sub AUTOLOAD
+{
+    return Image::ExifTool::DoAutoLoad($AUTOLOAD, @_);
+}
+
+#------------------------------------------------------------------------------
+# Calculate LV (Light Value)
+# Inputs: 0) Aperture, 1) ShutterSpeed, 2) ISO
+# Returns: LV value
+sub CalculateLV($$$)
+{
+    local $_;
+    # do validity checks on arguments
+    return undef unless @_ >= 3;
+    foreach (@_) {
+        return undef unless defined $_ and Image::ExifTool::IsFloat($_) and $_ > 0;
+    }
+    # (A light value of 0 is defined as f/1.0 at 1 second with ISO 100)
+    return (2*log($_[0]) - log($_[1]) - log($_[2]/100)) / log(2);
+}
+
+#------------------------------------------------------------------------------
+# Calculate scale factor for 35mm effective focal length
+# Inputs: 0) Focal length
+#         1) Focal length in 35mm format
+#         2) Canon digital zoom factor
+#         3) Focal plane diagonal size (in mm)
+#         4/5) Focal plane X/Y size (in mm)
+#         6) focal plane resolution units (1=None,2=inches,3=cm,4=mm,5=um)
+#         7/8) Focal plane X/Y resolution
+#         9/10,11/12...) Image width/height in order of precedence (first valid pair is used)
+# Returns: 35mm conversion factor (or undefined if it can't be calculated)
+sub CalcScaleFactor35efl
+{
+    my $focal = shift;
+    my $foc35 = shift;
+
+    return $foc35 / $focal if $focal and $foc35;
+
+    my $digz = shift || 1;
+    my $diag = shift;
+    unless ($diag and Image::ExifTool::IsFloat($diag)) {
+        my $xsize = shift;
+        my $ysize = shift;
+        if ($xsize and $ysize) {
+            $diag = sqrt($xsize * $xsize + $ysize * $ysize);
+        } else {
+            # get number of mm in units (assume inches unless otherwise specified)
+            my $units = { 3=>10, 4=>1, 5=>0.001 }->{ shift() || '' } || 25.4;
+            my $x_res = shift || return undef;
+            my $y_res = shift || return undef;
+            Image::ExifTool::IsFloat($x_res) and $x_res != 0 or return undef;
+            Image::ExifTool::IsFloat($y_res) and $y_res != 0 or return undef;
+            my ($w, $h);
+            for (;;) {
+                @_ < 2 and return undef;
+                $w = shift;
+                $h = shift;
+                next unless $w and $h;
+                my $a = $w / $h;
+                last if $a > 0.5 and $a < 2; # stop if we get a reasonable value
+            }
+            # calculate focal plane size in mm
+            $w *= $units / $x_res;
+            $h *= $units / $y_res;
+            $diag = sqrt($w*$w+$h*$h);
+            # make sure size is reasonable
+            return undef unless $diag > 1 and $diag < 100;
+        }
+    }
+    return sqrt(36*36+24*24) * $digz / $diag;
+}
+
+#------------------------------------------------------------------------------
+# Convert exposure compensation fraction
+sub ConvertFraction($)
+{
+    my $val = shift;
+    my $str;
+    if (defined $val) {
+        $val *= 1.00001;    # avoid round-off errors
+        if (not $val) {
+            $str = '0';
+        } elsif (int($val)/$val > 0.999) {
+            $str = sprintf("%+d", int($val));
+        } elsif ((int($val*2))/($val*2) > 0.999) {
+            $str = sprintf("%+d/2", int($val * 2));
+        } elsif ((int($val*3))/($val*3) > 0.999) {
+            $str = sprintf("%+d/3", int($val * 3));
+        } else {
+            $str = sprintf("%.3g", $val);
+        }
+    }
+    return $str;
+}
+
+#------------------------------------------------------------------------------
+# Convert EXIF text to something readable
+# Inputs: 0) ExifTool object reference, 1) EXIF text
+# Returns: UTF8 or Latin text
+sub ConvertExifText($$)
+{
+    my ($exifTool, $val) = @_;
+    return $val if length($val) < 8;
+    my $id = substr($val, 0, 8);
+    my $str = substr($val, 8);
+    # by the EXIF spec, the string should be "UNICODE\0", but apparently Kodak
+    # sometimes uses "Unicode\0" in the APP3 "Meta" information.  But unfortunately
+    # Ricoh uses "Unicode\0" in the RR30 EXIF UserComment when the text is actually
+    # ASCII, so only recognize uppercase "UNICODE\0" here.
+    if ($id eq "UNICODE\0") {
+        # MicrosoftPhoto writes as little-endian even in big-endian EXIF,
+        # so we must guess at the true byte ordering
+        my ($ii, $mm) = (0, 0);
+        foreach (unpack('n*', $str)) {
+            ++$mm unless $_ & 0xff00;
+            ++$ii unless $_ & 0x00ff;
+        }
+        my $order;
+        $order = ($ii > $mm) ? 'II' : 'MM' if $ii != $mm;
+        # convert from unicode
+        $str = $exifTool->Unicode2Charset($str, $order);
+    } else {
+        # assume everything else is ASCII (Don't convert JIS... yet)
+        $str =~ s/\0.*//s;   # truncate at null terminator
+    }
+    return $str;
+}
+
+#------------------------------------------------------------------------------
+# Print conversion for SpatialFrequencyResponse
+sub PrintSFR($)
+{
+    my $val = shift;
+    return $val unless length $val > 4;
+    my ($n, $m) = (Get16u(\$val, 0), Get16u(\$val, 2));
+    my @cols = split /\0/, substr($val, 4), $n+1;
+    my $pos = length($val) - 8 * $n * $m;
+    return $val unless @cols == $n+1 and $pos >= 4;
+    pop @cols;
+    my ($i, $j);
+    for ($i=0; $i<$n; ++$i) {
+        my @rows;
+        for ($j=0; $j<$m; ++$j) {
+            push @rows, Image::ExifTool::GetRational64u(\$val, $pos + 8*($i+$j*$n));
+        }
+        $cols[$i] .= '=' . join(',',@rows) . '';
+    }
+    return join '; ', @cols;
+}
+
+#------------------------------------------------------------------------------
+# Print numerical parameter value (with sign, or 'Normal' for zero)
+sub PrintParameter($)
+{
+    my $val = shift;
+    if ($val > 0) {
+        if ($val > 0xfff0) {    # a negative value in disguise?
+            $val = $val - 0x10000;
+        } else {
+            $val = "+$val";
+        }
+    } elsif ($val == 0) {
+        $val = 'Normal';
+    }
+    return $val;
+}
+
+#------------------------------------------------------------------------------
+# Convert parameter back to standard EXIF value
+#   0 or "Normal" => 0
+#   -1,-2,etc or "Soft" or "Low" => 1
+#   +1,+2,1,2,etc or "Hard" or "High" => 2
+sub ConvertParameter($)
+{
+    my $val = shift;
+    # normal is a value of zero
+    return 0 if $val =~ /\bn/i or not $val;
+    # "soft", "low" or any negative number is a value of 1
+    return 1 if $val =~ /\b(s|l|-)/i;
+    # "hard", "high" or any positive number is a vail of 2
+    return 2 if $val =~ /\b(h|\+|\d)/i;
+    return undef;
+}
+
+#------------------------------------------------------------------------------
+# Calculate Red/BlueBalance
+# Inputs: 0) 0=red, 1=blue, 1-6) WB_RGGB/RGBG/RBGG/GRBG/GRGB/RBLevels,
+#         7) red or blue level, 8) green level
+my @rggbLookup = (
+    [ 0, 1, 2, 3 ], # RGGB
+    [ 0, 1, 3, 2 ], # RGBG
+    [ 0, 2, 3, 1 ], # RBGG
+    [ 1, 0, 3, 2 ], # GRBG
+    [ 1, 0, 2, 3 ], # GRGB
+    [ 0, 256, 256, 1 ], # RB (green level is 256)
+);
+sub RedBlueBalance($@)
+{
+    my $blue = shift;
+    my ($i, $val, $levels);
+    for ($i=0; $i<@rggbLookup; ++$i) {
+        $levels = shift or next;
+        my @levels = split ' ', $levels;
+        next if @levels < 2;
+        my $lookup = $rggbLookup[$i];
+        my $g = $$lookup[1];    # get green level or index
+        if ($g < 4) {
+            next if @levels < 4;
+            $g = ($levels[$g] + $levels[$$lookup[2]]) / 2 or next;
+        }
+        $val = $levels[$$lookup[$blue * 3]] / $g;
+        last;
+    }
+    $val = $_[0] / $_[1] if not defined $val and ($_[0] and $_[1]);
+    return $val;
+}
+
+#------------------------------------------------------------------------------
+# Print exposure time as a fraction
+sub PrintExposureTime($)
+{
+    my $secs = shift;
+    if ($secs < 0.25001 and $secs > 0) {
+        return sprintf("1/%d",int(0.5 + 1/$secs));
+    }
+    $_ = sprintf("%.1f",$secs);
+    s/\.0$//;
+    return $_;
+}
+
+#------------------------------------------------------------------------------
+# Print CFA Pattern
+sub PrintCFAPattern($)
+{
+    my $val = shift;
+    return '<truncated data>' unless length $val > 4;
+    # some panasonic cameras (SV-AS3, SV-AS30) write this in ascii (very odd)
+    $val =~ /^[0-6]+$/ and $val =~ tr/0-6/\x00-\x06/;
+    my ($nx, $ny) = (Get16u(\$val, 0), Get16u(\$val, 2));
+    return '<zero pattern size>' unless $nx and $ny;
+    my $end = 4 + $nx * $ny;
+    if ($end > length $val) {
+        # try swapping byte order (I have seen this order different than in EXIF)
+        ($nx, $ny) = unpack('n2',pack('v2',$nx,$ny));
+        $end = 4 + $nx * $ny;
+        return '<invalid pattern size>' if $end > length $val;
+    }
+    my @cfaColor = qw(Red Green Blue Cyan Magenta Yellow White);
+    my ($pos, $rtnVal) = (4, '[');
+    for (;;) {
+        $rtnVal .= $cfaColor[Get8u(\$val,$pos)] || 'Unknown';
+        last if ++$pos >= $end;
+        ($pos - 4) % $ny and $rtnVal .= ',', next;
+        $rtnVal .= '][';
+    }
+    return $rtnVal . ']';
+}
+
+#------------------------------------------------------------------------------
+# translate date into standard EXIF format
+# Inputs: 0) date
+# Returns: date in format '2003:10:22'
+# - bad formats recognized: '2003-10-22','2003/10/22','2003 10 22','20031022'
+# - removes null terminator if it exists
+sub ExifDate($)
+{
+    my $date = shift;
+    $date =~ s/\0$//;       # remove any null terminator
+    # separate year:month:day with colons (instead of -, /, space or nothing)
+    $date =~ s/(\d{4})[- \/:]*(\d{2})[- \/:]*(\d{2})$/$1:$2:$3/;
+    return $date;
+}
+
+#------------------------------------------------------------------------------
+# translate time into standard EXIF format
+# Inputs: 0) time
+# Returns: time in format '10:30:55'
+# - bad formats recognized: '10 30 55', '103055', '103055+0500'
+# - removes null terminator if it exists
+# - leaves time zone intact if specified (ie. '10:30:55+05:00')
+sub ExifTime($)
+{
+    my $time = shift;
+    $time =~ tr/ /:/;   # use ':' (not ' ') as a separator
+    $time =~ s/\0$//;   # remove any null terminator
+    # add separators if they don't exist
+    $time =~ s/^(\d{2})(\d{2})(\d{2})/$1:$2:$3/;
+    $time =~ s/([+-]\d{2})(\d{2})\s*$/$1:$2/;   # to timezone too
+    return $time;
+}
+
+#------------------------------------------------------------------------------
+# extract image from file
+# Inputs: 0) ExifTool object reference, 1) data offset (in file), 2) data length
+#         3) [optional] tag name
+# Returns: Reference to Image if specifically requested or "Binary data" message
+#          Returns undef if there was an error loading the image
+sub ExtractImage($$$$)
+{
+    my ($exifTool, $offset, $len, $tag) = @_;
+    my $dataPt = \$exifTool->{EXIF_DATA};
+    my $dataPos = $exifTool->{EXIF_POS};
+    my $image;
+
+    return undef unless $len;   # no image if length is zero
+
+    # take data from EXIF block if possible
+    if (defined $dataPos and $offset>=$dataPos and $offset+$len<=$dataPos+length($$dataPt)) {
+        $image = substr($$dataPt, $offset-$dataPos, $len);
+    } else {
+        $image = $exifTool->ExtractBinary($offset, $len, $tag);
+        return undef unless defined $image;
+    }
+    return $exifTool->ValidateImage(\$image, $tag);
+}
+
+#------------------------------------------------------------------------------
+# Process EXIF directory
+# Inputs: 0) ExifTool object reference
+#         1) Reference to directory information hash
+#         2) Pointer to tag table for this directory
+# Returns: 1 on success, otherwise returns 0 and sets a Warning
+sub ProcessExif($$$)
+{
+    my ($exifTool, $dirInfo, $tagTablePtr) = @_;
+    my $dataPt = $$dirInfo{DataPt};
+    my $dataPos = $$dirInfo{DataPos} || 0;
+    my $dataLen = $$dirInfo{DataLen};
+    my $dirStart = $$dirInfo{DirStart} || 0;
+    my $dirLen = $$dirInfo{DirLen} || $dataLen - $dirStart;
+    my $dirName = $$dirInfo{DirName};
+    my $base = $$dirInfo{Base} || 0;
+    my $firstBase = $base;
+    my $raf = $$dirInfo{RAF};
+    my $verbose = $exifTool->Options('Verbose');
+    my $htmlDump = $exifTool->{HTML_DUMP};
+    my $success = 1;
+    my ($tagKey, $name, $dirSize, $makerAddr);
+
+    $verbose = -1 if $htmlDump; # mix htmlDump into verbose so we can test for both at once
+    $dirName eq 'EXIF' and $dirName = $$dirInfo{DirName} = 'IFD0';
+    $$dirInfo{Multi} = 1 if $dirName eq 'IFD0' or $dirName eq 'SubIFD';
+    $htmlDump and $name = ($dirName eq 'MakerNotes') ? $$dirInfo{Name} : $dirName;
+
+    my ($numEntries, $dirEnd);
+    if ($dirStart >= 0 and $dirStart <= $dataLen-2) {
+        # make sure data is large enough (patches bug in Olympus subdirectory lengths)
+        $numEntries = Get16u($dataPt, $dirStart);
+        $dirSize = 2 + 12 * $numEntries;
+        $dirEnd = $dirStart + $dirSize;
+        if ($dirSize > $dirLen) {
+            if ($verbose > 0 and not $$dirInfo{SubIFD}) {
+                my $short = $dirSize - $dirLen;
+                $exifTool->Warn("Short directory size (missing $short bytes)");
+            }
+            undef $dirSize if $dirEnd > $dataLen; # read from file if necessary
+        }
+    }
+    # read IFD from file if necessary
+    unless ($dirSize) {
+        $success = 0;
+        if ($raf) {
+            # read the count of entries in this IFD
+            my $offset = $dirStart + $dataPos;
+            my ($buff, $buf2);
+            if ($raf->Seek($offset + $base, 0) and $raf->Read($buff,2) == 2) {
+                my $len = 12 * Get16u(\$buff,0);
+                # also read next IFD pointer if available
+                if ($raf->Read($buf2, $len+4) >= $len) {
+                    $buff .= $buf2;
+                    # make copy of dirInfo since we're going to modify it
+                    my %newDirInfo = %$dirInfo;
+                    $dirInfo = \%newDirInfo;
+                    # update directory parameters for the newly loaded IFD
+                    $dataPt = $$dirInfo{DataPt} = \$buff;
+                    $dataPos = $$dirInfo{DataPos} = $offset;
+                    $dataLen = $$dirInfo{DataLen} = length $buff;
+                    $dirStart = $$dirInfo{DirStart} = 0;
+                    $success = 1;
+                }
+            }
+        }
+        unless ($success) {
+            $exifTool->Warn("Bad $dirName directory");
+            return 0;
+        }
+        $numEntries = Get16u($dataPt, $dirStart);
+        $dirSize = 2 + 12 * $numEntries;
+        $dirEnd = $dirStart + $dirSize;
+    }
+    $verbose > 0 and $exifTool->VerboseDir($dirName, $numEntries);
+    my $bytesFromEnd = $dataLen - $dirEnd;
+    if ($bytesFromEnd < 4) {
+        unless ($bytesFromEnd==2 or $bytesFromEnd==0) {
+            $exifTool->Warn(sprintf"Illegal $dirName directory size (0x%x entries)",$numEntries);
+            return 0;
+        }
+    }
+    # fix base offset for maker notes if necessary
+    if (defined $$dirInfo{MakerNoteAddr}) {
+        $makerAddr = $$dirInfo{MakerNoteAddr};
+        delete $$dirInfo{MakerNoteAddr};
+        if (Image::ExifTool::MakerNotes::FixBase($exifTool, $dirInfo)) {
+            $base = $$dirInfo{Base};
+            $dataPos = $$dirInfo{DataPos};
+        }
+    }
+    if ($htmlDump) {
+        if (defined $makerAddr) {
+            my $hdrLen = $dirStart + $dataPos + $base - $makerAddr;
+            $exifTool->HtmlDump($makerAddr, $hdrLen, "MakerNotes header", $name) if $hdrLen > 0;
+        }
+        $exifTool->HtmlDump($dirStart + $dataPos + $base, 2, "$name entries",
+                 "Entry count: $numEntries");
+        my $tip;
+        if ($bytesFromEnd >= 4) {
+            my $nxt = ($name =~ /^IFD(\d+)/) ? "IFD" . ($1 + 1) : 'Next IFD';
+            $tip = sprintf("$nxt offset: 0x%.4x", Get32u($dataPt, $dirEnd));
+        }
+        $exifTool->HtmlDump($dirEnd + $dataPos + $base, 4, "Next IFD", $tip, 0);
+        $name = $dirName if $name =~ /^MakerNote/;
+    }
+
+    # loop through all entries in an EXIF directory (IFD)
+    my ($index, $valEnd);
+    for ($index=0; $index<$numEntries; ++$index) {
+        my $entry = $dirStart + 2 + 12 * $index;
+        my $tagID = Get16u($dataPt, $entry);
+        my $format = Get16u($dataPt, $entry+2);
+        my $count = Get32u($dataPt, $entry+4);
+        if ($format < 1 or $format > 13) {
+            $exifTool->HtmlDump($entry+$dataPos+$base,12,"[invalid IFD entry]",
+                     "Bad format type: $format", 1);
+            # warn unless the IFD was just padded with zeros
+            $format and $exifTool->Warn(
+                sprintf("Unknown format ($format) for $dirName tag 0x%x",$tagID));
+            return 0 unless $index; # assume corrupted IFD if this is our first entry
+            next;
+        }
+        my $formatStr = $formatName[$format];   # get name of this format
+        my $size = $count * $formatSize[$format];
+        my $valueDataPt = $dataPt;
+        my $valueDataPos = $dataPos;
+        my $valueDataLen = $dataLen;
+        my $valuePtr = $entry + 8;      # pointer to value within $$dataPt
+        my $tagInfo = $exifTool->GetTagInfo($tagTablePtr, $tagID);
+        if ($size > 4) {
+            if ($size > 0x7fffffff) {
+                $exifTool->Warn(sprintf("Invalid size ($size) for $dirName tag 0x%x",$tagID));
+                next;
+            }
+            $valuePtr = Get32u($dataPt, $valuePtr);
+            # fix valuePtr if necessary
+            if ($$dirInfo{FixOffsets}) {
+                my $wFlag;
+                $valEnd or $valEnd = $dataPos + $dirEnd + 4;
+                #### eval FixOffsets($valuePtr, $valEnd, $size, $tagID, $wFlag)
+                eval $$dirInfo{FixOffsets};
+            }
+            # convert offset to pointer in $$dataPt
+            if ($$dirInfo{EntryBased} or (ref $$tagTablePtr{$tagID} eq 'HASH' and
+                $tagTablePtr->{$tagID}->{EntryBased}))
+            {
+                $valuePtr += $entry;
+            } else {
+                $valuePtr -= $dataPos;
+            }
+            if ($valuePtr < 0 or $valuePtr+$size > $dataLen) {
+                # get value by seeking in file if we are allowed
+                my $buff;
+                if ($raf) {
+                    if ($raf->Seek($base + $valuePtr + $dataPos,0) and
+                        $raf->Read($buff,$size) == $size)
+                    {
+                        $valueDataPt = \$buff;
+                        $valueDataPos = $valuePtr + $dataPos;
+                        $valueDataLen = $size;
+                        $valuePtr = 0;
+                    } else {
+                        $exifTool->Warn("Error reading value for $dirName entry $index");
+                        return 0;
+                    }
+                } else {
+                    my $tagStr;
+                    if ($tagInfo) {
+                        $tagStr = $$tagInfo{Name};
+                    } elsif (defined $tagInfo) {
+                        my $tmpInfo = $exifTool->GetTagInfo($tagTablePtr, $tagID, \ '', $formatStr, $count);
+                        $tagStr = $$tmpInfo{Name} if $tmpInfo;
+                    }
+                    $tagStr or $tagStr = sprintf("tag 0x%x",$tagID);
+                    # allow PreviewImage to run outside EXIF data
+                    if ($tagStr eq 'PreviewImage' and $exifTool->{RAF}) {
+                        my $pos = $exifTool->{RAF}->Tell();
+                        $buff = $exifTool->ExtractBinary($base + $valuePtr + $dataPos, $size, 'PreviewImage');
+                        $exifTool->{RAF}->Seek($pos, 0);
+                        $valueDataPt = \$buff;
+                        $valueDataPos = $valuePtr + $dataPos;
+                        $valueDataLen = $size;
+                        $valuePtr = 0;
+                    } else {
+                        $exifTool->Warn("Bad $dirName directory pointer for $tagStr");
+                    }
+                    unless (defined $buff) {
+                        next unless $htmlDump and $size < 1000000;
+                        $valueDataPt = \ (' ' x $size);
+                        $valueDataPos = $valuePtr + $dataPos;
+                        $valueDataLen = -1; # flag the bad pointer
+                        $valuePtr = 0;
+                    }
+                }
+            }
+        }
+        # treat single unknown byte as int8u
+        $formatStr = 'int8u' if $format == 7 and $count == 1;
+
+        my $val;
+        if ($tagID > 0xf000 and not $$tagTablePtr{$tagID}
+            and $tagTablePtr eq \%Image::ExifTool::Exif::Main)
+        {
+            # handle special case of Photoshop RAW tags (0xfde8-0xfe58)
+            # --> generate tags from the value if possible
+            $val = ReadValue($valueDataPt,$valuePtr,$formatStr,$count,$size);
+            if (defined $val and $val =~ /(.*): (.*)/) {
+                my $tag = $1;
+                $val = $2;
+                $tag =~ s/'s//; # remove 's (so "Owner's Name" becomes "OwnerName")
+                $tag =~ tr/a-zA-Z0-9_//cd; # remove unknown characters
+                if ($tag) {
+                    $tagInfo = {
+                        Name => $tag,
+                        ValueConv => '$_=$val;s/.*: //;$_', # remove descr
+                    };
+                    Image::ExifTool::AddTagToTable($tagTablePtr, $tagID, $tagInfo);
+                }
+            }
+        }
+        if (defined $tagInfo and not $tagInfo) {
+            # GetTagInfo() required the value for a Condition
+            my $tmpVal = substr($$valueDataPt, $valuePtr, $size < 48 ? $size : 48);
+            $tagInfo = $exifTool->GetTagInfo($tagTablePtr, $tagID, \$tmpVal, $formatStr, $count);
+        }
+        # override EXIF format if specified
+        my $origFormStr;
+        if (defined $tagInfo) {
+            if ($$tagInfo{Format}) {
+                $formatStr = $$tagInfo{Format};
+                # must adjust number of items for new format size
+                my $newNum = $formatNumber{$formatStr};
+                if ($newNum) {
+                    $origFormStr = $formatName[$format] . '[' . $count . ']';
+                    $format = $newNum;
+                    $count = $size / $formatSize[$format];
+                }
+            }
+        } else {
+            next unless $verbose;
+        }
+        # convert according to specified format
+        $val = ReadValue($valueDataPt,$valuePtr,$formatStr,$count,$size);
+
+        if ($verbose) {
+            if ($htmlDump) {
+                my ($tagName, $colName);
+                if ($tagID == 0x927c and $dirName eq 'ExifIFD') {
+                    $tagName = 'MakerNotes';
+                } elsif ($tagInfo) {
+                    $tagName = $$tagInfo{Name};
+                } else {
+                    $tagName = sprintf("Tag 0x%.4x",$tagID);
+                }
+                my $dname = sprintf("${name}-%.2d", $index);
+                # build our tool tip
+                $size < 0 and $size = $count * $formatSize[$format];
+                my $tip = sprintf("Tag ID: 0x%.4x\\n", $tagID) .
+                          "Format: $formatName[$format]\[$count]\\nSize: $size bytes\\n";
+                if ($size > 4) {
+                    my $offPt = Get32u($dataPt,$entry+8);
+                    $tip .= sprintf("Value offset: 0x%.4x\\n", $offPt);
+                    my $actPt = $valuePtr + $valueDataPos + $base - ($$exifTool{EXIF_POS} || 0);
+                    # highlight tag name (red for bad size)
+                    my $style = ($valueDataLen < 0) ? 'V' : 'H';
+                    if ($actPt != $offPt) {
+                        $tip .= sprintf("Actual offset: 0x%.4x\\n", $actPt);
+                        $style = 'F' if $style eq 'H';  # purple for different offsets
+                    }
+                    $colName = "<span class=$style>$tagName</span>";
+                } else {
+                    $colName = $tagName;
+                }
+                my $tval;
+                if ($valueDataLen < 0) {
+                    $tval = '<bad offset>';
+                } else {
+                    $tval = (length $val < 32) ? $val : substr($val,0,28) . '[...]';
+                    if ($formatStr =~ /^(string|undef|binary)/) {
+                        # translate all characters that could mess up JavaScript
+                        $tval =~ tr/\\\x00-\x1f\x7f-\xff/./;
+                        $tval =~ tr/"/'/;
+                        $tval = "$tval";
+                    } elsif ($tagInfo and Image::ExifTool::IsInt($tval)) {
+                        if ($$tagInfo{IsOffset}) {
+                            $tval = sprintf('0x%.4x', $tval);
+                        } elsif ($$tagInfo{PrintHex}) {
+                            $tval = sprintf('0x%x', $tval);
+                        }
+                    }
+                }
+                $tip .= "Value: $tval";
+                $exifTool->HtmlDump($entry+$dataPos+$base, 12, "$dname $colName", $tip, 1);
+                next if $valueDataLen < 0;  # don't process bad pointer entry
+                if ($size > 4) {
+                    my $exifDumpPos = $valuePtr + $valueDataPos + $base;
+                    # add value data block (underlining maker notes data)
+                    $exifTool->HtmlDump($exifDumpPos,$size,"$tagName value",'SAME',
+                        ($tagInfo and $$tagInfo{SubDirectory} and
+                        ($$tagInfo{MakerNotes} or $$tagInfo{DNGMakerNotes})) ? 4 : 0);
+                }
+            } else {
+                my $fstr = $formatName[$format];
+                $origFormStr and $fstr = "$origFormStr read as $fstr";
+                $exifTool->VerboseInfo($tagID, $tagInfo,
+                    Table   => $tagTablePtr,
+                    Index   => $index,
+                    Value   => $val,
+                    DataPt  => $valueDataPt,
+                    DataPos => $valueDataPos + $base,
+                    Size    => $size,
+                    Start   => $valuePtr,
+                    Format  => $fstr,
+                    Count   => $count,
+                );
+            }
+            next unless $tagInfo;
+        }
+#..............................................................................
+# Handle SubDirectory tag types
+#
+        my $subdir = $$tagInfo{SubDirectory};
+        if ($subdir) {
+            my (@values, $newTagTable, $dirNum, $newByteOrder, $invalid);
+            my $tagStr = $$tagInfo{Name};
+            if ($$subdir{MaxSubdirs}) {
+                @values = split ' ', $val;
+                # limit the number of subdirectories we parse
+                my $over = @values - $$subdir{MaxSubdirs};
+                if ($over > 0) {
+                    $exifTool->Warn("Ignoring $over $tagStr directories");
+                    pop @values while $over--;
+                }
+                $val = shift @values;
+            }
+            if ($$subdir{TagTable}) {
+                $newTagTable = GetTagTable($$subdir{TagTable});
+                $newTagTable or warn("Unknown tag table $$subdir{TagTable}"), next;
+            } else {
+                $newTagTable = $tagTablePtr;    # use existing table
+            }
+            # loop through all sub-directories specified by this tag
+            for ($dirNum=0; ; ++$dirNum) {
+                my $subdirBase = $base;
+                my $subdirDataPt = $valueDataPt;
+                my $subdirDataPos = $valueDataPos;
+                my $subdirDataLen = $valueDataLen;
+                my $subdirStart = $valuePtr;
+                if (defined $$subdir{Start}) {
+                    # set local $valuePtr relative to file $base for eval
+                    my $valuePtr = $subdirStart + $subdirDataPos;
+                    #### eval Start ($valuePtr, $val)
+                    my $newStart = eval($$subdir{Start});
+                    unless (Image::ExifTool::IsInt($newStart)) {
+                        $exifTool->Warn("Bad value for $tagStr");
+                        last;
+                    }
+                    # convert back to relative to $subdirDataPt
+                    $newStart -= $subdirDataPos;
+                    # must adjust directory size if start changed
+                    $size -= $newStart - $subdirStart unless $$subdir{BadOffset};
+                    $subdirStart = $newStart;
+                }
+                # this is a pain, but some maker notes are always a specific
+                # byte order, regardless of the byte order of the file
+                my $oldByteOrder = GetByteOrder();
+                $newByteOrder = $$subdir{ByteOrder};
+                if ($newByteOrder) {
+                    if ($newByteOrder =~ /^Little/i) {
+                        $newByteOrder = 'II';
+                    } elsif ($newByteOrder =~ /^Big/i) {
+                        $newByteOrder = 'MM';
+                    } elsif ($$subdir{OffsetPt}) {
+                        undef $newByteOrder;
+                        warn "Can't have variable byte ordering for SubDirectories using OffsetPt";
+                        last;
+                    } elsif ($subdirStart + 2 <= $subdirDataLen) {
+                        # attempt to determine the byte ordering by checking
+                        # at the number of directory entries.  This is an int16u
+                        # that should be a reasonable value.
+                        my $num = Get16u($subdirDataPt, $subdirStart);
+                        if ($num & 0xff00 and ($num>>8) > ($num&0xff)) {
+                            # This looks wrong, we shouldn't have this many entries
+                            my %otherOrder = ( II=>'MM', MM=>'II' );
+                            $newByteOrder = $otherOrder{$oldByteOrder};
+                        } else {
+                            $newByteOrder = $oldByteOrder;
+                        }
+                    }
+                } else {
+                    $newByteOrder = $oldByteOrder;
+                }
+                # set base offset if necessary
+                if ($$subdir{Base}) {
+                    # calculate subdirectory start relative to $base for eval
+                    my $start = $subdirStart + $subdirDataPos;
+                    #### eval Base ($start)
+                    $subdirBase = eval($$subdir{Base}) + $base;
+                }
+                # add offset to the start of the directory if necessary
+                if ($$subdir{OffsetPt}) {
+                    #### eval OffsetPt ($valuePtr)
+                    my $pos = eval $$subdir{OffsetPt};
+                    if ($pos + 4 > $subdirDataLen) {
+                        $exifTool->Warn("Bad $tagStr OffsetPt");
+                        last;
+                    }                    
+                    SetByteOrder($newByteOrder);
+                    $subdirStart += Get32u($subdirDataPt, $pos);
+                    SetByteOrder($oldByteOrder);
+                }
+                if ($subdirStart < 0 or $subdirStart + 2 > $subdirDataLen) {
+                    # convert $subdirStart back to a file offset
+                    $subdirStart += $subdirDataPos;
+                    my $dirOK;
+                    if ($raf) {
+                        # read the directory from the file
+                        if ($raf->Seek($subdirStart + $base,0)) {
+                            my ($buff, $buf2);
+                            if ($raf->Read($buff,2) == 2) {
+                                # get no. dir entries
+                                $size = 12 * Get16u(\$buff, 0);
+                                # read dir (plus next IFD pointer)
+                                if ($raf->Read($buf2,$size+4) >= $size) {
+                                    # set up variables to process new subdir data
+                                    $buff .= $buf2;
+                                    $size = length $buff;
+                                    $subdirDataPt = \$buff;
+                                    $subdirDataPos = $subdirStart;
+                                    $subdirDataLen = $size;
+                                    $subdirStart = 0;
+                                    $dirOK = 1;
+                                }
+                            }
+                        }
+                    }
+                    unless ($dirOK) {
+                        my $msg = "Bad $tagStr SubDirectory start";
+                        if ($verbose > 0) {
+                            if ($subdirStart < 0) {
+                                $msg .= " (directory start $subdirStart is before EXIF start)";
+                            } else {
+                                my $end = $subdirStart + $size;
+                                $msg .= " (directory end is $end but EXIF size is only $subdirDataLen)";
+                            }
+                        }
+                        $exifTool->Warn($msg);
+                        last;
+                    }
+                }
+
+                # must update subdirDataPos if $base changes for this subdirectory
+                $subdirDataPos += $base - $subdirBase;
+
+                # build information hash for new directory
+                my %subdirInfo = (
+                    Name       => $tagStr,
+                    Base       => $subdirBase,
+                    DataPt     => $subdirDataPt,
+                    DataPos    => $subdirDataPos,
+                    DataLen    => $subdirDataLen,
+                    DirStart   => $subdirStart,
+                    DirLen     => $size,
+                    RAF        => $raf,
+                    Parent     => $dirName,
+                    FixBase    => $$subdir{FixBase},
+                    FixOffsets => $$subdir{FixOffsets},
+                    EntryBased => $$subdir{EntryBased},
+                    TagInfo    => $tagInfo,
+                    SubIFD     => $$tagInfo{SubIFD},
+                );
+                if ($$tagInfo{MakerNotes}) {
+                    $subdirInfo{MakerNoteAddr} = $valuePtr + $valueDataPos + $base;
+                    $subdirInfo{NoFixBase} = 1 if $$subdir{Base};
+                }
+                # some Pentax cameras (Optio 330) write maker notes in IFD0
+                # set directory IFD name from group name of family 1 in tag information if it exists
+                if ($$tagInfo{Groups}) {
+                    $subdirInfo{DirName} = $tagInfo->{Groups}->{1};
+                    # number multiple subdirectories
+                    $dirNum and $subdirInfo{DirName} .= $dirNum;
+                }
+                SetByteOrder($newByteOrder);    # set byte order for this subdir
+                # validate the subdirectory if necessary
+                my $dirData = $subdirDataPt;    # set data pointer to be used in eval
+                #### eval Validate ($val, $dirData, $subdirStart, $size)
+                my $ok = 0;
+                if (defined $$subdir{Validate} and not eval $$subdir{Validate}) {
+                    $exifTool->Warn("Invalid $tagStr data");
+                    $invalid = 1;
+                } else {
+                    # process the subdirectory
+                    $ok = $exifTool->ProcessDirectory(\%subdirInfo, $newTagTable, $$subdir{ProcessProc});
+                }
+                # print debugging information if there were errors
+                if (not $ok and $verbose > 1 and $subdirStart != $valuePtr) {
+                    my $out = $exifTool->Options('TextOut');
+                    printf $out "%s    (SubDirectory start = 0x%x)\n", $exifTool->{INDENT}, $subdirStart;
+                }
+                SetByteOrder($oldByteOrder);    # restore original byte swapping
+
+                @values or last;
+                $val = shift @values;           # continue with next subdir
+            }
+            my $doMaker = $exifTool->Options('MakerNotes');
+            next unless $doMaker or $exifTool->{REQ_TAG_LOOKUP}->{lc($tagStr)};
+            # extract as a block if specified
+            if ($$tagInfo{MakerNotes}) {
+                # save maker note byte order (if it was significant and valid)
+                if ($$subdir{ByteOrder} and not $invalid) {
+                    $exifTool->{MAKER_NOTE_BYTE_ORDER} = 
+                        defined ($exifTool->{UnknownByteOrder}) ? 
+                                 $exifTool->{UnknownByteOrder} : $newByteOrder;
+                }
+                if ($doMaker and $doMaker eq '2') {
+                    # extract maker notes without rebuilding (no fixup information)
+                    delete $exifTool->{MAKER_NOTE_FIXUP};
+                } elsif (not $$tagInfo{NotIFD}) {
+                    # this is a pain, but we must rebuild EXIF-typemaker notes to
+                    # include all the value data if data was outside the maker notes
+                    my %makerDirInfo = (
+                        Name       => $tagStr,
+                        Base       => $base,
+                        DataPt     => $valueDataPt,
+                        DataPos    => $valueDataPos,
+                        DataLen    => $valueDataLen,
+                        DirStart   => $valuePtr,
+                        DirLen     => $size,
+                        RAF        => $raf,
+                        Parent     => $dirName,
+                        DirName    => 'MakerNotes',
+                        FixOffsets => $$subdir{FixOffsets},
+                        TagInfo    => $tagInfo,
+                    );
+                    $makerDirInfo{FixBase} = 1 if $$subdir{FixBase};
+                    # rebuild maker notes (creates $exifTool->{MAKER_NOTE_FIXUP})
+                    my $val2 = RebuildMakerNotes($exifTool, $newTagTable, \%makerDirInfo);
+                    if (defined $val2) {
+                        $val = $val2;
+                    } else {
+                        $exifTool->Warn('Error rebuilding maker notes (may be corrupt)');
+                    }
+                }
+            } else {
+                # extract this directory as a block if specified
+                next unless $$tagInfo{Writable};
+            }
+        }
+ #..............................................................................
+        # convert to absolute offsets if this tag is an offset
+        if ($$tagInfo{IsOffset}) {
+            my $offsetBase = $$tagInfo{IsOffset} eq '2' ? $firstBase : $base;
+            $offsetBase += $$exifTool{BASE};
+            my @vals = split(' ',$val);
+            foreach $val (@vals) {
+                $val += $offsetBase;
+            }
+            $val = join(' ', @vals);
+        }
+        # save the value of this tag
+        $tagKey = $exifTool->FoundTag($tagInfo, $val);
+        # set the group 1 name for tags in main table
+        if (defined $tagKey and $tagTablePtr eq \%Image::ExifTool::Exif::Main) {
+            $exifTool->SetGroup1($tagKey, $dirName);
+        }
+    }
+
+    # scan for subsequent IFD's if specified
+    if ($$dirInfo{Multi} and $bytesFromEnd >= 4) {
+        my $offset = Get32u($dataPt, $dirEnd);
+        if ($offset) {
+            my $subdirStart = $offset - $dataPos;
+            # use same directory information for trailing directory,
+            # but change the start location (ProcessDirectory will
+            # test to make sure we don't reprocess the same dir twice)
+            my %newDirInfo = %$dirInfo;
+            $newDirInfo{DirStart} = $subdirStart;
+            # increment IFD number
+            $newDirInfo{DirName} =~ s/(\d+)$//;
+            $newDirInfo{DirName} .= ($1 || 0) + 1;
+            # must validate SubIFD1 because the nextIFD pointer is invalid for some RAW formats
+            if ($newDirInfo{DirName} ne 'SubIFD1' or ValidateIFD(\%newDirInfo)) {
+                $exifTool->{INDENT} =~ s/..$//; # keep indent the same
+                $exifTool->ProcessDirectory(\%newDirInfo, $tagTablePtr) or $success = 0;
+            } elsif ($verbose or $exifTool->{TIFF_TYPE} eq 'TIFF') {
+                $exifTool->Warn('Ignored bad IFD linked from SubIFD');
+            }
+        }
+    }
+    return $success;
+}
+
+1; # end
+
+__END__
+
+=head1 NAME
+
+Image::ExifTool::Exif - Read EXIF meta information
+
+=head1 SYNOPSIS
+
+This module is required by Image::ExifTool.
+
+=head1 DESCRIPTION
+
+This module contains routines required by Image::ExifTool for processing
+EXIF meta information.
+
+=head1 AUTHOR
+
+Copyright 2003-2007, Phil Harvey (phil at owl.phy.queensu.ca)
+
+This library is free software; you can redistribute it and/or modify it
+under the same terms as Perl itself.
+
+=head1 REFERENCES
+
+=over 4
+
+=item L<http://www.exif.org/Exif2-2.PDF>
+
+=item L<http://partners.adobe.com/asn/developer/pdfs/tn/TIFF6.pdf>
+
+=item L<http://partners.adobe.com/public/developer/en/tiff/TIFFPM6.pdf>
+
+=item L<http://www.adobe.com/products/dng/pdfs/dng_spec.pdf>
+
+=item L<http://www.awaresystems.be/imaging/tiff/tifftags.html>
+
+=item L<http://www.remotesensing.org/libtiff/TIFFTechNote2.html>
+
+=item L<http://www.asmail.be/msg0054681802.html>
+
+=item L<http://park2.wakwak.com/~tsuruzoh/Computer/Digicams/exif-e.html>
+
+=item L<http://www.fine-view.com/jp/lab/doc/ps6ffspecsv2.pdf>
+
+=item L<http://www.ozhiker.com/electronics/pjmt/jpeg_info/meta.html>
+
+=item L<http://hul.harvard.edu/jhove/tiff-tags.html>
+
+=item L<http://www.microsoft.com/whdc/xps/wmphoto.mspx>
+
+=back
+
+=head1 ACKNOWLEDGEMENTS
+
+Thanks to Matt Madrid for his help with the XP character code conversions.
+
+=head1 SEE ALSO
+
+L<Image::ExifTool::TagNames/EXIF Tags>,
+L<Image::ExifTool(3pm)|Image::ExifTool>
+
+=cut
Index: /gsdl/trunk/perllib/cpan/Image/ExifTool/FLAC.pm
===================================================================
--- /gsdl/trunk/perllib/cpan/Image/ExifTool/FLAC.pm	(revision 16842)
+++ /gsdl/trunk/perllib/cpan/Image/ExifTool/FLAC.pm	(revision 16842)
@@ -0,0 +1,235 @@
+#------------------------------------------------------------------------------
+# File:         FLAC.pm
+#
+# Description:  Read Free Lossless Audio Codec information
+#
+# Revisions:    11/13/2006 - P. Harvey Created
+#
+# References:   1) http://flac.sourceforge.net/
+#------------------------------------------------------------------------------
+
+package Image::ExifTool::FLAC;
+
+use strict;
+use vars qw($VERSION);
+use Image::ExifTool qw(:DataAccess :Utils);
+
+$VERSION = '1.00';
+
+sub ProcessBitStream($$$);
+
+# FLAC metadata blocks
+%Image::ExifTool::FLAC::Main = (
+    NOTES => q{
+        Free Lossless Audio Codec (FLAC) meta information.  ExifTool also extracts
+        ID3 information from these files.
+    },
+    0 => {
+        Name => 'StreamInfo',
+        SubDirectory => { TagTable => 'Image::ExifTool::FLAC::StreamInfo' },
+    },
+    4 => {
+        Name => 'VorbisComment',
+        SubDirectory => { TagTable => 'Image::ExifTool::Vorbis::Comments' },
+    },
+);
+
+%Image::ExifTool::FLAC::StreamInfo = (
+    PROCESS_PROC => \&ProcessBitStream,
+    NOTES => 'FLAC is big-endian, so bit 0 is the high-order bit in this table.',
+    GROUPS => { 2 => 'Audio' },
+    'Bit000-015' => 'BlockSizeMin',
+    'Bit016-031' => 'BlockSizeMax',
+    'Bit032-055' => 'FrameSizeMin',
+    'Bit056-079' => 'FrameSizeMax',
+    'Bit080-099' => 'SampleRate',
+    'Bit100-102' => {
+        Name => 'Channels',
+        ValueConv => '$val + 1',
+    },
+    'Bit103-107' => {
+        Name => 'BitsPerSample',
+        ValueConv => '$val + 1',
+    },
+    'Bit108-143' => 'TotalSamples',
+);
+
+# FLAC composite tags
+%Image::ExifTool::FLAC::Composite = (
+    Duration => {
+        Require => {
+            0 => 'FLAC:SampleRate',
+            1 => 'FLAC:Channels',
+            2 => 'FLAC:TotalSamples',
+        },
+        ValueConv => '$val[0] and $val[1] and $val[2] ? $val[2] / ($val[0] * $val[1]) : undef',
+        PrintConv => 'sprintf("%.2fs",$val)',
+    },
+);
+
+# add our composite tags
+Image::ExifTool::AddCompositeTags('Image::ExifTool::FLAC');
+
+
+#------------------------------------------------------------------------------
+# Process information in a bit stream
+# Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
+# Notes: Byte order is used to determine the ordering of bits in the stream:
+# 'MM' = bit 0 is most significant, 'II' = bit 0 is least significant
+# - can handle arbitrarily wide values (ie. 8-byte or larger integers)
+sub ProcessBitStream($$$)
+{
+    my ($exifTool, $dirInfo, $tagTablePtr) = @_;
+    my $dataPt   = $$dirInfo{DataPt};
+    my $dataPos  = $$dirInfo{DataPos};
+    my $dirStart = $$dirInfo{DirStart} || 0;
+    my $dirLen   = $$dirInfo{DirLen} || (length($$dataPt) - $dirStart);
+    my $verbose  = $exifTool->Options('Verbose');
+    my $byteOrder = GetByteOrder();
+    my $tag;
+
+    if ($verbose) {
+        $exifTool->VPrint(0, "  + [BitStream directory, $dirLen bytes, '$byteOrder' order]\n");
+    }
+    foreach $tag (sort keys %$tagTablePtr) {
+        next unless $tag =~ /^Bit(\d+)-?(\d+)?/;
+        my ($b1, $b2) = ($1, $2 || $1);     # start/end bit numbers in stream
+        my ($i1, $i2) = (int($b1 / 8), int($b2 / 8)); # start/end byte numbers
+        my ($f1, $f2) = ($b1 % 8, $b2 % 8); # start/end bit numbers within each byte
+        last if $i2 >= $dirLen;
+        my $val = 0;
+        my ($i, $mask, $extra);
+        $extra = ', Mask=0x' if $verbose and ($f1 != 0 or $f2 != 7);
+        if ($byteOrder eq 'MM') {
+            # loop from high byte to low byte
+            for ($i=$i1; $i<=$i2; ++$i) {
+                $mask = 0xff;
+                if ($i == $i1 and $f1) {
+                    # mask off high bits in first word (0 is high bit)
+                    foreach ((8-$f1) .. 7) { $mask ^= (1 << $_) }
+                }
+                if ($i == $i2 and $f2 < 7) {
+                    # mask off low bits in last word (7 is low bit)
+                    foreach (0 .. (6-$f2)) { $mask ^= (1 << $_) }
+                }
+                $val = $val * 256 + ($mask & Get8u($dataPt, $i + $dirStart));
+                $extra .= sprintf('%.2x', $mask) if $extra;
+            }
+        } else {
+            # (FLAC is big-endian, but support little-endian bit streams
+            #  so this routine can be used by other modules)
+            # loop from high byte to low byte
+            for ($i=$i2; $i>=$i1; --$i) {
+                $mask = 0xff;
+                if ($i == $i1 and $f1) {
+                    # mask off low bits in first word (0 is low bit)
+                    foreach (0 .. ($f1-1)) { $mask ^= (1 << $_) }
+                }
+                if ($i == $i2 and $f2 < 7) {
+                    # mask off high bits in last word (7 is high bit)
+                    foreach (($f2+1) .. 7) { $mask ^= (1 << $_) }
+                }
+                $val = $val * 256 + ($mask & Get8u($dataPt, $i + $dirStart));
+                $extra .= sprintf('%.2x', $mask) if $extra;
+            }
+        }
+        # shift word down until low bit is in position 0
+        until ($mask & 0x01) {
+            $val /= 2;
+            $mask >>= 1;
+        }
+        $exifTool->HandleTag($tagTablePtr, $tag, $val,
+            DataPt  => $dataPt,
+            DataPos => $dataPos,
+            Start   => $dirStart + $i1,
+            Size    => $i2 - $i1 + 1,
+            Extra   => $extra,
+        );
+    }
+    return 1;
+}
+
+#------------------------------------------------------------------------------
+# Extract information from an Ogg FLAC file
+# Inputs: 0) ExifTool object reference, 1) dirInfo reference
+# Returns: 1 on success, 0 if this wasn't a valid Ogg FLAC file
+sub ProcessFLAC($$)
+{
+    my ($exifTool, $dirInfo) = @_;
+
+    # must first check for leading/trailing ID3 information
+    unless ($exifTool->{DONE_ID3}) {
+        require Image::ExifTool::ID3;
+        Image::ExifTool::ID3::ProcessID3($exifTool, $dirInfo) and return 1;
+    }
+    my $raf = $$dirInfo{RAF};
+    my $verbose = $exifTool->Options('Verbose');
+    my $out = $exifTool->Options('TextOut');
+    my ($buff, $err);
+
+    # check FLAC signature
+    $raf->Read($buff, 4) == 4 and $buff eq 'fLaC' or return 0;
+    $exifTool->SetFileType() unless $exifTool->{VALUE}->{FileType};
+    SetByteOrder('MM');
+    my $tagTablePtr = GetTagTable('Image::ExifTool::FLAC::Main');
+    for (;;) {
+        # read next metadata block header
+        $raf->Read($buff, 4) == 4 or last;
+        my $flag = unpack('C', $buff);
+        my $size = unpack('N', $buff) & 0x00ffffff;
+        $raf->Read($buff, $size) == $size or $err = 1, last;
+        my $last = $flag & 0x80;    # last-metadata-block flag
+        my $tag  = $flag & 0x7f;    # tag bits
+        if ($verbose) {
+            print $out "FLAC metadata block, type $tag:\n";
+            $exifTool->VerboseDump(\$buff, DataPos => $raf->Tell() - $size);
+        }
+        $exifTool->HandleTag($tagTablePtr, $tag, undef,
+            DataPt  => \$buff,
+            DataPos => $raf->Tell() - $size,
+        );
+        last if $last;   # all done if  is set
+    }
+    $err and $exifTool->Warn('Format error in FLAC file');
+    return 1;
+}
+
+1;  # end
+
+__END__
+
+=head1 NAME
+
+Image::ExifTool::FLAC - Read Free Lossless Audio Codec information
+
+=head1 SYNOPSIS
+
+This module is used by Image::ExifTool
+
+=head1 DESCRIPTION
+
+This module contains definitions required by Image::ExifTool to extract meta
+information from Free Lossless Audio Codec (FLAC) audio files.
+
+=head1 AUTHOR
+
+Copyright 2003-2007, Phil Harvey (phil at owl.phy.queensu.ca)
+
+This library is free software; you can redistribute it and/or modify it
+under the same terms as Perl itself.
+
+=head1 REFERENCES
+
+=over 4
+
+=item L<http://flac.sourceforge.net/>
+
+=back
+
+=head1 SEE ALSO
+
+L<Image::ExifTool::TagNames/FLAC Tags>,
+L<Image::ExifTool(3pm)|Image::ExifTool>
+
+=cut
+
Index: /gsdl/trunk/perllib/cpan/Image/ExifTool/Fixup.pm
===================================================================
--- /gsdl/trunk/perllib/cpan/Image/ExifTool/Fixup.pm	(revision 16842)
+++ /gsdl/trunk/perllib/cpan/Image/ExifTool/Fixup.pm	(revision 16842)
@@ -0,0 +1,333 @@
+#------------------------------------------------------------------------------
+# File:         Fixup.pm
+#
+# Description:  Utility to handle pointer fixups
+#
+# Revisions:    01/19/2005 - P. Harvey Created
+#               04/11/2005 - P. Harvey Allow fixups to be tagged with a marker,
+#                            and add new marker-related routines
+#               06/21/2006 - P. Harvey Patch to work with negative offsets
+#               07/07/2006 - P. Harvey Added support for 16-bit pointers
+#
+# Data Members:
+#
+#   Start     - Position in data where a zero pointer points to.
+#   Shift     - Amount to shift offsets (relative to Start).
+#   Fixups    - List of Fixup object references to to shift relative to this Fixup.
+#   Pointers  - Hash of references to fixup pointer arrays, keyed by ByteOrder
+#               string (with "2" added if pointer is 16-bit [default is 32-bit],
+#               plus "_$marker" suffix if tagged with a marker name).
+#------------------------------------------------------------------------------
+
+package Image::ExifTool::Fixup;
+
+use strict;
+use Image::ExifTool qw(GetByteOrder SetByteOrder Get32u Get32s Set32u
+                       Get16u Get16s Set16u);
+use vars qw($VERSION);
+
+$VERSION = '1.04';
+
+sub AddFixup($$;$$);
+sub ApplyFixup($$);
+sub Dump($;$);
+
+#------------------------------------------------------------------------------
+# New - create new Fixup object
+# Inputs: 0) reference to Fixup object or Fixup class name
+sub new
+{
+    local $_;
+    my $that = shift;
+    my $class = ref($that) || $that || 'Image::ExifTool::Fixup';
+    my $self = bless {}, $class;
+
+    # initialize required members
+    $self->{Start} = 0;
+    $self->{Shift} = 0;
+
+    return $self;
+}
+
+#------------------------------------------------------------------------------
+# Clone this object
+# Inputs: 0) reference to Fixup object or Fixup class name
+# Returns: reference to new Fixup object
+sub Clone($)
+{
+    my $self = shift;
+    my $clone = new Image::ExifTool::Fixup;
+    $clone->{Start} = $self->{Start};
+    $clone->{Shift} = $self->{Shift};
+    my $phash = $self->{Pointers};
+    if ($phash) {
+        $clone->{Pointers} = { };
+        my $byteOrder;
+        foreach $byteOrder (keys %$phash) {
+            my @pointers = @{$phash->{$byteOrder}};
+            $clone->{Pointers}->{$byteOrder} = \@pointers;
+        }
+    }
+    if ($self->{Fixups}) {
+        $clone->{Fixups} = [ ];
+        my $subFixup;
+        foreach $subFixup (@{$self->{Fixups}}) {
+            push @{$clone->{Fixups}}, $subFixup->Clone();
+        }
+    }
+    return $clone;
+}
+
+#------------------------------------------------------------------------------
+# Add fixup pointer or another fixup object below this one
+# Inputs: 0) Fixup object reference
+#         1) Scalar for pointer offset, or reference to Fixup object
+#         2) Optional marker name for the pointer
+#         3) Optional pointer format ('int16u' or 'int32u', defaults to 'int32u')
+# Notes: Byte ordering must be set properly for the pointer being added (must keep
+# track of the byte order of each offset since MakerNotes may have different byte order!)
+sub AddFixup($$;$$)
+{
+    my ($self, $pointer, $marker, $format) = @_;
+    if (ref $pointer) {
+        $self->{Fixups} or $self->{Fixups} = [ ];
+        push @{$self->{Fixups}}, $pointer;
+    } else {
+        my $byteOrder = GetByteOrder();
+        if (defined $format) {
+            if ($format eq 'int16u') {
+                $byteOrder .= '2';
+            } elsif ($format ne 'int32u') {
+                warn "Bad Fixup pointer format $format\n";
+            }
+        }
+        $byteOrder .= "_$marker" if defined $marker;
+        my $phash = $self->{Pointers};
+        $phash or $phash = $self->{Pointers} = { };
+        $phash->{$byteOrder} or $phash->{$byteOrder} = [ ];
+        push @{$phash->{$byteOrder}}, $pointer;
+    }
+}
+
+#------------------------------------------------------------------------------
+# fix up pointer offsets
+# Inputs: 0) Fixup object reference, 1) data reference
+# Outputs: Collapses fixup hierarchy into linear lists of fixup pointers
+sub ApplyFixup($$)
+{
+    my ($self, $dataPt) = @_;
+
+    my $start = $self->{Start};
+    my $shift = $self->{Shift} + $start;   # make shift relative to start
+    my $phash = $self->{Pointers};
+
+    # fix up pointers in this fixup
+    if ($phash and ($start or $shift)) {
+        my $saveOrder = GetByteOrder(); # save original byte ordering
+        my ($byteOrder, $ptr);
+        foreach $byteOrder (keys %$phash) {
+            SetByteOrder(substr($byteOrder,0,2));
+            # apply the fixup offset shift (must get as signed integer
+            # to avoid overflow in case it was negative before)
+            my ($get, $set) = ($byteOrder =~ /^(II2|MM2)/) ?
+                              (\&Get16s, \&Set16u) : (\&Get32s, \&Set32u);
+            foreach $ptr (@{$phash->{$byteOrder}}) {
+                $ptr += $start;         # update pointer to new start location
+                next unless $shift;
+                &$set(&$get($dataPt, $ptr) + $shift, $dataPt, $ptr);
+            }
+        }
+        SetByteOrder($saveOrder);       # restore original byte ordering
+    }
+    # recurse into contained fixups
+    if ($self->{Fixups}) {
+        # create our pointer hash if it doesn't exist
+        $phash or $phash = $self->{Pointers} = { };
+        # loop through all contained fixups
+        my $subFixup;
+        foreach $subFixup (@{$self->{Fixups}}) {
+            # adjust the subfixup start and shift
+            $subFixup->{Start} += $start;
+            $subFixup->{Shift} += $shift - $start;
+            # recursively apply contained fixups
+            ApplyFixup($subFixup, $dataPt);
+            my $shash = $subFixup->{Pointers} or next;
+            # add all pointers to our collapsed lists
+            my $byteOrder;
+            foreach $byteOrder (keys %$shash) {
+                $phash->{$byteOrder} or $phash->{$byteOrder} = [ ];
+                push @{$phash->{$byteOrder}}, @{$shash->{$byteOrder}};
+                delete $shash->{$byteOrder};
+            }
+            delete $subFixup->{Pointers};
+        }
+        delete $self->{Fixups};    # remove our contained fixups
+    }
+    # reset our Start/Shift for the collapsed fixup
+    $self->{Start} = $self->{Shift} = 0;
+}
+
+#------------------------------------------------------------------------------
+# Does specified marker exist?
+# Inputs: 0) Fixup object reference, 1) marker name
+# Returns: True if fixup contains specified marker name
+sub HasMarker($$)
+{
+    my ($self, $marker) = @_;
+    my $phash = $self->{Pointers};
+    return 0 unless $phash;
+    return 1 if grep /_$marker$/, keys %$phash;
+    return 0 unless $self->{Fixups};
+    my $subFixup;
+    foreach $subFixup (@{$self->{Fixups}}) {
+        return 1 if $subFixup->HasMarker($marker);
+    }
+    return 0;
+}
+
+#------------------------------------------------------------------------------
+# Set all marker pointers to specified value
+# Inputs: 0) Fixup object reference, 1) data reference
+#         2) marker name, 3) pointer value, 4) offset to start of data
+sub SetMarkerPointers($$$$;$)
+{
+    my ($self, $dataPt, $marker, $value, $startOffset) = @_;
+    my $start = $self->{Start} + ($startOffset || 0);
+    my $phash = $self->{Pointers};
+
+    if ($phash) {
+        my $saveOrder = GetByteOrder(); # save original byte ordering
+        my ($byteOrder, $ptr);
+        foreach $byteOrder (keys %$phash) {
+            next unless $byteOrder =~ /^(II|MM)(2?)_$marker$/;
+            SetByteOrder($1);
+            my $set = $2 ? \&Set16u : \&Set32u;
+            foreach $ptr (@{$phash->{$byteOrder}}) {
+                &$set($value, $dataPt, $ptr + $start);
+            }
+        }
+        SetByteOrder($saveOrder);       # restore original byte ordering
+    }
+    if ($self->{Fixups}) {
+        my $subFixup;
+        foreach $subFixup (@{$self->{Fixups}}) {
+            $subFixup->SetMarkerPointers($dataPt, $marker, $value, $start);
+        }
+    }
+}
+
+#------------------------------------------------------------------------------
+# Get pointer values for specified marker
+# Inputs: 0) Fixup object reference, 1) data reference,
+#         2) marker name, 3) offset to start of data
+# Returns: List of marker pointers in list context, or first marker pointer otherwise
+sub GetMarkerPointers($$$;$)
+{
+    my ($self, $dataPt, $marker, $startOffset) = @_;
+    my $start = $self->{Start} + ($startOffset || 0);
+    my $phash = $self->{Pointers};
+    my @pointers;
+
+    if ($phash) {
+        my $saveOrder = GetByteOrder();
+        my ($byteOrder, $ptr);
+        foreach $byteOrder (grep /_$marker$/, keys %$phash) {
+            SetByteOrder(substr($byteOrder,0,2));
+            my $get = ($byteOrder =~ /^(II2|MM2)/) ? \&Get16u : \&Get32u;
+            foreach $ptr (@{$phash->{$byteOrder}}) {
+                push @pointers, &$get($dataPt, $ptr + $start);
+            }
+        }
+        SetByteOrder($saveOrder);       # restore original byte ordering
+    }
+    if ($self->{Fixups}) {
+        my $subFixup;
+        foreach $subFixup (@{$self->{Fixups}}) {
+            push @pointers, $subFixup->GetMarkerPointers($dataPt, $marker, $start);
+        }
+    }
+    return @pointers if wantarray;
+    return $pointers[0];
+}
+
+#------------------------------------------------------------------------------
+# Dump fixup to console for debugging
+# Inputs: 0) Fixup object reference, 1) optional initial indent string
+sub Dump($;$)
+{
+    my ($self, $indent) = @_;
+    $indent or $indent = '';
+    printf "${indent}Fixup start=0x%x shift=0x%x\n", $self->{Start}, $self->{Shift};
+    my $phash = $self->{Pointers};
+    if ($phash) {
+        my $byteOrder;
+        foreach $byteOrder (sort keys %$phash) {
+            print "$indent  $byteOrder: ", join(' ',@{$phash->{$byteOrder}}),"\n";
+        }
+    }
+    if ($self->{Fixups}) {
+        my $subFixup;
+        foreach $subFixup (@{$self->{Fixups}}) {
+            Dump($subFixup, $indent . '  ');
+        }
+    }
+}
+
+
+1; # end
+
+__END__
+
+=head1 NAME
+
+Image::ExifTool::Fixup - Utility to handle pointer fixups
+
+=head1 SYNOPSIS
+
+    use Image::ExifTool::Fixup;
+
+    $fixup = new Image::ExifTool::Fixup;
+
+    # add a new fixup to a pointer at the specified offset in data
+    $fixup->AddFixup($offset);
+
+    # add a new Fixup object to the tree
+    $fixup->AddFixup($subFixup);
+
+    $fixup->{Start} += $shift1;   # shift pointer offsets and values
+
+    $fixup->{Shift} += $shift2;   # shift pointer values only
+
+    # recursively apply fixups to the specified data
+    $fixup->ApplyFixups(\$data);
+
+    $fixup->Dump();               # dump debugging information
+
+=head1 DESCRIPTION
+
+This module contains the code to keep track of pointers in memory and to
+shift these pointers as required.  It is used by ExifTool to maintain the
+pointers in image file directories (IFD's).
+
+=head1 NOTES
+
+Keeps track of pointers with different byte ordering, and relies on
+Image::ExifTool::GetByteOrder() to determine the current byte ordering
+when adding new pointers to a fixup.
+
+Maintains a hierarchical list of fixups so that the whole hierarchy can
+be shifted by a simple shift at the base.  Hierarchy is collapsed to a
+linear list when ApplyFixups() is called.
+
+=head1 AUTHOR
+
+Copyright 2003-2007, Phil Harvey (phil at owl.phy.queensu.ca)
+
+This library is free software; you can redistribute it and/or modify it
+under the same terms as Perl itself.
+
+=head1 SEE ALSO
+
+L<Image::ExifTool(3pm)|Image::ExifTool>
+
+=cut
Index: /gsdl/trunk/perllib/cpan/Image/ExifTool/Flash.pm
===================================================================
--- /gsdl/trunk/perllib/cpan/Image/ExifTool/Flash.pm	(revision 16842)
+++ /gsdl/trunk/perllib/cpan/Image/ExifTool/Flash.pm	(revision 16842)
@@ -0,0 +1,608 @@
+#------------------------------------------------------------------------------
+# File:         Flash.pm
+#
+# Description:  Read Shockwave Flash meta information
+#
+# Revisions:    05/16/2006 - P. Harvey Created
+#               06/07/2007 - PH Added support for FLV (Flash Video) files
+#
+# References:   1) http://www.the-labs.com/MacromediaFlash/SWF-Spec/SWFfileformat.html
+#               2) http://sswf.sourceforge.net/SWFalexref.html
+#               3) http://osflash.org/flv/
+#
+# Notes:        I'll add AMF3 support if someone sends me a FLV with AMF3 data
+#------------------------------------------------------------------------------
+
+package Image::ExifTool::Flash;
+
+use strict;
+use vars qw($VERSION);
+use Image::ExifTool qw(:DataAccess :Utils);
+use Image::ExifTool::FLAC;
+
+$VERSION = '1.02';
+
+sub ProcessMeta($$$;$);
+
+# information extracted from SWF header
+%Image::ExifTool::Flash::Main = (
+    GROUPS => { 2 => 'Video' },
+    NOTES => q{
+        The information below is extracted from the header of SWF (Shockwave Flash)
+        files.
+    },
+    FlashVersion => { },
+    Compressed   => { PrintConv => { 0 => 'False', 1 => 'True' } },
+    ImageWidth   => { },
+    ImageHeight  => { },
+    FrameRate    => { },
+    FrameCount   => { },
+    Duration => {
+        Notes => 'calculated from FrameRate and FrameCount',
+        PrintConv => 'sprintf("%.2f sec",$val)',
+    },
+);
+
+# packets in Flash Video files
+%Image::ExifTool::Flash::FLV = (
+    NOTES => q{
+        Information is extracted from the following packets in FLV (Flash Video)
+        files.
+    },
+    0x08 => {
+        Name => 'Audio',
+        BitMask => 0x04,
+        SubDirectory => { TagTable => 'Image::ExifTool::Flash::Audio' },
+    },
+    0x09 => {
+        Name => 'Video',
+        BitMask => 0x01,
+        SubDirectory => { TagTable => 'Image::ExifTool::Flash::Video' },
+    },
+    0x12 => {
+        Name => 'Meta',
+        SubDirectory => { TagTable => 'Image::ExifTool::Flash::Meta' },
+    },
+);
+
+# tags in Flash Video packet header
+%Image::ExifTool::Flash::Audio = (
+    PROCESS_PROC => \&Image::ExifTool::FLAC::ProcessBitStream,
+    GROUPS => { 2 => 'Audio' },
+    NOTES => 'Information extracted from the Flash Audio header.',
+    'Bit0-3' => {
+        Name => 'AudioEncoding',
+        PrintConv => {
+            0 => 'Uncompressed',
+            1 => 'ADPCM',
+            2 => 'MP3',
+            5 => 'Nellymoser 8kHz mono',
+            6 => 'Nellymoser',
+        },
+    },
+    'Bit4-5' => {
+        Name => 'AudioSampleRate',
+        ValueConv => {
+            0 => 5512,
+            1 => 11025,
+            2 => 22050,
+            3 => 44100,
+        },
+    },
+    'Bit6' => {
+        Name => 'AudioSampleBits',
+        ValueConv => '8 * ($val + 1)',
+    },
+    'Bit7' => {
+        Name => 'AudioChannels',
+        ValueConv => '$val + 1',
+        PrintConv => {
+            1 => '1 (mono)',
+            2 => '2 (stereo)',
+        },
+    },
+);
+
+# tags in Flash Video packet header
+%Image::ExifTool::Flash::Video = (
+    PROCESS_PROC => \&Image::ExifTool::FLAC::ProcessBitStream,
+    GROUPS => { 2 => 'Video' },
+    NOTES => 'Information extracted from the Flash Video header.',
+    'Bit4-7' => {
+        Name => 'VideoEncoding',
+        PrintConv => {
+            2 => 'Sorensen H.263',
+            3 => 'Screen video',
+            4 => 'On2 VP6',
+        },
+    },
+);
+
+# tags in Flash META packet (in ActionScript Message Format)
+%Image::ExifTool::Flash::Meta = (
+    PROCESS_PROC => \&ProcessMeta,
+    GROUPS => { 2 => 'Video' },
+    NOTES => q{
+        Below are a few observed FLV Meta tags, but ExifTool will attempt to extract
+        information from any tag found.
+    },
+    'audiocodecid'  => { Name => 'AudioCodecID',    Groups => { 2 => 'Audio' } },
+    'audiodatarate' => {
+        Name => 'AudioBitrate',
+        Groups => { 2 => 'Audio' },
+        ValueConv => '$val * 1000',
+        PrintConv => 'int($val + 0.5)',
+    },
+    'audiodelay'    => { Name => 'AudioDelay',      Groups => { 2 => 'Audio' } },
+    'audiosamplerate'=>{ Name => 'AudioSampleRate', Groups => { 2 => 'Audio' } },
+    'audiosamplesize'=>{ Name => 'AudioSampleSize', Groups => { 2 => 'Audio' } },
+    'audiosize'     => { Name => 'AudioSize',       Groups => { 2 => 'Audio' } },
+    'canSeekToEnd'  => 'CanSeekToEnd',
+    'creationdate'  => {
+        # (not an AMF date type in my sample)
+        Name => 'CreateDate',
+        Groups => { 2 => 'Time' },
+        ValueConv => '$val=~s/\s+$//; $val',    # trim trailing whitespace
+    },
+    'cuePoints'     => {
+        Name => 'CuePoint',
+        SubDirectory => { TagTable => 'Image::ExifTool::Flash::CuePoint' },
+    },
+    'datasize'      => 'DataSize',
+    'duration' => {
+        Name => 'Duration',
+        PrintConv => 'sprintf("%.3fs",$val)',
+    },
+    'filesize'      => 'FileSizeBytes',
+    'framerate'     => {
+        Name => 'FrameRate',
+        PrintConv => 'int($val * 1000 + 0.5) / 1000',
+    },
+    'hasAudio'      => { Name => 'HasAudio',        Groups => { 2 => 'Audio' } },
+    'hasCuePoints'  => 'HasCuePoints',
+    'hasKeyframes'  => 'HasKeyFrames',
+    'hasMetadata'   => 'HasMetadata',
+    'hasVideo'      => 'HasVideo',
+    'height'        => 'ImageHeight',
+    'keyframesTimes'=> 'KeyFramesTimes',
+    'keyframesFilepositions' => 'KeyFramePositions',
+    'lasttimestamp' => 'LastTimeStamp',
+    'lastkeyframetimestamp' => 'LastKeyFrameTime',
+    'metadatacreator'=>'MetadataCreator',
+    'metadatadate'  => {
+        Name => 'MetadataDate',
+        Groups => { 2 => 'Time' },
+        PrintConv => '$self->ConvertDateTime($val)',
+    },
+    'stereo'        => { Name => 'Stereo',          Groups => { 2 => 'Audio' } },
+    'videocodecid'  => 'VideoCodecID',
+    'videodatarate' => {
+        Name => 'VideoBitrate',
+        ValueConv => '$val * 1000',
+        PrintConv => 'int($val + 0.5)',
+    },
+    'videosize'     => 'VideoSize',
+    'width'         => 'ImageWidth',
+);
+
+# tags in Flash META CuePoint structure
+%Image::ExifTool::Flash::CuePoint = (
+    PROCESS_PROC => \&ProcessMeta,
+    GROUPS => { 2 => 'Video' },
+    NOTES => q{
+        These tag names are added to the CuePoint name to generate complete tag
+        names like "CuePoint0Name".
+    },
+    'name' => 'Name',
+    'type' => 'Type',
+    'time' => 'Time',
+    'parameters' => {
+        Name => 'Parameter',
+        SubDirectory => { TagTable => 'Image::ExifTool::Flash::Parameter' },
+    },
+);
+
+# tags in Flash META CuePoint Parameter structure
+%Image::ExifTool::Flash::Parameter = (
+    PROCESS_PROC => \&ProcessMeta,
+    GROUPS => { 2 => 'Video' },
+    NOTES => q{
+        There are no pre-defined parameter tags, but ExifTool will extract any
+        existing parameters, with tag names like "CuePoint0ParameterXxx".
+    },
+);
+
+# name lookup for known AMF data types
+my @amfType = qw(double boolean string object movieClip null undefined reference
+                 mixedArray objectEnd array date longString unsupported recordSet
+                 XML typedObject AMF3data);
+
+# test for AMF structure types (object, mixed array or typed object)
+my %isStruct = ( 0x03 => 1, 0x08 => 1, 0x10 => 1 );
+
+#------------------------------------------------------------------------------
+# Process Flash Video AMF Meta packet (ref 3)
+# Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
+#         3) Set to extract single type/value only
+# Returns: 1 on success, (or type/value if extracting single value)
+# Notes: Updates DataPos in dirInfo if extracting single value
+sub ProcessMeta($$$;$)
+{
+    my ($exifTool, $dirInfo, $tagTablePtr, $single) = @_;
+    my $dataPt = $$dirInfo{DataPt};
+    my $dataPos = $$dirInfo{DataPos};
+    my $dirLen = $$dirInfo{DirLen} || length($$dataPt);
+    my $pos = $$dirInfo{Pos} || 0;
+    my $verbose = $exifTool->Options('Verbose');
+    my ($type, $val, $rec);
+
+    $exifTool->VerboseDir('Meta') unless $single;
+
+Record: for ($rec=0; ; ++$rec) {
+        last if $pos >= $dirLen;
+        $type = ord(substr($$dataPt, $pos));
+        ++$pos;
+        if ($type == 0x00 or $type == 0x0b) {   # double or date
+            last if $pos + 8 > $dirLen;
+            $val = GetDouble($dataPt, $pos);
+            $pos += 8;
+            if ($type == 0x0b) {    # date
+                $val /= 1000;       # convert to seconds
+                my $frac = $val - int($val);    # fractional seconds
+                # get time zone
+                last if $pos + 2 > $dirLen;
+                my $tz = Get16s($dataPt, $pos);
+                $pos += 2;
+                # construct date/time string
+                $val = Image::ExifTool::ConvertUnixTime(int($val));
+                if ($frac) {
+                    $frac = sprintf('%.6f', $frac);
+                    $frac =~ s/(^0|0+$)//g;
+                    $val .= $frac;
+                }
+                # add timezone
+                if ($tz < 0) {
+                    $val .= '-';
+                    $tz *= -1;
+                } else {
+                    $val .= '+';
+                }
+                $val .= sprintf('%.2d:%.2d', int($tz/60), $tz%60);
+            }
+        } elsif ($type == 0x01) {   # boolean
+            last if $pos + 1 > $dirLen;
+            $val = Get8u($dataPt, $pos);
+            $val = { 0 => 'No', 1 => 'Yes' }->{$val} if $val < 2;
+            ++$pos;
+        } elsif ($type == 0x02) {   # string
+            last if $pos + 2 > $dirLen;
+            my $len = Get16u($dataPt, $pos);
+            last if $pos + 2 + $len > $dirLen;
+            $val = substr($$dataPt, $pos + 2, $len);
+            $pos += 2 + $len;
+        } elsif ($isStruct{$type}) {   # object, mixed array or typed object
+            $exifTool->VPrint(1, "  + [$amfType[$type]]\n");
+            my $getName;
+            $val = '';  # dummy value
+            if ($type == 0x08) {        # mixed array
+                # skip last array index for mixed array
+                last if $pos + 4 > $dirLen;
+                $pos += 4;
+            } elsif ($type == 0x10) {   # typed object
+                $getName = 1;
+            }
+            for (;;) {
+                # get tag ID (or typed object name)
+                last Record if $pos + 2 > $dirLen;
+                my $len = Get16u($dataPt, $pos);
+                if ($pos + 2 + $len > $dirLen) {
+                    $exifTool->Warn("Truncated $amfType[$type] record");
+                    last Record;
+                }
+                my $tag = substr($$dataPt, $pos + 2, $len);
+                $pos += 2 + $len;
+                # first string of a typed object is the object name
+                if ($getName) {
+                    $exifTool->VPrint(1,"  | (object name '$tag')\n");
+                    undef $getName;
+                    next; # (ignore name for now)
+                }
+                my $subTablePtr = $tagTablePtr;
+                my $tagInfo = $$subTablePtr{$tag};
+                # switch to subdirectory table if necessary
+                if ($tagInfo and $$tagInfo{SubDirectory}) {
+                    $tag = $$tagInfo{Name}; # use our name for the tag
+                    $subTablePtr = GetTagTable($tagInfo->{SubDirectory}->{TagTable});
+                }
+                # get object value
+                my $valPos = $pos + 1;
+                $$dirInfo{Pos} = $pos;
+                my $structName = $$dirInfo{StructName};
+                # add structure name to start of tag name
+                $tag = $structName . ucfirst($tag) if defined $structName;
+                $$dirInfo{StructName} = $tag;       # set new structure name
+                my ($t, $v) = ProcessMeta($exifTool, $dirInfo, $subTablePtr, 1);
+                $$dirInfo{StructName} = $structName;# restore original structure name
+                $pos = $$dirInfo{Pos};  # update to new position in packet
+                # all done if this value contained tags
+                last Record unless defined $t and defined $v;
+                next if $isStruct{$t};  # already handled tags in sub-structures
+                next if ref($v) eq 'ARRAY' and not @$v; # ignore empty arrays
+                last if $t == 0x09; # (end of object)
+                if (not $$subTablePtr{$tag} and $tag =~ /^\w+$/) {
+                    Image::ExifTool::AddTagToTable($subTablePtr, $tag, { Name => ucfirst($tag) });
+                    $verbose > 1 and $exifTool->VPrint(1, "  | (adding $tag)\n");
+                }
+                $exifTool->HandleTag($subTablePtr, $tag, $v,
+                    DataPt  => $dataPt,
+                    DataPos => $dataPos,
+                    Start   => $valPos,
+                    Size    => $pos - $valPos,
+                    Format  => $amfType[$t] || sprintf('0x%x',$t),
+                );
+            }
+      # } elsif ($type == 0x04) {   # movie clip (not supported)
+        } elsif ($type == 0x05 or $type == 0x06 or $type == 0x09 or $type == 0x0d) {
+            # null, undefined, dirLen of object, or unsupported
+            $val = '';
+        } elsif ($type == 0x07) {   # reference
+            last if $pos + 2 > $dirLen;
+            $val = Get16u($dataPt, $pos);
+            $pos += 2;
+        } elsif ($type == 0x0a) {   # array
+            last if $pos + 4 > $dirLen;
+            my $num = Get32u($dataPt, $pos);
+            $$dirInfo{Pos} = $pos + 4;
+            my ($i, @vals);
+            # add array index to compount tag name
+            my $structName = $$dirInfo{StructName};
+            for ($i=0; $i<$num; ++$i) {
+                $$dirInfo{StructName} = $structName . $i if defined $structName;
+                my ($t, $v) = ProcessMeta($exifTool, $dirInfo, $tagTablePtr, 1);
+                last Record unless defined $v;
+                # save value unless contained in a sub-structure
+                push @vals, $v unless $isStruct{$t};
+            }
+            $$dirInfo{StructName} = $structName;
+            $pos = $$dirInfo{Pos};
+            $val = \@vals;
+        } elsif ($type == 0x0c or $type == 0x0f) {  # long string or XML
+            last if $pos + 4 > $dirLen;
+            my $len = Get32u($dataPt, $pos);
+            last if $pos + 4 + $len > $dirLen;
+            $val = substr($$dataPt, $pos + 4, $len);
+            $pos += 4 + $len;
+      # } elsif ($type == 0x0e) {   # record set (not supported)
+      # } elsif ($type == 0x11) {   # AMF3 data (can't add support for this without a test sample)
+        } else {
+            my $t = $amfType[$type] || sprintf('type 0x%x',$type);
+            $exifTool->Warn("AMF $t record not yet supported");
+            undef $type;    # (so we don't print another warning)
+            last;           # can't continue
+        }
+        last if $single;        # all done if extracting single value
+        unless ($isStruct{$type}) {
+            # only process "onMetaData" Meta packets
+            if ($type == 0x02 and not $rec) {
+                my $verb = ($val eq 'onMetaData') ? 'processing' : 'ignoring';
+                $exifTool->VPrint(0, "  | ($verb $val information)\n");
+                last unless $val eq 'onMetaData';
+            } else {
+                # give verbose indication if we ignore a lone value
+                my $t = $amfType[$type] || sprintf('type 0x%x',$type);
+                $exifTool->VPrint(1, "  | (ignored lone $t value '$val')\n");
+            }
+        }
+    }
+    if (not defined $val and defined $type) {
+        $exifTool->Warn(sprintf("Truncated AMF record 0x%x",$type));
+    }
+    return 1 unless $single;    # all done
+    $$dirInfo{Pos} = $pos;      # update position
+    return($type,$val);         # return single type/value pair
+}
+
+#------------------------------------------------------------------------------
+# Read information frame a Flash Video file
+# Inputs: 0) ExifTool object reference, 1) Directory information reference
+# Returns: 1 on success, 0 if this wasn't a valid Flash Video file
+sub ProcessFLV($$)
+{
+    my ($exifTool, $dirInfo) = @_;
+    my $verbose = $exifTool->Options('Verbose');
+    my $raf = $$dirInfo{RAF};
+    my $buff;
+
+    $raf->Read($buff, 9) == 9 or return 0;
+    $buff =~ /^FLV\x01/ or return 0;
+    SetByteOrder('MM');
+    $exifTool->SetFileType();
+    my ($flags, $offset) = unpack('x4CN', $buff);
+    $raf->Seek($offset-9, 1) or return 1 if $offset > 9;
+    $flags &= 0x05; # only look for audio/video
+    my $found = 0;
+    my $tagTablePtr = GetTagTable('Image::ExifTool::Flash::FLV');
+    for (;;) {
+        $raf->Read($buff, 15) == 15 or last;
+        my $len = unpack('x4N', $buff);
+        my $type = $len >> 24;
+        $len &= 0x00ffffff;
+        my $tagInfo = $exifTool->GetTagInfo($tagTablePtr, $type);
+        if ($verbose > 1) {
+            my $name = $tagInfo ? $$tagInfo{Name} : "type $type";
+            $exifTool->VPrint(1, "FLV $name packet, len $len\n");
+        }
+        undef $buff;
+        if ($tagInfo and $$tagInfo{SubDirectory}) {
+            my $mask = $$tagInfo{BitMask};
+            if ($mask) {
+                # handle audio or video packet
+                unless ($found & $mask) {
+                    $found |= $mask;
+                    $flags &= ~$mask;
+                    if ($len>=1 and $raf->Read($buff, 1) == 1) {
+                        $len -= 1;
+                    } else {
+                        $exifTool->Warn("Bad $$tagInfo{Name} packet");
+                        last;
+                    }
+                }
+            } elsif ($raf->Read($buff, $len) == $len) {
+                $len = 0;
+            } else {
+                $exifTool->Warn('Truncated Meta packet');
+                last;
+            }
+        }
+        if (defined $buff) {
+            $exifTool->HandleTag($tagTablePtr, $type, undef,
+                DataPt  => \$buff,
+                DataPos => $raf->Tell() - length($buff),
+            );
+        }
+        last unless $flags;
+        $raf->Seek($len, 1) or last if $len;
+    }
+    return 1;
+}
+
+#------------------------------------------------------------------------------
+# Found a Flash tag
+# Inputs: 0) ExifTool object ref, 1) tag name, 2) tag value
+sub FoundFlashTag($$$)
+{
+    my ($exifTool, $tag, $val) = @_;
+    $exifTool->HandleTag(\%Image::ExifTool::Flash::Main, $tag, $val);
+}
+
+#------------------------------------------------------------------------------
+# Read information frame a Flash file
+# Inputs: 0) ExifTool object reference, 1) Directory information reference
+# Returns: 1 on success, 0 if this wasn't a valid Flash file
+sub ProcessSWF($$)
+{
+    my ($exifTool, $dirInfo) = @_;
+    my $verbose = $exifTool->Options('Verbose');
+    my $raf = $$dirInfo{RAF};
+    my $buff;
+
+    $raf->Read($buff, 8) == 8 or return 0;
+    $buff =~ /^(F|C)WS([^\0])/ or return 0;
+    my ($compressed, $vers) = ($1 eq 'C' ? 1 : 0, ord($2));
+
+    # read the first bit of the file
+    $raf->Read($buff, 64) or return 0;
+
+    $exifTool->SetFileType();
+    GetTagTable('Image::ExifTool::Flash::Main');  # make sure table is initialized
+
+    FoundFlashTag($exifTool, FlashVersion => $vers);
+    FoundFlashTag($exifTool, Compressed => $compressed);
+
+    # uncompress if necessary
+    if ($compressed) {
+        unless (eval 'require Compress::Zlib') {
+            $exifTool->Warn('Install Compress::Zlib to extract compressed information');
+            return 1;
+        }
+        my $inflate = Compress::Zlib::inflateInit();
+        my $tmp = $buff;
+        $buff = '';
+        # read file 64 bytes at a time and inflate until we get enough uncompressed data
+        for (;;) {
+            unless ($inflate) {
+                $exifTool->Warn('Error inflating compressed Flash data');
+                return 1;
+            }
+            my ($dat, $stat) = $inflate->inflate($tmp);
+            if ($stat == Compress::Zlib::Z_STREAM_END() or
+                $stat == Compress::Zlib::Z_OK())
+            {
+                $buff .= $dat;  # add inflated data to buffer
+                last if length $buff >= 64 or $stat == Compress::Zlib::Z_STREAM_END();
+                $raf->Read($tmp,64) or last;    # read some more data
+            } else {
+                undef $inflate; # issue warning the next time around
+            }
+        }
+    }
+    # unpack elements of bit-packed Flash Rect structure
+    my ($nBits, $totBits, $nBytes);
+    for (;;) {
+        if (length($buff)) {
+            $nBits = unpack('C', $buff) >> 3;    # bits in x1,x2,y1,y2 elements
+            $totBits = 5 + $nBits * 4;           # total bits in Rect structure
+            $nBytes = int(($totBits + 7) / 8);   # byte length of Rect structure
+            last if length $buff >= $nBytes + 4; # make sure header is long enough
+        }
+        $exifTool->Warn('Truncated Flash file');
+        return 1;
+    }
+    my $bits = unpack("B$totBits", $buff);
+    # isolate Rect elements and convert from ASCII bit strings to integers
+    my @vals = unpack('x5' . "a$nBits" x 4, $bits);
+    # (do conversion the hard way because oct("0b$val") requires Perl 5.6)
+    map { $_ = unpack('N', pack('B32', '0' x (32 - length $_) . $_)) } @vals;
+
+    # calculate and store ImageWidth/Height
+    FoundFlashTag($exifTool, ImageWidth  => ($vals[1] - $vals[0]) / 20);
+    FoundFlashTag($exifTool, ImageHeight => ($vals[3] - $vals[2]) / 20);
+
+    # get frame rate and count
+    @vals = unpack("x${nBytes}v2", $buff);
+    FoundFlashTag($exifTool, FrameRate => $vals[0] / 256);
+    FoundFlashTag($exifTool, FrameCount => $vals[1]);
+    FoundFlashTag($exifTool, Duration => $vals[1] * 256 / $vals[0]) if $vals[0];
+
+    return 1;
+}
+
+1;  # end
+
+__END__
+
+=head1 NAME
+
+Image::ExifTool::Flash - Read Shockwave Flash meta information
+
+=head1 SYNOPSIS
+
+This module is used by Image::ExifTool
+
+=head1 DESCRIPTION
+
+This module contains definitions required by Image::ExifTool to read SWF
+(Shockwave Flash) and FLV (Flash Video) files.
+
+=head1 NOTES
+
+Flash Video AMF3 support has not yet been added because I haven't yet found
+a FLV file containing AMF3 information.  If someone sends me a sample then I
+will add AMF3 support.
+
+=head1 AUTHOR
+
+Copyright 2003-2007, Phil Harvey (phil at owl.phy.queensu.ca)
+
+This library is free software; you can redistribute it and/or modify it
+under the same terms as Perl itself.
+
+=head1 REFERENCES
+
+=over 4
+
+=item L<http://www.the-labs.com/MacromediaFlash/SWF-Spec/SWFfileformat.html>
+
+=item L<http://sswf.sourceforge.net/SWFalexref.html>
+
+=item L<http://osflash.org/flv/>
+
+=back
+
+=head1 SEE ALSO
+
+L<Image::ExifTool::TagNames/Flash Tags>,
+L<Image::ExifTool(3pm)|Image::ExifTool>
+
+=cut
+
Index: /gsdl/trunk/perllib/cpan/Image/ExifTool/FlashPix.pm
===================================================================
--- /gsdl/trunk/perllib/cpan/Image/ExifTool/FlashPix.pm	(revision 16842)
+++ /gsdl/trunk/perllib/cpan/Image/ExifTool/FlashPix.pm	(revision 16842)
@@ -0,0 +1,1494 @@
+#------------------------------------------------------------------------------
+# File:         FlashPix.pm
+#
+# Description:  Read FlashPix meta information
+#
+# Revisions:    05/29/2006 - P. Harvey Created
+#
+# References:   1) http://www.exif.org/Exif2-2.PDF
+#               2) http://www.graphcomp.com/info/specs/livepicture/fpx.pdf
+#               3) http://search.cpan.org/~jdb/libwin32/
+#               4) http://msdn2.microsoft.com/en-us/library/aa380374.aspx
+#------------------------------------------------------------------------------
+
+package Image::ExifTool::FlashPix;
+
+use strict;
+use vars qw($VERSION);
+use Image::ExifTool qw(:DataAccess :Utils);
+use Image::ExifTool::Exif;
+use Image::ExifTool::ASF;   # for GetGUID()
+
+$VERSION = '1.04';
+
+sub ProcessFPX($$);
+sub ProcessFPXR($$$);
+sub ProcessProperties($$$);
+sub ReadFPXValue($$$$$;$);
+sub ConvertTimeSpan($);
+sub ProcessHyperlinks($$);
+
+# sector type constants
+sub HDR_SIZE           () { 512; }
+sub DIF_SECT           () { 0xfffffffc; }
+sub FAT_SECT           () { 0xfffffffd; }
+sub END_OF_CHAIN       () { 0xfffffffe; }
+sub FREE_SECT          () { 0xffffffff; }
+
+# format flags
+sub VT_VECTOR          () { 0x1000; }
+sub VT_ARRAY           () { 0x2000; }
+sub VT_BYREF           () { 0x4000; }
+sub VT_RESERVED        () { 0x8000; }
+
+# other constants
+sub VT_VARIANT         () { 12; }
+sub VT_LPSTR           () { 30; }
+
+# list of OLE format codes (unsupported codes commented out)
+my %oleFormat = (
+    0  => undef,        # VT_EMPTY
+    1  => undef,        # VT_NULL
+    2  => 'int16s',     # VT_I2
+    3  => 'int32s',     # VT_I4
+    4  => 'float',      # VT_R4
+    5  => 'double',     # VT_R8
+    6  => undef,        # VT_CY
+    7  => 'VT_DATE',    # VT_DATE (double, number of days since Dec 30, 1899)
+    8  => 'VT_BSTR',    # VT_BSTR (int32u count, followed by binary string)
+#   9  => 'VT_DISPATCH',
+    10 => 'int32s',     # VT_ERROR
+    11 => 'int16s',     # VT_BOOL
+    12 => 'VT_VARIANT', # VT_VARIANT
+#   13 => 'VT_UNKNOWN',
+#   14 => 'VT_DECIMAL',
+    16 => 'int8s',      # VT_I1
+    17 => 'int8u',      # VT_UI1
+    18 => 'int16u',     # VT_UI2
+    19 => 'int32u',     # VT_UI4
+    20 => 'int64s',     # VT_I8
+    21 => 'int64u',     # VT_UI8
+#   22 => 'VT_INT',
+#   23 => 'VT_UINT',
+#   24 => 'VT_VOID',
+#   25 => 'VT_HRESULT',
+#   26 => 'VT_PTR',
+#   27 => 'VT_SAFEARRAY',
+#   28 => 'VT_CARRAY',
+#   29 => 'VT_USERDEFINED',
+    30 => 'VT_LPSTR',   # VT_LPSTR (int32u count, followed by string)
+    31 => 'VT_LPWSTR',  # VT_LPWSTR (int32u word count, followed by Unicode string)
+    64 => 'VT_FILETIME',# VT_FILETIME (int64u, number of nanoseconds since Jan 1, 1601)
+    65 => 'VT_BLOB',    # VT_BLOB
+#   66 => 'VT_STREAM',
+#   67 => 'VT_STORAGE',
+#   68 => 'VT_STREAMED_OBJECT',
+#   69 => 'VT_STORED_OBJECT',
+#   70 => 'VT_BLOB_OBJECT',
+    71 => 'VT_CF',      # VT_CF
+    72 => 'VT_CLSID',   # VT_CLSID
+);
+
+# OLE flag codes (high nibble of property type)
+my %oleFlags = (
+    0x1000 => 'VT_VECTOR',
+    0x2000 => 'VT_ARRAY',   # not yet supported
+    0x4000 => 'VT_BYREF',   # ditto
+    0x8000 => 'VT_RESERVED',
+);
+
+# byte sizes for supported VT_* format and flag types
+my %oleFormatSize = (
+    VT_DATE     => 8,
+    VT_BSTR     => 4,   # (+ string length)
+    VT_VARIANT  => 4,   # (+ data length)
+    VT_LPSTR    => 4,   # (+ string length)
+    VT_LPWSTR   => 4,   # (+ string character length)
+    VT_FILETIME => 8,
+    VT_BLOB     => 4,   # (+ data length)
+    VT_CF       => 4,   # (+ data length)
+    VT_CLSID    => 16,
+    VT_VECTOR   => 4,   # (+ vector elements)
+);
+
+# names for each type of directory entry
+my @dirEntryType = qw(INVALID STORAGE STREAM LOCKBYTES PROPERTY ROOT);
+
+%Image::ExifTool::FlashPix::Main = (
+    PROCESS_PROC => \&ProcessFPXR,
+    GROUPS => { 2 => 'Image' },
+    NOTES => q{
+        The FlashPix file format, introduced in 1996, was developed by Kodak,
+        Hewlett-Packard and Microsoft.  Internally the FPX file structure mimics
+        that of an old DOS disk with fixed-sized "sectors" (usually 512 bytes) and a
+        "file allocation table" (FAT).  No wonder the format never became popular.
+
+        However, some of the structures used in FlashPix streams are part of the
+        EXIF specification, and are still being used in the APP2 FPXR segment of
+        JPEG images by some Kodak and Hewlett-Packard digital cameras.
+
+        ExifTool extracts FlashPix information from both FPX images and the APP2
+        FPXR segment of JPEG images.  As well, FlashPix information is extracted
+        from DOC, XLS and PPT (Microsoft Word, Excel and PowerPoint) documents since
+        the FlashPix file format is closely related to the formats of these files.
+    },
+    "\x05SummaryInformation" => {
+        Name => 'SummaryInfo',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::FlashPix::SummaryInfo',
+        },
+    },
+    "\x05DocumentSummaryInformation" => {
+        Name => 'DocumentInfo',
+        Multi => 1, # flag to process UserDefined information after this
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::FlashPix::DocumentInfo',
+        },
+    },
+    "\x01CompObj" => {
+        Name => 'CompObj',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::FlashPix::CompObj',
+            DirStart => 0x1c,   # skip stream header
+        },
+    },
+    "\x05Image Info" => {
+        Name => 'ImageInfo',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::FlashPix::ImageInfo',
+        },
+    },
+    "\x05Image Contents" => {
+        Name => 'Image',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::FlashPix::Image',
+        },
+    },
+    "ICC Profile 0001" => {
+        Name => 'ICC_Profile',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::ICC_Profile::Main',
+            DirStart => 0x1c,   # skip stream header
+        },
+    },
+    "\x05Extension List" => {
+        Name => 'Extensions',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::FlashPix::Extensions',
+        },
+    },
+    'Subimage 0000 Header' => {
+        Name => 'SubimageHdr',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::FlashPix::SubimageHdr',
+            DirStart => 0x1c,   # skip stream header
+        },
+    },
+#   'Subimage 0000 Data'
+    "\x05Data Object" => {  # plus instance number (ie. " 000000")
+        Name => 'DataObject',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::FlashPix::DataObject',
+        },
+    },
+#   "\x05Data Object Store" => { # plus instance number (ie. " 000000")
+    "\x05Transform" => {    # plus instance number (ie. " 000000")
+        Name => 'Transform',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::FlashPix::Transform',
+        },
+    },
+    "\x05Operation" => {    # plus instance number (ie. " 000000")
+        Name => 'Operation',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::FlashPix::Operation',
+        },
+    },
+    "\x05Global Info" => {
+        Name => 'GlobalInfo',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::FlashPix::GlobalInfo',
+        },
+    },
+    "\x05Screen Nail" => { # plus class ID (ie. "_bd0100609719a180")
+        Name => 'ScreenNail',
+        Groups => { 2 => 'Other' },
+        # strip off stream header
+        ValueConv => 'length($val) > 0x1c and $val = substr($val, 0x1c); \$val',
+    },
+    "\x05Audio Info" => {
+        Name => 'AudioInfo',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::FlashPix::AudioInfo',
+        },
+    },
+    'Audio Stream' => { # plus instance number (ie. " 000000")
+        Name => 'AudioStream',
+        Groups => { 2 => 'Audio' },
+        # strip off stream header
+        ValueConv => 'length($val) > 0x1c and $val = substr($val, 0x1c); \$val',
+    },
+    "Current User" => { #PH
+        Name => 'CurrentUser',
+        # not sure what the rest of this data is, but extract ASCII name from it - PH
+        ValueConv => q{
+            return undef if length $val < 12;
+            my ($size,$pos) = unpack('x4VV', $val);
+            my $len = $size - $pos - 4;
+            return undef if $len < 0 or length $val < $size + 8;
+            return substr($val, 8 + $pos, $len);
+        },
+    },
+);
+
+# Summary Information properties
+%Image::ExifTool::FlashPix::SummaryInfo = (
+    PROCESS_PROC => \&ProcessProperties,
+    GROUPS => { 2 => 'Image' },
+    NOTES => q{
+        The Dictionary, CodePage and LocalIndicator tags are common to all FlashPix
+        property tables, even though they are only listed in the SummaryInfo table.
+    },
+    0x00 => { Name => 'Dictionary', Groups => { 2 => 'Other' }, Binary => 1 },
+    0x01 => { Name => 'CodePage',   Groups => { 2 => 'Other' } },
+    0x02 => 'Title',
+    0x03 => 'Subject',
+    0x04 => { Name => 'Author',     Groups => { 2 => 'Author' } },
+    0x05 => 'Keywords',
+    0x06 => 'Comments',
+    0x07 => 'Template',
+    0x08 => { Name => 'LastSavedBy',Groups => { 2 => 'Author' } },
+    0x09 => 'RevisionNumber',
+    0x0a => { Name => 'TotalEditTime', PrintConv => \&ConvertTimeSpan },
+    0x0b => 'LastPrinted',
+    0x0c => { Name => 'CreateDate', Groups => { 2 => 'Time' } },
+    0x0d => { Name => 'ModifyDate', Groups => { 2 => 'Time' } },
+    0x0e => 'PageCount',
+    0x0f => 'WordCount',
+    0x10 => 'CharCount',
+    0x11 => { Name => 'ThumbnailClip', Binary => 1 },
+    0x12 => 'Software',
+    0x13 => 'Security',
+    0x80000000 => { Name => 'LocaleIndicator', Groups => { 2 => 'Other' } },
+);
+
+# Document Summary Information properties (ref 4)
+%Image::ExifTool::FlashPix::DocumentInfo = (
+    PROCESS_PROC => \&ProcessProperties,
+    GROUPS => { 2 => 'Document' },
+    NOTES => q{
+        The DocumentSummaryInformation property set includes a UserDefined property
+        set for which only the Hyperlinks and HyperlinkBase tags are pre-defined. 
+        However, ExifTool will also extract any other information found in the
+        UserDefined properties.
+    },
+    0x02 => 'Category',
+    0x03 => 'PresentationTarget',
+    0x04 => 'Bytes',
+    0x05 => 'Lines',
+    0x06 => 'Paragraphs',
+    0x07 => 'Slides',
+    0x08 => 'Notes',
+    0x09 => 'HiddenSlides',
+    0x0a => 'MMClips',
+    0x0b => 'ScaleCrop',
+    0x0c => 'HeadingPairs',
+    0x0d => 'TitleOfParts',
+    0x0e => 'Manager',
+    0x0f => 'Company',
+    0x10 => 'LinksUpToDate',
+    0x11 => 'CharCountWithSpaces',
+  # 0x12 ?
+    0x13 => 'SharedDoc', #PH (unconfirmed)
+  # 0x14 ?
+  # 0x15 ?
+    0x16 => 'HyperlinksChanged',
+    0x17 => { #PH (unconfirmed)
+        Name => 'AppVersion',
+        # (not sure what the lower 16 bits mean, so print them in hex inside brackets)
+        ValueConv => 'sprintf("%d (%.4x)",$val >> 16, $val & 0xffff)',
+    },
+   '_PID_LINKBASE' => 'HyperlinkBase',
+   '_PID_HLINKS' => {
+        Name => 'Hyperlinks',
+        RawConv => \&ProcessHyperlinks,
+    },
+);
+
+# Image Information properties
+%Image::ExifTool::FlashPix::ImageInfo = (
+    PROCESS_PROC => \&ProcessProperties,
+    GROUPS => { 2 => 'Image' },
+    0x21000000 => {
+        Name => 'FileSource',
+        PrintConv => {
+            1 => 'Film Scanner',
+            2 => 'Reflection Print Scanner',
+            3 => 'Digital Camera',
+            4 => 'Video Capture',
+            5 => 'Computer Graphics',
+        },
+    },
+    0x21000001 => {
+        Name => 'SceneType',
+        PrintConv => {
+            1 => 'Original Scene',
+            2 => 'Second Generation Scene',
+            3 => 'Digital Scene Generation',
+        },
+    },
+    0x21000002 => 'CreationPathVector',
+    0x21000003 => 'SoftwareRelease',
+    0x21000004 => 'UserDefinedID',
+    0x21000005 => 'SharpnessApproximation',
+    0x22000000 => { Name => 'Copyright',                 Groups => { 2 => 'Author' } },
+    0x22000001 => { Name => 'OriginalImageBroker',       Groups => { 2 => 'Author' } },
+    0x22000002 => { Name => 'DigitalImageBroker',        Groups => { 2 => 'Author' } },
+    0x22000003 => { Name => 'Authorship',                Groups => { 2 => 'Author' } },
+    0x22000004 => { Name => 'IntellectualPropertyNotes', Groups => { 2 => 'Author' } },
+    0x23000000 => {
+        Name => 'TestTarget',
+        PrintConv => {
+            1 => 'Color Chart',
+            2 => 'Gray Card',
+            3 => 'Grayscale',
+            4 => 'Resolution Chart',
+            5 => 'Inch Scale',
+            6 => 'Centimeter Scale',
+            7 => 'Millimeter Scale',
+            8 => 'Micrometer Scale',
+        },
+    },
+    0x23000002 => 'GroupCaption',
+    0x23000003 => 'CaptionText',
+    0x23000004 => 'People',
+    0x23000007 => 'Things',
+    0x2300000A => { Name => 'DateTimeOriginal', Groups => { 2 => 'Time' } },
+    0x2300000B => 'Events',
+    0x2300000C => 'Places',
+    0x2300000F => 'ContentDescriptionNotes',
+    0x24000000 => { Name => 'Make',             Groups => { 2 => 'Camera' } },
+    0x24000001 => {
+        Name => 'Model',
+        Description => 'Camera Model Name',
+        Groups => { 2 => 'Camera' },
+    },
+    0x24000002 => { Name => 'SerialNumber',     Groups => { 2 => 'Camera' } },
+    0x25000000 => { Name => 'CreateDate',       Groups => { 2 => 'Time' } },
+    0x25000001 => {
+        Name => 'ExposureTime',
+        PrintConv => 'Image::ExifTool::Exif::PrintExposureTime($val)',
+    },
+    0x25000002 => {
+        Name => 'FNumber',
+        PrintConv => 'sprintf("%.1f",$val)',
+    },
+    0x25000003 => {
+        Name => 'ExposureProgram',
+        Groups => { 2 => 'Camera' },
+        # use PrintConv of corresponding EXIF tag
+        PrintConv => $Image::ExifTool::Exif::Main{0x8822}->{PrintConv},
+    },
+    0x25000004 => 'BrightnessValue',
+    0x25000005 => 'ExposureCompensation',
+    0x25000006 => {
+        Name => 'SubjectDistance',
+        Groups => { 2 => 'Camera' },
+        PrintConv => 'sprintf("%.3f m", $val)',
+    },
+    0x25000007 => {
+        Name => 'MeteringMode',
+        Groups => { 2 => 'Camera' },
+        PrintConv => $Image::ExifTool::Exif::Main{0x9207}->{PrintConv},
+    },
+    0x25000008 => {
+        Name => 'LightSource',
+        Groups => { 2 => 'Camera' },
+        PrintConv => $Image::ExifTool::Exif::Main{0x9208}->{PrintConv},
+    },
+    0x25000009 => {
+        Name => 'FocalLength',
+        Groups => { 2 => 'Camera' },
+        PrintConv => 'sprintf("%.1fmm",$val)',
+    },
+    0x2500000A => {
+        Name => 'MaxApertureValue',
+        Groups => { 2 => 'Camera' },
+        ValueConv => '2 ** ($val / 2)',
+        PrintConv => 'sprintf("%.1f",$val)',
+    },
+    0x2500000B => {
+        Name => 'Flash',
+        Groups => { 2 => 'Camera' },
+        PrintConv => {
+            1 => 'No Flash',
+            2 => 'Flash Fired',
+        },
+    },
+    0x2500000C => {
+        Name => 'FlashEnergy',
+        Groups => { 2 => 'Camera' },
+    },
+    0x2500000D => {
+        Name => 'FlashReturn',
+        Groups => { 2 => 'Camera' },
+        PrintConv => {
+            1 => 'Subject Outside Flash Range',
+            2 => 'Subject Inside Flash Range',
+        },
+    },
+    0x2500000E => {
+        Name => 'BackLight',
+        PrintConv => {
+            1 => 'Front Lit',
+            2 => 'Back Lit 1',
+            3 => 'Back Lit 2',
+        },
+    },
+    0x2500000F => { Name => 'SubjectLocation', Groups => { 2 => 'Camera' } },
+    0x25000010 => 'ExposureIndex',
+    0x25000011 => {
+        Name => 'SpecialEffectsOpticalFilter',
+        PrintConv => {
+            1 => 'None',
+            2 => 'Colored',
+            3 => 'Diffusion',
+            4 => 'Multi-image',
+            5 => 'Polarizing',
+            6 => 'Split-field',
+            7 => 'Star',
+        },
+    },
+    0x25000012 => 'PerPictureNotes',
+    0x26000000 => {
+        Name => 'SensingMethod',
+        Groups => { 2 => 'Camera' },
+        PrintConv => $Image::ExifTool::Exif::Main{0x9217}->{PrintConv},
+    },
+    0x26000001 => { Name => 'FocalPlaneXResolution', Groups => { 2 => 'Camera' } },
+    0x26000002 => { Name => 'FocalPlaneYResolution', Groups => { 2 => 'Camera' } },
+    0x26000003 => {
+        Name => 'FocalPlaneResolutionUnit',
+        Groups => { 2 => 'Camera' },
+        PrintConv => $Image::ExifTool::Exif::Main{0xa210}->{PrintConv},
+    },
+    0x26000004 => 'SpatialFrequencyResponse',
+    0x26000005 => 'CFAPattern',
+    0x27000001 => {
+        Name => 'FilmCategory',
+        PrintConv => {
+            1 => 'Negative B&W',
+            2 => 'Negative Color',
+            3 => 'Reversal B&W',
+            4 => 'Reversal Color',
+            5 => 'Chromagenic',
+            6 => 'Internegative B&W',
+            7 => 'Internegative Color',
+        },
+    },
+    0x26000007 => 'ISO',
+    0x26000008 => 'Opto-ElectricConvFactor',
+    0x27000000 => 'FilmBrand',
+    0x27000001 => 'FilmCategory',
+    0x27000002 => 'FilmSize',
+    0x27000003 => 'FilmRollNumber',
+    0x27000004 => 'FilmFrameNumber',
+    0x29000000 => 'OriginalScannedImageSize',
+    0x29000001 => 'OriginalDocumentSize',
+    0x29000002 => {
+        Name => 'OriginalMedium',
+        PrintConv => {
+            1 => 'Continuous Tone Image',
+            2 => 'Halftone Image',
+            3 => 'Line Art',
+        },
+    },
+    0x29000003 => {
+        Name => 'TypeOfOriginal',
+        PrintConv => {
+            1 => 'B&W Print',
+            2 => 'Color Print',
+            3 => 'B&W Document',
+            4 => 'Color Document',
+        },
+    },
+    0x28000000 => 'ScannerMake',
+    0x28000001 => 'ScannerModel',
+    0x28000002 => 'ScannerSerialNumber',
+    0x28000003 => 'ScanSoftware',
+    0x28000004 => { Name => 'ScanSoftwareRevisionDate', Groups => { 2 => 'Time' } },
+    0x28000005 => 'ServiceOrganizationName',
+    0x28000006 => 'ScanOperatorID',
+    0x28000008 => { Name => 'ScanDate',   Groups => { 2 => 'Time' } },
+    0x28000009 => { Name => 'ModifyDate', Groups => { 2 => 'Time' } },
+    0x2800000A => 'ScannerPixelSize',
+);
+
+# Image Contents properties
+%Image::ExifTool::FlashPix::Image = (
+    PROCESS_PROC => \&ProcessProperties,
+    GROUPS => { 2 => 'Image' },
+    # VARS storage is used as a hash lookup for tagID's which aren't constant.
+    # The key is a mask for significant bits of the tagID, and the value
+    # is a lookup for tagID's for which this mask is valid.
+    VARS => {
+        # ID's are different for each subimage
+        0xff00ffff => {
+            0x02000000=>1, 0x02000001=>1, 0x02000002=>1, 0x02000003=>1,
+            0x02000004=>1, 0x02000005=>1, 0x02000006=>1, 0x02000007=>1,
+            0x03000001=>1,
+        },
+    },
+    0x01000000 => 'NumberOfResolutions',
+    0x01000002 => 'ImageWidth',     # width of highest resolution image
+    0x01000003 => 'ImageHeight',
+    0x01000004 => 'DefaultDisplayHeight',
+    0x01000005 => 'DefaultDisplayWidth',
+    0x01000006 => {
+        Name => 'DisplayUnits',
+        PrintConv => {
+            0 => 'inches',
+            1 => 'meters',
+            2 => 'cm',
+            3 => 'mm',
+        },
+    },
+    0x02000000 => 'SubimageWidth',
+    0x02000001 => 'SubimageHeight',
+    0x02000002 => {
+        Name => 'SubimageColor',
+        # decode only component count and color space of first component
+        ValueConv => 'sprintf("%.2x %.4x", unpack("x4vx4v",$val))',
+        PrintConv => {
+            '01 0000' => 'Opacity Only',
+            '01 8000' => 'Opacity Only (uncalibrated)',
+            '01 0001' => 'Monochrome',
+            '01 8001' => 'Monochrome (uncalibrated)',
+            '03 0002' => 'YCbCr',
+            '03 8002' => 'YCbCr (uncalibrated)',
+            '03 0003' => 'RGB',
+            '03 8003' => 'RGB (uncalibrated)',
+            '04 0002' => 'YCbCr with Opacity',
+            '04 8002' => 'YCbCr with Opacity (uncalibrated)',
+            '04 0003' => 'RGB with Opacity',
+            '04 8003' => 'RGB with Opacity (uncalibrated)',
+        },
+    },
+    0x02000003 => {
+        Name => 'SubimageNumericalFormat',
+        PrintConv => {
+            17 => '8-bit, Unsigned',
+            18 => '16-bit, Unsigned',
+            19 => '32-bit, Unsigned',
+        },
+    },
+    0x02000004 => {
+        Name => 'DecimationMethod',
+        PrintConv => {
+            0 => 'None (Full-sized Image)',
+            8 => '8-point Prefilter',
+        },
+    },
+    0x02000005 => 'DecimationPrefilterWidth',
+    0x02000007 => 'SubimageICC_Profile',
+    0x03000001 => { Name => 'JPEGTables', Binary => 1 },
+    0x03000002 => 'MaxJPEGTableIndex',
+);
+
+# Extension List properties
+%Image::ExifTool::FlashPix::Extensions = (
+    PROCESS_PROC => \&ProcessProperties,
+    GROUPS => { 2 => 'Other' },
+    VARS => {
+        # ID's are different for each extension type
+        0x0000ffff => {
+            0x0001=>1, 0x0002=>1, 0x0003=>1, 0x0004=>1,
+            0x0005=>1, 0x0006=>1, 0x0007=>1, 0x1000=>1,
+            0x2000=>1, 0x2001=>1, 0x3000=>1, 0x4000=>1,
+        },
+        0x0000f00f => { 0x3001=>1, 0x3002=>1 },
+    },
+    0x10000000 => 'UsedExtensionNumbers',
+    0x0001 => 'ExtensionName',
+    0x0002 => 'ExtensionClassID',
+    0x0003 => {
+        Name => 'ExtensionPersistence',
+        PrintConv => {
+            0 => 'Always Valid',
+            1 => 'Invalidated By Modification',
+            2 => 'Potentially Invalidated By Modification',
+        },
+    },
+    0x0004 => { Name => 'ExtensionCreateDate', Groups => { 2 => 'Time' } },
+    0x0005 => { Name => 'ExtensionModifyDate', Groups => { 2 => 'Time' } },
+    0x0006 => 'CreatingApplication',
+    0x0007 => 'ExtensionDescription',
+    0x1000 => 'Storage-StreamPathname',
+    0x2000 => 'FlashPixStreamPathname',
+    0x2001 => 'FlashPixStreamFieldOffset',
+    0x3000 => 'PropertySetPathname',
+    0x3001 => 'PropertySetIDCodes',
+    0x3002 => 'PropertyVectorElements',
+    0x4000 => 'SubimageResolutions',
+);
+
+# Subimage Header tags
+%Image::ExifTool::FlashPix::SubimageHdr = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    FORMAT => 'int32u',
+#   0 => 'HeaderLength',
+    1 => 'SubimageWidth',
+    2 => 'SubimageHeight',
+    3 => 'SubimageTileCount',
+    4 => 'SubimageTileWidth',
+    5 => 'SubimageTileHeight',
+    6 => 'NumChannels',
+#   7 => 'TileHeaderOffset',
+#   8 => 'TileHeaderLength',
+    # ... followed by tile header table
+);
+
+# Data Object properties
+%Image::ExifTool::FlashPix::DataObject = (
+    PROCESS_PROC => \&ProcessProperties,
+    GROUPS => { 2 => 'Other' },
+    0x00010000 => 'DataObjectID',
+    0x00010002 => 'LockedPropertyList',
+    0x00010003 => 'DataObjectTitle',
+    0x00010004 => 'LastModifier',
+    0x00010005 => 'RevisionNumber',
+    0x00010006 => { Name => 'DataCreateDate', Groups => { 2 => 'Time' } },
+    0x00010007 => { Name => 'DataModifyDate', Groups => { 2 => 'Time' } },
+    0x00010008 => 'CreatingApplication',
+    0x00010100 => {
+        Name => 'DataObjectStatus',
+        PrintConv => q{
+            ($val & 0x0000ffff ? 'Exists' : 'Does Not Exist') .
+            ', ' . ($val & 0xffff0000 ? 'Not ' : '') . 'Purgeable'
+        },
+    },
+    0x00010101 => {
+        Name => 'CreatingTransform',
+        PrintConv => '$val ? $val :  "Source Image"',
+    },
+    0x00010102 => 'UsingTransforms',
+    0x10000000 => 'CachedImageHeight',
+    0x10000001 => 'CachedImageWidth',
+);
+
+# Transform properties
+%Image::ExifTool::FlashPix::Transform = (
+    PROCESS_PROC => \&ProcessProperties,
+    GROUPS => { 2 => 'Other' },
+    0x00010000 => 'TransformNodeID',
+    0x00010001 => 'OperationClassID',
+    0x00010002 => 'LockedPropertyList',
+    0x00010003 => 'TransformTitle',
+    0x00010004 => 'LastModifier',
+    0x00010005 => 'RevisionNumber',
+    0x00010006 => { Name => 'TransformCreateDate', Groups => { 2 => 'Time' } },
+    0x00010007 => { Name => 'TransformModifyDate', Groups => { 2 => 'Time' } },
+    0x00010008 => 'CreatingApplication',
+    0x00010100 => 'InputDataObjectList',
+    0x00010101 => 'OutputDataObjectList',
+    0x00010102 => 'OperationNumber',
+    0x10000000 => 'ResultAspectRatio',
+    0x10000001 => 'RectangleOfInterest',
+    0x10000002 => 'Filtering',
+    0x10000003 => 'SpatialOrientation',
+    0x10000004 => 'ColorTwistMatrix',
+    0x10000005 => 'ContrastAdjustment',
+);
+
+# Operation properties
+%Image::ExifTool::FlashPix::Operation = (
+    PROCESS_PROC => \&ProcessProperties,
+    0x00010000 => 'OperationID',
+);
+
+# Global Info properties
+%Image::ExifTool::FlashPix::GlobalInfo = (
+    PROCESS_PROC => \&ProcessProperties,
+    0x00010002 => 'LockedPropertyList',
+    0x00010003 => 'TransformedImageTitle',
+    0x00010004 => 'LastModifier',
+    0x00010100 => 'VisibleOutputs',
+    0x00010101 => 'MaximumImageIndex',
+    0x00010102 => 'MaximumTransformIndex',
+    0x00010103 => 'MaximumOperationIndex',
+);
+
+# Audio Info properties
+%Image::ExifTool::FlashPix::AudioInfo = (
+    PROCESS_PROC => \&ProcessProperties,
+    GROUPS => { 2 => 'Audio' },
+);
+
+# CompObj tags
+%Image::ExifTool::FlashPix::CompObj = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    GROUPS => { 2 => 'Other' },
+    FORMAT => 'int32u',
+    0 => { Name => 'CompObjUserTypeLen' },
+    1 => { Name => 'CompObjUserType', Format => 'string[$val{0}]' },
+);
+
+# composite FlashPix tags
+%Image::ExifTool::FlashPix::Composite = (
+    GROUPS => { 2 => 'Image' },
+    PreviewImage => {
+        # extract JPEG preview from ScreenNail if possible
+        Require => {
+            0 => 'ScreenNail',
+        },
+        Binary => 1,
+        RawConv => q{
+            return undef unless $val[0] =~ /\xff\xd8\xff/g;
+            return substr($val[0], pos($val[0])-3);
+        },
+    },
+);
+
+# add our composite tags
+Image::ExifTool::AddCompositeTags('Image::ExifTool::FlashPix');
+
+#------------------------------------------------------------------------------
+# Process hyperlinks from PID_HYPERLINKS array
+# (ref http://msdn.microsoft.com/archive/default.asp?url=/archive/en-us/dnaro97ta/html/msdn_hyper97.asp)
+# Inputs: 0) value, 1) ExifTool ref
+# Returns: list of hyperlinks
+sub ProcessHyperlinks($$)
+{
+    my ($val, $exifTool) = @_;
+
+    # process as an array of VT_VARIANT's
+    my $dirEnd = length $val;
+    return undef if $dirEnd < 4;
+    my $num = Get32u(\$val, 0);
+    my $valPos = 4;
+    my ($i, @vals);
+    for ($i=0; $i<$num; ++$i) {
+        # read VT_BLOB entries as an array of VT_VARIANT's
+        my $value = ReadFPXValue($exifTool, \$val, $valPos, VT_VARIANT, $dirEnd);
+        last unless defined $value;
+        push @vals, $value;
+    }
+    # filter values to extract only the links
+    my @links;
+    for ($i=0; $i<@vals; $i+=6) {
+        push @links, $vals[$i+4];    # get address
+        $links[-1] .= '#' . $vals[$i+5] if length $vals[$i+5]; # add subaddress
+    }
+    return \@links;
+}
+
+#------------------------------------------------------------------------------
+# Print conversion for time span value
+sub ConvertTimeSpan($)
+{
+    my $val = shift;
+    if (Image::ExifTool::IsFloat($val) and $val != 0) {
+        if ($val < 60) {
+            $val = "$val seconds";
+        } elsif ($val < 3600) {
+            $val = sprintf("%.1f minutes", $val / 60);
+        } elsif ($val < 24 * 3600) {
+            $val = sprintf("%.1f hours", $val / 3600);
+        } else {
+            $val = sprintf("%.1f days", $val / (24 * 3600));
+        }
+    }
+    return $val;
+}
+
+#------------------------------------------------------------------------------
+# Read FlashPix value
+# Inputs: 0) ExifTool ref, 1) data ref, 2) value offset, 3) FPX format number,
+#         4) end offset, 5) options: 0x01=no padding, 0x02=translate to UTF8
+# Returns: converted value (or list of values in list context) and updates
+#          value offset to end of value if successful, or returns undef on error
+sub ReadFPXValue($$$$$;$)
+{
+    my ($exifTool, $dataPt, $valPos, $type, $dirEnd, $opts) = @_;
+    $opts = 0 unless defined $opts;
+    my @vals;
+
+    my $format = $oleFormat{$type & 0x0fff};
+    while ($format) {
+        my $count = 1;
+        # handle VT_VECTOR types
+        my $flags = $type & 0xf000;
+        if ($flags) {
+            if ($flags == VT_VECTOR) {
+                $opts |= 0x01;  # values don't seem to be padded inside vectors
+                my $size = $oleFormatSize{VT_VECTOR};
+                last if $valPos + $size > $dirEnd;
+                $count = Get32u($dataPt, $valPos);
+                push @vals, '' if $count == 0;  # allow zero-element vector
+                $valPos += 4;
+            } else {
+                # can't yet handle this property flag
+                last;
+            }
+        }
+        unless ($format =~ /^VT_/) {
+            my $size = Image::ExifTool::FormatSize($format) * $count;
+            last if $valPos + $size > $dirEnd;
+            @vals = ReadValue($dataPt, $valPos, $format, $count, $size);
+            # update position to end of value plus padding
+            $valPos += ($count * $size + 3) & 0xfffffffc;
+            last;
+        }
+        my $size = $oleFormatSize{$format};
+        my ($item, $val);
+        for ($item=0; $item<$count; ++$item) {
+            last if $valPos + $size > $dirEnd;
+            if ($format eq 'VT_VARIANT') {
+                my $subType = Get32u($dataPt, $valPos);
+                $valPos += $size;
+                $val = ReadFPXValue($exifTool, $dataPt, $valPos, $subType, $dirEnd, $opts);
+                last unless defined $val;
+                push @vals, $val;
+                next;   # avoid adding $size to $valPos again
+            } elsif ($format eq 'VT_FILETIME') {
+                # get time in seconds
+                $val = 1e-7 * Image::ExifTool::Get64u($dataPt, $valPos);
+                # print as date/time if value is greater than one year (PH hack)
+                if ($val > 365 * 24 * 3600) {
+                    # shift from Jan 1, 1601 to Jan 1, 1970
+                    $val -= 134774 * 24 * 3600 if $val != 0;
+                    $val = Image::ExifTool::ConvertUnixTime($val);
+                }
+            } elsif ($format eq 'VT_DATE') {
+                $val = Image::ExifTool::GetDouble($dataPt, $valPos);
+                # shift zero from Dec 30, 1899 to Jan 1, 1970 and convert to secs
+                $val = ($val - 25569) * 24 * 3600 if $val != 0;
+                $val = Image::ExifTool::ConvertUnixTime($val);
+            } elsif ($format =~ /STR$/) {
+                my $len = Get32u($dataPt, $valPos);
+                $len *= 2 if $format eq 'VT_LPWSTR';    # convert to byte count
+                last if $valPos + $len + 4 > $dirEnd;
+                $val = substr($$dataPt, $valPos + 4, $len);
+                if ($format eq 'VT_LPWSTR') {
+                    # convert wide string from Unicode
+                    $val = $exifTool->Unicode2Charset($val);
+                } elsif ($opts & 0x02) {
+                    # convert from Latin1 to UTF-8
+                    $val = Image::ExifTool::Latin2Unicode($val,'v');
+                    $val = Image::ExifTool::Unicode2UTF8($val,'v');
+                }
+                $val =~ s/\0.*//s;  # truncate at null terminator
+                # update position for string length
+                # (the spec states that strings should be padded to align
+                #  on even 32-bit boundaries, but this isn't always the case)
+                $valPos += ($opts & 0x01) ? $len : ($len + 3) & 0xfffffffc;
+            } elsif ($format eq 'VT_BLOB' or $format eq 'VT_CF') {
+                my $len = Get32u($dataPt, $valPos);
+                last if $valPos + $len + 4 > $dirEnd;
+                $val = substr($$dataPt, $valPos + 4, $len);
+                # update position for data length plus padding
+                # (does this padding disappear in arrays too?)
+                $valPos += ($len + 3) & 0xfffffffc;
+            } elsif ($format eq 'VT_CLSID') {
+                $val = Image::ExifTool::ASF::GetGUID(substr($$dataPt, $valPos, $size));
+            }
+            $valPos += $size;   # update value pointer to end of value
+            push @vals, $val;
+        }
+        # join VT_ values with commas unless we want an array
+        @vals = ( join ', ', @vals ) if @vals > 1 and not wantarray;
+        last;   # didn't really want to loop
+    }
+    $_[2] = $valPos;    # return updated value position
+
+    if (wantarray) {
+        return @vals;
+    } elsif (@vals > 1) {
+        return join(' ', @vals);
+    } else {
+        return $vals[0];
+    }
+}
+
+#------------------------------------------------------------------------------
+# Check FPX byte order mark (BOM) and set byte order appropriately
+# Inputs: 0) data ref, 1) offset to BOM
+# Returns: true on success
+sub CheckBOM($$)
+{
+    my ($dataPt, $pos) = @_;
+    my $bom = Get16u($dataPt, $pos);
+    return 1 if $bom == 0xfffe;
+    return 0 unless $bom == 0xfeff;
+    ToggleByteOrder();
+    return 1;
+}
+
+#------------------------------------------------------------------------------
+# Process FlashPix properties
+# Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
+# Returns: 1 on success
+sub ProcessProperties($$$)
+{
+    my ($exifTool, $dirInfo, $tagTablePtr) = @_;
+    my $dataPt = $$dirInfo{DataPt};
+    my $pos = $$dirInfo{DirStart} || 0;
+    my $dirLen = $$dirInfo{DirLen} || length($$dataPt) - $pos;
+    my $dirEnd = $pos + $dirLen;
+    my $verbose = $exifTool->Options('Verbose');
+    my ($out, $n);
+
+    if ($dirLen < 48) {
+        $exifTool->Warn('Truncated FPX properties');
+        return 0;
+    }
+    # check and set our byte order if necessary
+    unless (CheckBOM($dataPt, $pos)) {
+        $exifTool->Warn('Bad FPX property byte order mark');
+        return 0;
+    }
+    # get position of start of section
+    $pos = Get32u($dataPt, $pos + 44);
+    if ($pos < 48) {
+        $exifTool->Warn('Bad FPX property section offset');
+        return 0;
+    }
+    for ($n=0; $n<2; ++$n) {
+        my %dictionary;     # dictionary to translate user-defined properties
+        my $opts = 0;       # option flags for converting values
+        last if $pos + 8 > $dirEnd;
+        # read property section header
+        my $size = Get32u($dataPt, $pos);
+        last unless $size;
+        my $numEntries = Get32u($dataPt, $pos + 4);
+        $verbose and $exifTool->VerboseDir('Property Info', $numEntries, $size);
+        if ($pos + 8 + 8 * $numEntries > $dirEnd) {
+            $exifTool->Warn('Truncated property list');
+            last;
+        }
+        my $index;
+        for ($index=0; $index<$numEntries; ++$index) {
+            my $entry = $pos + 8 + 8 * $index;
+            my $tag = Get32u($dataPt, $entry);
+            my $offset = Get32u($dataPt, $entry + 4);
+            my $valStart = $pos + 4 + $offset;
+            last if $valStart >= $dirEnd;
+            my $valPos = $valStart;
+            my $type = Get32u($dataPt, $pos + $offset);
+            if ($tag == 0) {
+                # read dictionary to get tag name lookup for this property set
+                my $i;
+                for ($i=0; $i<$type; ++$i) {
+                    last if $valPos + 8 > $dirEnd;
+                    $tag = Get32u($dataPt, $valPos);
+                    my $len = Get32u($dataPt, $valPos + 4);
+                    $valPos += 8 + $len;
+                    last if $valPos > $dirEnd;
+                    my $name = substr($$dataPt, $valPos - $len, $len);
+                    $name =~ s/\0.*//s;
+                    next unless length $name;
+                    $dictionary{$tag} = $name;
+                    next if $$tagTablePtr{$name};
+                    $tag = $name;
+                    $name =~ tr/a-zA-Z0-9//dc;
+                    next unless length $name;
+                    Image::ExifTool::AddTagToTable($tagTablePtr, $tag, { Name => ucfirst($name) });
+                }
+                next;
+            }
+            # use tag name from dictionary if available
+            my ($custom, $val);
+            if (defined $dictionary{$tag}) {
+                $tag = $dictionary{$tag};
+                $custom = 1;
+            }
+            my @vals = ReadFPXValue($exifTool, $dataPt, $valPos, $type, $dirEnd, $opts);
+            @vals or $exifTool->Warn('Error reading property value');
+            $val = @vals > 1 ? \@vals : $vals[0];
+            my $format = $type & 0x0fff;
+            my $flags = $type & 0xf000;
+            my $formStr = $oleFormat{$format} || "Type $format";
+            $formStr .= '|' . ($oleFlags{$flags} || sprintf("0x%x",$flags)) if $flags;
+            my $tagInfo;
+            # check for common tag ID's: Dictionary, CodePage and LocaleIndicator
+            # (must be done before masking because masked tags may overlap these ID's)
+            if (not $custom and ($tag == 1 or $tag == 0x80000000)) {
+                # get tagInfo from SummaryInfo table
+                my $summaryTable = GetTagTable('Image::ExifTool::FlashPix::SummaryInfo');
+                $tagInfo = $exifTool->GetTagInfo($summaryTable, $tag);
+                if ($tag == 1 and $val == 1252 and $exifTool->Options('Charset') eq 'UTF8') {
+                    # set flag to translate 8-bit text only if
+                    # code page is cp1252 and Charset is UTF8
+                    $opts |= 0x02;
+                }
+            } elsif ($$tagTablePtr{$tag}) {
+                $tagInfo = $exifTool->GetTagInfo($tagTablePtr, $tag);
+            } elsif ($$tagTablePtr{VARS} and not $custom) {
+                # mask off insignificant bits of tag ID if necessary
+                my $masked = $$tagTablePtr{VARS};
+                my $mask;
+                foreach $mask (keys %$masked) {
+                    if ($masked->{$mask}->{$tag & $mask}) {
+                        $tagInfo = $exifTool->GetTagInfo($tagTablePtr, $tag & $mask);
+                        last;
+                    }
+                }
+            }
+            $exifTool->HandleTag($tagTablePtr, $tag, $val,
+                DataPt => $dataPt,
+                Start => $valStart,
+                Size => $valPos - $valStart,
+                Format => $formStr,
+                Index => $index,
+                TagInfo => $tagInfo,
+                Extra => ", type=$type",
+            );
+        }
+        # issue warning if we hit end of property section prematurely
+        $exifTool->Warn('Truncated property data') if $index < $numEntries;
+        last unless $$dirInfo{Multi};
+        $pos += $size;
+    }
+
+    return 1;
+}
+
+#------------------------------------------------------------------------------
+# Load chain of sectors from file
+# Inputs: 0) RAF ref, 1) first sector number, 2) FAT ref, 3) sector size, 4) header size
+sub LoadChain($$$$$)
+{
+    my ($raf, $sect, $fatPt, $sectSize, $hdrSize) = @_;
+    return undef unless $raf;
+    my $chain = '';
+    my ($buff, %loadedSect);
+    for (;;) {
+        last if $sect == END_OF_CHAIN;
+        return undef if $loadedSect{$sect}; # avoid infinite loop
+        $loadedSect{$sect} = 1;
+        my $offset = $sect * $sectSize + $hdrSize;
+        return undef unless $offset <= 0x7fffffff and
+                            $raf->Seek($offset, 0) and
+                            $raf->Read($buff, $sectSize) == $sectSize;
+        $chain .= $buff;
+        # step to next sector in chain
+        return undef if $sect * 4 > length($$fatPt) - 4;
+        $sect = Get32u($fatPt, $sect * 4);
+    }
+    return $chain;
+}
+
+#------------------------------------------------------------------------------
+# Extract information from a JPEG APP2 FPXR segment
+# Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
+# Returns: 1 on success
+sub ProcessFPXR($$$)
+{
+    my ($exifTool, $dirInfo, $tagTablePtr) = @_;
+    my $dataPt = $$dirInfo{DataPt};
+    my $dirStart = $$dirInfo{DirStart};
+    my $dirLen = $$dirInfo{DirLen};
+    my $verbose = $exifTool->Options('Verbose');
+
+    if ($dirLen < 13) {
+        $exifTool->Warn('FPXR segment to small');
+        return 0;
+    }
+
+    # get version and segment type (version is 0 in all my samples)
+    my ($vers, $type) = unpack('x5C2', $$dataPt);
+
+    if ($type == 1) {   # a "Contents List" segment
+
+        $vers != 0 and $exifTool->Warn("Untested FPXR version $vers");
+        if ($$exifTool{FPXR}) {
+            $exifTool->Warn('Multiple FPXR contents lists');
+            delete $$exifTool{FPXR};
+        }
+        my $numEntries = unpack('x7n', $$dataPt);
+        my @contents;
+        $verbose and $exifTool->VerboseDir('Contents List', $numEntries);
+        my $pos = 9;
+        my $entry;
+        for ($entry = 0; $entry < $numEntries; ++$entry) {
+            if ($pos + 4 > $dirLen) {
+                $exifTool->Warn('Truncated FPXR contents');
+                return 0;
+            }
+            my ($size, $default) = unpack("x${pos}Na", $$dataPt);
+            pos($$dataPt) = $pos + 5;
+            # according to the spec, this string is little-endian
+            # (very odd, since the size word is big-endian),
+            # and the first char must be '/'
+            unless ($$dataPt =~ m{\G(/\0(..)*?)\0\0}sg) {
+                $exifTool->Warn('Invalid FPXR stream name');
+                return 0;
+            }
+            # convert stream pathname to ascii
+            my $name = Image::ExifTool::Unicode2Latin($1, 'v');
+            if ($verbose) {
+                my $psize = ($size == 0xffffffff) ? 'storage' : "$size bytes";
+                $exifTool->VPrint(0,"  |  $entry) Name: '$name' [$psize]\n");
+            }
+            # remove directory specification
+            $name =~ s{.*/}{}s;
+            # read storage class ID if necessary
+            my $classID;
+            if ($size == 0xffffffff) {
+                unless ($$dataPt =~ m{(.{16})}sg) {
+                    $exifTool->Warn('Truncated FPXR storage class ID');
+                    return 0;
+                }
+                # unpack class ID in case we want to use it sometime
+                $classID = Image::ExifTool::ASF::GetGUID($1);
+            }
+            # update position in list
+            $pos = pos($$dataPt);
+            # add to our contents list
+            push @contents, {
+                Name => $name,
+                Size => $size,
+                Default => $default,
+                ClassID => $classID,
+            };
+        }
+        # save contents list as $exifTool member variable
+        # (must do this last so we don't save list on error)
+        $$exifTool{FPXR} = \@contents;
+
+    } elsif ($type == 2) {  # a "Stream Data" segment
+
+        # get the contents list index and stream data offset
+        my ($index, $offset) = unpack('x7nN', $$dataPt);
+        my $fpxr = $$exifTool{FPXR};
+        if ($fpxr and $$fpxr[$index]) {
+            my $obj = $$fpxr[$index];
+            # extract stream data (after 13-byte header)
+            if (not defined $$obj{Stream}) {
+                # ignore offset for first segment of this type
+                # (in my sample images, this isn't always zero as one would expect)
+                $$obj{Stream} = substr($$dataPt, $dirStart+13);
+            } else {
+                # add data to the stream at the proper offset
+                my $pad = $offset - length($$obj{Stream});
+                if ($pad >= 0) {
+                    if ($pad) {
+                        if ($pad > 0x10000) {
+                            $exifTool->Warn("Bad FPXR stream offset ($offset)");
+                        } else {
+                            # pad with default value to specified offset
+                            $exifTool->Warn("Padding FPXR stream with $pad default bytes",1);
+                            $$obj{Stream} .= ($$obj{Default} x $pad);
+                        }
+                    }
+                    # concatinate data with this stream
+                    $$obj{Stream} .= substr($$dataPt, $dirStart+13);
+                } else {
+                    $exifTool->Warn("Duplicate FPXR stream data at offset $offset");
+                    substr($$obj{Stream}, $offset, -$pad) = substr($$dataPt, $dirStart+13);
+                }
+            }
+            # save value for this tag if stream is complete
+            my $len = length $$obj{Stream};
+            if ($len >= $$obj{Size}) {
+                if ($verbose) {
+                    $exifTool->VPrint(0, "  + [FPXR stream, Contents index $index, $len bytes]\n");
+                }
+                if ($len > $$obj{Size}) {
+                    $exifTool->Warn('Extra data in FPXR segment (truncated)');
+                    $$obj{Stream} = substr($$obj{Stream}, 0, $$obj{Size});
+                }
+                my $tag = $$obj{Name};
+                my $tagInfo;
+                unless ($$tagTablePtr{$tag}) {
+                    # remove instance number or class ID from tag if necessary
+                    $tagInfo = $exifTool->GetTagInfo($tagTablePtr, $1) if
+                        ($tag =~ /(.*) \d{6}$/s and $$tagTablePtr{$1}) or
+                        ($tag =~ /(.*)_[0-9a-f]{16}$/s and $$tagTablePtr{$1});
+                }
+                # save the data for this tag
+                $exifTool->HandleTag($tagTablePtr, $tag, $$obj{Stream},
+                    DataPt => \$$obj{Stream},
+                    TagInfo => $tagInfo,
+                );
+                delete $$obj{Stream}; # done with this stream
+            }
+        } else {
+            $exifTool->Warn("Unlisted FPXR segment (index $index)");
+        }
+
+    } elsif ($type ne 3) {  # not a "Reserved" segment
+
+        $exifTool->Warn("Unknown FPXR segment (type $type)");
+
+    }
+
+    # clean up if this was the last FPXR segment
+    if ($$dirInfo{LastFPXR} and $$exifTool{FPXR}) {
+        my $obj;
+        my $i = 0;
+        foreach $obj (@{$$exifTool{FPXR}}) {
+            $exifTool->Warn("Missing stream for FPXR object $i") if defined $$obj{Stream};
+            ++$i;
+        }
+        delete $$exifTool{FPXR};    # delete our temporary variables
+    }
+    return 1;
+}
+
+#------------------------------------------------------------------------------
+# Extract information from a FlashPix (FPX) file
+# Inputs: 0) ExifTool object ref, 1) dirInfo ref
+# Returns: 1 on success, 0 if this wasn't a valid FPX image
+sub ProcessFPX($$)
+{
+    my ($exifTool, $dirInfo) = @_;
+    my $raf = $$dirInfo{RAF};
+    my ($buff, $out, %dumpParms, $oldIndent, $miniStreamBuff);
+
+    # read header
+    return 0 unless $raf->Read($buff,HDR_SIZE) == HDR_SIZE;
+    # check signature
+    return 0 unless $buff =~ /^\xd0\xcf\x11\xe0\xa1\xb1\x1a\xe1/;
+
+    my $fileType = $exifTool->{FILE_EXT};
+    $fileType = 'FPX' unless $fileType and $fileType =~ /^(DOC|XLS|PPT)$/;
+    $exifTool->SetFileType($fileType);
+    SetByteOrder(substr($buff, 0x1c, 2) eq "\xff\xfe" ? 'MM' : 'II');
+    my $tagTablePtr = GetTagTable('Image::ExifTool::FlashPix::Main');
+    my $verbose = $exifTool->Options('Verbose');
+
+    my $sectSize = 1 << Get16u(\$buff, 0x1e);
+    my $miniSize = 1 << Get16u(\$buff, 0x20);
+    my $fatCount   = Get32u(\$buff, 0x2c);  # number of FAT sectors
+    my $dirStart   = Get32u(\$buff, 0x30);  # first directory sector
+    my $miniCutoff = Get32u(\$buff, 0x38);  # minimum size for big-FAT streams
+    my $miniStart  = Get32u(\$buff, 0x3c);  # first sector of mini-FAT
+    my $miniCount  = Get32u(\$buff, 0x40);  # number of mini-FAT sectors
+    my $difStart   = Get32u(\$buff, 0x44);  # first sector of DIF chain
+    my $difCount   = Get32u(\$buff, 0x48);  # number of DIF sectors
+
+    if ($verbose) {
+        $out = $exifTool->Options('TextOut');
+        $dumpParms{MaxLen} = 96 if $verbose == 3;
+        print $out "  Sector size=$sectSize\n  FAT: Count=$fatCount\n";
+        print $out "  DIR: Start=$dirStart\n";
+        print $out "  MiniFAT: Mini-sector size=$miniSize Start=$miniStart Count=$miniCount Cutoff=$miniCutoff\n";
+        print $out "  DIF FAT: Start=$difStart Count=$difCount\n";
+    }
+#
+# load the FAT
+#
+    my $pos = 0x4c;
+    my $endPos = length($buff);
+    my $fat = '';
+    my $fatCountCheck = 0;
+    for (;;) {
+        while ($pos <= $endPos - 4) {
+            my $sect = Get32u(\$buff, $pos);
+            $pos += 4;
+            next if $sect == FREE_SECT;
+            my $offset = $sect * $sectSize + HDR_SIZE;
+            my $fatSect;
+            unless ($raf->Seek($offset, 0) and
+                    $raf->Read($fatSect, $sectSize) == $sectSize)
+            {
+                $exifTool->Error("Error reading FAT from sector $sect");
+                return 1;
+            }
+            $fat .= $fatSect;
+            ++$fatCountCheck;
+        }
+        last if $difStart == END_OF_CHAIN;
+        # read next DIF (Dual Indirect FAT) sector
+        my $offset = $difStart * $sectSize + HDR_SIZE;
+        unless ($raf->Seek($offset, 0) and $raf->Read($buff, $sectSize) == $sectSize) {
+            $exifTool->Error("Error reading DIF sector $difStart");
+            return 1;
+        }
+        # set end of sector information in this DIF
+        $endPos = $sectSize - 4;
+        # next time around we want to read next DIF in chain
+        $difStart = Get32u(\$buff, $endPos);
+    }
+    if ($fatCountCheck != $fatCount) {
+        $exifTool->Warn("Bad number of FAT sectors (expected $fatCount but found $fatCountCheck)");
+    }
+#
+# load the mini-FAT and the directory
+#
+    my $miniFat = LoadChain($raf, $miniStart, \$fat, $sectSize, HDR_SIZE);
+    my $dir = LoadChain($raf, $dirStart, \$fat, $sectSize, HDR_SIZE);
+    unless (defined $miniFat and defined $dir) {
+        $exifTool->Error('Error reading mini-FAT or directory stream');
+        return 1;
+    }
+    if ($verbose) {
+        print $out "  FAT [",length($fat)," bytes]:\n";
+        Image::ExifTool::HexDump(\$fat, undef, %dumpParms) if $verbose > 2;
+        print $out "  Mini-FAT [",length($miniFat)," bytes]:\n";
+        Image::ExifTool::HexDump(\$miniFat, undef, %dumpParms) if $verbose > 2;
+        print $out "  Directory [",length($dir)," bytes]:\n";
+        Image::ExifTool::HexDump(\$dir, undef, %dumpParms) if $verbose > 2;
+    }
+#
+# process the directory
+#
+    if ($verbose) {
+        $oldIndent = $exifTool->{INDENT};
+        $exifTool->{INDENT} .= '| ';
+        $exifTool->VerboseDir('FPX', undef, length $dir);
+    }
+    my $miniStream;
+    $endPos = length($dir);
+    my $index = 0;
+
+    for ($pos=0; $pos<=$endPos-128; $pos+=128) {
+
+        # get directory entry type
+        # (0=invalid, 1=storage, 2=stream, 3=lockbytes, 4=property, 5=root)
+        my $type = Get8u(\$dir, $pos + 0x42);
+        next if $type == 0; # skip invalid entries
+        if ($type > 5) {
+            $exifTool->Warn("Invalid directory entry type $type");
+            last;   # rest of directory is probably garbage
+        }
+        # get entry name (note: this is supposed to be length in 2-byte
+        # characters but this isn't what is done in my sample FPX file, so
+        # be very tolerant of this count -- it's null terminated anyway)
+        my $len = Get16u(\$dir, $pos + 0x40);
+        $len > 32 and $len = 32;
+        my $tag = Image::ExifTool::Unicode2Latin(substr($dir, $pos, $len * 2), 'v');
+        $tag =~ s/\0.*//s;  # truncate at null (in case length was wrong)
+
+        my $sect = Get32u(\$dir, $pos + 0x74);  # start sector number
+        my $size = Get32u(\$dir, $pos + 0x78);  # stream length
+
+        # load Ministream (referenced from first directory entry)
+        unless ($miniStream) {
+            $miniStreamBuff = LoadChain($raf, $sect, \$fat, $sectSize, HDR_SIZE);
+            unless (defined $miniStreamBuff) {
+                $exifTool->Warn('Error loading Mini-FAT stream');
+                last;
+            }
+            $miniStream = new File::RandomAccess(\$miniStreamBuff);
+        }
+
+        my $tagInfo;
+        if ($$tagTablePtr{$tag}) {
+            $tagInfo = $exifTool->GetTagInfo($tagTablePtr, $tag);
+        } else {
+            # remove instance number or class ID from tag if necessary
+            $tagInfo = $exifTool->GetTagInfo($tagTablePtr, $1) if
+                ($tag =~ /(.*) \d{6}$/s and $$tagTablePtr{$1}) or
+                ($tag =~ /(.*)_[0-9a-f]{16}$/s and $$tagTablePtr{$1});
+        }
+
+        next unless $tagInfo or $verbose;
+
+        # load the data for stream types
+        my $extra = '';
+        my $typeStr = $dirEntryType[$type] || $type;
+        if ($typeStr eq 'STREAM') {
+            if ($size >= $miniCutoff) {
+                # stream is in the main FAT
+                $buff = LoadChain($raf, $sect, \$fat, $sectSize, HDR_SIZE);
+            } elsif ($size) {
+                # stream is in the mini-FAT
+                $buff = LoadChain($miniStream, $sect, \$miniFat, $miniSize, 0);
+            } else {
+                $buff = ''; # an empty stream
+            }
+            unless (defined $buff) {
+                my $name = $tagInfo ? $$tagInfo{Name} : 'unknown';
+                $exifTool->Warn("Error reading $name stream");
+                $buff = '';
+            }
+        } elsif ($typeStr eq 'ROOT') {
+            $buff = $miniStreamBuff;
+            $extra .= ' (Ministream)';
+        } else {
+            $buff = '';
+            undef $size;
+        }
+        if ($verbose) {
+            my $flags = Get8u(\$dir, $pos + 0x43);  # 0=red, 1=black
+            my $lSib = Get32u(\$dir, $pos + 0x44);  # left sibling
+            my $rSib = Get32u(\$dir, $pos + 0x48);  # right sibling
+            my $chld = Get32u(\$dir, $pos + 0x4c);  # child directory
+            my $col = { 0 => 'Red', 1 => 'Black' }->{$flags} || $flags;
+            $extra .= " Type=$typeStr Flags=$col";
+            $extra .= " Left=$lSib" unless $lSib == FREE_SECT;
+            $extra .= " Right=$rSib" unless $rSib == FREE_SECT;
+            $extra .= " Child=$chld" unless $chld == FREE_SECT;
+            $exifTool->VerboseInfo($tag, $tagInfo,
+                Index => $index++,
+                Value => $buff,
+                DataPt => \$buff,
+                Extra => $extra,
+                Size => $size,
+            );
+        }
+        if ($tagInfo and $buff) {
+            if ($$tagInfo{SubDirectory}) {
+                my %dirInfo = (
+                    DataPt => \$buff,
+                    DirStart => $tagInfo->{SubDirectory}->{DirStart},
+                    DirLen => length $buff,
+                    Multi => $$tagInfo{Multi},
+                );
+                my $subTablePtr = GetTagTable($tagInfo->{SubDirectory}->{TagTable});
+                $exifTool->ProcessDirectory(\%dirInfo, $subTablePtr);
+            } else {
+                $exifTool->FoundTag($tagInfo, $buff);
+            }
+        }
+    }
+    $exifTool->{INDENT} = $oldIndent if $verbose;
+    return 1;
+}
+
+1;  # end
+
+__END__
+
+=head1 NAME
+
+Image::ExifTool::FlashPix - Read FlashPix meta information
+
+=head1 SYNOPSIS
+
+This module is used by Image::ExifTool
+
+=head1 DESCRIPTION
+
+This module contains routines required by Image::ExifTool to extract
+FlashPix meta information from FPX images, and from the APP2 FPXR segment of
+JPEG images.
+
+=head1 AUTHOR
+
+Copyright 2003-2007, Phil Harvey (phil at owl.phy.queensu.ca)
+
+This library is free software; you can redistribute it and/or modify it
+under the same terms as Perl itself.
+
+=head1 REFERENCES
+
+=over 4
+
+=item L<http://www.exif.org/Exif2-2.PDF>
+
+=item L<http://www.graphcomp.com/info/specs/livepicture/fpx.pdf>
+
+=item L<http://search.cpan.org/~jdb/libwin32/>
+
+=back
+
+=head1 SEE ALSO
+
+L<Image::ExifTool::TagNames/FlashPix Tags>,
+L<Image::ExifTool(3pm)|Image::ExifTool>
+
+=cut
+
Index: /gsdl/trunk/perllib/cpan/Image/ExifTool/FotoStation.pm
===================================================================
--- /gsdl/trunk/perllib/cpan/Image/ExifTool/FotoStation.pm	(revision 16842)
+++ /gsdl/trunk/perllib/cpan/Image/ExifTool/FotoStation.pm	(revision 16842)
@@ -0,0 +1,260 @@
+#------------------------------------------------------------------------------
+# File:         FotoStation.pm
+#
+# Description:  Read/write FotoWare FotoStation trailer
+#
+# Revisions:    10/28/2006 - P. Harvey Created
+#------------------------------------------------------------------------------
+
+package Image::ExifTool::FotoStation;
+
+use strict;
+use vars qw($VERSION);
+use Image::ExifTool qw(:DataAccess :Utils);
+
+$VERSION = '1.00';
+
+sub ProcessFotoStation($$);
+
+%Image::ExifTool::FotoStation::Main = (
+    PROCESS_PROC => \&ProcessFotoStation,
+    WRITE_PROC => \&ProcessFotoStation,
+    GROUPS => { 2 => 'Image' },
+    NOTES => q{
+        The following tables define information found in the FotoWare FotoStation
+        trailer.
+    },
+    0x01 => {
+        Name => 'IPTC',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::IPTC::Main',
+        },
+    },
+    0x02 => {
+        Name => 'SoftEdit',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::FotoStation::SoftEdit',
+        },
+    },
+    0x03 => {
+        Name => 'ThumbnailImage',
+        Writable => 1,
+        ValueConv => '$self->ValidateImage(\$val,$tag)',
+        ValueConvInv => '$val',
+    },
+    0x04 => {
+        Name => 'PreviewImage',
+        Writable => 1,
+        ValueConv => '$self->ValidateImage(\$val,$tag)',
+        ValueConvInv => '$val',
+    },
+);
+
+# crop coordinate conversions
+my %cropConv = (
+    ValueConv => '$val / 1000',
+    ValueConvInv => '$val * 1000',
+    PrintConv => '"$val%"',
+    PrintConvInv => '$val=~tr/ %//d; $val',
+);
+
+# soft crop record
+%Image::ExifTool::FotoStation::SoftEdit = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
+    CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
+    WRITABLE => 1,
+    FORMAT => 'int32s',
+    FIRST_ENTRY => 0,
+    GROUPS => { 2 => 'Image' },
+    0 => {
+        Name => 'OriginalImageWidth',
+    },
+    1 => 'OriginalImageHeight',
+    2 => 'ColorPlanes',
+    3 => {
+        Name => 'XYResolution',
+        ValueConv => '$val / 1000',
+        ValueConvInv => '$val * 1000',
+    },
+    4 => {
+        Name => 'Rotation',
+        Notes => q{
+            rotations are stored as degrees CCW * 100, but converted to degrees CW by
+            ExifTool
+        },
+        # raw value is 0, 9000, 18000 or 27000
+        ValueConv => '$val ? 360 - $val / 100 : 0',
+        ValueConvInv => '$val ? (360 - $val) * 100 : 0',
+    },
+    # 5 Validity Check (0x11222211)
+    6 => {
+        Name => 'CropLeft',
+        %cropConv,
+    },
+    7 => {
+        Name => 'CropTop',
+        %cropConv,
+    },
+    8 => {
+        Name => 'CropRight',
+        %cropConv,
+    },
+    9 => {
+        Name => 'CropBottom',
+        %cropConv,
+    },
+    11 => {
+        Name => 'CropRotation',
+        # raw value in the range -4500 to 4500
+        ValueConv => '-$val / 100',
+        ValueConvInv => '-$val * 100',
+    },
+);
+
+#------------------------------------------------------------------------------
+# Read/write FotoStation information in a file
+# Inputs: 0) ExifTool object reference, 1) dirInfo reference
+# Returns: 1 on success, 0 if this file didn't contain FotoStation information
+# - updates DataPos to point to start of FotoStation information
+# - updates DirLen to trailer length
+sub ProcessFotoStation($$)
+{
+    my ($exifTool, $dirInfo) = @_;
+    $exifTool or return 1;    # allow dummy access to autoload this package
+    my ($buff, $footer, $dirBuff, $tagTablePtr);
+    my $raf = $$dirInfo{RAF};
+    my $outfile = $$dirInfo{OutFile};
+    my $offset = $$dirInfo{Offset} || 0;
+    my $verbose = $exifTool->Options('Verbose');
+    my $out = $exifTool->Options('TextOut');
+    my $rtnVal = 0;
+
+    $$dirInfo{DirLen} = 0;      # initialize returned trailer length
+    $raf->Seek(-$offset, 2);    # seek to specified offset from end of file
+
+    # loop through FotoStation records
+    for (;;) {
+        # look for trailer signature
+        last unless $raf->Seek(-10, 1) and $raf->Read($footer, 10) == 10;
+        my ($tag, $size, $sig) = unpack('nNN', $footer);
+        last unless $sig == 0xa1b2c3d4 and $size >= 10 and $raf->Seek(-$size, 1);
+        $size -= 10; # size of data only
+        last unless $raf->Read($buff, $size) == $size;
+        $raf->Seek(-$size, 1);
+        # set variables returned in dirInfo hash
+        $$dirInfo{DataPos} = $raf->Tell();
+        $$dirInfo{DirLen} += $size + 10;
+
+        unless ($tagTablePtr) {
+            $tagTablePtr = GetTagTable('Image::ExifTool::FotoStation::Main');
+            SetByteOrder('MM'); # necessary for the binary data
+            $rtnVal = 1;        # we found a valid FotoStation trailer
+        }
+        unless ($outfile) {
+            # print verbose trailer information
+            if ($verbose or $exifTool->{HTML_DUMP}) {
+                $exifTool->DumpTrailer({
+                    RAF => $raf,
+                    DataPos => $$dirInfo{DataPos},
+                    DirLen => $size + 10,
+                    DirName => "FotoStation_$tag",
+                });
+            }
+            # extract information for this tag
+            $exifTool->HandleTag($tagTablePtr, $tag, $buff,
+                                 DataPt => \$buff,
+                                 Start => 0,
+                                 Size => $size,
+                                 DataPos => $$dirInfo{DataPos});
+            next;
+        }
+        if ($exifTool->{DEL_GROUP}->{FotoStation}) {
+            $verbose and printf $out "  Deleting FotoStation trailer\n";
+            $verbose = 0;   # no more verbose messages after this
+            ++$exifTool->{CHANGED};
+            next;
+        }
+        # rewrite this information
+        my $tagInfo = $exifTool->GetTagInfo($tagTablePtr, $tag);
+        if ($tagInfo) {
+            my $newVal;
+            my $tagName = $$tagInfo{Name};
+            if ($$tagInfo{SubDirectory}) {
+                my %subdirInfo = (
+                    DataPt => \$buff,
+                    DirStart => 0,
+                    DirLen => $size,
+                    DataPos => $$dirInfo{DataPos},
+                    DirName => $tagName,
+                    Parent => 'FotoStation',
+                );
+                my $subTable = GetTagTable($tagInfo->{SubDirectory}->{TagTable});
+                $newVal = $exifTool->WriteDirectory(\%subdirInfo, $subTable);
+            } else {
+                my $newValueHash = $exifTool->GetNewValueHash($tagInfo);
+                if (Image::ExifTool::IsOverwriting($newValueHash) > 0) {
+                    $newVal = Image::ExifTool::GetNewValues($newValueHash);
+                    $newVal = '' unless defined $newVal;
+                    if ($verbose > 1) {
+                        my $n = length $newVal;
+                        print $out "    - FotoStation:$tagName ($size bytes)\n" if $size;
+                        print $out "    + FotoStation:$tagName ($n bytes)\n" if $n;
+                    }
+                    ++$exifTool->{CHANGED};
+                }
+            }
+            if (defined $newVal) {
+                # note: length may be 0 here, but we write the empty record anyway
+                $buff = $newVal;
+                $size = length($newVal) + 10;
+                $footer = pack('nNN', $tag, $size, $sig);
+            }
+        }
+        if (defined $dirBuff) {
+            # maintain original record order
+            $dirBuff = $buff . $footer . $dirBuff;
+        } else {
+            $dirBuff = $buff . $footer;
+        }
+    }
+    # write the modified FotoStation trailer
+    Write($outfile, $dirBuff) or $rtnVal = -1 if $dirBuff;
+    return $rtnVal;
+}
+
+1;  # end
+
+__END__
+
+=head1 NAME
+
+Image::ExifTool::FotoStation - Read/write FotoWare FotoStation trailer
+
+=head1 SYNOPSIS
+
+This module is used by Image::ExifTool
+
+=head1 DESCRIPTION
+
+This module contains definitions required by Image::ExifTool to read and
+write information from the FotoWare FotoStation trailer.
+
+=head1 AUTHOR
+
+Copyright 2003-2007, Phil Harvey (phil at owl.phy.queensu.ca)
+
+This library is free software; you can redistribute it and/or modify it
+under the same terms as Perl itself.
+
+=head1 ACKNOWLEDGEMENTS
+
+Thanks to Mark Tate for information about the FotoStation data format.
+
+=head1 SEE ALSO
+
+L<Image::ExifTool::TagNames/FotoStation Tags>,
+L<Image::ExifTool(3pm)|Image::ExifTool>
+
+=cut
+
Index: /gsdl/trunk/perllib/cpan/Image/ExifTool/FujiFilm.pm
===================================================================
--- /gsdl/trunk/perllib/cpan/Image/ExifTool/FujiFilm.pm	(revision 16842)
+++ /gsdl/trunk/perllib/cpan/Image/ExifTool/FujiFilm.pm	(revision 16842)
@@ -0,0 +1,490 @@
+#------------------------------------------------------------------------------
+# File:         FujiFilm.pm
+#
+# Description:  FujiFilm EXIF maker notes tags
+#
+# Revisions:    11/25/2003  - P. Harvey Created
+#
+# References:   1) http://park2.wakwak.com/~tsuruzoh/Computer/Digicams/exif-e.html
+#               2) http://homepage3.nifty.com/kamisaka/makernote/makernote_fuji.htm
+#               3) Michael Meissner private communication
+#               4) Paul Samuelson private communication (S5)
+#               5) http://www.cybercom.net/~dcoffin/dcraw/
+#------------------------------------------------------------------------------
+
+package Image::ExifTool::FujiFilm;
+
+use strict;
+use vars qw($VERSION);
+use Image::ExifTool qw(:DataAccess :Utils);
+use Image::ExifTool::Exif;
+
+$VERSION = '1.14';
+
+%Image::ExifTool::FujiFilm::Main = (
+    WRITE_PROC => \&Image::ExifTool::Exif::WriteExif,
+    CHECK_PROC => \&Image::ExifTool::Exif::CheckExif,
+    WRITABLE => 1,
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
+    0x0 => {
+        Name => 'Version',
+        Writable => 'undef',
+    },
+    0x0010 => { #PH (how does this compare to actual serial number?)
+        Name => 'InternalSerialNumber',
+        Writable => 'string',
+        Notes => q{
+            this number is unique, and contains the date of manufacture, but doesn't
+            necessarily correspond to the camera body number -- this needs to be checked
+        },
+        # ie)  "FPX20017035 592D31313034060427796060110384"
+        # "FPX 20495643     592D313335310701318AD010110047" (F40fd)
+        #                               yymmdd
+        PrintConv => q{
+            return $val unless $val=~/^(.*)(\d{2})(\d{2})(\d{2})(.{12})$/;
+            my $yr = $2 + ($2 < 70 ? 2000 : 1900);
+            return "$1 $yr:$3:$4 $5";
+        },
+        PrintConvInv => '$_=$val; s/ (19|20)(\d{2}):(\d{2}):(\d{2}) /$2$3$4/; $_',
+    },
+    0x1000 => {
+        Name => 'Quality',
+        Writable => 'string',
+    },
+    0x1001 => {
+        Name => 'Sharpness',
+        Writable => 'int16u',
+        PrintConv => {
+            1 => 'Soft',
+            2 => 'Soft2',
+            3 => 'Normal',
+            4 => 'Hard',
+            5 => 'Hard2',
+        },
+    },
+    0x1002 => {
+        Name => 'WhiteBalance',
+        Flags => 'PrintHex',
+        Writable => 'int16u',
+        PrintConv => {
+            0x0   => 'Auto',
+            0x100 => 'Daylight',
+            0x200 => 'Cloudy',
+            0x300 => 'Daylight Fluorescent',
+            0x301 => 'Day White Fluorescent',
+            0x302 => 'White Fluorescent',
+            0x400 => 'Incandescent',
+            0x500 => 'Flash', #4
+            0xf00 => 'Custom',
+            0xff0 => 'Kelvin', #4
+        },
+    },
+    0x1003 => {
+        Name => 'Saturation',
+        Flags => 'PrintHex',
+        Writable => 'int16u',
+        PrintConv => {
+            0x0   => 'Normal',
+            0x100 => 'High',
+            0x200 => 'Low',
+            0x300 => 'None (B&W)', #2
+        },
+    },
+    0x1004 => {
+        Name => 'Contrast',
+        Flags => 'PrintHex',
+        Writable => 'int16u',
+        PrintConv => {
+            0x0   => 'Normal',
+            0x100 => 'High',
+            0x200 => 'Low',
+        },
+    },
+    0x1005 => { #4
+        Name => 'ColorTemperature',
+        Writable => 'int16u',
+    },
+    0x1010 => {
+        Name => 'FujiFlashMode',
+        Writable => 'int16u',
+        PrintConv => {
+            0 => 'Auto',
+            1 => 'On',
+            2 => 'Off',
+            3 => 'Red-eye reduction',
+        },
+    },
+    0x1011 => {
+        Name => 'FlashStrength',
+        Writable => 'rational64s',
+    },
+    0x1020 => {
+        Name => 'Macro',
+        Writable => 'int16u',
+        PrintConv => {
+            0 => 'Off',
+            1 => 'On',
+        },
+    },
+    0x1021 => {
+        Name => 'FocusMode',
+        Writable => 'int16u',
+        PrintConv => {
+            0 => 'Auto',
+            1 => 'Manual',
+        },
+    },
+    0x1023 => { #2
+        Name => 'FocusPixel',
+        Writable => 'int16u',
+        Count => 2,
+    },
+    0x1030 => {
+        Name => 'SlowSync',
+        Writable => 'int16u',
+        PrintConv => {
+            0 => 'Off',
+            1 => 'On',
+        },
+    },
+    0x1031 => {
+        Name => 'PictureMode',
+        Flags => 'PrintHex',
+        Writable => 'int16u',
+        PrintConv => {
+            0x0 => 'Auto',
+            0x1 => 'Portrait',
+            0x2 => 'Landscape',
+            0x4 => 'Sports',
+            0x5 => 'Night Scene',
+            0x6 => 'Program AE',
+            0x7 => 'Natural Light', #3
+            0x8 => 'Anti-blur', #3
+            0xa => 'Sunset', #3
+            0xb => 'Museum', #3
+            0xc => 'Party', #3
+            0xd => 'Flower', #3
+            0xe => 'Text', #3
+            0xf => 'Natural Light & Flash', #3
+            0x10 => 'Beach', #3
+            0x11 => 'Snow', #3
+            0x12 => 'Fireworks', #3
+            0x13 => 'Underwater', #3
+            0x100 => 'Aperture-priority AE',
+            0x200 => 'Shutter speed priority AE',
+            0x300 => 'Manual',
+        },
+    },
+# this usually has a value of 1
+#    0x1032 => { #2
+#        Name => 'ShutterCount',
+#        Writable => 'int16u',
+#    },
+    0x1100 => {
+        Name => 'AutoBracketing',
+        Writable => 'int16u',
+        PrintConv => {
+            0 => 'Off',
+            1 => 'On',
+            2 => 'No flash & flash', #3
+        },
+    },
+    0x1101 => {
+        Name => 'SequenceNumber',
+        Writable => 'int16u',
+    },
+    0x1210 => { #2
+        Name => 'ColorMode',
+        Writable => 'int16u',
+        PrintHex => 1,
+        PrintConv => {
+            0x00 => 'Standard',
+            0x10 => 'Chrome',
+            0x30 => 'B & W',
+        },
+    },
+    0x1300 => {
+        Name => 'BlurWarning',
+        Writable => 'int16u',
+        PrintConv => {
+            0 => 'None',
+            1 => 'Blur Warning',
+        },
+    },
+    0x1301 => {
+        Name => 'FocusWarning',
+        Writable => 'int16u',
+        PrintConv => {
+            0 => 'Good',
+            1 => 'Out of focus',
+        },
+    },
+    0x1302 => {
+        Name => 'ExposureWarning',
+        Writable => 'int16u',
+        PrintConv => {
+            0 => 'Good',
+            1 => 'Bad exposure',
+        },
+    },
+    0x1400 => { #2
+        Name => 'DynamicRange',
+        Writable => 'int16u',
+        PrintConv => {
+            1 => 'Standard',
+            3 => 'Wide',
+        },
+    },
+    0x1401 => { #2
+        Name => 'FilmMode',
+        Writable => 'int16u',
+        PrintHex => 1,
+        PrintConv => {
+            0x000 => 'F0/Standard',
+            0x100 => 'F1/Studio Portrait',
+            0x200 => 'F2/Fujichrome',
+            0x300 => 'F3/Studio Portrait Ex',
+            0x400 => 'F4/Velvia',
+        },
+    },
+    0x1402 => { #2
+        Name => 'DynamicRangeSetting',
+        Writable => 'int16u',
+        PrintHex => 1,
+        PrintConv => {
+            0x000 => 'Auto (100-400%)',
+            0x001 => 'RAW',
+            0x100 => 'Standard (100%)',
+            0x200 => 'Wide1 (230%)',
+            0x201 => 'Wide2 (400%)',
+            0x8000 => 'Film Simulation Mode',
+        },
+    },
+    0x1403 => { #2
+        Name => 'DevelopmentDynamicRange',
+        Writable => 'int16u',
+    },
+    0x1404 => { #2
+        Name => 'MinFocalLength',
+        Writable => 'rational64s',
+    },
+    0x1405 => { #2
+        Name => 'MaxFocalLength',
+        Writable => 'rational64s',
+    },
+    0x1406 => { #2
+        Name => 'MaxApertureAtMinFocal',
+        Writable => 'rational64s',
+    },
+    0x1407 => { #2
+        Name => 'MaxApertureAtMaxFocal',
+        Writable => 'rational64s',
+    },
+    0x8000 => { #2
+        Name => 'FileSource',
+        Writable => 'string',
+    },
+    0x8002 => { #2
+        Name => 'OrderNumber',
+        Writable => 'int32u',
+    },
+    0x8003 => { #2
+        Name => 'FrameNumber',
+        Writable => 'int16u',
+    },
+);
+
+# tags in RAF images (ref 5)
+%Image::ExifTool::FujiFilm::RAF = (
+    GROUPS => { 0 => 'RAF', 1 => 'RAF', 2 => 'Image' },
+    NOTES => 'Tags extracted from FujiFilm RAF-format information.',
+    0x100 => {
+        Name => 'RawImageFullSize',
+        Format => 'int16u',
+        Groups => { 1 => 'RAF2' }, # (so RAF2 shows up in family 1 list)
+        Count => 2,
+        Notes => 'including borders',
+        ValueConv => 'my @v=reverse split(" ",$val);"@v"',
+        PrintConv => '$val=~tr/ /x/; $val',
+    },
+    0x121 => [
+        {
+            Name => 'RawImageSize',
+            Condition => '$$self{CameraModel} eq "FinePixS2Pro"',
+            Format => 'int16u',
+            Count => 2,
+            ValueConv => q{
+                my @v=split(" ",$val);
+                $v[0]*=2, $v[1]/=2;
+                return "@v";
+            },
+            PrintConv => '$val=~tr/ /x/; $val',
+        },
+        {
+            Name => 'RawImageSize',
+            Format => 'int16u',
+            Count => 2,
+            # values are height then width, adjusted for the layout
+            ValueConv => q{
+                my @v=reverse split(" ",$val);
+                $$self{FujiLayout} and $v[0]/=2, $v[1]*=2;
+                return "@v";
+            },
+            PrintConv => '$val=~tr/ /x/; $val',
+        },
+    ],
+    0x130 => {
+        Name => 'FujiLayout',
+        Format => 'int8u',
+        RawConv => q{
+            my ($v) = split ' ', $val;
+            $$self{FujiLayout} = $v & 0x80 ? 1 : 0;
+            return $val;
+        },
+    },
+    0x2ff0 => {
+        Name => 'WB_GRGBLevels',
+        Format => 'int16u',
+        Count => 4,
+    },
+);
+
+#------------------------------------------------------------------------------
+# get information from FujiFilm RAF directory
+# Inputs: 0) ExifTool object reference, 1) dirInfo reference, 2) tag table ref
+# Returns: 1 if this was a valid FujiFilm directory
+sub ProcessFujiDir($$$)
+{
+    my ($exifTool, $dirInfo, $tagTablePtr) = @_;
+    my $raf = $$dirInfo{RAF};
+    my $offset = $$dirInfo{DirStart};
+    $raf->Seek($offset, 0) or return 0;
+    my $buff;
+    $raf->Read($buff, 4) or return 0;
+    my $entries = unpack 'N', $buff;
+    $entries < 256 or return 0;
+    SetByteOrder('MM');
+    while ($entries--) {
+        $raf->Read($buff,4) or return 0;
+        my ($tag, $len) = unpack 'nn', $buff;
+        my ($val, $vbuf);
+        $raf->Read($vbuf, $len) or return 0;
+        my $tagInfo = $exifTool->GetTagInfo($tagTablePtr, $tag);
+        if ($tagInfo and $$tagInfo{Format}) {
+            $val = ReadValue(\$vbuf, 0, $$tagInfo{Format}, $$tagInfo{Count}, $len);
+            next unless defined $val;
+        } else {
+            $val = \$vbuf;
+        }
+        $exifTool->HandleTag($tagTablePtr, $tag, $val);
+    }
+    return 1;
+}
+
+#------------------------------------------------------------------------------
+# get information from FujiFilm RAW file
+# Inputs: 0) ExifTool object reference, 1) dirInfo reference
+# Returns: 1 if this was a valid FujiFilm RAW file
+sub ProcessRAF($$)
+{
+    my ($exifTool, $dirInfo) = @_;
+    my ($buff, $warn);
+
+    my $raf = $$dirInfo{RAF};
+    $raf->Read($buff,8) == 8        or return 0;
+    $buff eq 'FUJIFILM'             or return 0;
+    $raf->Seek(84, 0)               or return 0;
+    $raf->Read($buff, 8) == 8       or return 0;
+    my ($pos, $len) = unpack('NN', $buff);
+    $pos & 0x8000                  and return 0;
+    $raf->Seek($pos, 0)             or return 0;
+    $raf->Read($buff, $len) == $len or return 0;
+    my %dirInfo = (
+        Parent => 'RAF',
+        RAF    => new File::RandomAccess(\$buff),
+    );
+    $$exifTool{BASE} += $pos;
+    my $rtnVal = $exifTool->ProcessJPEG(\%dirInfo);
+    $$exifTool{BASE} -= $pos;
+    $exifTool->FoundTag('PreviewImage', \$buff) if $rtnVal;
+
+    if ($raf->Seek(92, 0) and $raf->Read($buff, 4)) {
+        my $tagTablePtr = GetTagTable('Image::ExifTool::FujiFilm::RAF');
+        %dirInfo = (
+            RAF => $raf,
+            DirStart => unpack('N', $buff),
+        );
+        $$exifTool{SET_GROUP1} = 'RAF';
+        ProcessFujiDir($exifTool, \%dirInfo, $tagTablePtr) or $warn = 1;
+    
+        # extract information from 2nd image if available
+        if ($pos > 120) {
+            $raf->Seek(120, 0) or return 0;
+            $raf->Read($buff, 4) or return 0;
+            my $start = unpack('N',$buff);
+            if ($start) {
+                $$dirInfo{DirStart} = $start;
+                $$exifTool{SET_GROUP1} = 'RAF2';
+                ProcessFujiDir($exifTool, \%dirInfo, $tagTablePtr) or $warn = 1;
+            }
+        }
+        delete $$exifTool{SET_GROUP1};
+    } else {
+        $warn = 1;
+    }
+    $warn and $exifTool->Warn('Possibly corrupt RAF information');
+
+    return $rtnVal;
+}
+
+1; # end
+
+__END__
+
+=head1 NAME
+
+Image::ExifTool::FujiFilm - FujiFilm EXIF maker notes tags
+
+=head1 SYNOPSIS
+
+This module is loaded automatically by Image::ExifTool when required.
+
+=head1 DESCRIPTION
+
+This module contains definitions required by Image::ExifTool to interpret
+FujiFilm maker notes in EXIF information, and to read FujiFilm RAW (RAF)
+images.
+
+=head1 AUTHOR
+
+Copyright 2003-2007, Phil Harvey (phil at owl.phy.queensu.ca)
+
+This library is free software; you can redistribute it and/or modify it
+under the same terms as Perl itself.
+
+=head1 REFERENCES
+
+=over 4
+
+=item L<http://park2.wakwak.com/~tsuruzoh/Computer/Digicams/exif-e.html>
+
+=item L<http://homepage3.nifty.com/kamisaka/makernote/makernote_fuji.htm>
+
+=item L<http://www.cybercom.net/~dcoffin/dcraw/>
+
+=item (...plus testing with my own FinePix 2400 Zoom)
+
+=back
+
+=head1 ACKNOWLEDGEMENTS
+
+Thanks to Michael Meissner for decoding some new PictureMode and
+AutoBracketing values, and to Paul Samuelson for decoding some WhiteBalance
+values and the ColorTemperature tag.
+
+=head1 SEE ALSO
+
+L<Image::ExifTool::TagNames/FujiFilm Tags>,
+L<Image::ExifTool(3pm)|Image::ExifTool>
+
+=cut
Index: /gsdl/trunk/perllib/cpan/Image/ExifTool/GIF.pm
===================================================================
--- /gsdl/trunk/perllib/cpan/Image/ExifTool/GIF.pm	(revision 16842)
+++ /gsdl/trunk/perllib/cpan/Image/ExifTool/GIF.pm	(revision 16842)
@@ -0,0 +1,221 @@
+#------------------------------------------------------------------------------
+# File:         GIF.pm
+#
+# Description:  Read and write GIF meta information
+#
+# Revisions:    10/18/2005 - P. Harvey Separated from ExifTool.pm
+#
+# References:   http://www.w3.org/Graphics/GIF/spec-gif89a.txt
+#
+# Notes:        GIF really doesn't have much meta information, except for
+#               comments which are allowed in GIF89a images
+#------------------------------------------------------------------------------
+
+package Image::ExifTool::GIF;
+
+use strict;
+use vars qw($VERSION);
+use Image::ExifTool qw(:DataAccess);
+
+$VERSION = '1.04';
+
+#------------------------------------------------------------------------------
+# Process meta information in GIF image
+# Inputs: 0) ExifTool object reference, 1) Directory information ref
+# Returns: 1 on success, 0 if this wasn't a valid GIF file, or -1 if
+#          an output file was specified and a write error occurred
+sub ProcessGIF($$)
+{
+    my ($exifTool, $dirInfo) = @_;
+    my ($type, $a, $s, $ch, $length, $buff);
+    my ($err, $newComment, $setComment);
+    my $verbose = $exifTool->Options('Verbose');
+    my $out = $exifTool->Options('TextOut');
+    my $outfile = $$dirInfo{OutFile};
+    my $raf = $$dirInfo{RAF};
+    my $rtnVal = 0;
+
+    # verify this is a valid GIF file
+    # (must do a RAF read until we know the file is ours)
+    return 0 unless $raf->Read($type, 6) == 6
+        and $type =~ /^GIF8[79]a$/
+        and $raf->Read($s, 4) == 4;
+
+    $verbose and print $out "GIF file version $type\n";
+    if ($outfile) {
+        Write($outfile, $type, $s) or $err = 1;
+        if ($exifTool->{DEL_GROUP}->{File}) {
+            $setComment = 1;
+            if ($exifTool->{DEL_GROUP}->{File} == 2) {
+                $newComment = $exifTool->GetNewValues('Comment');
+            }
+        } else {
+            my $newValueHash;
+            $newComment = $exifTool->GetNewValues('Comment', \$newValueHash);
+            $setComment = 1 if $newValueHash;
+        }
+    }
+    $exifTool->SetFileType();   # set file type
+    my ($w, $h) = unpack("v"x2, $s);
+    $exifTool->FoundTag('ImageWidth', $w);
+    $exifTool->FoundTag('ImageHeight', $h);
+    if ($raf->Read($s, 3) == 3) {
+        Write($outfile, $s) or $err = 1 if $outfile;
+        if (ord($s) & 0x80) { # does this image contain a color table?
+            # calculate color table size
+            $length = 3 * (2 << (ord($s) & 0x07));
+            $raf->Read($buff, $length) == $length or return 0; # skip color table
+            Write($outfile, $buff) or $err = 1 if $outfile;
+        }
+        # write the comment first if necessary
+        if ($outfile and defined $newComment) {
+            if ($type ne 'GIF87a') {
+                # write comment marker
+                Write($outfile, "\x21\xfe") or $err = 1;
+                my $len = length($newComment);
+                # write out the comment in 255-byte chunks, each
+                # chunk beginning with a length byte
+                my $n;
+                for ($n=0; $n<$len; $n+=255) {
+                    my $size = $len - $n;
+                    $size > 255 and $size = 255;
+                    my $str = substr($newComment,$n,$size);
+                    Write($outfile, pack('C',$size), $str) or $err = 1;
+                }
+                Write($outfile, "\0") or $err = 1;  # empty chunk as terminator
+                undef $newComment;
+                ++$exifTool->{CHANGED};     # increment file changed flag
+            } else {
+                $exifTool->Warn("The GIF87a format doesn't support comments");
+            }
+        }
+        my $comment;
+        for (;;) {
+            last unless $raf->Read($ch, 1);
+            if (ord($ch) == 0x2c) {
+                Write($outfile, $ch) or $err = 1 if $outfile;
+                # image descriptor
+                last unless $raf->Read($buff, 8) == 8;
+                last unless $raf->Read($ch, 1);
+                Write($outfile, $buff, $ch) or $err = 1 if $outfile;
+                if (ord($ch) & 0x80) { # does color table exist?
+                    $length = 3 * (2 << (ord($ch) & 0x07));
+                    # skip the color table
+                    last unless $raf->Read($buff, $length) == $length;
+                    Write($outfile, $buff) or $err = 1 if $outfile;
+                }
+                # skip "LZW Minimum Code Size" byte
+                last unless $raf->Read($buff, 1);
+                Write($outfile,$buff) or $err = 1 if $outfile;
+                # skip image blocks
+                for (;;) {
+                    last unless $raf->Read($ch, 1);
+                    Write($outfile, $ch) or $err = 1 if $outfile;
+                    last unless ord($ch);
+                    last unless $raf->Read($buff, ord($ch));
+                    Write($outfile,$buff) or $err = 1 if $outfile;
+                }
+                next;  # continue with next field
+            }
+#               last if ord($ch) == 0x3b;  # normal end of GIF marker
+            unless (ord($ch) == 0x21) {
+                if ($outfile) {
+                    Write($outfile, $ch) or $err = 1;
+                    # copy the rest of the file
+                    while ($raf->Read($buff, 65536)) {
+                        Write($outfile, $buff) or $err = 1;
+                    }
+                }
+                $rtnVal = 1;
+                last;
+            }
+            # get extension block type/size
+            last unless $raf->Read($s, 2) == 2;
+            # get marker and block size
+            ($a,$length) = unpack("C"x2, $s);
+            if ($a == 0xfe) {  # is this a comment?
+                if ($setComment) {
+                    ++$exifTool->{CHANGED}; # increment the changed flag
+                } else {
+                    Write($outfile, $ch, $s) or $err = 1 if $outfile;
+                }
+                while ($length) {
+                    last unless $raf->Read($buff, $length) == $length;
+                    $verbose > 2 and Image::ExifTool::HexDump(\$buff, undef, Out => $out);
+                    if (defined $comment) {
+                        $comment .= $buff;  # add to comment string
+                    } else {
+                        $comment = $buff;
+                    }
+                    last unless $raf->Read($ch, 1);  # read next block header
+                    unless ($setComment) {
+                        Write($outfile, $buff, $ch) or $err = 1 if $outfile;
+                    }
+                    $length = ord($ch);  # get next block size
+                }
+                last if $length;    # was a read error if length isn't zero
+                unless ($outfile) {
+                    $rtnVal = 1;
+                    $exifTool->FoundTag('Comment', $comment) if $comment;
+                    undef $comment;
+                    # assume no more than one comment in FastScan mode
+                    last if $exifTool->Options('FastScan');
+                }
+            } else {
+                Write($outfile, $ch, $s) or $err = 1 if $outfile;
+                # skip the block
+                while ($length) {
+                    last unless $raf->Read($buff, $length) == $length;
+                    Write($outfile, $buff) or $err = 1 if $outfile;
+                    last unless $raf->Read($ch, 1);  # read next block header
+                    Write($outfile, $ch) or $err = 1 if $outfile;
+                    $length = ord($ch);  # get next block size
+                }
+            }
+        }
+        $exifTool->FoundTag('Comment', $comment) if $comment;
+    }
+    # set return value to -1 if we only had a write error
+    $rtnVal = -1 if $rtnVal and $err;
+    return $rtnVal;
+}
+
+
+1;  #end
+
+__END__
+
+=head1 NAME
+
+Image::ExifTool::GIF - Read and write GIF meta information
+
+=head1 SYNOPSIS
+
+This module is loaded automatically by Image::ExifTool when required.
+
+=head1 DESCRIPTION
+
+This module contains definitions required by Image::ExifTool to read and
+write GIF meta information.  GIF87a images contain no meta information, and
+only the Comment tag is currently supported in GIF89a images.
+
+=head1 AUTHOR
+
+Copyright 2003-2007, Phil Harvey (phil at owl.phy.queensu.ca)
+
+This library is free software; you can redistribute it and/or modify it
+under the same terms as Perl itself.
+
+=head1 REFERENCES
+
+=over 4
+
+=item L<http://www.w3.org/Graphics/GIF/spec-gif89a.txt>
+
+=back
+
+=head1 SEE ALSO
+
+L<Image::ExifTool(3pm)|Image::ExifTool>
+
+=cut
Index: /gsdl/trunk/perllib/cpan/Image/ExifTool/GPS.pm
===================================================================
--- /gsdl/trunk/perllib/cpan/Image/ExifTool/GPS.pm	(revision 16842)
+++ /gsdl/trunk/perllib/cpan/Image/ExifTool/GPS.pm	(revision 16842)
@@ -0,0 +1,413 @@
+#------------------------------------------------------------------------------
+# File:         GPS.pm
+#
+# Description:  EXIF GPS meta information tags
+#
+# Revisions:    12/09/2003  - P. Harvey Created
+#------------------------------------------------------------------------------
+
+package Image::ExifTool::GPS;
+
+use strict;
+use vars qw($VERSION);
+use Image::ExifTool::Exif;
+
+$VERSION = '1.17';
+
+my %coordConv = (
+    ValueConv    => 'Image::ExifTool::GPS::ToDegrees($val)',
+    ValueConvInv => 'Image::ExifTool::GPS::ToDMS($self, $val)',
+    PrintConv    => 'Image::ExifTool::GPS::ToDMS($self, $val, 1)',
+    PrintConvInv => 'Image::ExifTool::GPS::ToDegrees($val)',
+);
+
+%Image::ExifTool::GPS::Main = (
+    GROUPS => { 0 => 'EXIF', 1 => 'GPS', 2 => 'Location' },
+    WRITE_PROC => \&Image::ExifTool::Exif::WriteExif,
+    CHECK_PROC => \&Image::ExifTool::Exif::CheckExif,
+    WRITABLE => 1,
+    WRITE_GROUP => 'GPS',
+    NOTES => q{
+        When adding GPS information to an image, it is important to set all of the
+        following tags: GPSLatitude, GPSLatitudeRef, GPSLongitude, GPSLongitudeRef,
+        GPSAltitude and GPSAltitudeRef.  ExifTool will write the required
+        GPSVersionID tag automatically if new a GPS IFD is added to an image.
+    },
+    0x0000 => {
+        Name => 'GPSVersionID',
+        Writable => 'int8u',
+        Count => 4,
+        PrintConv => '$val =~ tr/ /./; $val',
+        PrintConvInv => '$val =~ tr/./ /; $val',
+    },
+    0x0001 => {
+        Name => 'GPSLatitudeRef',
+        Writable => 'string',
+        Count => 2,
+        PrintConv => {
+            N => 'North',
+            S => 'South',
+        },
+    },
+    0x0002 => {
+        Name => 'GPSLatitude',
+        Writable => 'rational64u',
+        Count => 3,
+        %coordConv,
+    },
+    0x0003 => {
+        Name => 'GPSLongitudeRef',
+        Writable => 'string',
+        Count => 2,
+        PrintConv => {
+            E => 'East',
+            W => 'West',
+        },
+    },
+    0x0004 => {
+        Name => 'GPSLongitude',
+        Writable => 'rational64u',
+        Count => 3,
+        %coordConv,
+    },
+    0x0005 => {
+        Name => 'GPSAltitudeRef',
+        Writable => 'int8u',
+        PrintConv => {
+            0 => 'Above Sea Level',
+            1 => 'Below Sea Level',
+        },
+    },
+    0x0006 => {
+        Name => 'GPSAltitude',
+        Writable => 'rational64u',
+        PrintConv => '$val eq "inf" ? $val : "$val m"',
+        PrintConvInv => '$val=~s/\s*m$//;$val',
+    },
+    0x0007 => {
+        Name => 'GPSTimeStamp',
+        Groups => { 2 => 'Time' },
+        Writable => 'rational64u',
+        Count => 3,
+        Shift => 'Time',
+        ValueConv => sub {
+            my $val = shift;
+            my ($h,$m,$s) = split ' ', $val;
+            my $f = (($h || 0) * 60 + ($m || 0)) * 60 + ($s || 0);
+            $h = int($f / 3600); $f -= $h * 3600;
+            $m = int($f / 60);   $f -= $m * 60;
+            $s = int($f);        $f -= $s;
+            $f = int($f * 1000000 + 0.5);
+            if ($f) {
+                ($f = sprintf(".%.6d", $f)) =~ s/0+$//;
+            } else {
+                $f = ''
+            }
+            return sprintf("%.2d:%.2d:%.2d$f",$h,$m,$s);
+        },
+        ValueConvInv => '$val=~tr/:/ /;$val',
+        # pull time out of any format date/time string
+        # (eventually handle timezones? -- timestamp should be UTC)
+        PrintConvInv => sub {
+            my $v = shift;
+            $v =~ s/[-+].*//s; # remove timezone
+            my @a = ($v =~ /((?=\d|\.\d)\d*(?:\.\d*)?)/g);
+            push @a, '00' while @a < 3;
+            return "$a[-3]:$a[-2]:$a[-1]";
+        },
+    },
+    0x0008 => {
+        Name => 'GPSSatellites',
+        Writable => 'string',
+    },
+    0x0009 => {
+        Name => 'GPSStatus',
+        Writable => 'string',
+        Count => 2,
+        PrintConv => {
+            A => 'Measurement In Progress',
+            V => 'Measurement Interoperability',
+        },
+    },
+    0x000A => {
+        Name => 'GPSMeasureMode',
+        Writable => 'string',
+        Count => 2,
+        PrintConv => {
+            2 => '2-Dimensional Measurement',
+            3 => '3-Dimensional Measurement',
+        },
+    },
+    0x000B => {
+        Name => 'GPSDOP',
+        Description => 'GPS Dilution Of Precision',
+        Writable => 'rational64u',
+    },
+    0x000C => {
+        Name => 'GPSSpeedRef',
+        Writable => 'string',
+        Count => 2,
+        PrintConv => {
+            K => 'km/h',
+            M => 'mph',
+            N => 'knots',
+        },
+    },
+    0x000D => {
+        Name => 'GPSSpeed',
+        Writable => 'rational64u',
+    },
+    0x000E => {
+        Name => 'GPSTrackRef',
+        Writable => 'string',
+        Count => 2,
+        PrintConv => {
+            M => 'Magnetic North',
+            T => 'True North',
+        },
+    },
+    0x000F => {
+        Name => 'GPSTrack',
+        Writable => 'rational64u',
+    },
+    0x0010 => {
+        Name => 'GPSImgDirectionRef',
+        Writable => 'string',
+        Count => 2,
+        PrintConv => {
+            M => 'Magnetic North',
+            T => 'True North',
+        },
+    },
+    0x0011 => {
+        Name => 'GPSImgDirection',
+        Writable => 'rational64u',
+    },
+    0x0012 => {
+        Name => 'GPSMapDatum',
+        Writable => 'string',
+    },
+    0x0013 => {
+        Name => 'GPSDestLatitudeRef',
+        Writable => 'string',
+        Count => 2,
+        PrintConv => {
+            N => 'North',
+            S => 'South',
+        },
+    },
+    0x0014 => {
+        Name => 'GPSDestLatitude',
+        Writable => 'rational64u',
+        Count => 3,
+        %coordConv,
+    },
+    0x0015 => {
+        Name => 'GPSDestLongitudeRef',
+        Writable => 'string',
+        Count => 2,
+        PrintConv => {
+            E => 'East',
+            W => 'West',
+        },
+    },
+    0x0016 => {
+        Name => 'GPSDestLongitude',
+        Writable => 'rational64u',
+        Count => 3,
+        %coordConv,
+    },
+    0x0017 => {
+        Name => 'GPSDestBearingRef',
+        Writable => 'string',
+        Count => 2,
+        PrintConv => {
+            M => 'Magnetic North',
+            T => 'True North',
+        },
+    },
+    0x0018 => {
+        Name => 'GPSDestBearing',
+        Writable => 'rational64u',
+    },
+    0x0019 => {
+        Name => 'GPSDestDistanceRef',
+        Writable => 'string',
+        Count => 2,
+        PrintConv => {
+            K => 'Kilometers',
+            M => 'Miles',
+            N => 'Nautical Miles',
+        },
+    },
+    0x001A => {
+        Name => 'GPSDestDistance',
+        Writable => 'rational64u',
+    },
+    0x001B => {
+        Name => 'GPSProcessingMethod',
+        Writable => 'undef',
+        PrintConv => 'Image::ExifTool::Exif::ConvertExifText($self,$val)',
+        PrintConvInv => 'Image::ExifTool::Exif::EncodeExifText($self,$val)',
+    },
+    0x001C => {
+        Name => 'GPSAreaInformation',
+        Writable => 'undef',
+        PrintConv => 'Image::ExifTool::Exif::ConvertExifText($self,$val)',
+        PrintConvInv => 'Image::ExifTool::Exif::EncodeExifText($self,$val)',
+    },
+    0x001D => {
+        Name => 'GPSDateStamp',
+        Groups => { 2 => 'Time' },
+        Writable => 'string',
+        Notes => 'YYYY:MM:DD',
+        Count => 11,
+        Shift => 'Time',
+        ValueConv => 'Image::ExifTool::Exif::ExifDate($val)',
+        ValueConvInv => '$val',
+        # pull date out of any format date/time string
+        PrintConvInv => '$val=~/(\d{4}).*?(\d{2}).*?(\d{2})/ ? "$1:$2:$3" : $val',
+    },
+    0x001E => {
+        Name => 'GPSDifferential',
+        Writable => 'int16u',
+        PrintConv => {
+            0 => 'No Correction',
+            1 => 'Differential Corrected',
+        },
+    },
+);
+
+# Composite GPS tags
+%Image::ExifTool::GPS::Composite = (
+    GPSDateTime => {
+        Description => 'GPS Date/Time',
+        Groups => { 2 => 'Time' },
+        Require => {
+            0 => 'GPSDateStamp',
+            1 => 'GPSTimeStamp',
+        },
+        ValueConv => '"$val[0] $val[1]"',
+        PrintConv => '$self->ConvertDateTime($val)',
+    },
+    GPSLatitude => {
+        Require => {
+            0 => 'GPS:GPSLatitude',
+            1 => 'GPS:GPSLatitudeRef',
+        },
+        ValueConv => '$val[1] =~ /^S/i ? -$val[0] : $val[0]',
+        PrintConv => 'Image::ExifTool::GPS::ToDMS($self, $val, 1, "N")',
+    },
+    GPSLongitude => {
+        Require => {
+            0 => 'GPS:GPSLongitude',
+            1 => 'GPS:GPSLongitudeRef',
+        },
+        ValueConv => '$val[1] =~ /^W/i ? -$val[0] : $val[0]',
+        PrintConv => 'Image::ExifTool::GPS::ToDMS($self, $val, 1, "E")',
+    },
+);
+
+# add our composite tags
+Image::ExifTool::AddCompositeTags('Image::ExifTool::GPS');
+
+#------------------------------------------------------------------------------
+# Convert degrees to DMS, or whatever the current settings are
+# Inputs: 0) ExifTool reference, 1) Value in degrees,
+#         2) format code (0=no format, 1=CoordFormat, 2=XMP format)
+#         3) 'N' or 'E' if sign is significant and N/S/E/W should be added
+# Returns: DMS string
+sub ToDMS($$;$$)
+{
+    my ($exifTool, $val, $doPrintConv, $ref) = @_;
+    my ($fmt, $num);
+
+    if ($ref) {
+        if ($val < 0) {
+            $val = -$val;
+            $ref = {N => 'S', E => 'W'}->{$ref};
+        }
+        $ref = " $ref" unless $doPrintConv and $doPrintConv eq '2';
+    } else {
+        $ref = '';
+    }
+    if ($doPrintConv) {
+        if ($doPrintConv eq '1') {
+            $fmt = ($exifTool->Options('CoordFormat') || q{%d deg %d' %.2f"}) . $ref;
+        } else {
+            $fmt = "%d,%.6f$ref";   # use XMP standard format
+        }
+        # count the number of format specifiers
+        $num = ($fmt =~ tr/%/%/);
+    } else {
+        $num = 3;
+    }
+    my ($d, $m, $s);
+    $d = $val;
+    if ($num > 1) {
+        $d = int($d);
+        $m = ($val - $d) * 60;
+        if ($num > 2) {
+            $m = int($m);
+            $s = ($val - $d - $m / 60) * 3600;
+        }
+    }
+    return $doPrintConv ? sprintf($fmt, $d, $m, $s) : "$d $m $s$ref";
+}
+
+#------------------------------------------------------------------------------
+# Convert to decimal degrees
+# Inputs: 0) a string containing 1-3 decimal numbers and any amount of other garbage
+#         1) true if value should be negative if coordinate ends in 'S' or 'W'
+# Returns: Coordinate in degrees
+sub ToDegrees($;$)
+{
+    my ($val, $doSign) = @_;
+    # extract decimal values out of any other garbage
+    my ($d, $m, $s) = ($val =~ /((?:[+-]?)(?=\d|\.\d)\d*(?:\.\d*)?)/g);
+    my $deg = ($d || 0) + (($m || 0) + ($s || 0)/60) / 60;
+    # make negative if S or W coordinate
+    $deg = -$deg if $doSign ? $val =~ /[^A-Z](S|W)$/i : $deg < 0;
+    return $deg;
+}
+
+
+1;  #end
+
+__END__
+
+=head1 NAME
+
+Image::ExifTool::GPS - EXIF GPS meta information tags
+
+=head1 SYNOPSIS
+
+This module is loaded automatically by Image::ExifTool when required.
+
+=head1 DESCRIPTION
+
+This module contains definitions required by Image::ExifTool to interpret
+GPS (Global Positioning System) meta information in EXIF data.
+
+=head1 AUTHOR
+
+Copyright 2003-2007, Phil Harvey (phil at owl.phy.queensu.ca)
+
+This library is free software; you can redistribute it and/or modify it
+under the same terms as Perl itself.
+
+=head1 REFERENCES
+
+=over 4
+
+=item L<Image::Info|Image::Info>
+
+=back
+
+=head1 SEE ALSO
+
+L<Image::ExifTool::TagNames/GPS Tags>,
+L<Image::ExifTool(3pm)|Image::ExifTool>,
+L<Image::Info(3pm)|Image::Info>
+
+=cut
Index: /gsdl/trunk/perllib/cpan/Image/ExifTool/GeoTiff.pm
===================================================================
--- /gsdl/trunk/perllib/cpan/Image/ExifTool/GeoTiff.pm	(revision 16842)
+++ /gsdl/trunk/perllib/cpan/Image/ExifTool/GeoTiff.pm	(revision 16842)
@@ -0,0 +1,2190 @@
+#------------------------------------------------------------------------------
+# File:         GeoTiff.pm
+#
+# Description:  Read GeoTiff meta information
+#
+# Revisions:    02/23/04 - P. Harvey Created
+#               02/25/04 - P. Harvey Added new codes from libgeotiff-1.2.1
+#               02/01/07 - P. Harvey Added new codes from libgeotiff-1.2.3
+#
+# Reference:    ftp://ftp.remotesensing.org/geotiff/libgeotiff/libgeotiff-1.1.4.tar.gz
+#------------------------------------------------------------------------------
+
+package Image::ExifTool::GeoTiff;
+
+use strict;
+use vars qw($VERSION);
+use Image::ExifTool qw(:DataAccess);
+
+$VERSION = '1.06';
+
+# format codes for geoTiff directory entries
+my %geoTiffFormat = (
+    0      => 'int16u',
+    0x87b0 => 'double',
+    0x87b1 => 'string',
+);
+
+my %epsg_units = (
+    9001 => 'Linear Meter',
+    9002 => 'Linear Foot',
+    9003 => 'Linear Foot US Survey',
+    9004 => 'Linear Foot Modified American',
+    9005 => 'Linear Foot Clarke',
+    9006 => 'Linear Foot Indian',
+    9007 => 'Linear Link',
+    9008 => 'Linear Link Benoit',
+    9009 => 'Linear Link Sears',
+    9010 => 'Linear Chain Benoit',
+    9011 => 'Linear Chain Sears',
+    9012 => 'Linear Yard Sears',
+    9013 => 'Linear Yard Indian',
+    9014 => 'Linear Fathom',
+    9015 => 'Linear Mile International Nautical',
+    9101 => 'Angular Radian',
+    9102 => 'Angular Degree',
+    9103 => 'Angular Arc Minute',
+    9104 => 'Angular Arc Second',
+    9105 => 'Angular Grad',
+    9106 => 'Angular Gon',
+    9107 => 'Angular DMS',
+    9108 => 'Angular DMS Hemisphere',
+    32767 => 'User Defined',
+);
+
+%Image::ExifTool::GeoTiff::Main = (
+    GROUPS => { 2 => 'Location' },
+    1 => {
+        # this isn't a real GeoTiff key, but put it here
+        # so it will appear in tag lists since we generate it below
+        Name => 'GeoTiffVersion',
+    },
+    1024 => {
+        Name => 'GTModelType',
+        PrintConv => {
+            1 => 'Projected',
+            2 => 'Geographic',
+            3 => 'Geocentric',
+            32767 => 'User Defined',
+        },
+    },
+    1025 => {
+        Name => 'GTRasterType',
+        PrintConv => {
+            1 => 'Pixel Is Area',
+            2 => 'Pixel Is Point',
+            32767 => 'User Defined',
+        },
+    },
+    1026 => 'GTCitation',
+    2048 => {
+        Name => 'GeographicType',
+        PrintConv => {
+            # epsg_gcs
+            4001 => 'Airy 1830',
+            4002 => 'Airy Modified 1849',
+            4003 => 'Australian National Spheroid',
+            4004 => 'Bessel 1841',
+            4005 => 'Bessel Modified',
+            4006 => 'Bessel Namibia',
+            4007 => 'Clarke 1858',
+            4008 => 'Clarke 1866',
+            4009 => 'Clarke 1866 Michigan',
+            4010 => 'Clarke 1880 Benoit',
+            4011 => 'Clarke 1880 IGN',
+            4012 => 'Clarke 1880 RGS',
+            4013 => 'Clarke 1880 Arc',
+            4014 => 'Clarke 1880 SGA 1922',
+            4015 => 'Everest 1830 1937 Adjustment',
+            4016 => 'Everest 1830 1967 Definition',
+            4017 => 'Everest 1830 1975 Definition',
+            4018 => 'Everest 1830 Modified',
+            4019 => 'GRS 1980',
+            4020 => 'Helmert 1906',
+            4021 => 'Indonesian National Spheroid',
+            4022 => 'International 1924',
+            4023 => 'International 1967',
+            4024 => 'Krassowsky 1940',
+            4025 => 'NWL9D',
+            4026 => 'NWL10D',
+            4027 => 'Plessis 1817',
+            4028 => 'Struve 1860',
+            4029 => 'War Office',
+            4030 => 'WGS84',
+            4031 => 'GEM10C',
+            4032 => 'OSU86F',
+            4033 => 'OSU91A',
+            4034 => 'Clarke 1880',
+            4035 => 'Sphere',
+            4120 => 'Greek',
+            4121 => 'GGRS87',
+            4123 => 'KKJ',
+            4124 => 'RT90',
+            4133 => 'EST92',
+            4815 => 'Greek Athens',
+            4201 => 'Adindan',
+            4202 => 'AGD66',
+            4203 => 'AGD84',
+            4204 => 'Ain el Abd',
+            4205 => 'Afgooye',
+            4206 => 'Agadez',
+            4207 => 'Lisbon',
+            4208 => 'Aratu',
+            4209 => 'Arc 1950',
+            4210 => 'Arc 1960',
+            4211 => 'Batavia',
+            4212 => 'Barbados',
+            4213 => 'Beduaram',
+            4214 => 'Beijing 1954',
+            4215 => 'Belge 1950',
+            4216 => 'Bermuda 1957',
+            4217 => 'Bern 1898',
+            4218 => 'Bogota',
+            4219 => 'Bukit Rimpah',
+            4220 => 'Camacupa',
+            4221 => 'Campo Inchauspe',
+            4222 => 'Cape',
+            4223 => 'Carthage',
+            4224 => 'Chua',
+            4225 => 'Corrego Alegre',
+            4226 => 'Cote d Ivoire',
+            4227 => 'Deir ez Zor',
+            4228 => 'Douala',
+            4229 => 'Egypt 1907',
+            4230 => 'ED50',
+            4231 => 'ED87',
+            4232 => 'Fahud',
+            4233 => 'Gandajika 1970',
+            4234 => 'Garoua',
+            4235 => 'Guyane Francaise',
+            4236 => 'Hu Tzu Shan',
+            4237 => 'HD72',
+            4238 => 'ID74',
+            4239 => 'Indian 1954',
+            4240 => 'Indian 1975',
+            4241 => 'Jamaica 1875',
+            4242 => 'JAD69',
+            4243 => 'Kalianpur',
+            4244 => 'Kandawala',
+            4245 => 'Kertau',
+            4246 => 'KOC',
+            4247 => 'La Canoa',
+            4248 => 'PSAD56',
+            4249 => 'Lake',
+            4250 => 'Leigon',
+            4251 => 'Liberia 1964',
+            4252 => 'Lome',
+            4253 => 'Luzon 1911',
+            4254 => 'Hito XVIII 1963',
+            4255 => 'Herat North',
+            4256 => 'Mahe 1971',
+            4257 => 'Makassar',
+            4258 => 'EUREF89',
+            4259 => 'Malongo 1987',
+            4260 => 'Manoca',
+            4261 => 'Merchich',
+            4262 => 'Massawa',
+            4263 => 'Minna',
+            4264 => 'Mhast',
+            4265 => 'Monte Mario',
+            4266 => 'M poraloko',
+            4267 => 'NAD27',
+            4268 => 'NAD Michigan',
+            4269 => 'NAD83',
+            4270 => 'Nahrwan 1967',
+            4271 => 'Naparima 1972',
+            4272 => 'GD49',
+            4273 => 'NGO 1948',
+            4274 => '73',
+            4275 => 'NTF',
+            4276 => 'NSWC 9Z 2',
+            4277 => 'OSGB 1936',
+            4278 => 'OSGB70',
+            4279 => 'OS SN80',
+            4280 => 'Padang',
+            4281 => 'Palestine 1923',
+            4282 => 'Pointe Noire',
+            4283 => 'GDA94',
+            4284 => 'Pulkovo 1942',
+            4285 => 'Qatar',
+            4286 => 'Qatar 1948',
+            4287 => 'Qornoq',
+            4288 => 'Loma Quintana',
+            4289 => 'Amersfoort',
+            4290 => 'RT38',
+            4291 => 'SAD69',
+            4292 => 'Sapper Hill 1943',
+            4293 => 'Schwarzeck',
+            4294 => 'Segora',
+            4295 => 'Serindung',
+            4296 => 'Sudan',
+            4297 => 'Tananarive',
+            4298 => 'Timbalai 1948',
+            4299 => 'TM65',
+            4300 => 'TM75',
+            4301 => 'Tokyo',
+            4302 => 'Trinidad 1903',
+            4303 => 'TC 1948',
+            4304 => 'Voirol 1875',
+            4305 => 'Voirol Unifie',
+            4306 => 'Bern 1938',
+            4307 => 'Nord Sahara 1959',
+            4308 => 'Stockholm 1938',
+            4309 => 'Yacare',
+            4310 => 'Yoff',
+            4311 => 'Zanderij',
+            4312 => 'MGI',
+            4313 => 'Belge 1972',
+            4314 => 'DHDN',
+            4315 => 'Conakry 1905',
+            4317 => 'Dealul Piscului 1970',
+            4322 => 'WGS 72',
+            4324 => 'WGS 72BE',
+            4326 => 'WGS 84',
+            4801 => 'Bern 1898 Bern',
+            4802 => 'Bogota Bogota',
+            4803 => 'Lisbon Lisbon',
+            4804 => 'Makassar Jakarta',
+            4805 => 'MGI Ferro',
+            4806 => 'Monte Mario Rome',
+            4807 => 'NTF Paris',
+            4808 => 'Padang Jakarta',
+            4809 => 'Belge 1950 Brussels',
+            4810 => 'Tananarive Paris',
+            4811 => 'Voirol 1875 Paris',
+            4812 => 'Voirol Unifie Paris',
+            4813 => 'Batavia Jakarta',
+            4901 => 'ATF Paris',
+            4902 => 'NDG Paris',
+            32767 => 'User Defined',
+        },
+    },
+    2049 => 'GeogCitation',
+    2050 => {
+        Name => 'GeogGeodeticDatum',
+        PrintConv => {
+            # epsg_datum,
+            6001 => 'Airy 1830',
+            6002 => 'Airy Modified 1849',
+            6003 => 'Australian National Spheroid',
+            6004 => 'Bessel 1841',
+            6005 => 'Bessel Modified',
+            6006 => 'Bessel Namibia',
+            6007 => 'Clarke 1858',
+            6008 => 'Clarke 1866',
+            6009 => 'Clarke 1866 Michigan',
+            6010 => 'Clarke 1880 Benoit',
+            6011 => 'Clarke 1880 IGN',
+            6012 => 'Clarke 1880 RGS',
+            6013 => 'Clarke 1880 Arc',
+            6014 => 'Clarke 1880 SGA 1922',
+            6015 => 'Everest 1830 1937 Adjustment',
+            6016 => 'Everest 1830 1967 Definition',
+            6017 => 'Everest 1830 1975 Definition',
+            6018 => 'Everest 1830 Modified',
+            6019 => 'GRS 1980',
+            6020 => 'Helmert 1906',
+            6021 => 'Indonesian National Spheroid',
+            6022 => 'International 1924',
+            6023 => 'International 1967',
+            6024 => 'Krassowsky 1960',
+            6025 => 'NWL9D',
+            6026 => 'NWL10D',
+            6027 => 'Plessis 1817',
+            6028 => 'Struve 1860',
+            6029 => 'War Office',
+            6030 => 'WGS84',
+            6031 => 'GEM10C',
+            6032 => 'OSU86F',
+            6033 => 'OSU91A',
+            6034 => 'Clarke 1880',
+            6035 => 'Sphere',
+            6201 => 'Adindan',
+            6202 => 'Australian Geodetic Datum 1966',
+            6203 => 'Australian Geodetic Datum 1984',
+            6204 => 'Ain el Abd 1970',
+            6205 => 'Afgooye',
+            6206 => 'Agadez',
+            6207 => 'Lisbon',
+            6208 => 'Aratu',
+            6209 => 'Arc 1950',
+            6210 => 'Arc 1960',
+            6211 => 'Batavia',
+            6212 => 'Barbados',
+            6213 => 'Beduaram',
+            6214 => 'Beijing 1954',
+            6215 => 'Reseau National Belge 1950',
+            6216 => 'Bermuda 1957',
+            6217 => 'Bern 1898',
+            6218 => 'Bogota',
+            6219 => 'Bukit Rimpah',
+            6220 => 'Camacupa',
+            6221 => 'Campo Inchauspe',
+            6222 => 'Cape',
+            6223 => 'Carthage',
+            6224 => 'Chua',
+            6225 => 'Corrego Alegre',
+            6226 => 'Cote d Ivoire',
+            6227 => 'Deir ez Zor',
+            6228 => 'Douala',
+            6229 => 'Egypt 1907',
+            6230 => 'European Datum 1950',
+            6231 => 'European Datum 1987',
+            6232 => 'Fahud',
+            6233 => 'Gandajika 1970',
+            6234 => 'Garoua',
+            6235 => 'Guyane Francaise',
+            6236 => 'Hu Tzu Shan',
+            6237 => 'Hungarian Datum 1972',
+            6238 => 'Indonesian Datum 1974',
+            6239 => 'Indian 1954',
+            6240 => 'Indian 1975',
+            6241 => 'Jamaica 1875',
+            6242 => 'Jamaica 1969',
+            6243 => 'Kalianpur',
+            6244 => 'Kandawala',
+            6245 => 'Kertau',
+            6246 => 'Kuwait Oil Company',
+            6247 => 'La Canoa',
+            6248 => 'Provisional S American Datum 1956',
+            6249 => 'Lake',
+            6250 => 'Leigon',
+            6251 => 'Liberia 1964',
+            6252 => 'Lome',
+            6253 => 'Luzon 1911',
+            6254 => 'Hito XVIII 1963',
+            6255 => 'Herat North',
+            6256 => 'Mahe 1971',
+            6257 => 'Makassar',
+            6258 => 'European Reference System 1989',
+            6259 => 'Malongo 1987',
+            6260 => 'Manoca',
+            6261 => 'Merchich',
+            6262 => 'Massawa',
+            6263 => 'Minna',
+            6264 => 'Mhast',
+            6265 => 'Monte Mario',
+            6266 => 'M poraloko',
+            6267 => 'North American Datum 1927',
+            6268 => 'NAD Michigan',
+            6269 => 'North American Datum 1983',
+            6270 => 'Nahrwan 1967',
+            6271 => 'Naparima 1972',
+            6272 => 'New Zealand Geodetic Datum 1949',
+            6273 => 'NGO 1948',
+            6274 => 'Datum 73',
+            6275 => 'Nouvelle Triangulation Francaise',
+            6276 => 'NSWC 9Z 2',
+            6277 => 'OSGB 1936',
+            6278 => 'OSGB 1970 SN',
+            6279 => 'OS SN 1980',
+            6280 => 'Padang 1884',
+            6281 => 'Palestine 1923',
+            6282 => 'Pointe Noire',
+            6283 => 'Geocentric Datum of Australia 1994',
+            6284 => 'Pulkovo 1942',
+            6285 => 'Qatar',
+            6286 => 'Qatar 1948',
+            6287 => 'Qornoq',
+            6288 => 'Loma Quintana',
+            6289 => 'Amersfoort',
+            6290 => 'RT38',
+            6291 => 'South American Datum 1969',
+            6292 => 'Sapper Hill 1943',
+            6293 => 'Schwarzeck',
+            6294 => 'Segora',
+            6295 => 'Serindung',
+            6296 => 'Sudan',
+            6297 => 'Tananarive 1925',
+            6298 => 'Timbalai 1948',
+            6299 => 'TM65',
+            6300 => 'TM75',
+            6301 => 'Tokyo',
+            6302 => 'Trinidad 1903',
+            6303 => 'Trucial Coast 1948',
+            6304 => 'Voirol 1875',
+            6305 => 'Voirol Unifie 1960',
+            6306 => 'Bern 1938',
+            6307 => 'Nord Sahara 1959',
+            6308 => 'Stockholm 1938',
+            6309 => 'Yacare',
+            6310 => 'Yoff',
+            6311 => 'Zanderij',
+            6312 => 'Militar Geographische Institut',
+            6313 => 'Reseau National Belge 1972',
+            6314 => 'Deutsche Hauptdreiecksnetz',
+            6315 => 'Conakry 1905',
+            6317 => 'Dealul Piscului 1970',
+            6322 => 'WGS72',
+            6324 => 'WGS72 Transit Broadcast Ephemeris',
+            6326 => 'WGS84',
+            6901 => 'Ancienne Triangulation Francaise',
+            6902 => 'Nord de Guerre',
+            32767 => 'User Defined',
+        },
+    },
+    2051 => {
+        Name => 'GeogPrimeMeridian',
+        PrintConv => {
+            # epsg_pm
+            8901 => 'Greenwich',
+            8902 => 'Lisbon',
+            8903 => 'Paris',
+            8904 => 'Bogota',
+            8905 => 'Madrid',
+            8906 => 'Rome',
+            8907 => 'Bern',
+            8908 => 'Jakarta',
+            8909 => 'Ferro',
+            8910 => 'Brussels',
+            8911 => 'Stockholm',
+            32767 => 'User Defined',
+        },
+    },
+    2052 => {
+        Name => 'GeogLinearUnits',
+        SeparateTable => 'Units',
+        PrintConv => \%epsg_units,
+    },
+    2053 => 'GeogLinearUnitSize',
+    2054 => {
+        Name => 'GeogAngularUnits',
+        SeparateTable => 'Units',
+        PrintConv => \%epsg_units,
+    },
+    2055 => 'GeogAngularUnitSize',
+    2056 => {
+        Name => 'GeogEllipsoid',
+        PrintConv => {
+            # epsg_ellipse
+            7001 => 'Airy 1830',
+            7002 => 'Airy Modified 1849',
+            7003 => 'Australian National Spheroid',
+            7004 => 'Bessel 1841',
+            7005 => 'Bessel Modified',
+            7006 => 'Bessel Namibia',
+            7007 => 'Clarke 1858',
+            7008 => 'Clarke 1866',
+            7009 => 'Clarke 1866 Michigan',
+            7010 => 'Clarke 1880 Benoit',
+            7011 => 'Clarke 1880 IGN',
+            7012 => 'Clarke 1880 RGS',
+            7013 => 'Clarke 1880 Arc',
+            7014 => 'Clarke 1880 SGA 1922',
+            7015 => 'Everest 1830 1937 Adjustment',
+            7016 => 'Everest 1830 1967 Definition',
+            7017 => 'Everest 1830 1975 Definition',
+            7018 => 'Everest 1830 Modified',
+            7019 => 'GRS 1980',
+            7020 => 'Helmert 1906',
+            7021 => 'Indonesian National Spheroid',
+            7022 => 'International 1924',
+            7023 => 'International 1967',
+            7024 => 'Krassowsky 1940',
+            7025 => 'NWL 9D',
+            7026 => 'NWL 10D',
+            7027 => 'Plessis 1817',
+            7028 => 'Struve 1860',
+            7029 => 'War Office',
+            7030 => 'WGS 84',
+            7031 => 'GEM 10C',
+            7032 => 'OSU86F',
+            7033 => 'OSU91A',
+            7034 => 'Clarke 1880',
+            7035 => 'Sphere',
+            32767 => 'User Defined',
+        },
+    },
+    2057 => 'GeogSemiMajorAxis',
+    2058 => 'GeogSemiMinorAxis',
+    2059 => 'GeogInvFlattening',
+    2060 => {
+        Name => 'GeogAzimuthUnits',
+        SeparateTable => 'Units',
+        PrintConv => \%epsg_units,
+    },
+    2061 => 'GeogPrimeMeridianLong',
+    3072 => {
+        Name => 'ProjectedCSType',
+        PrintConv => {
+            # epsg_pcs
+            2100 => 'GGRS87 Greek Grid',
+            2176 => 'ETRS89 Poland CS2000 zone 5',
+            2177 => 'ETRS89 Poland CS2000 zone 6',
+            2177 => 'ETRS89 Poland CS2000 zone 7',
+            2178 => 'ETRS89 Poland CS2000 zone 8',
+            2180 => 'ETRS89 Poland CS92',
+            2204 => 'NAD27 Tennessee',
+            2205 => 'NAD83 Kentucky North',
+            2391 => 'KKJ Finland zone 1',
+            2392 => 'KKJ Finland zone 2',
+            2393 => 'KKJ Finland zone 3',
+            2394 => 'KKJ Finland zone 4',
+            2400 => 'RT90 2 5 gon W',
+            2600 => 'Lietuvos Koordinoei Sistema 1994',
+            3053 => 'Hjorsey 1955 Lambert',
+            3057 => 'ISN93 Lambert 1993',
+            3300 => 'Estonian Coordinate System of 1992',
+            20137 => 'Adindan UTM zone 37N',
+            20138 => 'Adindan UTM zone 38N',
+            20248 => 'AGD66 AMG zone 48',
+            20249 => 'AGD66 AMG zone 49',
+            20250 => 'AGD66 AMG zone 50',
+            20251 => 'AGD66 AMG zone 51',
+            20252 => 'AGD66 AMG zone 52',
+            20253 => 'AGD66 AMG zone 53',
+            20254 => 'AGD66 AMG zone 54',
+            20255 => 'AGD66 AMG zone 55',
+            20256 => 'AGD66 AMG zone 56',
+            20257 => 'AGD66 AMG zone 57',
+            20258 => 'AGD66 AMG zone 58',
+            20348 => 'AGD84 AMG zone 48',
+            20349 => 'AGD84 AMG zone 49',
+            20350 => 'AGD84 AMG zone 50',
+            20351 => 'AGD84 AMG zone 51',
+            20352 => 'AGD84 AMG zone 52',
+            20353 => 'AGD84 AMG zone 53',
+            20354 => 'AGD84 AMG zone 54',
+            20355 => 'AGD84 AMG zone 55',
+            20356 => 'AGD84 AMG zone 56',
+            20357 => 'AGD84 AMG zone 57',
+            20358 => 'AGD84 AMG zone 58',
+            20437 => 'Ain el Abd UTM zone 37N',
+            20438 => 'Ain el Abd UTM zone 38N',
+            20439 => 'Ain el Abd UTM zone 39N',
+            20499 => 'Ain el Abd Bahrain Grid',
+            20538 => 'Afgooye UTM zone 38N',
+            20539 => 'Afgooye UTM zone 39N',
+            20700 => 'Lisbon Portugese Grid',
+            20822 => 'Aratu UTM zone 22S',
+            20823 => 'Aratu UTM zone 23S',
+            20824 => 'Aratu UTM zone 24S',
+            20973 => 'Arc 1950 Lo13',
+            20975 => 'Arc 1950 Lo15',
+            20977 => 'Arc 1950 Lo17',
+            20979 => 'Arc 1950 Lo19',
+            20981 => 'Arc 1950 Lo21',
+            20983 => 'Arc 1950 Lo23',
+            20985 => 'Arc 1950 Lo25',
+            20987 => 'Arc 1950 Lo27',
+            20989 => 'Arc 1950 Lo29',
+            20991 => 'Arc 1950 Lo31',
+            20993 => 'Arc 1950 Lo33',
+            20995 => 'Arc 1950 Lo35',
+            21100 => 'Batavia NEIEZ',
+            21148 => 'Batavia UTM zone 48S',
+            21149 => 'Batavia UTM zone 49S',
+            21150 => 'Batavia UTM zone 50S',
+            21413 => 'Beijing Gauss zone 13',
+            21414 => 'Beijing Gauss zone 14',
+            21415 => 'Beijing Gauss zone 15',
+            21416 => 'Beijing Gauss zone 16',
+            21417 => 'Beijing Gauss zone 17',
+            21418 => 'Beijing Gauss zone 18',
+            21419 => 'Beijing Gauss zone 19',
+            21420 => 'Beijing Gauss zone 20',
+            21421 => 'Beijing Gauss zone 21',
+            21422 => 'Beijing Gauss zone 22',
+            21423 => 'Beijing Gauss zone 23',
+            21473 => 'Beijing Gauss 13N',
+            21474 => 'Beijing Gauss 14N',
+            21475 => 'Beijing Gauss 15N',
+            21476 => 'Beijing Gauss 16N',
+            21477 => 'Beijing Gauss 17N',
+            21478 => 'Beijing Gauss 18N',
+            21479 => 'Beijing Gauss 19N',
+            21480 => 'Beijing Gauss 20N',
+            21481 => 'Beijing Gauss 21N',
+            21482 => 'Beijing Gauss 22N',
+            21483 => 'Beijing Gauss 23N',
+            21500 => 'Belge Lambert 50',
+            21790 => 'Bern 1898 Swiss Old',
+            21817 => 'Bogota UTM zone 17N',
+            21818 => 'Bogota UTM zone 18N',
+            21891 => 'Bogota Colombia 3W',
+            21892 => 'Bogota Colombia Bogota',
+            21893 => 'Bogota Colombia 3E',
+            21894 => 'Bogota Colombia 6E',
+            22032 => 'Camacupa UTM 32S',
+            22033 => 'Camacupa UTM 33S',
+            22191 => 'C Inchauspe Argentina 1',
+            22192 => 'C Inchauspe Argentina 2',
+            22193 => 'C Inchauspe Argentina 3',
+            22194 => 'C Inchauspe Argentina 4',
+            22195 => 'C Inchauspe Argentina 5',
+            22196 => 'C Inchauspe Argentina 6',
+            22197 => 'C Inchauspe Argentina 7',
+            22332 => 'Carthage UTM zone 32N',
+            22391 => 'Carthage Nord Tunisie',
+            22392 => 'Carthage Sud Tunisie',
+            22523 => 'Corrego Alegre UTM 23S',
+            22524 => 'Corrego Alegre UTM 24S',
+            22832 => 'Douala UTM zone 32N',
+            22992 => 'Egypt 1907 Red Belt',
+            22993 => 'Egypt 1907 Purple Belt',
+            22994 => 'Egypt 1907 Ext Purple',
+            23028 => 'ED50 UTM zone 28N',
+            23029 => 'ED50 UTM zone 29N',
+            23030 => 'ED50 UTM zone 30N',
+            23031 => 'ED50 UTM zone 31N',
+            23032 => 'ED50 UTM zone 32N',
+            23033 => 'ED50 UTM zone 33N',
+            23034 => 'ED50 UTM zone 34N',
+            23035 => 'ED50 UTM zone 35N',
+            23036 => 'ED50 UTM zone 36N',
+            23037 => 'ED50 UTM zone 37N',
+            23038 => 'ED50 UTM zone 38N',
+            23239 => 'Fahud UTM zone 39N',
+            23240 => 'Fahud UTM zone 40N',
+            23433 => 'Garoua UTM zone 33N',
+            23700 => 'HD72 EOV',
+            23846 => 'ID74 UTM zone 46N',
+            23847 => 'ID74 UTM zone 47N',
+            23848 => 'ID74 UTM zone 48N',
+            23849 => 'ID74 UTM zone 49N',
+            23850 => 'ID74 UTM zone 50N',
+            23851 => 'ID74 UTM zone 51N',
+            23852 => 'ID74 UTM zone 52N',
+            23853 => 'ID74 UTM zone 53N',
+            23886 => 'ID74 UTM zone 46S',
+            23887 => 'ID74 UTM zone 47S',
+            23888 => 'ID74 UTM zone 48S',
+            23889 => 'ID74 UTM zone 49S',
+            23890 => 'ID74 UTM zone 50S',
+            23891 => 'ID74 UTM zone 51S',
+            23892 => 'ID74 UTM zone 52S',
+            23893 => 'ID74 UTM zone 53S',
+            23894 => 'ID74 UTM zone 54S',
+            23947 => 'Indian 1954 UTM 47N',
+            23948 => 'Indian 1954 UTM 48N',
+            24047 => 'Indian 1975 UTM 47N',
+            24048 => 'Indian 1975 UTM 48N',
+            24100 => 'Jamaica 1875 Old Grid',
+            24200 => 'JAD69 Jamaica Grid',
+            24370 => 'Kalianpur India 0',
+            24371 => 'Kalianpur India I',
+            24372 => 'Kalianpur India IIa',
+            24373 => 'Kalianpur India IIIa',
+            24374 => 'Kalianpur India IVa',
+            24382 => 'Kalianpur India IIb',
+            24383 => 'Kalianpur India IIIb',
+            24384 => 'Kalianpur India IVb',
+            24500 => 'Kertau Singapore Grid',
+            24547 => 'Kertau UTM zone 47N',
+            24548 => 'Kertau UTM zone 48N',
+            24720 => 'La Canoa UTM zone 20N',
+            24721 => 'La Canoa UTM zone 21N',
+            24818 => 'PSAD56 UTM zone 18N',
+            24819 => 'PSAD56 UTM zone 19N',
+            24820 => 'PSAD56 UTM zone 20N',
+            24821 => 'PSAD56 UTM zone 21N',
+            24877 => 'PSAD56 UTM zone 17S',
+            24878 => 'PSAD56 UTM zone 18S',
+            24879 => 'PSAD56 UTM zone 19S',
+            24880 => 'PSAD56 UTM zone 20S',
+            24891 => 'PSAD56 Peru west zone',
+            24892 => 'PSAD56 Peru central',
+            24893 => 'PSAD56 Peru east zone',
+            25000 => 'Leigon Ghana Grid',
+            25231 => 'Lome UTM zone 31N',
+            25391 => 'Luzon Philippines I',
+            25392 => 'Luzon Philippines II',
+            25393 => 'Luzon Philippines III',
+            25394 => 'Luzon Philippines IV',
+            25395 => 'Luzon Philippines V',
+            25700 => 'Makassar NEIEZ',
+            25932 => 'Malongo 1987 UTM 32S',
+            26191 => 'Merchich Nord Maroc',
+            26192 => 'Merchich Sud Maroc',
+            26193 => 'Merchich Sahara',
+            26237 => 'Massawa UTM zone 37N',
+            26331 => 'Minna UTM zone 31N',
+            26332 => 'Minna UTM zone 32N',
+            26391 => 'Minna Nigeria West',
+            26392 => 'Minna Nigeria Mid Belt',
+            26393 => 'Minna Nigeria East',
+            26432 => 'Mhast UTM zone 32S',
+            26591 => 'Monte Mario Italy 1',
+            26592 => 'Monte Mario Italy 2',
+            26632 => 'M poraloko UTM 32N',
+            26692 => 'M poraloko UTM 32S',
+            26703 => 'NAD27 UTM zone 3N',
+            26704 => 'NAD27 UTM zone 4N',
+            26705 => 'NAD27 UTM zone 5N',
+            26706 => 'NAD27 UTM zone 6N',
+            26707 => 'NAD27 UTM zone 7N',
+            26708 => 'NAD27 UTM zone 8N',
+            26709 => 'NAD27 UTM zone 9N',
+            26710 => 'NAD27 UTM zone 10N',
+            26711 => 'NAD27 UTM zone 11N',
+            26712 => 'NAD27 UTM zone 12N',
+            26713 => 'NAD27 UTM zone 13N',
+            26714 => 'NAD27 UTM zone 14N',
+            26715 => 'NAD27 UTM zone 15N',
+            26716 => 'NAD27 UTM zone 16N',
+            26717 => 'NAD27 UTM zone 17N',
+            26718 => 'NAD27 UTM zone 18N',
+            26719 => 'NAD27 UTM zone 19N',
+            26720 => 'NAD27 UTM zone 20N',
+            26721 => 'NAD27 UTM zone 21N',
+            26722 => 'NAD27 UTM zone 22N',
+            26729 => 'NAD27 Alabama East',
+            26730 => 'NAD27 Alabama West',
+            26731 => 'NAD27 Alaska zone 1',
+            26732 => 'NAD27 Alaska zone 2',
+            26733 => 'NAD27 Alaska zone 3',
+            26734 => 'NAD27 Alaska zone 4',
+            26735 => 'NAD27 Alaska zone 5',
+            26736 => 'NAD27 Alaska zone 6',
+            26737 => 'NAD27 Alaska zone 7',
+            26738 => 'NAD27 Alaska zone 8',
+            26739 => 'NAD27 Alaska zone 9',
+            26740 => 'NAD27 Alaska zone 10',
+            26741 => 'NAD27 California I',
+            26742 => 'NAD27 California II',
+            26743 => 'NAD27 California III',
+            26744 => 'NAD27 California IV',
+            26745 => 'NAD27 California V',
+            26746 => 'NAD27 California VI',
+            26747 => 'NAD27 California VII',
+            26748 => 'NAD27 Arizona East',
+            26749 => 'NAD27 Arizona Central',
+            26750 => 'NAD27 Arizona West',
+            26751 => 'NAD27 Arkansas North',
+            26752 => 'NAD27 Arkansas South',
+            26753 => 'NAD27 Colorado North',
+            26754 => 'NAD27 Colorado Central',
+            26755 => 'NAD27 Colorado South',
+            26756 => 'NAD27 Connecticut',
+            26757 => 'NAD27 Delaware',
+            26758 => 'NAD27 Florida East',
+            26759 => 'NAD27 Florida West',
+            26760 => 'NAD27 Florida North',
+            26761 => 'NAD27 Hawaii zone 1',
+            26762 => 'NAD27 Hawaii zone 2',
+            26763 => 'NAD27 Hawaii zone 3',
+            26764 => 'NAD27 Hawaii zone 4',
+            26765 => 'NAD27 Hawaii zone 5',
+            26766 => 'NAD27 Georgia East',
+            26767 => 'NAD27 Georgia West',
+            26768 => 'NAD27 Idaho East',
+            26769 => 'NAD27 Idaho Central',
+            26770 => 'NAD27 Idaho West',
+            26771 => 'NAD27 Illinois East',
+            26772 => 'NAD27 Illinois West',
+            26773 => 'NAD27 Indiana East',
+            26774 => 'NAD27 BLM 14N feet',
+            26774 => 'NAD27 Indiana West',
+            26775 => 'NAD27 BLM 15N feet',
+            26775 => 'NAD27 Iowa North',
+            26776 => 'NAD27 BLM 16N feet',
+            26776 => 'NAD27 Iowa South',
+            26777 => 'NAD27 BLM 17N feet',
+            26777 => 'NAD27 Kansas North',
+            26778 => 'NAD27 Kansas South',
+            26779 => 'NAD27 Kentucky North',
+            26780 => 'NAD27 Kentucky South',
+            26781 => 'NAD27 Louisiana North',
+            26782 => 'NAD27 Louisiana South',
+            26783 => 'NAD27 Maine East',
+            26784 => 'NAD27 Maine West',
+            26785 => 'NAD27 Maryland',
+            26786 => 'NAD27 Massachusetts',
+            26787 => 'NAD27 Massachusetts Is',
+            26788 => 'NAD27 Michigan North',
+            26789 => 'NAD27 Michigan Central',
+            26790 => 'NAD27 Michigan South',
+            26791 => 'NAD27 Minnesota North',
+            26792 => 'NAD27 Minnesota Cent',
+            26793 => 'NAD27 Minnesota South',
+            26794 => 'NAD27 Mississippi East',
+            26795 => 'NAD27 Mississippi West',
+            26796 => 'NAD27 Missouri East',
+            26797 => 'NAD27 Missouri Central',
+            26798 => 'NAD27 Missouri West',
+            26801 => 'NAD Michigan Michigan East',
+            26802 => 'NAD Michigan Michigan Old Central',
+            26803 => 'NAD Michigan Michigan West',
+            26903 => 'NAD83 UTM zone 3N',
+            26904 => 'NAD83 UTM zone 4N',
+            26905 => 'NAD83 UTM zone 5N',
+            26906 => 'NAD83 UTM zone 6N',
+            26907 => 'NAD83 UTM zone 7N',
+            26908 => 'NAD83 UTM zone 8N',
+            26909 => 'NAD83 UTM zone 9N',
+            26910 => 'NAD83 UTM zone 10N',
+            26911 => 'NAD83 UTM zone 11N',
+            26912 => 'NAD83 UTM zone 12N',
+            26913 => 'NAD83 UTM zone 13N',
+            26914 => 'NAD83 UTM zone 14N',
+            26915 => 'NAD83 UTM zone 15N',
+            26916 => 'NAD83 UTM zone 16N',
+            26917 => 'NAD83 UTM zone 17N',
+            26918 => 'NAD83 UTM zone 18N',
+            26919 => 'NAD83 UTM zone 19N',
+            26920 => 'NAD83 UTM zone 20N',
+            26921 => 'NAD83 UTM zone 21N',
+            26922 => 'NAD83 UTM zone 22N',
+            26923 => 'NAD83 UTM zone 23N',
+            26929 => 'NAD83 Alabama East',
+            26930 => 'NAD83 Alabama West',
+            26931 => 'NAD83 Alaska zone 1',
+            26932 => 'NAD83 Alaska zone 2',
+            26933 => 'NAD83 Alaska zone 3',
+            26934 => 'NAD83 Alaska zone 4',
+            26935 => 'NAD83 Alaska zone 5',
+            26936 => 'NAD83 Alaska zone 6',
+            26937 => 'NAD83 Alaska zone 7',
+            26938 => 'NAD83 Alaska zone 8',
+            26939 => 'NAD83 Alaska zone 9',
+            26940 => 'NAD83 Alaska zone 10',
+            26941 => 'NAD83 California 1',
+            26942 => 'NAD83 California 2',
+            26943 => 'NAD83 California 3',
+            26944 => 'NAD83 California 4',
+            26945 => 'NAD83 California 5',
+            26946 => 'NAD83 California 6',
+            26948 => 'NAD83 Arizona East',
+            26949 => 'NAD83 Arizona Central',
+            26950 => 'NAD83 Arizona West',
+            26951 => 'NAD83 Arkansas North',
+            26952 => 'NAD83 Arkansas South',
+            26953 => 'NAD83 Colorado North',
+            26954 => 'NAD83 Colorado Central',
+            26955 => 'NAD83 Colorado South',
+            26956 => 'NAD83 Connecticut',
+            26957 => 'NAD83 Delaware',
+            26958 => 'NAD83 Florida East',
+            26959 => 'NAD83 Florida West',
+            26960 => 'NAD83 Florida North',
+            26961 => 'NAD83 Hawaii zone 1',
+            26962 => 'NAD83 Hawaii zone 2',
+            26963 => 'NAD83 Hawaii zone 3',
+            26964 => 'NAD83 Hawaii zone 4',
+            26965 => 'NAD83 Hawaii zone 5',
+            26966 => 'NAD83 Georgia East',
+            26967 => 'NAD83 Georgia West',
+            26968 => 'NAD83 Idaho East',
+            26969 => 'NAD83 Idaho Central',
+            26970 => 'NAD83 Idaho West',
+            26971 => 'NAD83 Illinois East',
+            26972 => 'NAD83 Illinois West',
+            26973 => 'NAD83 Indiana East',
+            26974 => 'NAD83 Indiana West',
+            26975 => 'NAD83 Iowa North',
+            26976 => 'NAD83 Iowa South',
+            26977 => 'NAD83 Kansas North',
+            26978 => 'NAD83 Kansas South',
+            26979 => 'NAD83 Kentucky North',
+            26980 => 'NAD83 Kentucky South',
+            26981 => 'NAD83 Louisiana North',
+            26982 => 'NAD83 Louisiana South',
+            26983 => 'NAD83 Maine East',
+            26984 => 'NAD83 Maine West',
+            26985 => 'NAD83 Maryland',
+            26986 => 'NAD83 Massachusetts',
+            26987 => 'NAD83 Massachusetts Is',
+            26988 => 'NAD83 Michigan North',
+            26989 => 'NAD83 Michigan Central',
+            26990 => 'NAD83 Michigan South',
+            26991 => 'NAD83 Minnesota North',
+            26992 => 'NAD83 Minnesota Cent',
+            26993 => 'NAD83 Minnesota South',
+            26994 => 'NAD83 Mississippi East',
+            26995 => 'NAD83 Mississippi West',
+            26996 => 'NAD83 Missouri East',
+            26997 => 'NAD83 Missouri Central',
+            26998 => 'NAD83 Missouri West',
+            27038 => 'Nahrwan 1967 UTM 38N',
+            27039 => 'Nahrwan 1967 UTM 39N',
+            27040 => 'Nahrwan 1967 UTM 40N',
+            27120 => 'Naparima UTM 20N',
+            27200 => 'GD49 NZ Map Grid',
+            27291 => 'GD49 North Island Grid',
+            27292 => 'GD49 South Island Grid',
+            27429 => 'Datum 73 UTM zone 29N',
+            27500 => 'ATF Nord de Guerre',
+            27581 => 'NTF France I',
+            27582 => 'NTF France II',
+            27583 => 'NTF France III',
+            27591 => 'NTF Nord France',
+            27592 => 'NTF Centre France',
+            27593 => 'NTF Sud France',
+            27700 => 'British National Grid',
+            28232 => 'Point Noire UTM 32S',
+            28348 => 'GDA94 MGA zone 48',
+            28349 => 'GDA94 MGA zone 49',
+            28350 => 'GDA94 MGA zone 50',
+            28351 => 'GDA94 MGA zone 51',
+            28352 => 'GDA94 MGA zone 52',
+            28353 => 'GDA94 MGA zone 53',
+            28354 => 'GDA94 MGA zone 54',
+            28355 => 'GDA94 MGA zone 55',
+            28356 => 'GDA94 MGA zone 56',
+            28357 => 'GDA94 MGA zone 57',
+            28358 => 'GDA94 MGA zone 58',
+            28404 => 'Pulkovo Gauss zone 4',
+            28405 => 'Pulkovo Gauss zone 5',
+            28406 => 'Pulkovo Gauss zone 6',
+            28407 => 'Pulkovo Gauss zone 7',
+            28408 => 'Pulkovo Gauss zone 8',
+            28409 => 'Pulkovo Gauss zone 9',
+            28410 => 'Pulkovo Gauss zone 10',
+            28411 => 'Pulkovo Gauss zone 11',
+            28412 => 'Pulkovo Gauss zone 12',
+            28413 => 'Pulkovo Gauss zone 13',
+            28414 => 'Pulkovo Gauss zone 14',
+            28415 => 'Pulkovo Gauss zone 15',
+            28416 => 'Pulkovo Gauss zone 16',
+            28417 => 'Pulkovo Gauss zone 17',
+            28418 => 'Pulkovo Gauss zone 18',
+            28419 => 'Pulkovo Gauss zone 19',
+            28420 => 'Pulkovo Gauss zone 20',
+            28421 => 'Pulkovo Gauss zone 21',
+            28422 => 'Pulkovo Gauss zone 22',
+            28423 => 'Pulkovo Gauss zone 23',
+            28424 => 'Pulkovo Gauss zone 24',
+            28425 => 'Pulkovo Gauss zone 25',
+            28426 => 'Pulkovo Gauss zone 26',
+            28427 => 'Pulkovo Gauss zone 27',
+            28428 => 'Pulkovo Gauss zone 28',
+            28429 => 'Pulkovo Gauss zone 29',
+            28430 => 'Pulkovo Gauss zone 30',
+            28431 => 'Pulkovo Gauss zone 31',
+            28432 => 'Pulkovo Gauss zone 32',
+            28464 => 'Pulkovo Gauss 4N',
+            28465 => 'Pulkovo Gauss 5N',
+            28466 => 'Pulkovo Gauss 6N',
+            28467 => 'Pulkovo Gauss 7N',
+            28468 => 'Pulkovo Gauss 8N',
+            28469 => 'Pulkovo Gauss 9N',
+            28470 => 'Pulkovo Gauss 10N',
+            28471 => 'Pulkovo Gauss 11N',
+            28472 => 'Pulkovo Gauss 12N',
+            28473 => 'Pulkovo Gauss 13N',
+            28474 => 'Pulkovo Gauss 14N',
+            28475 => 'Pulkovo Gauss 15N',
+            28476 => 'Pulkovo Gauss 16N',
+            28477 => 'Pulkovo Gauss 17N',
+            28478 => 'Pulkovo Gauss 18N',
+            28479 => 'Pulkovo Gauss 19N',
+            28480 => 'Pulkovo Gauss 20N',
+            28481 => 'Pulkovo Gauss 21N',
+            28482 => 'Pulkovo Gauss 22N',
+            28483 => 'Pulkovo Gauss 23N',
+            28484 => 'Pulkovo Gauss 24N',
+            28485 => 'Pulkovo Gauss 25N',
+            28486 => 'Pulkovo Gauss 26N',
+            28487 => 'Pulkovo Gauss 27N',
+            28488 => 'Pulkovo Gauss 28N',
+            28489 => 'Pulkovo Gauss 29N',
+            28490 => 'Pulkovo Gauss 30N',
+            28491 => 'Pulkovo Gauss 31N',
+            28492 => 'Pulkovo Gauss 32N',
+            28600 => 'Qatar National Grid',
+            28991 => 'RD Netherlands Old',
+            28992 => 'RD Netherlands New',
+            29118 => 'SAD69 UTM zone 18N',
+            29119 => 'SAD69 UTM zone 19N',
+            29120 => 'SAD69 UTM zone 20N',
+            29121 => 'SAD69 UTM zone 21N',
+            29122 => 'SAD69 UTM zone 22N',
+            29177 => 'SAD69 UTM zone 17S',
+            29178 => 'SAD69 UTM zone 18S',
+            29179 => 'SAD69 UTM zone 19S',
+            29180 => 'SAD69 UTM zone 20S',
+            29181 => 'SAD69 UTM zone 21S',
+            29182 => 'SAD69 UTM zone 22S',
+            29183 => 'SAD69 UTM zone 23S',
+            29184 => 'SAD69 UTM zone 24S',
+            29185 => 'SAD69 UTM zone 25S',
+            29220 => 'Sapper Hill UTM 20S',
+            29221 => 'Sapper Hill UTM 21S',
+            29333 => 'Schwarzeck UTM 33S',
+            29635 => 'Sudan UTM zone 35N',
+            29636 => 'Sudan UTM zone 36N',
+            29700 => 'Tananarive Laborde',
+            29738 => 'Tananarive UTM 38S',
+            29739 => 'Tananarive UTM 39S',
+            29800 => 'Timbalai 1948 Borneo',
+            29849 => 'Timbalai 1948 UTM 49N',
+            29850 => 'Timbalai 1948 UTM 50N',
+            29900 => 'TM65 Irish Nat Grid',
+            30200 => 'Trinidad 1903 Trinidad',
+            30339 => 'TC 1948 UTM zone 39N',
+            30340 => 'TC 1948 UTM zone 40N',
+            30491 => 'Voirol N Algerie ancien',
+            30492 => 'Voirol S Algerie ancien',
+            30591 => 'Voirol Unifie N Algerie',
+            30592 => 'Voirol Unifie S Algerie',
+            30600 => 'Bern 1938 Swiss New',
+            30729 => 'Nord Sahara UTM 29N',
+            30730 => 'Nord Sahara UTM 30N',
+            30731 => 'Nord Sahara UTM 31N',
+            30732 => 'Nord Sahara UTM 32N',
+            31028 => 'Yoff UTM zone 28N',
+            31121 => 'Zanderij UTM zone 21N',
+            31291 => 'MGI Austria West',
+            31292 => 'MGI Austria Central',
+            31293 => 'MGI Austria East',
+            31300 => 'Belge Lambert 72',
+            31491 => 'DHDN Germany zone 1',
+            31492 => 'DHDN Germany zone 2',
+            31493 => 'DHDN Germany zone 3',
+            31494 => 'DHDN Germany zone 4',
+            31495 => 'DHDN Germany zone 5',
+            31700 => 'Dealul Piscului 1970 Stereo 70',
+            32001 => 'NAD27 Montana North',
+            32002 => 'NAD27 Montana Central',
+            32003 => 'NAD27 Montana South',
+            32005 => 'NAD27 Nebraska North',
+            32006 => 'NAD27 Nebraska South',
+            32007 => 'NAD27 Nevada East',
+            32008 => 'NAD27 Nevada Central',
+            32009 => 'NAD27 Nevada West',
+            32010 => 'NAD27 New Hampshire',
+            32011 => 'NAD27 New Jersey',
+            32012 => 'NAD27 New Mexico East',
+            32013 => 'NAD27 New Mexico Cent',
+            32014 => 'NAD27 New Mexico West',
+            32015 => 'NAD27 New York East',
+            32016 => 'NAD27 New York Central',
+            32017 => 'NAD27 New York West',
+            32018 => 'NAD27 New York Long Is',
+            32019 => 'NAD27 North Carolina',
+            32020 => 'NAD27 North Dakota N',
+            32021 => 'NAD27 North Dakota S',
+            32022 => 'NAD27 Ohio North',
+            32023 => 'NAD27 Ohio South',
+            32024 => 'NAD27 Oklahoma North',
+            32025 => 'NAD27 Oklahoma South',
+            32026 => 'NAD27 Oregon North',
+            32027 => 'NAD27 Oregon South',
+            32028 => 'NAD27 Pennsylvania N',
+            32029 => 'NAD27 Pennsylvania S',
+            32030 => 'NAD27 Rhode Island',
+            32031 => 'NAD27 South Carolina N',
+            32033 => 'NAD27 South Carolina S',
+            32034 => 'NAD27 South Dakota N',
+            32035 => 'NAD27 South Dakota S',
+            32036 => 'NAD27 Tennessee',
+            32037 => 'NAD27 Texas North',
+            32038 => 'NAD27 Texas North Cen',
+            32039 => 'NAD27 Texas Central',
+            32040 => 'NAD27 Texas South Cen',
+            32041 => 'NAD27 Texas South',
+            32042 => 'NAD27 Utah North',
+            32043 => 'NAD27 Utah Central',
+            32044 => 'NAD27 Utah South',
+            32045 => 'NAD27 Vermont',
+            32046 => 'NAD27 Virginia North',
+            32047 => 'NAD27 Virginia South',
+            32048 => 'NAD27 Washington North',
+            32049 => 'NAD27 Washington South',
+            32050 => 'NAD27 West Virginia N',
+            32051 => 'NAD27 West Virginia S',
+            32052 => 'NAD27 Wisconsin North',
+            32053 => 'NAD27 Wisconsin Cen',
+            32054 => 'NAD27 Wisconsin South',
+            32055 => 'NAD27 Wyoming East',
+            32056 => 'NAD27 Wyoming E Cen',
+            32057 => 'NAD27 Wyoming W Cen',
+            32058 => 'NAD27 Wyoming West',
+            32059 => 'NAD27 Puerto Rico',
+            32060 => 'NAD27 St Croix',
+            32100 => 'NAD83 Montana',
+            32104 => 'NAD83 Nebraska',
+            32107 => 'NAD83 Nevada East',
+            32108 => 'NAD83 Nevada Central',
+            32109 => 'NAD83 Nevada West',
+            32110 => 'NAD83 New Hampshire',
+            32111 => 'NAD83 New Jersey',
+            32112 => 'NAD83 New Mexico East',
+            32113 => 'NAD83 New Mexico Cent',
+            32114 => 'NAD83 New Mexico West',
+            32115 => 'NAD83 New York East',
+            32116 => 'NAD83 New York Central',
+            32117 => 'NAD83 New York West',
+            32118 => 'NAD83 New York Long Is',
+            32119 => 'NAD83 North Carolina',
+            32120 => 'NAD83 North Dakota N',
+            32121 => 'NAD83 North Dakota S',
+            32122 => 'NAD83 Ohio North',
+            32123 => 'NAD83 Ohio South',
+            32124 => 'NAD83 Oklahoma North',
+            32125 => 'NAD83 Oklahoma South',
+            32126 => 'NAD83 Oregon North',
+            32127 => 'NAD83 Oregon South',
+            32128 => 'NAD83 Pennsylvania N',
+            32129 => 'NAD83 Pennsylvania S',
+            32130 => 'NAD83 Rhode Island',
+            32133 => 'NAD83 South Carolina',
+            32134 => 'NAD83 South Dakota N',
+            32135 => 'NAD83 South Dakota S',
+            32136 => 'NAD83 Tennessee',
+            32137 => 'NAD83 Texas North',
+            32138 => 'NAD83 Texas North Cen',
+            32139 => 'NAD83 Texas Central',
+            32140 => 'NAD83 Texas South Cen',
+            32141 => 'NAD83 Texas South',
+            32142 => 'NAD83 Utah North',
+            32143 => 'NAD83 Utah Central',
+            32144 => 'NAD83 Utah South',
+            32145 => 'NAD83 Vermont',
+            32146 => 'NAD83 Virginia North',
+            32147 => 'NAD83 Virginia South',
+            32148 => 'NAD83 Washington North',
+            32149 => 'NAD83 Washington South',
+            32150 => 'NAD83 West Virginia N',
+            32151 => 'NAD83 West Virginia S',
+            32152 => 'NAD83 Wisconsin North',
+            32153 => 'NAD83 Wisconsin Cen',
+            32154 => 'NAD83 Wisconsin South',
+            32155 => 'NAD83 Wyoming East',
+            32156 => 'NAD83 Wyoming E Cen',
+            32157 => 'NAD83 Wyoming W Cen',
+            32158 => 'NAD83 Wyoming West',
+            32161 => 'NAD83 Puerto Rico Virgin Is',
+            32201 => 'WGS72 UTM zone 1N',
+            32202 => 'WGS72 UTM zone 2N',
+            32203 => 'WGS72 UTM zone 3N',
+            32204 => 'WGS72 UTM zone 4N',
+            32205 => 'WGS72 UTM zone 5N',
+            32206 => 'WGS72 UTM zone 6N',
+            32207 => 'WGS72 UTM zone 7N',
+            32208 => 'WGS72 UTM zone 8N',
+            32209 => 'WGS72 UTM zone 9N',
+            32210 => 'WGS72 UTM zone 10N',
+            32211 => 'WGS72 UTM zone 11N',
+            32212 => 'WGS72 UTM zone 12N',
+            32213 => 'WGS72 UTM zone 13N',
+            32214 => 'WGS72 UTM zone 14N',
+            32215 => 'WGS72 UTM zone 15N',
+            32216 => 'WGS72 UTM zone 16N',
+            32217 => 'WGS72 UTM zone 17N',
+            32218 => 'WGS72 UTM zone 18N',
+            32219 => 'WGS72 UTM zone 19N',
+            32220 => 'WGS72 UTM zone 20N',
+            32221 => 'WGS72 UTM zone 21N',
+            32222 => 'WGS72 UTM zone 22N',
+            32223 => 'WGS72 UTM zone 23N',
+            32224 => 'WGS72 UTM zone 24N',
+            32225 => 'WGS72 UTM zone 25N',
+            32226 => 'WGS72 UTM zone 26N',
+            32227 => 'WGS72 UTM zone 27N',
+            32228 => 'WGS72 UTM zone 28N',
+            32229 => 'WGS72 UTM zone 29N',
+            32230 => 'WGS72 UTM zone 30N',
+            32231 => 'WGS72 UTM zone 31N',
+            32232 => 'WGS72 UTM zone 32N',
+            32233 => 'WGS72 UTM zone 33N',
+            32234 => 'WGS72 UTM zone 34N',
+            32235 => 'WGS72 UTM zone 35N',
+            32236 => 'WGS72 UTM zone 36N',
+            32237 => 'WGS72 UTM zone 37N',
+            32238 => 'WGS72 UTM zone 38N',
+            32239 => 'WGS72 UTM zone 39N',
+            32240 => 'WGS72 UTM zone 40N',
+            32241 => 'WGS72 UTM zone 41N',
+            32242 => 'WGS72 UTM zone 42N',
+            32243 => 'WGS72 UTM zone 43N',
+            32244 => 'WGS72 UTM zone 44N',
+            32245 => 'WGS72 UTM zone 45N',
+            32246 => 'WGS72 UTM zone 46N',
+            32247 => 'WGS72 UTM zone 47N',
+            32248 => 'WGS72 UTM zone 48N',
+            32249 => 'WGS72 UTM zone 49N',
+            32250 => 'WGS72 UTM zone 50N',
+            32251 => 'WGS72 UTM zone 51N',
+            32252 => 'WGS72 UTM zone 52N',
+            32253 => 'WGS72 UTM zone 53N',
+            32254 => 'WGS72 UTM zone 54N',
+            32255 => 'WGS72 UTM zone 55N',
+            32256 => 'WGS72 UTM zone 56N',
+            32257 => 'WGS72 UTM zone 57N',
+            32258 => 'WGS72 UTM zone 58N',
+            32259 => 'WGS72 UTM zone 59N',
+            32260 => 'WGS72 UTM zone 60N',
+            32301 => 'WGS72 UTM zone 1S',
+            32302 => 'WGS72 UTM zone 2S',
+            32303 => 'WGS72 UTM zone 3S',
+            32304 => 'WGS72 UTM zone 4S',
+            32305 => 'WGS72 UTM zone 5S',
+            32306 => 'WGS72 UTM zone 6S',
+            32307 => 'WGS72 UTM zone 7S',
+            32308 => 'WGS72 UTM zone 8S',
+            32309 => 'WGS72 UTM zone 9S',
+            32310 => 'WGS72 UTM zone 10S',
+            32311 => 'WGS72 UTM zone 11S',
+            32312 => 'WGS72 UTM zone 12S',
+            32313 => 'WGS72 UTM zone 13S',
+            32314 => 'WGS72 UTM zone 14S',
+            32315 => 'WGS72 UTM zone 15S',
+            32316 => 'WGS72 UTM zone 16S',
+            32317 => 'WGS72 UTM zone 17S',
+            32318 => 'WGS72 UTM zone 18S',
+            32319 => 'WGS72 UTM zone 19S',
+            32320 => 'WGS72 UTM zone 20S',
+            32321 => 'WGS72 UTM zone 21S',
+            32322 => 'WGS72 UTM zone 22S',
+            32323 => 'WGS72 UTM zone 23S',
+            32324 => 'WGS72 UTM zone 24S',
+            32325 => 'WGS72 UTM zone 25S',
+            32326 => 'WGS72 UTM zone 26S',
+            32327 => 'WGS72 UTM zone 27S',
+            32328 => 'WGS72 UTM zone 28S',
+            32329 => 'WGS72 UTM zone 29S',
+            32330 => 'WGS72 UTM zone 30S',
+            32331 => 'WGS72 UTM zone 31S',
+            32332 => 'WGS72 UTM zone 32S',
+            32333 => 'WGS72 UTM zone 33S',
+            32334 => 'WGS72 UTM zone 34S',
+            32335 => 'WGS72 UTM zone 35S',
+            32336 => 'WGS72 UTM zone 36S',
+            32337 => 'WGS72 UTM zone 37S',
+            32338 => 'WGS72 UTM zone 38S',
+            32339 => 'WGS72 UTM zone 39S',
+            32340 => 'WGS72 UTM zone 40S',
+            32341 => 'WGS72 UTM zone 41S',
+            32342 => 'WGS72 UTM zone 42S',
+            32343 => 'WGS72 UTM zone 43S',
+            32344 => 'WGS72 UTM zone 44S',
+            32345 => 'WGS72 UTM zone 45S',
+            32346 => 'WGS72 UTM zone 46S',
+            32347 => 'WGS72 UTM zone 47S',
+            32348 => 'WGS72 UTM zone 48S',
+            32349 => 'WGS72 UTM zone 49S',
+            32350 => 'WGS72 UTM zone 50S',
+            32351 => 'WGS72 UTM zone 51S',
+            32352 => 'WGS72 UTM zone 52S',
+            32353 => 'WGS72 UTM zone 53S',
+            32354 => 'WGS72 UTM zone 54S',
+            32355 => 'WGS72 UTM zone 55S',
+            32356 => 'WGS72 UTM zone 56S',
+            32357 => 'WGS72 UTM zone 57S',
+            32358 => 'WGS72 UTM zone 58S',
+            32359 => 'WGS72 UTM zone 59S',
+            32360 => 'WGS72 UTM zone 60S',
+            32401 => 'WGS72BE UTM zone 1N',
+            32402 => 'WGS72BE UTM zone 2N',
+            32403 => 'WGS72BE UTM zone 3N',
+            32404 => 'WGS72BE UTM zone 4N',
+            32405 => 'WGS72BE UTM zone 5N',
+            32406 => 'WGS72BE UTM zone 6N',
+            32407 => 'WGS72BE UTM zone 7N',
+            32408 => 'WGS72BE UTM zone 8N',
+            32409 => 'WGS72BE UTM zone 9N',
+            32410 => 'WGS72BE UTM zone 10N',
+            32411 => 'WGS72BE UTM zone 11N',
+            32412 => 'WGS72BE UTM zone 12N',
+            32413 => 'WGS72BE UTM zone 13N',
+            32414 => 'WGS72BE UTM zone 14N',
+            32415 => 'WGS72BE UTM zone 15N',
+            32416 => 'WGS72BE UTM zone 16N',
+            32417 => 'WGS72BE UTM zone 17N',
+            32418 => 'WGS72BE UTM zone 18N',
+            32419 => 'WGS72BE UTM zone 19N',
+            32420 => 'WGS72BE UTM zone 20N',
+            32421 => 'WGS72BE UTM zone 21N',
+            32422 => 'WGS72BE UTM zone 22N',
+            32423 => 'WGS72BE UTM zone 23N',
+            32424 => 'WGS72BE UTM zone 24N',
+            32425 => 'WGS72BE UTM zone 25N',
+            32426 => 'WGS72BE UTM zone 26N',
+            32427 => 'WGS72BE UTM zone 27N',
+            32428 => 'WGS72BE UTM zone 28N',
+            32429 => 'WGS72BE UTM zone 29N',
+            32430 => 'WGS72BE UTM zone 30N',
+            32431 => 'WGS72BE UTM zone 31N',
+            32432 => 'WGS72BE UTM zone 32N',
+            32433 => 'WGS72BE UTM zone 33N',
+            32434 => 'WGS72BE UTM zone 34N',
+            32435 => 'WGS72BE UTM zone 35N',
+            32436 => 'WGS72BE UTM zone 36N',
+            32437 => 'WGS72BE UTM zone 37N',
+            32438 => 'WGS72BE UTM zone 38N',
+            32439 => 'WGS72BE UTM zone 39N',
+            32440 => 'WGS72BE UTM zone 40N',
+            32441 => 'WGS72BE UTM zone 41N',
+            32442 => 'WGS72BE UTM zone 42N',
+            32443 => 'WGS72BE UTM zone 43N',
+            32444 => 'WGS72BE UTM zone 44N',
+            32445 => 'WGS72BE UTM zone 45N',
+            32446 => 'WGS72BE UTM zone 46N',
+            32447 => 'WGS72BE UTM zone 47N',
+            32448 => 'WGS72BE UTM zone 48N',
+            32449 => 'WGS72BE UTM zone 49N',
+            32450 => 'WGS72BE UTM zone 50N',
+            32451 => 'WGS72BE UTM zone 51N',
+            32452 => 'WGS72BE UTM zone 52N',
+            32453 => 'WGS72BE UTM zone 53N',
+            32454 => 'WGS72BE UTM zone 54N',
+            32455 => 'WGS72BE UTM zone 55N',
+            32456 => 'WGS72BE UTM zone 56N',
+            32457 => 'WGS72BE UTM zone 57N',
+            32458 => 'WGS72BE UTM zone 58N',
+            32459 => 'WGS72BE UTM zone 59N',
+            32460 => 'WGS72BE UTM zone 60N',
+            32501 => 'WGS72BE UTM zone 1S',
+            32502 => 'WGS72BE UTM zone 2S',
+            32503 => 'WGS72BE UTM zone 3S',
+            32504 => 'WGS72BE UTM zone 4S',
+            32505 => 'WGS72BE UTM zone 5S',
+            32506 => 'WGS72BE UTM zone 6S',
+            32507 => 'WGS72BE UTM zone 7S',
+            32508 => 'WGS72BE UTM zone 8S',
+            32509 => 'WGS72BE UTM zone 9S',
+            32510 => 'WGS72BE UTM zone 10S',
+            32511 => 'WGS72BE UTM zone 11S',
+            32512 => 'WGS72BE UTM zone 12S',
+            32513 => 'WGS72BE UTM zone 13S',
+            32514 => 'WGS72BE UTM zone 14S',
+            32515 => 'WGS72BE UTM zone 15S',
+            32516 => 'WGS72BE UTM zone 16S',
+            32517 => 'WGS72BE UTM zone 17S',
+            32518 => 'WGS72BE UTM zone 18S',
+            32519 => 'WGS72BE UTM zone 19S',
+            32520 => 'WGS72BE UTM zone 20S',
+            32521 => 'WGS72BE UTM zone 21S',
+            32522 => 'WGS72BE UTM zone 22S',
+            32523 => 'WGS72BE UTM zone 23S',
+            32524 => 'WGS72BE UTM zone 24S',
+            32525 => 'WGS72BE UTM zone 25S',
+            32526 => 'WGS72BE UTM zone 26S',
+            32527 => 'WGS72BE UTM zone 27S',
+            32528 => 'WGS72BE UTM zone 28S',
+            32529 => 'WGS72BE UTM zone 29S',
+            32530 => 'WGS72BE UTM zone 30S',
+            32531 => 'WGS72BE UTM zone 31S',
+            32532 => 'WGS72BE UTM zone 32S',
+            32533 => 'WGS72BE UTM zone 33S',
+            32534 => 'WGS72BE UTM zone 34S',
+            32535 => 'WGS72BE UTM zone 35S',
+            32536 => 'WGS72BE UTM zone 36S',
+            32537 => 'WGS72BE UTM zone 37S',
+            32538 => 'WGS72BE UTM zone 38S',
+            32539 => 'WGS72BE UTM zone 39S',
+            32540 => 'WGS72BE UTM zone 40S',
+            32541 => 'WGS72BE UTM zone 41S',
+            32542 => 'WGS72BE UTM zone 42S',
+            32543 => 'WGS72BE UTM zone 43S',
+            32544 => 'WGS72BE UTM zone 44S',
+            32545 => 'WGS72BE UTM zone 45S',
+            32546 => 'WGS72BE UTM zone 46S',
+            32547 => 'WGS72BE UTM zone 47S',
+            32548 => 'WGS72BE UTM zone 48S',
+            32549 => 'WGS72BE UTM zone 49S',
+            32550 => 'WGS72BE UTM zone 50S',
+            32551 => 'WGS72BE UTM zone 51S',
+            32552 => 'WGS72BE UTM zone 52S',
+            32553 => 'WGS72BE UTM zone 53S',
+            32554 => 'WGS72BE UTM zone 54S',
+            32555 => 'WGS72BE UTM zone 55S',
+            32556 => 'WGS72BE UTM zone 56S',
+            32557 => 'WGS72BE UTM zone 57S',
+            32558 => 'WGS72BE UTM zone 58S',
+            32559 => 'WGS72BE UTM zone 59S',
+            32560 => 'WGS72BE UTM zone 60S',
+            32601 => 'WGS84 UTM zone 1N',
+            32602 => 'WGS84 UTM zone 2N',
+            32603 => 'WGS84 UTM zone 3N',
+            32604 => 'WGS84 UTM zone 4N',
+            32605 => 'WGS84 UTM zone 5N',
+            32606 => 'WGS84 UTM zone 6N',
+            32607 => 'WGS84 UTM zone 7N',
+            32608 => 'WGS84 UTM zone 8N',
+            32609 => 'WGS84 UTM zone 9N',
+            32610 => 'WGS84 UTM zone 10N',
+            32611 => 'WGS84 UTM zone 11N',
+            32612 => 'WGS84 UTM zone 12N',
+            32613 => 'WGS84 UTM zone 13N',
+            32614 => 'WGS84 UTM zone 14N',
+            32615 => 'WGS84 UTM zone 15N',
+            32616 => 'WGS84 UTM zone 16N',
+            32617 => 'WGS84 UTM zone 17N',
+            32618 => 'WGS84 UTM zone 18N',
+            32619 => 'WGS84 UTM zone 19N',
+            32620 => 'WGS84 UTM zone 20N',
+            32621 => 'WGS84 UTM zone 21N',
+            32622 => 'WGS84 UTM zone 22N',
+            32623 => 'WGS84 UTM zone 23N',
+            32624 => 'WGS84 UTM zone 24N',
+            32625 => 'WGS84 UTM zone 25N',
+            32626 => 'WGS84 UTM zone 26N',
+            32627 => 'WGS84 UTM zone 27N',
+            32628 => 'WGS84 UTM zone 28N',
+            32629 => 'WGS84 UTM zone 29N',
+            32630 => 'WGS84 UTM zone 30N',
+            32631 => 'WGS84 UTM zone 31N',
+            32632 => 'WGS84 UTM zone 32N',
+            32633 => 'WGS84 UTM zone 33N',
+            32634 => 'WGS84 UTM zone 34N',
+            32635 => 'WGS84 UTM zone 35N',
+            32636 => 'WGS84 UTM zone 36N',
+            32637 => 'WGS84 UTM zone 37N',
+            32638 => 'WGS84 UTM zone 38N',
+            32639 => 'WGS84 UTM zone 39N',
+            32640 => 'WGS84 UTM zone 40N',
+            32641 => 'WGS84 UTM zone 41N',
+            32642 => 'WGS84 UTM zone 42N',
+            32643 => 'WGS84 UTM zone 43N',
+            32644 => 'WGS84 UTM zone 44N',
+            32645 => 'WGS84 UTM zone 45N',
+            32646 => 'WGS84 UTM zone 46N',
+            32647 => 'WGS84 UTM zone 47N',
+            32648 => 'WGS84 UTM zone 48N',
+            32649 => 'WGS84 UTM zone 49N',
+            32650 => 'WGS84 UTM zone 50N',
+            32651 => 'WGS84 UTM zone 51N',
+            32652 => 'WGS84 UTM zone 52N',
+            32653 => 'WGS84 UTM zone 53N',
+            32654 => 'WGS84 UTM zone 54N',
+            32655 => 'WGS84 UTM zone 55N',
+            32656 => 'WGS84 UTM zone 56N',
+            32657 => 'WGS84 UTM zone 57N',
+            32658 => 'WGS84 UTM zone 58N',
+            32659 => 'WGS84 UTM zone 59N',
+            32660 => 'WGS84 UTM zone 60N',
+            32701 => 'WGS84 UTM zone 1S',
+            32702 => 'WGS84 UTM zone 2S',
+            32703 => 'WGS84 UTM zone 3S',
+            32704 => 'WGS84 UTM zone 4S',
+            32705 => 'WGS84 UTM zone 5S',
+            32706 => 'WGS84 UTM zone 6S',
+            32707 => 'WGS84 UTM zone 7S',
+            32708 => 'WGS84 UTM zone 8S',
+            32709 => 'WGS84 UTM zone 9S',
+            32710 => 'WGS84 UTM zone 10S',
+            32711 => 'WGS84 UTM zone 11S',
+            32712 => 'WGS84 UTM zone 12S',
+            32713 => 'WGS84 UTM zone 13S',
+            32714 => 'WGS84 UTM zone 14S',
+            32715 => 'WGS84 UTM zone 15S',
+            32716 => 'WGS84 UTM zone 16S',
+            32717 => 'WGS84 UTM zone 17S',
+            32718 => 'WGS84 UTM zone 18S',
+            32719 => 'WGS84 UTM zone 19S',
+            32720 => 'WGS84 UTM zone 20S',
+            32721 => 'WGS84 UTM zone 21S',
+            32722 => 'WGS84 UTM zone 22S',
+            32723 => 'WGS84 UTM zone 23S',
+            32724 => 'WGS84 UTM zone 24S',
+            32725 => 'WGS84 UTM zone 25S',
+            32726 => 'WGS84 UTM zone 26S',
+            32727 => 'WGS84 UTM zone 27S',
+            32728 => 'WGS84 UTM zone 28S',
+            32729 => 'WGS84 UTM zone 29S',
+            32730 => 'WGS84 UTM zone 30S',
+            32731 => 'WGS84 UTM zone 31S',
+            32732 => 'WGS84 UTM zone 32S',
+            32733 => 'WGS84 UTM zone 33S',
+            32734 => 'WGS84 UTM zone 34S',
+            32735 => 'WGS84 UTM zone 35S',
+            32736 => 'WGS84 UTM zone 36S',
+            32737 => 'WGS84 UTM zone 37S',
+            32738 => 'WGS84 UTM zone 38S',
+            32739 => 'WGS84 UTM zone 39S',
+            32740 => 'WGS84 UTM zone 40S',
+            32741 => 'WGS84 UTM zone 41S',
+            32742 => 'WGS84 UTM zone 42S',
+            32743 => 'WGS84 UTM zone 43S',
+            32744 => 'WGS84 UTM zone 44S',
+            32745 => 'WGS84 UTM zone 45S',
+            32746 => 'WGS84 UTM zone 46S',
+            32747 => 'WGS84 UTM zone 47S',
+            32748 => 'WGS84 UTM zone 48S',
+            32749 => 'WGS84 UTM zone 49S',
+            32750 => 'WGS84 UTM zone 50S',
+            32751 => 'WGS84 UTM zone 51S',
+            32752 => 'WGS84 UTM zone 52S',
+            32753 => 'WGS84 UTM zone 53S',
+            32754 => 'WGS84 UTM zone 54S',
+            32755 => 'WGS84 UTM zone 55S',
+            32756 => 'WGS84 UTM zone 56S',
+            32757 => 'WGS84 UTM zone 57S',
+            32758 => 'WGS84 UTM zone 58S',
+            32759 => 'WGS84 UTM zone 59S',
+            32760 => 'WGS84 UTM zone 60S',
+            32767 => 'User Defined',
+        },
+    },
+    3073 => 'PCSCitation',
+    3074 => {
+        Name => 'Projection',
+        PrintConv => {
+            # epsg_proj
+            10101 => 'Alabama CS27 East',
+            10102 => 'Alabama CS27 West',
+            10131 => 'Alabama CS83 East',
+            10132 => 'Alabama CS83 West',
+            10201 => 'Arizona Coordinate System east',
+            10202 => 'Arizona Coordinate System Central',
+            10203 => 'Arizona Coordinate System west',
+            10231 => 'Arizona CS83 east',
+            10232 => 'Arizona CS83 Central',
+            10233 => 'Arizona CS83 west',
+            10301 => 'Arkansas CS27 North',
+            10302 => 'Arkansas CS27 South',
+            10331 => 'Arkansas CS83 North',
+            10332 => 'Arkansas CS83 South',
+            10401 => 'California CS27 I',
+            10402 => 'California CS27 II',
+            10403 => 'California CS27 III',
+            10404 => 'California CS27 IV',
+            10405 => 'California CS27 V',
+            10406 => 'California CS27 VI',
+            10407 => 'California CS27 VII',
+            10431 => 'California CS83 1',
+            10432 => 'California CS83 2',
+            10433 => 'California CS83 3',
+            10434 => 'California CS83 4',
+            10435 => 'California CS83 5',
+            10436 => 'California CS83 6',
+            10501 => 'Colorado CS27 North',
+            10502 => 'Colorado CS27 Central',
+            10503 => 'Colorado CS27 South',
+            10531 => 'Colorado CS83 North',
+            10532 => 'Colorado CS83 Central',
+            10533 => 'Colorado CS83 South',
+            10600 => 'Connecticut CS27',
+            10630 => 'Connecticut CS83',
+            10700 => 'Delaware CS27',
+            10730 => 'Delaware CS83',
+            10901 => 'Florida CS27 East',
+            10902 => 'Florida CS27 West',
+            10903 => 'Florida CS27 North',
+            10931 => 'Florida CS83 East',
+            10932 => 'Florida CS83 West',
+            10933 => 'Florida CS83 North',
+            11001 => 'Georgia CS27 East',
+            11002 => 'Georgia CS27 West',
+            11031 => 'Georgia CS83 East',
+            11032 => 'Georgia CS83 West',
+            11101 => 'Idaho CS27 East',
+            11102 => 'Idaho CS27 Central',
+            11103 => 'Idaho CS27 West',
+            11131 => 'Idaho CS83 East',
+            11132 => 'Idaho CS83 Central',
+            11133 => 'Idaho CS83 West',
+            11201 => 'Illinois CS27 East',
+            11202 => 'Illinois CS27 West',
+            11231 => 'Illinois CS83 East',
+            11232 => 'Illinois CS83 West',
+            11301 => 'Indiana CS27 East',
+            11302 => 'Indiana CS27 West',
+            11331 => 'Indiana CS83 East',
+            11332 => 'Indiana CS83 West',
+            11401 => 'Iowa CS27 North',
+            11402 => 'Iowa CS27 South',
+            11431 => 'Iowa CS83 North',
+            11432 => 'Iowa CS83 South',
+            11501 => 'Kansas CS27 North',
+            11502 => 'Kansas CS27 South',
+            11531 => 'Kansas CS83 North',
+            11532 => 'Kansas CS83 South',
+            11601 => 'Kentucky CS27 North',
+            11602 => 'Kentucky CS27 South',
+            11631 => 'Kentucky CS83 North',
+            11632 => 'Kentucky CS83 South',
+            11701 => 'Louisiana CS27 North',
+            11702 => 'Louisiana CS27 South',
+            11731 => 'Louisiana CS83 North',
+            11732 => 'Louisiana CS83 South',
+            11801 => 'Maine CS27 East',
+            11802 => 'Maine CS27 West',
+            11831 => 'Maine CS83 East',
+            11832 => 'Maine CS83 West',
+            11900 => 'Maryland CS27',
+            11930 => 'Maryland CS83',
+            12001 => 'Massachusetts CS27 Mainland',
+            12002 => 'Massachusetts CS27 Island',
+            12031 => 'Massachusetts CS83 Mainland',
+            12032 => 'Massachusetts CS83 Island',
+            12101 => 'Michigan State Plane East',
+            12102 => 'Michigan State Plane Old Central',
+            12103 => 'Michigan State Plane West',
+            12111 => 'Michigan CS27 North',
+            12112 => 'Michigan CS27 Central',
+            12113 => 'Michigan CS27 South',
+            12141 => 'Michigan CS83 North',
+            12142 => 'Michigan CS83 Central',
+            12143 => 'Michigan CS83 South',
+            12201 => 'Minnesota CS27 North',
+            12202 => 'Minnesota CS27 Central',
+            12203 => 'Minnesota CS27 South',
+            12231 => 'Minnesota CS83 North',
+            12232 => 'Minnesota CS83 Central',
+            12233 => 'Minnesota CS83 South',
+            12301 => 'Mississippi CS27 East',
+            12302 => 'Mississippi CS27 West',
+            12331 => 'Mississippi CS83 East',
+            12332 => 'Mississippi CS83 West',
+            12401 => 'Missouri CS27 East',
+            12402 => 'Missouri CS27 Central',
+            12403 => 'Missouri CS27 West',
+            12431 => 'Missouri CS83 East',
+            12432 => 'Missouri CS83 Central',
+            12433 => 'Missouri CS83 West',
+            12501 => 'Montana CS27 North',
+            12502 => 'Montana CS27 Central',
+            12503 => 'Montana CS27 South',
+            12530 => 'Montana CS83',
+            12601 => 'Nebraska CS27 North',
+            12602 => 'Nebraska CS27 South',
+            12630 => 'Nebraska CS83',
+            12701 => 'Nevada CS27 East',
+            12702 => 'Nevada CS27 Central',
+            12703 => 'Nevada CS27 West',
+            12731 => 'Nevada CS83 East',
+            12732 => 'Nevada CS83 Central',
+            12733 => 'Nevada CS83 West',
+            12800 => 'New Hampshire CS27',
+            12830 => 'New Hampshire CS83',
+            12900 => 'New Jersey CS27',
+            12930 => 'New Jersey CS83',
+            13001 => 'New Mexico CS27 East',
+            13002 => 'New Mexico CS27 Central',
+            13003 => 'New Mexico CS27 West',
+            13031 => 'New Mexico CS83 East',
+            13032 => 'New Mexico CS83 Central',
+            13033 => 'New Mexico CS83 West',
+            13101 => 'New York CS27 East',
+            13102 => 'New York CS27 Central',
+            13103 => 'New York CS27 West',
+            13104 => 'New York CS27 Long Island',
+            13131 => 'New York CS83 East',
+            13132 => 'New York CS83 Central',
+            13133 => 'New York CS83 West',
+            13134 => 'New York CS83 Long Island',
+            13200 => 'North Carolina CS27',
+            13230 => 'North Carolina CS83',
+            13301 => 'North Dakota CS27 North',
+            13302 => 'North Dakota CS27 South',
+            13331 => 'North Dakota CS83 North',
+            13332 => 'North Dakota CS83 South',
+            13401 => 'Ohio CS27 North',
+            13402 => 'Ohio CS27 South',
+            13431 => 'Ohio CS83 North',
+            13432 => 'Ohio CS83 South',
+            13501 => 'Oklahoma CS27 North',
+            13502 => 'Oklahoma CS27 South',
+            13531 => 'Oklahoma CS83 North',
+            13532 => 'Oklahoma CS83 South',
+            13601 => 'Oregon CS27 North',
+            13602 => 'Oregon CS27 South',
+            13631 => 'Oregon CS83 North',
+            13632 => 'Oregon CS83 South',
+            13701 => 'Pennsylvania CS27 North',
+            13702 => 'Pennsylvania CS27 South',
+            13731 => 'Pennsylvania CS83 North',
+            13732 => 'Pennsylvania CS83 South',
+            13800 => 'Rhode Island CS27',
+            13830 => 'Rhode Island CS83',
+            13901 => 'South Carolina CS27 North',
+            13902 => 'South Carolina CS27 South',
+            13930 => 'South Carolina CS83',
+            14001 => 'South Dakota CS27 North',
+            14002 => 'South Dakota CS27 South',
+            14031 => 'South Dakota CS83 North',
+            14032 => 'South Dakota CS83 South',
+            14100 => 'Tennessee CS27',
+            14130 => 'Tennessee CS83',
+            14201 => 'Texas CS27 North',
+            14202 => 'Texas CS27 North Central',
+            14203 => 'Texas CS27 Central',
+            14204 => 'Texas CS27 South Central',
+            14205 => 'Texas CS27 South',
+            14231 => 'Texas CS83 North',
+            14232 => 'Texas CS83 North Central',
+            14233 => 'Texas CS83 Central',
+            14234 => 'Texas CS83 South Central',
+            14235 => 'Texas CS83 South',
+            14301 => 'Utah CS27 North',
+            14302 => 'Utah CS27 Central',
+            14303 => 'Utah CS27 South',
+            14331 => 'Utah CS83 North',
+            14332 => 'Utah CS83 Central',
+            14333 => 'Utah CS83 South',
+            14400 => 'Vermont CS27',
+            14430 => 'Vermont CS83',
+            14501 => 'Virginia CS27 North',
+            14502 => 'Virginia CS27 South',
+            14531 => 'Virginia CS83 North',
+            14532 => 'Virginia CS83 South',
+            14601 => 'Washington CS27 North',
+            14602 => 'Washington CS27 South',
+            14631 => 'Washington CS83 North',
+            14632 => 'Washington CS83 South',
+            14701 => 'West Virginia CS27 North',
+            14702 => 'West Virginia CS27 South',
+            14731 => 'West Virginia CS83 North',
+            14732 => 'West Virginia CS83 South',
+            14801 => 'Wisconsin CS27 North',
+            14802 => 'Wisconsin CS27 Central',
+            14803 => 'Wisconsin CS27 South',
+            14831 => 'Wisconsin CS83 North',
+            14832 => 'Wisconsin CS83 Central',
+            14833 => 'Wisconsin CS83 South',
+            14901 => 'Wyoming CS27 East',
+            14902 => 'Wyoming CS27 East Central',
+            14903 => 'Wyoming CS27 West Central',
+            14904 => 'Wyoming CS27 West',
+            14931 => 'Wyoming CS83 East',
+            14932 => 'Wyoming CS83 East Central',
+            14933 => 'Wyoming CS83 West Central',
+            14934 => 'Wyoming CS83 West',
+            15001 => 'Alaska CS27 1',
+            15002 => 'Alaska CS27 2',
+            15003 => 'Alaska CS27 3',
+            15004 => 'Alaska CS27 4',
+            15005 => 'Alaska CS27 5',
+            15006 => 'Alaska CS27 6',
+            15007 => 'Alaska CS27 7',
+            15008 => 'Alaska CS27 8',
+            15009 => 'Alaska CS27 9',
+            15010 => 'Alaska CS27 10',
+            15031 => 'Alaska CS83 1',
+            15032 => 'Alaska CS83 2',
+            15033 => 'Alaska CS83 3',
+            15034 => 'Alaska CS83 4',
+            15035 => 'Alaska CS83 5',
+            15036 => 'Alaska CS83 6',
+            15037 => 'Alaska CS83 7',
+            15038 => 'Alaska CS83 8',
+            15039 => 'Alaska CS83 9',
+            15040 => 'Alaska CS83 10',
+            15101 => 'Hawaii CS27 1',
+            15102 => 'Hawaii CS27 2',
+            15103 => 'Hawaii CS27 3',
+            15104 => 'Hawaii CS27 4',
+            15105 => 'Hawaii CS27 5',
+            15131 => 'Hawaii CS83 1',
+            15132 => 'Hawaii CS83 2',
+            15133 => 'Hawaii CS83 3',
+            15134 => 'Hawaii CS83 4',
+            15135 => 'Hawaii CS83 5',
+            15201 => 'Puerto Rico CS27',
+            15202 => 'St Croix',
+            15230 => 'Puerto Rico Virgin Is',
+            15302 => 'Kentucky CS27',
+            15303 => 'Kentucky CS83 North',
+            15914 => 'BLM 14N feet',
+            15915 => 'BLM 15N feet',
+            15916 => 'BLM 16N feet',
+            15917 => 'BLM 17N feet',
+            16001 => 'UTM zone 1N',
+            16002 => 'UTM zone 2N',
+            16003 => 'UTM zone 3N',
+            16004 => 'UTM zone 4N',
+            16005 => 'UTM zone 5N',
+            16006 => 'UTM zone 6N',
+            16007 => 'UTM zone 7N',
+            16008 => 'UTM zone 8N',
+            16009 => 'UTM zone 9N',
+            16010 => 'UTM zone 10N',
+            16011 => 'UTM zone 11N',
+            16012 => 'UTM zone 12N',
+            16013 => 'UTM zone 13N',
+            16014 => 'UTM zone 14N',
+            16015 => 'UTM zone 15N',
+            16016 => 'UTM zone 16N',
+            16017 => 'UTM zone 17N',
+            16018 => 'UTM zone 18N',
+            16019 => 'UTM zone 19N',
+            16020 => 'UTM zone 20N',
+            16021 => 'UTM zone 21N',
+            16022 => 'UTM zone 22N',
+            16023 => 'UTM zone 23N',
+            16024 => 'UTM zone 24N',
+            16025 => 'UTM zone 25N',
+            16026 => 'UTM zone 26N',
+            16027 => 'UTM zone 27N',
+            16028 => 'UTM zone 28N',
+            16029 => 'UTM zone 29N',
+            16030 => 'UTM zone 30N',
+            16031 => 'UTM zone 31N',
+            16032 => 'UTM zone 32N',
+            16033 => 'UTM zone 33N',
+            16034 => 'UTM zone 34N',
+            16035 => 'UTM zone 35N',
+            16036 => 'UTM zone 36N',
+            16037 => 'UTM zone 37N',
+            16038 => 'UTM zone 38N',
+            16039 => 'UTM zone 39N',
+            16040 => 'UTM zone 40N',
+            16041 => 'UTM zone 41N',
+            16042 => 'UTM zone 42N',
+            16043 => 'UTM zone 43N',
+            16044 => 'UTM zone 44N',
+            16045 => 'UTM zone 45N',
+            16046 => 'UTM zone 46N',
+            16047 => 'UTM zone 47N',
+            16048 => 'UTM zone 48N',
+            16049 => 'UTM zone 49N',
+            16050 => 'UTM zone 50N',
+            16051 => 'UTM zone 51N',
+            16052 => 'UTM zone 52N',
+            16053 => 'UTM zone 53N',
+            16054 => 'UTM zone 54N',
+            16055 => 'UTM zone 55N',
+            16056 => 'UTM zone 56N',
+            16057 => 'UTM zone 57N',
+            16058 => 'UTM zone 58N',
+            16059 => 'UTM zone 59N',
+            16060 => 'UTM zone 60N',
+            16101 => 'UTM zone 1S',
+            16102 => 'UTM zone 2S',
+            16103 => 'UTM zone 3S',
+            16104 => 'UTM zone 4S',
+            16105 => 'UTM zone 5S',
+            16106 => 'UTM zone 6S',
+            16107 => 'UTM zone 7S',
+            16108 => 'UTM zone 8S',
+            16109 => 'UTM zone 9S',
+            16110 => 'UTM zone 10S',
+            16111 => 'UTM zone 11S',
+            16112 => 'UTM zone 12S',
+            16113 => 'UTM zone 13S',
+            16114 => 'UTM zone 14S',
+            16115 => 'UTM zone 15S',
+            16116 => 'UTM zone 16S',
+            16117 => 'UTM zone 17S',
+            16118 => 'UTM zone 18S',
+            16119 => 'UTM zone 19S',
+            16120 => 'UTM zone 20S',
+            16121 => 'UTM zone 21S',
+            16122 => 'UTM zone 22S',
+            16123 => 'UTM zone 23S',
+            16124 => 'UTM zone 24S',
+            16125 => 'UTM zone 25S',
+            16126 => 'UTM zone 26S',
+            16127 => 'UTM zone 27S',
+            16128 => 'UTM zone 28S',
+            16129 => 'UTM zone 29S',
+            16130 => 'UTM zone 30S',
+            16131 => 'UTM zone 31S',
+            16132 => 'UTM zone 32S',
+            16133 => 'UTM zone 33S',
+            16134 => 'UTM zone 34S',
+            16135 => 'UTM zone 35S',
+            16136 => 'UTM zone 36S',
+            16137 => 'UTM zone 37S',
+            16138 => 'UTM zone 38S',
+            16139 => 'UTM zone 39S',
+            16140 => 'UTM zone 40S',
+            16141 => 'UTM zone 41S',
+            16142 => 'UTM zone 42S',
+            16143 => 'UTM zone 43S',
+            16144 => 'UTM zone 44S',
+            16145 => 'UTM zone 45S',
+            16146 => 'UTM zone 46S',
+            16147 => 'UTM zone 47S',
+            16148 => 'UTM zone 48S',
+            16149 => 'UTM zone 49S',
+            16150 => 'UTM zone 50S',
+            16151 => 'UTM zone 51S',
+            16152 => 'UTM zone 52S',
+            16153 => 'UTM zone 53S',
+            16154 => 'UTM zone 54S',
+            16155 => 'UTM zone 55S',
+            16156 => 'UTM zone 56S',
+            16157 => 'UTM zone 57S',
+            16158 => 'UTM zone 58S',
+            16159 => 'UTM zone 59S',
+            16160 => 'UTM zone 60S',
+            16200 => 'Gauss Kruger zone 0',
+            16201 => 'Gauss Kruger zone 1',
+            16202 => 'Gauss Kruger zone 2',
+            16203 => 'Gauss Kruger zone 3',
+            16204 => 'Gauss Kruger zone 4',
+            16205 => 'Gauss Kruger zone 5',
+            17348 => 'Map Grid of Australia 48',
+            17349 => 'Map Grid of Australia 49',
+            17350 => 'Map Grid of Australia 50',
+            17351 => 'Map Grid of Australia 51',
+            17352 => 'Map Grid of Australia 52',
+            17353 => 'Map Grid of Australia 53',
+            17354 => 'Map Grid of Australia 54',
+            17355 => 'Map Grid of Australia 55',
+            17356 => 'Map Grid of Australia 56',
+            17357 => 'Map Grid of Australia 57',
+            17358 => 'Map Grid of Australia 58',
+            17448 => 'Australian Map Grid 48',
+            17449 => 'Australian Map Grid 49',
+            17450 => 'Australian Map Grid 50',
+            17451 => 'Australian Map Grid 51',
+            17452 => 'Australian Map Grid 52',
+            17453 => 'Australian Map Grid 53',
+            17454 => 'Australian Map Grid 54',
+            17455 => 'Australian Map Grid 55',
+            17456 => 'Australian Map Grid 56',
+            17457 => 'Australian Map Grid 57',
+            17458 => 'Australian Map Grid 58',
+            18031 => 'Argentina 1',
+            18032 => 'Argentina 2',
+            18033 => 'Argentina 3',
+            18034 => 'Argentina 4',
+            18035 => 'Argentina 5',
+            18036 => 'Argentina 6',
+            18037 => 'Argentina 7',
+            18051 => 'Colombia 3W',
+            18052 => 'Colombia Bogota',
+            18053 => 'Colombia 3E',
+            18054 => 'Colombia 6E',
+            18072 => 'Egypt Red Belt',
+            18073 => 'Egypt Purple Belt',
+            18074 => 'Extended Purple Belt',
+            18141 => 'New Zealand North Island Nat Grid',
+            18142 => 'New Zealand South Island Nat Grid',
+            19900 => 'Bahrain Grid',
+            19905 => 'Netherlands E Indies Equatorial',
+            19912 => 'RSO Borneo',
+            19926 => 'Stereo 70',
+            32767 => 'User Defined',
+        },
+    },
+    3075 => {
+        Name => 'ProjCoordTrans',
+        PrintConv => {
+            # geo_ctrans
+            1 => 'Transverse Mercator',
+            2 => 'Transverse Mercator Modified Alaska',
+            3 => 'Oblique Mercator',
+            4 => 'Oblique Mercator Laborde',
+            5 => 'Oblique Mercator Rosenmund',
+            6 => 'Oblique Mercator Spherical',   # not advisable
+            7 => 'Mercator',
+            8 => 'Lambert Conf Conic 2SP',
+            9 => 'Lambert Conf Conic 1SP',
+            10 => 'Lambert Azim Equal Area',
+            11 => 'Albers Equal Area',
+            12 => 'Azimuthal Equidistant',
+            13 => 'Equidistant Conic',
+            14 => 'Stereographic',
+            15 => 'Polar Stereographic',
+            16 => 'Oblique Stereographic',   # not advisable
+            17 => 'Equirectangular',
+            18 => 'Cassini Soldner',
+            19 => 'Gnomonic',
+            20 => 'Miller Cylindrical',
+            21 => 'Orthographic',
+            22 => 'Polyconic',
+            23 => 'Robinson',
+            24 => 'Sinusoidal',
+            25 => 'VanDerGrinten',
+            26 => 'New Zealand Map Grid',
+            27 => 'Transverse Mercator South Orientated',
+            28 => 'Cylindrical Equal Area',
+            32767 => 'User Defined',
+        },
+    },
+    3076 => {
+        Name => 'ProjLinearUnits',
+        SeparateTable => 'Units',
+        PrintConv => \%epsg_units,
+    },
+    3077 => 'ProjLinearUnitSize',
+    3078 => 'ProjStdParallel1',
+    3079 => 'ProjStdParallel2',
+    3080 => 'ProjNatOriginLong',
+    3081 => 'ProjNatOriginLat',
+    3082 => 'ProjFalseEasting',
+    3083 => 'ProjFalseNorthing',
+    3084 => 'ProjFalseOriginLong',
+    3085 => 'ProjFalseOriginLat',
+    3086 => 'ProjFalseOriginEasting',
+    3087 => 'ProjFalseOriginNorthing',
+    3088 => 'ProjCenterLong',
+    3089 => 'ProjCenterLat',
+    3090 => 'ProjCenterEasting',
+    3091 => 'ProjCenterNorthing',
+    3092 => 'ProjScaleAtNatOrigin',
+    3093 => 'ProjScaleAtCenter',
+    3094 => 'ProjAzimuthAngle',
+    3095 => 'ProjStraightVertPoleLong',
+    3096 => 'ProjRectifiedGridAngle',
+    4096 => {
+        Name => 'VerticalCSType',
+        PrintConv => {
+            # epsg_vertcs
+            5001 => 'Airy 1830 ellipsoid',
+            5002 => 'Airy Modified 1849 ellipsoid',
+            5003 => 'ANS ellipsoid',
+            5004 => 'Bessel 1841 ellipsoid',
+            5005 => 'Bessel Modified ellipsoid',
+            5006 => 'Bessel Namibia ellipsoid',
+            5007 => 'Clarke 1858 ellipsoid',
+            5008 => 'Clarke 1866 ellipsoid',
+            5010 => 'Clarke 1880 Benoit ellipsoid',
+            5011 => 'Clarke 1880 IGN ellipsoid',
+            5012 => 'Clarke 1880 RGS ellipsoid',
+            5013 => 'Clarke 1880 Arc ellipsoid',
+            5014 => 'Clarke 1880 SGA 1922 ellipsoid',
+            5015 => 'Everest 1830 1937 Adjustment ellipsoid',
+            5016 => 'Everest 1830 1967 Definition ellipsoid',
+            5017 => 'Everest 1830 1975 Definition ellipsoid',
+            5018 => 'Everest 1830 Modified ellipsoid',
+            5019 => 'GRS 1980 ellipsoid',
+            5020 => 'Helmert 1906 ellipsoid',
+            5021 => 'INS ellipsoid',
+            5022 => 'International 1924 ellipsoid',
+            5023 => 'International 1967 ellipsoid',
+            5024 => 'Krassowsky 1940 ellipsoid',
+            5025 => 'NWL 9D ellipsoid',
+            5026 => 'NWL 10D ellipsoid',
+            5027 => 'Plessis 1817 ellipsoid',
+            5028 => 'Struve 1860 ellipsoid',
+            5029 => 'War Office ellipsoid',
+            5030 => 'WGS 84 ellipsoid',
+            5031 => 'GEM 10C ellipsoid',
+            5032 => 'OSU86F ellipsoid',
+            5033 => 'OSU91A ellipsoid',
+            5101 => 'Newlyn',
+            5102 => 'North American Vertical Datum 1929',
+            5103 => 'North American Vertical Datum 1988',
+            5104 => 'Yellow Sea 1956',
+            5105 => 'Baltic Sea',
+            5106 => 'Caspian Sea',
+            32767 => 'User Defined',
+        },
+    },
+    4097 => 'VerticalCitation',
+    4098 => {
+        Name => 'VerticalDatum',
+        PrintConv => {
+            1 => 'Vertical Datum Base',
+            32767 => 'User Defined',
+        },
+    },
+    4099 => {
+        Name => 'VerticalUnits',
+        SeparateTable => 'Units',
+        PrintConv => \%epsg_units,
+    },
+);
+
+#------------------------------------------------------------------------------
+# Inputs: 0) ExifTool object reference
+#         1) tag table reference
+sub ProcessGeoTiff($)
+{
+    my ($exifTool) = @_;
+    my $dirData = $exifTool->GetValue('GeoTiffDirectory', 'ValueConv') or return;
+    my $doubleData = $exifTool->GetValue('GeoTiffDoubleParams', 'ValueConv');
+    my $asciiData = $exifTool->GetValue('GeoTiffAsciiParams', 'ValueConv');
+    my $verbose = $exifTool->Options('Verbose');
+    my @double;
+
+    # restore or original EXIF byte order setting
+    my $byteOrder = $exifTool->{EXIF_BYTE_ORDER};
+    $byteOrder and SetByteOrder($byteOrder);
+    if (length($$dirData) >= 8 and
+        length($$dirData) >= 8 * (Get16u($dirData,6) + 1))
+    {
+        my $version    = Get16u($dirData,0);
+        my $revision   = Get16u($dirData,2);
+        my $minorRev   = Get16u($dirData,4);
+        my $numEntries = Get16u($dirData,6);
+    
+        if ($verbose) {
+            $exifTool->{INDENT} .= '| ';
+            $exifTool->VerboseDir('GeoTiff',$numEntries);
+        }
+        # generate version number tag (not a real GeoTiff tag)
+        my $tagTable = Image::ExifTool::GetTagTable("Image::ExifTool::GeoTiff::Main");
+        my $tagInfo = $exifTool->GetTagInfo($tagTable, 1);
+        $tagInfo and $exifTool->FoundTag($tagInfo,"$version.$revision.$minorRev");
+    
+        my $i;
+        for ($i=0; $i<$numEntries; ++$i) {
+            my $pt = 8 * ($i + 1);
+            my $tag    = Get16u($dirData, $pt);
+            $tagInfo   = $exifTool->GetTagInfo($tagTable, $tag) or next;
+            my $loc    = Get16u($dirData, $pt+2);
+            my $count  = Get16u($dirData, $pt+4);
+            my $offset = Get16u($dirData, $pt+6);
+            my $format = $geoTiffFormat{$loc};
+            my ($val, $dataPt);
+            if ($format eq 'double') {          # in the double parms
+                $dataPt = $doubleData;
+                $offset *= 8;
+                $val = Image::ExifTool::ReadValue($dataPt, $offset, $format,
+                                                  $count, length($$doubleData)-$offset);
+            } elsif ($format eq 'string') {     # in the ASCII parms
+                $dataPt = $asciiData;
+                $val = substr($$dataPt, $offset, $count);
+                $val =~ s/(\0|\|)$//;   # remove trailing terminator (NULL or '|')
+            } elsif ($format eq 'int16u') {     # use the offset as the value
+                $dataPt = $dirData;
+                $val = $offset;
+                $offset = $pt+6;
+            } else {
+                $exifTool->Warn("Unknown GeoTiff location: $loc");
+                next;
+            }
+            $verbose and $exifTool->VerboseInfo($tag, $tagInfo,
+                'Table'  => $tagTable,
+                'Index'  => $i,
+                'Value'  => $val,
+                'DataPt' => $dataPt,
+                'Start'  => $offset,
+                'Format' => $format,
+                'Count'  => $count,
+                'Size'   => $count * Image::ExifTool::FormatSize($format),
+            );
+            $exifTool->FoundTag($tagInfo, $val);
+        }
+        if ($verbose) {
+            $exifTool->{INDENT} =~ s/..$//;
+        }
+    } else {
+        $exifTool->Warn('Bad GeoTIFF directory');
+    }
+    # must delete these tags once we've processed this information
+    # (to avoid re-processing if another EXIF directory is found)
+    $exifTool->DeleteTag('GeoTiffDirectory');
+    $exifTool->DeleteTag('GeoTiffDoubleParams');
+    $exifTool->DeleteTag('GeoTiffAsciiParams');
+}
+
+
+1;  #end
+
+__END__
+
+=head1 NAME
+
+Image::ExifTool::GeoTiff - Read GeoTiff meta information
+
+=head1 SYNOPSIS
+
+This module is loaded automatically by Image::ExifTool when required.
+
+=head1 DESCRIPTION
+
+This module contains definitions required by Image::ExifTool to interpret
+GeoTiff meta information.  GeoTiff information is used in images to specify
+exact geometric mappings used to transform the image to real world
+coordinates.
+
+=head1 AUTHOR
+
+Copyright 2003-2007, Phil Harvey (phil at owl.phy.queensu.ca)
+
+This library is free software; you can redistribute it and/or modify it
+under the same terms as Perl itself.
+
+=head1 REFERENCES
+
+=over 4
+
+=item ftp://ftp.remotesensing.org/geotiff/libgeotiff/libgeotiff-1.1.4.tar.gz
+
+=back
+
+=head1 SEE ALSO
+
+L<Image::ExifTool::TagNames/GeoTiff Tags>,
+L<Image::ExifTool(3pm)|Image::ExifTool>
+
+=cut
Index: /gsdl/trunk/perllib/cpan/Image/ExifTool/HP.pm
===================================================================
--- /gsdl/trunk/perllib/cpan/Image/ExifTool/HP.pm	(revision 16842)
+++ /gsdl/trunk/perllib/cpan/Image/ExifTool/HP.pm	(revision 16842)
@@ -0,0 +1,176 @@
+#------------------------------------------------------------------------------
+# File:         HP.pm
+#
+# Description:  Hewlett-Packard maker notes tags
+#
+# Revisions:    2007-05-03 - P. Harvey Created
+#------------------------------------------------------------------------------
+
+package Image::ExifTool::HP;
+
+use strict;
+use vars qw($VERSION);
+use Image::ExifTool qw(:DataAccess :Utils);
+
+$VERSION = '1.01';
+
+sub ProcessHP($$$);
+
+# HP EXIF-format maker notes (or is it Vivitar?)
+%Image::ExifTool::HP::Main = (
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
+    NOTES => q{
+        These tables list tags found in the maker notes of some Hewlett-Packard
+        camera models.
+        
+        The first table lists tags found in the EXIF-format maker notes of the
+        PhotoSmart 720 (also used by the Vivitar ViviCam 3705, 3705B and 3715).
+    },
+    0x0e00 => {
+        Name => 'PrintIM',
+        Description => 'Print Image Matching',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::PrintIM::Main',
+        },
+    },
+);
+
+# other types of HP maker notes
+%Image::ExifTool::HP::Type2 = (
+    PROCESS_PROC => \&ProcessHP,
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
+    NOTES => 'These tags are used by the PhotoSmart E427.',
+   'PreviewImage' => {
+        Name => 'PreviewImage',
+        ValueConv => '$self->ValidateImage(\$val,$tag)',
+    },
+   'Serial Number' => 'SerialNumber',
+   'Lens Shading'  => 'LensShading',
+);
+
+%Image::ExifTool::HP::Type4 = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
+    NOTES => 'These tags are used by the PhotoSmart M627.',
+    0x0c => {
+        Name => 'MaxAperture',
+        Format => 'int16u',
+        ValueConv => '$val / 10',
+    },
+    0x10 => {
+        Name => 'ExposureTime',
+        Format => 'int32u',
+        ValueConv => '$val / 1e6',
+        PrintConv => 'Image::ExifTool::Exif::PrintExposureTime($val)',
+    },
+    0x14 => {
+        Name => 'CameraDateTime',
+        Groups => { 2 => 'Time' },
+        Format => 'string[20]',
+    },
+    0x34 => {
+        Name => 'ISO',
+        Format => 'int16u',
+    },
+    0x5c => {
+        Name => 'SerialNumber',
+        Format => 'string[26]',
+        RawConv => '$val =~ s/^SERIAL NUMBER:// ? $val : undef',
+    },
+);
+
+%Image::ExifTool::HP::Type6 = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
+    NOTES => 'These tags are used by the PhotoSmart M425, M525 and M527.',
+    0x0c => {
+        Name => 'FNumber',
+        Format => 'int16u',
+        ValueConv => '$val / 10',
+    },
+    0x10 => {
+        Name => 'ExposureTime',
+        Format => 'int32u',
+        ValueConv => '$val / 1e6',
+        PrintConv => 'Image::ExifTool::Exif::PrintExposureTime($val)',
+    },
+    0x14 => {
+        Name => 'CameraDateTime',
+        Groups => { 2 => 'Time' },
+        Format => 'string[20]',
+    },
+    0x34 => {
+        Name => 'ISO',
+        Format => 'int16u',
+    },
+    0x58 => {
+        Name => 'SerialNumber',
+        Format => 'string[26]',
+        RawConv => '$val =~ s/^SERIAL NUMBER:// ? $val : undef',
+    },
+);
+
+#------------------------------------------------------------------------------
+# Process HP maker notes
+# Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
+# Returns: 1 on success, otherwise returns 0 and sets a Warning
+sub ProcessHP($$$)
+{
+    my ($exifTool, $dirInfo, $tagTablePtr) = @_;
+    my $dataPt = $$dirInfo{DataPt};
+    my $dataLen = $$dirInfo{DataLen};
+    my $dirStart = $$dirInfo{DirStart} || 0;
+    my $dirLen = $$dirInfo{DirLen} || $dataLen - $dirStart;
+
+    # look for known text-type tags
+    if ($dirStart or $dirLen != length($$dataPt)) {
+        my $buff = substr($$dataPt, $dirStart, $dirLen);
+        $dataPt = \$buff;
+    }
+    my $tagID;
+    # brute-force scan for PreviewImage
+    if ($$tagTablePtr{PreviewImage} and $$dataPt =~ /(\xff\xd8\xff\xdb.*\xff\xd9)/gs) {
+        $exifTool->HandleTag($tagTablePtr, 'PreviewImage', $1);
+        # truncate preview to speed subsequent tag scans
+        my $buff = substr($$dataPt, 0, pos($$dataPt)-length($1));
+        $dataPt = \$buff;
+    }
+    # scan for other tag ID's
+    foreach $tagID (sort(TagTableKeys($tagTablePtr))) {
+        next if $tagID eq 'PreviewImage';
+        next unless $$dataPt =~ /$tagID:\s*([\x20-\x7f]+)/i;
+        $exifTool->HandleTag($tagTablePtr, $tagID, $1);
+    }
+    return 1;
+}
+
+1;  # end
+
+__END__
+
+=head1 NAME
+
+Image::ExifTool::HP - Hewlett-Packard maker notes tags
+
+=head1 SYNOPSIS
+
+This module is loaded automatically by Image::ExifTool when required.
+
+=head1 DESCRIPTION
+
+This module contains definitions required by Image::ExifTool to interpret
+Hewlett-Packard maker notes.
+
+=head1 AUTHOR
+
+Copyright 2003-2007, Phil Harvey (phil at owl.phy.queensu.ca)
+
+This library is free software; you can redistribute it and/or modify it
+under the same terms as Perl itself.
+
+=head1 SEE ALSO
+
+L<Image::ExifTool::TagNames/HP Tags>,
+L<Image::ExifTool(3pm)|Image::ExifTool>
+
+=cut
Index: /gsdl/trunk/perllib/cpan/Image/ExifTool/HTML.pm
===================================================================
--- /gsdl/trunk/perllib/cpan/Image/ExifTool/HTML.pm	(revision 16842)
+++ /gsdl/trunk/perllib/cpan/Image/ExifTool/HTML.pm	(revision 16842)
@@ -0,0 +1,462 @@
+#------------------------------------------------------------------------------
+# File:         HTML.pm
+#
+# Description:  Read HTML meta information
+#
+# Revisions:    01/30/2007 - P. Harvey Created
+#
+# References:   1) http://www.w3.org/TR/html4/
+#               2) http://www.daisy.org/publications/specifications/daisy_202.html
+#               3) http://vancouver-webpages.com/META/metatags.detail.html
+#               4) http://www.html-reference.com/META.htm
+#------------------------------------------------------------------------------
+
+package Image::ExifTool::HTML;
+
+use strict;
+use vars qw($VERSION @ISA @EXPORT_OK);
+use Image::ExifTool qw(:DataAccess :Utils);
+use Image::ExifTool::PostScript;
+use Image::ExifTool::XMP qw(EscapeXML UnescapeXML);
+require Exporter;
+
+$VERSION = '1.03';
+@ISA = qw(Exporter);
+@EXPORT_OK = qw(EscapeHTML UnescapeHTML);
+
+# HTML 4 character entity references
+my %entityNum = (
+    'quot'   => 34,   'eth'    => 240,  'lsquo'  => 8216,
+    'amp'    => 38,   'ntilde' => 241,  'rsquo'  => 8217,
+    'apos'   => 39,   'ograve' => 242,  'sbquo'  => 8218,
+    'lt'     => 60,   'oacute' => 243,  'ldquo'  => 8220,
+    'gt'     => 62,   'ocirc'  => 244,  'rdquo'  => 8221,
+    'nbsp'   => 160,  'otilde' => 245,  'bdquo'  => 8222,
+    'iexcl'  => 161,  'ouml'   => 246,  'dagger' => 8224,
+    'cent'   => 162,  'divide' => 247,  'Dagger' => 8225,
+    'pound'  => 163,  'oslash' => 248,  'bull'   => 8226,
+    'curren' => 164,  'ugrave' => 249,  'hellip' => 8230,
+    'yen'    => 165,  'uacute' => 250,  'permil' => 8240,
+    'brvbar' => 166,  'ucirc'  => 251,  'prime'  => 8242,
+    'sect'   => 167,  'uuml'   => 252,  'Prime'  => 8243,
+    'uml'    => 168,  'yacute' => 253,  'lsaquo' => 8249,
+    'copy'   => 169,  'thorn'  => 254,  'rsaquo' => 8250,
+    'ordf'   => 170,  'yuml'   => 255,  'oline'  => 8254,
+    'laquo'  => 171,  'OElig'  => 338,  'frasl'  => 8260,
+    'not'    => 172,  'oelig'  => 339,  'euro'   => 8364,
+    'shy'    => 173,  'Scaron' => 352,  'image'  => 8465,
+    'reg'    => 174,  'scaron' => 353,  'weierp' => 8472,
+    'macr'   => 175,  'Yuml'   => 376,  'real'   => 8476,
+    'deg'    => 176,  'fnof'   => 402,  'trade'  => 8482,
+    'plusmn' => 177,  'circ'   => 710,  'alefsym'=> 8501,
+    'sup2'   => 178,  'tilde'  => 732,  'larr'   => 8592,
+    'sup3'   => 179,  'Alpha'  => 913,  'uarr'   => 8593,
+    'acute'  => 180,  'Beta'   => 914,  'rarr'   => 8594,
+    'micro'  => 181,  'Gamma'  => 915,  'darr'   => 8595,
+    'para'   => 182,  'Delta'  => 916,  'harr'   => 8596,
+    'middot' => 183,  'Epsilon'=> 917,  'crarr'  => 8629,
+    'cedil'  => 184,  'Zeta'   => 918,  'lArr'   => 8656,
+    'sup1'   => 185,  'Eta'    => 919,  'uArr'   => 8657,
+    'ordm'   => 186,  'Theta'  => 920,  'rArr'   => 8658,
+    'raquo'  => 187,  'Iota'   => 921,  'dArr'   => 8659,
+    'frac14' => 188,  'Kappa'  => 922,  'hArr'   => 8660,
+    'frac12' => 189,  'Lambda' => 923,  'forall' => 8704,
+    'frac34' => 190,  'Mu'     => 924,  'part'   => 8706,
+    'iquest' => 191,  'Nu'     => 925,  'exist'  => 8707,
+    'Agrave' => 192,  'Xi'     => 926,  'empty'  => 8709,
+    'Aacute' => 193,  'Omicron'=> 927,  'nabla'  => 8711,
+    'Acirc'  => 194,  'Pi'     => 928,  'isin'   => 8712,
+    'Atilde' => 195,  'Rho'    => 929,  'notin'  => 8713,
+    'Auml'   => 196,  'Sigma'  => 931,  'ni'     => 8715,
+    'Aring'  => 197,  'Tau'    => 932,  'prod'   => 8719,
+    'AElig'  => 198,  'Upsilon'=> 933,  'sum'    => 8721,
+    'Ccedil' => 199,  'Phi'    => 934,  'minus'  => 8722,
+    'Egrave' => 200,  'Chi'    => 935,  'lowast' => 8727,
+    'Eacute' => 201,  'Psi'    => 936,  'radic'  => 8730,
+    'Ecirc'  => 202,  'Omega'  => 937,  'prop'   => 8733,
+    'Euml'   => 203,  'alpha'  => 945,  'infin'  => 8734,
+    'Igrave' => 204,  'beta'   => 946,  'ang'    => 8736,
+    'Iacute' => 205,  'gamma'  => 947,  'and'    => 8743,
+    'Icirc'  => 206,  'delta'  => 948,  'or'     => 8744,
+    'Iuml'   => 207,  'epsilon'=> 949,  'cap'    => 8745,
+    'ETH'    => 208,  'zeta'   => 950,  'cup'    => 8746,
+    'Ntilde' => 209,  'eta'    => 951,  'int'    => 8747,
+    'Ograve' => 210,  'theta'  => 952,  'there4' => 8756,
+    'Oacute' => 211,  'iota'   => 953,  'sim'    => 8764,
+    'Ocirc'  => 212,  'kappa'  => 954,  'cong'   => 8773,
+    'Otilde' => 213,  'lambda' => 955,  'asymp'  => 8776,
+    'Ouml'   => 214,  'mu'     => 956,  'ne'     => 8800,
+    'times'  => 215,  'nu'     => 957,  'equiv'  => 8801,
+    'Oslash' => 216,  'xi'     => 958,  'le'     => 8804,
+    'Ugrave' => 217,  'omicron'=> 959,  'ge'     => 8805,
+    'Uacute' => 218,  'pi'     => 960,  'sub'    => 8834,
+    'Ucirc'  => 219,  'rho'    => 961,  'sup'    => 8835,
+    'Uuml'   => 220,  'sigmaf' => 962,  'nsub'   => 8836,
+    'Yacute' => 221,  'sigma'  => 963,  'sube'   => 8838,
+    'THORN'  => 222,  'tau'    => 964,  'supe'   => 8839,
+    'szlig'  => 223,  'upsilon'=> 965,  'oplus'  => 8853,
+    'agrave' => 224,  'phi'    => 966,  'otimes' => 8855,
+    'aacute' => 225,  'chi'    => 967,  'perp'   => 8869,
+    'acirc'  => 226,  'psi'    => 968,  'sdot'   => 8901,
+    'atilde' => 227,  'omega'  => 969,  'lceil'  => 8968,
+    'auml'   => 228,  'thetasym'=>977,  'rceil'  => 8969,
+    'aring'  => 229,  'upsih'  => 978,  'lfloor' => 8970,
+    'aelig'  => 230,  'piv'    => 982,  'rfloor' => 8971,
+    'ccedil' => 231,  'ensp'   => 8194, 'lang'   => 9001,
+    'egrave' => 232,  'emsp'   => 8195, 'rang'   => 9002,
+    'eacute' => 233,  'thinsp' => 8201, 'loz'    => 9674,
+    'ecirc'  => 234,  'zwnj'   => 8204, 'spades' => 9824,
+    'euml'   => 235,  'zwj'    => 8205, 'clubs'  => 9827,
+    'igrave' => 236,  'lrm'    => 8206, 'hearts' => 9829,
+    'iacute' => 237,  'rlm'    => 8207, 'diams'  => 9830,
+    'icirc'  => 238,  'ndash'  => 8211,
+    'iuml'   => 239,  'mdash'  => 8212,
+);
+my %entityName; # look up entity names by number (built as necessary)
+
+# HTML info
+# (tag ID's are case insensitive and must be all lower case in tables)
+%Image::ExifTool::HTML::Main = (
+    GROUPS => { 2 => 'Document' },
+    NOTES => q{
+        Meta information extracted from the header of HTML and XHTML files.  This is
+        a mix of information found in the C<META> elements and the C<TITLE> element.
+    },
+    dc => {
+        Name => 'DC',
+        SubDirectory => { TagTable => 'Image::ExifTool::HTML::dc' },
+    },
+    ncc => {
+        Name => 'NCC',
+        SubDirectory => { TagTable => 'Image::ExifTool::HTML::ncc' },
+    },
+    prod => {
+        Name => 'Prod',
+        SubDirectory => { TagTable => 'Image::ExifTool::HTML::prod' },
+    },
+    vw96 => {
+        Name => 'VW96',
+        SubDirectory => { TagTable => 'Image::ExifTool::HTML::vw96' },
+    },
+   'http-equiv' => {
+        Name => 'HTTP-equiv',
+        SubDirectory => { TagTable => 'Image::ExifTool::HTML::equiv' },
+    },
+    abstract        => { },
+    author          => { },
+    classification  => { },
+    copyright       => { },
+    description     => { },
+    distribution    => { },
+   'doc-class'      => { Name => 'DocClass' },
+   'doc-rights'     => { Name => 'DocRights' },
+   'doc-type'       => { Name => 'DocType' },
+    formatter       => { },
+    generator       => { },
+    googlebot       => { Name => 'GoogleBot' },
+    keywords        => { List => 1 },
+    mssmarttagspreventparsing => { Name => 'NoMSSmartTags' },
+    owner           => { },
+    progid          => { Name => 'ProgID' },
+    rating          => { },
+    refresh         => { },
+   'resource-type'  => { Name => 'ResourceType' },
+   'revisit-after'  => { Name => 'RevisitAfter' },
+    robots          => { List => 1 },
+    title           => { Notes => "the only extracted tag which isn't from an HTML META element" },
+);
+
+# ref 2
+%Image::ExifTool::HTML::dc = (
+    GROUPS => { 1 => 'HTML-dc', 2 => 'Document' },
+    NOTES => 'Dublin Core schema tags (also used in XMP).',
+    contributor => { Groups => { 2 => 'Author' }, List => 'Bag' },
+    coverage    => { },
+    creator     => { Groups => { 2 => 'Author' }, List => 'Seq' },
+    date        => {
+        Groups => { 2 => 'Time'   },
+        List => 'Seq',
+        PrintConv => '$self->ConvertDateTime($val)',
+    },
+    description => { },
+   'format'     => { },
+    identifier  => { },
+    language    => { List => 'Bag' },
+    publisher   => { Groups => { 2 => 'Author' }, List => 'Bag' },
+    relation    => { List => 'Bag' },
+    rights      => { Groups => { 2 => 'Author' } },
+    source      => { Groups => { 2 => 'Author' } },
+    subject     => { List => 'Bag' },
+    title       => { },
+    type        => { List => 'Bag' },
+);
+
+# ref 2
+%Image::ExifTool::HTML::ncc = (
+    GROUPS => { 1 => 'HTML-ncc', 2 => 'Document' },
+    charset         => { },
+    depth           => { },
+    files           => { },
+    footnotes       => { },
+    generator       => { },
+    kbytesize       => { Name => 'KByteSize' },
+    maxpagenormal   => { Name => 'MaxPageNormal' },
+    multimediatype  => { Name => 'MultimediaType' },
+    narrator        => { },
+    pagefront       => { Name => 'PageFront' },
+    pagenormal      => { Name => 'PageNormal' },
+    pagespecial     => { Name => 'PageSpecial' },
+    prodnotes       => { Name => 'ProdNotes' },
+    producer        => { },
+    produceddate    => { Name => 'ProducedDate', Groups => { 2 => 'Time' } }, # yyyy-mm-dd
+    revision        => { },
+    revisiondate    => { Name => 'RevisionDate', Groups => { 2 => 'Time' } },
+    setinfo         => { Name => 'SetInfo' },
+    sidebars        => { },
+    sourcedate      => { Name => 'SourceDate', Groups => { 2 => 'Time' } },
+    sourceedition   => { Name => 'SourceEdition' },
+    sourcepublisher => { Name => 'SourcePublisher' },
+    sourcerights    => { Name => 'SourceRights' },
+    sourcetitle     => { Name => 'SourceTitle' },
+    tocitems        => { Name => 'TOCItems' },
+    totaltime       => { Name => 'Duration' }, # hh:mm:ss
+);
+
+# ref 3
+%Image::ExifTool::HTML::vw96 = (
+    GROUPS => { 1 => 'HTML-vw96', 2 => 'Document' },
+    objecttype      => { Name => 'ObjectType' },
+);
+
+# ref 2
+%Image::ExifTool::HTML::prod = (
+    GROUPS => { 1 => 'HTML-prod', 2 => 'Document' },
+    reclocation     => { Name => 'RecLocation' },
+    recengineer     => { Name => 'RecEngineer' },
+);
+
+# ref 3/4
+%Image::ExifTool::HTML::equiv = (
+    GROUPS => { 1 => 'HTTP-equiv', 2 => 'Document' },
+    NOTES => 'These tags have a family 1 group name of "HTTP-equiv".',
+   'cache-control'       => { Name => 'CacheControl' },
+   'content-disposition' => { Name => 'ContentDisposition' },
+   'content-language'    => { Name => 'ContentLanguage' },
+   'content-script-type' => { Name => 'ContentScriptType' },
+   'content-style-type'  => { Name => 'ContentStyleType' },
+   'content-type'        => { Name => 'ContentType' },
+   'default-style'       => { Name => 'DefaultStyle' },
+    expires              => { },
+   'ext-cache'           => { Name => 'ExtCache' },
+    imagetoolbar         => { Name => 'ImageToolbar' },
+    lotus                => { },
+   'page-enter'          => { Name => 'PageEnter' },
+   'page-exit'           => { Name => 'PageExit' },
+   'pics-label'          => { Name => 'PicsLabel' },
+    pragma               => { },
+    refresh              => { },
+   'reply-to'            => { Name => 'ReplyTo' },
+   'set-cookie'          => { Name => 'SetCookie' },
+   'site-enter'          => { Name => 'SiteEnter' },
+   'site-exit'           => { Name => 'SiteExit' },
+    vary                 => { },
+   'window-target'       => { Name => 'WindowTarget' },
+);
+
+#------------------------------------------------------------------------------
+# Convert single UTF-8 character to HTML character reference
+# Inputs: 0) UTF-8 character sequence
+# Returns: HML character reference (ie. "&quot;");
+# Note: Must be called via EscapeHTML to load name lookup
+sub EscapeChar($)
+{
+    my $ch = shift;
+    my ($val) = ($] >= 5.006001) ? unpack('U0U',$ch) : UnpackUTF8($ch);
+    return '?' unless defined $val;
+    return "&$entityName{$val};" if $entityName{$val};
+    return sprintf('&#x%x;',$val);
+}
+
+#------------------------------------------------------------------------------
+# Escape any special characters for HTML
+# Inputs: 0) UTF-8 string to be escaped
+# Returns: escaped string
+sub EscapeHTML($)
+{
+    my $str = shift;
+    # escape XML characters
+    $str = EscapeXML($str);
+    # escape other special characters if they exist
+    if ($str =~ /[\x80-\xff]/) {
+        # generate entity name lookup if necessary
+        unless (%entityName) {
+            local $_;
+            foreach (keys %entityNum) {
+                $entityName{$entityNum{$_}} = $_;
+            }
+            delete $entityName{39};  # 'apos' is not valid HTML
+        }
+        # supress warnings
+        local $SIG{'__WARN__'} = sub { 1 };
+        # escape any non-ascii characters for HTML
+        $str =~ s/([\xc2-\xf7][\x80-\xbf]+)/EscapeChar($1)/sge;
+    }
+    return $str;
+}
+
+#------------------------------------------------------------------------------
+# Unescape all HTML character references
+# Inputs: 0) string to be unescaped
+# Returns: unescaped string
+sub UnescapeHTML($)
+{
+    return UnescapeXML(shift, \%entityNum);
+}
+
+#------------------------------------------------------------------------------
+# Extract information from a HTML file
+# Inputs: 0) ExifTool object reference, 1) DirInfo reference
+# Returns: 1 on success, 0 if this wasn't a valid HTML file
+sub ProcessHTML($$)
+{
+    my ($exifTool, $dirInfo) = @_;
+    my $raf = $$dirInfo{RAF};
+    my $verbose = $exifTool->Options('Verbose');
+    my ($buff, $err);
+
+    # validate HTML or XHTML file
+    $raf->Read($buff, 256) or return 0;
+    $buff =~ /^<(!DOCTYPE HTML|HTML|\?xml)/i or return 0;
+    $buff =~ /<(!DOCTYPE )?HTML/i or return 0 if $1 eq '?xml';
+    $exifTool->SetFileType();
+
+    $raf->Seek(0,0) or $exifTool->Warn('Seek error'), return 1;
+
+    my $oldsep = Image::ExifTool::PostScript::SetInputRecordSeparator($raf);
+    $oldsep or $exifTool->Warn('Invalid HTML data'), return 1;
+
+    # extract header information
+    my $doc;
+    while ($raf->ReadLine($buff)) {
+        if (not defined $doc) {
+            # look for 'head' element
+            next unless $buff =~ /<head\b/ig;
+            $doc = substr($buff, pos($buff));
+            next;
+        }
+        $doc .= $buff;
+        last if $buff =~ m{</head>}i;
+    }
+
+    # process all elements in header
+    my $tagTablePtr = GetTagTable('Image::ExifTool::HTML::Main');
+    for (;;) {
+        last unless $doc =~ m{<([\w:.-]+)(.*?)>}sg;
+        my ($tagName, $attrs) = ($1, $2);
+        my $tag = lc($tagName);
+        my ($val, $grp);
+        unless ($attrs =~ m{/$}) {  # self-contained XHTML tags end in '/>'
+            # look for element close
+            my $pos = pos($doc);
+            my $close = "</$tagName>";
+            # the following doesn't work on Solaris Perl 5.6.1 due to Perl bug:
+            # if ($doc =~ m{(.*?)</$tagName>}sg) {
+            #     $val = $1;
+            if ($doc =~ m{$close}sg) {
+                $val = substr($doc, $pos, pos($doc)-$pos-length($close));
+            } else {
+                pos($doc) = $pos;
+                next unless $tag eq 'meta'; # META tags don't need to be closed
+            }
+        }
+        my $table = $tagTablePtr;
+        # parse HTML META element
+        if ($tag eq 'meta') {
+            undef $tag;
+            # tag name is in NAME or HTTP-EQUIV attribute
+            if ($attrs =~ /name=['"]?([\w:.-]+)/si) {
+                $tagName = $1;
+            } elsif ($attrs =~ /http-equiv=['"]?([\w:.-]+)/si) {
+                $tagName = "HTTP-equiv.$1";
+            } else {
+                next;   # no name
+            }
+            $tag = lc($tagName);
+            # tag value is in CONTENT attribute
+            $val = $2 if $attrs =~ /content=(['"])(.*?)\1/si;
+            next unless $tag and defined $val;
+            # isolate group name (separator is '.' in HTML, but ':' in ref 2)
+            if ($tag =~ /^([\w-]+)[:.]([\w-]+)/) {
+                ($grp, $tag) = ($1, $2);
+                my $tagInfo = $exifTool->GetTagInfo($tagTablePtr, $grp);
+                if ($tagInfo and $$tagInfo{SubDirectory}) {
+                    $table = GetTagTable($tagInfo->{SubDirectory}->{TagTable});
+                } else {
+                    $tag = "$grp.$tag";
+                }
+            }
+        } else {
+            # the only non-META element we process is TITLE
+            next unless $tag eq 'title';
+        }
+        unless ($$table{$tag}) {
+            my $name = $tagName;
+            $name =~ s/\W+(\w)/\u$1/sg;
+            my $info = { Name => $name, Groups => { 0 => 'HTML' } };
+            $info->{Groups}->{1} = ($grp eq 'http-equiv' ? 'HTTP-equiv' : "HTML-$grp") if $grp;
+            Image::ExifTool::AddTagToTable($table, $tag, $info);
+            $exifTool->VPrint(0, "  [adding $tag '$tagName']\n");
+        }
+        $val =~ s{\s*$/\s*}{ }sg;   # replace linefeeds and indenting spaces
+        $val = UnescapeHTML($val);  # unescape HTML character references
+        $exifTool->HandleTag($table, $tag, $val);
+    }
+    $/ = $oldsep;   # restore original separator
+    return 1;
+}
+
+1;  # end
+
+__END__
+
+=head1 NAME
+
+Image::ExifTool::HTML - Read HTML meta information
+
+=head1 SYNOPSIS
+
+This module is used by Image::ExifTool
+
+=head1 DESCRIPTION
+
+This module contains routines required by Image::ExifTool to extract
+meta information from HTML documents.
+
+=head1 AUTHOR
+
+Copyright 2003-2007, Phil Harvey (phil at owl.phy.queensu.ca)
+
+This library is free software; you can redistribute it and/or modify it
+under the same terms as Perl itself.
+
+=head1 REFERENCES
+
+=over 4
+
+=item L<http://www.w3.org/TR/html4/>
+
+=item L<http://www.daisy.org/publications/specifications/daisy_202.html>
+
+=item L<http://vancouver-webpages.com/META/metatags.detail.html>
+
+=item L<http://www.html-reference.com/META.htm>
+
+=back
+
+=head1 SEE ALSO
+
+L<Image::ExifTool::TagNames/HTML Tags>,
+L<Image::ExifTool(3pm)|Image::ExifTool>
+
+=cut
+
Index: /gsdl/trunk/perllib/cpan/Image/ExifTool/HtmlDump.pm
===================================================================
--- /gsdl/trunk/perllib/cpan/Image/ExifTool/HtmlDump.pm	(revision 16842)
+++ /gsdl/trunk/perllib/cpan/Image/ExifTool/HtmlDump.pm	(revision 16842)
@@ -0,0 +1,683 @@
+#------------------------------------------------------------------------------
+# File:         HtmlDump.pm
+#
+# Description:  Dump information in hex to HTML page
+#
+# Revisions:    12/05/2005 - P. Harvey Created
+#------------------------------------------------------------------------------
+
+package Image::ExifTool::HtmlDump;
+
+use strict;
+use vars qw($VERSION);
+use Image::ExifTool;    # only for FinishTiffDump()
+
+$VERSION = '1.17';
+
+sub DumpTable($$$;$$$$);
+sub Open($$$;@);
+sub Write($@);
+
+my ($bkgStart, $bkgEnd, $bkgSpan);
+
+my $htmlHeader1 = <<_END_PART_1_;
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"
+ "http://www.w3.org/TR/1998/REC-html40-19980424/loose.dtd">
+<html>
+<head>
+<title>
+_END_PART_1_
+
+# Note: Don't change font-weight style because it can affect line height
+my $htmlHeader2 = <<_END_PART_2_;
+</title>
+<style type="text/css">
+<!--
+/* character style ID's */
+.D { color: #000000 } /* default color */
+.V { color: #ff0000 } /* duplicate block 1 */
+.W { color: #004400 } /* normal block 1 */
+.X { color: #ff4488 } /* duplicate block 2 */
+.Y { color: #448844 } /* normal block 2 */
+.U { color: #cc8844 } /* unused data block */
+.H { color: #0000ff } /* highlighted tag name */
+.F { color: #aa00dd } /* actual offset differs */
+.M { text-decoration: underline } /* maker notes data */
+/* table styles */
+table.dump {
+  border-top: 1px solid gray;
+  border-bottom: 1px solid gray;
+}
+table.dump td { padding: .2em .3em }
+td.c2 {
+  border-left: 1px solid gray;
+  border-right: 1px solid gray;
+}
+pre   { margin: 0 }
+table { font-size: .9em }
+body  { color: black; background: white }
+-->
+</style>
+<script language="JavaScript" type="text/JavaScript">
+<!-- Begin
+var t = new Array;
+var mspan = new Array;
+function GetElementsByClass(classname, tagname) {
+  var found = new Array();
+  var list = document.getElementsByTagName(tagname);
+  var len = list.length;
+  for (var i=0, j=0; i<len; ++i) {
+    var classes = list[i].className.split(' ');
+    for (var k=0; k<classes.length; ++k) {
+      if (classes[k] == classname) {
+        found[j++] = list[i];
+        break;
+      }
+    }
+  }
+  delete list;
+  return found;
+}
+function high(e,on) {
+  var targ;
+  if (e.target) targ = e.target;
+  else if (e.srcElement) targ = e.srcElement;
+  if (targ.nodeType == 3) targ = targ.parentNode; // defeat Safari bug
+  if (!targ.name) targ = targ.parentNode; // go up another level if necessary
+  if (targ.name && document.getElementsByName) {
+    var col;
+    var tip;
+    if (on) {
+      col = "#ffcc99";
+      if (targ.name.substring(0,1) == 't') {
+        var index = parseInt(targ.name.substring(1));
+        tip = t[index];
+        if (tip) delete t[index];
+      }
+    } else {
+      col = "transparent";
+    }
+    // highlight anchor elements with the same name and add tool tip
+    var list = document.getElementsByName(targ.name);
+    for (var i=0; i<list.length; ++i) {
+      list[i].style.background = col;
+      if (tip) list[i].title += tip;
+    }
+    // use class name to highlight span elements if necessary
+    for (var i=0; i<mspan.length; ++i) {
+      if (mspan[i] != targ.name) continue;
+      list = GetElementsByClass(targ.name, 'span');
+      for (var j=0; j<list.length; ++j) {
+        list[j].style.background = col;
+      }
+      break;
+    }
+  }
+}
+_END_PART_2_
+
+my $htmlHeader3 = q[
+// End --->
+</script></head>
+<body><noscript><b class=V>--&gt;
+Enable JavaScript for active highlighting and information tool tips!
+</b></noscript><table class=dump cellspacing=0 cellpadding=2>
+<tr><td valign='top'><pre>];
+
+my $preMouse = q(<pre onmouseover="high(event,1)" onmouseout="high(event,0)">);
+
+#------------------------------------------------------------------------------
+# New - create new HtmlDump object
+# Inputs: 0) reference to HtmlDump object or HtmlDump class name
+sub new
+{
+    local $_;
+    my $that = shift;
+    my $class = ref($that) || $that || 'Image::ExifTool::HtmlDump';
+    return bless { Block => {}, TipNum => 0 }, $class;
+}
+
+#------------------------------------------------------------------------------
+# Add information to dump
+# Inputs: 0) HTML dump hash ref, 1) absolute offset in file, 2) data size,
+#         3) comment string, 4) tool tip (or SAME to use previous tip),
+#         5) bit flags (see below), 6) true to use same tooltip as last call
+# Bits: 0x01 - print at start of line
+#       0x02 - print red address
+#       0x04 - maker notes data ('M'-class span)
+#       0x08 - limit block length
+# Notes: Block will be shown in 'unused' color if comment string begins with '['
+sub Add($$$$;$$)
+{
+    my ($self, $start, $size, $msg, $tip, $flag, $sameTip) = @_;
+    my $block = $$self{Block};
+    $$block{$start} or $$block{$start} = [ ];
+    if ($tip and $tip eq 'SAME') {
+        $tip = '';
+    } else {
+        $tip = defined $tip ? '\n' . $tip : '';
+        my $m = $msg;
+        $m =~ s/<.*?>//g;       # remove html format codes
+        $tip = "$m$tip";        # add msg as first line in tooltip
+        # add size if not already done
+        $tip .= "\\n($size bytes)" unless $tip =~ /\\nSize:/;
+        ++$self->{TipNum};
+    }
+    push @{$$block{$start}}, [ $size, $msg, $tip, $flag, $self->{TipNum} ];
+}
+
+#------------------------------------------------------------------------------
+# Print dump information to HTML page
+# Inputs: 0) Dump information hash reference, 1) source file RAF reference,
+#         2) data pointer, 3) data position, 4) output file or scalar reference,
+#         5) limit level (1-3), 6) title
+# Returns: non-zero if useful output was generated
+sub Print($$;$$$$$)
+{
+    local $_;
+    my ($self, $raf, $dataPt, $dataPos, $outfile, $level, $title) = @_;
+    my ($i, $buff, $rtnVal);
+    my $block = $$self{Block};
+    $dataPos = 0 unless $dataPos;
+    $outfile = \*STDOUT unless ref $outfile;
+    $title = 'HtmlDump' unless $title;
+    $level or $level = 0;
+    my $tell = $raf->Tell();
+    my @starts = sort { $a <=> $b } keys %$block;
+    my $pos = 0;
+    my $dataEnd = $dataPos + ($dataPt ? length($$dataPt) : 0);
+    # initialize member variables
+    $$self{Open} = [];
+    $$self{Closed} = [];
+    $$self{TipList} = [];
+    $$self{MSpanList} = [];
+    $$self{Cols} = [ '', '', '', '' ];  # text columns
+    # set dump size limits (limits are 4x smaller if bit 0x08 set in flags)
+    if ($level <= 1) {
+        $$self{Limit} = 1024;
+    } elsif ($level <= 2) {
+        $$self{Limit} = 16384;
+    } else {
+        delete $$self{Limit};   # no limit
+    }
+    # pre-initialize open/closed hashes for all columns
+    for ($i=0; $i<4; ++$i) {
+        $self->{Open}->[$i] = { ID => [ ], Element => { } };
+        $self->{Closed}->[$i] = { ID => [ ], Element => { } };
+    }
+    $bkgStart = $bkgEnd = 0;
+    $bkgSpan = '';
+    my $index = 0;  # initialize tooltip index
+    my @names;
+    for ($i=0; $i<@starts; ++$i) {
+        my $start = $starts[$i];
+        my $bytes = $start - $pos;
+        if ($bytes > 0) {
+            if ($pos >= $dataPos and $pos + $bytes <= $dataEnd) {
+                $buff = substr($$dataPt, $pos-$dataPos, $bytes);
+            } else {
+                $buff = '';
+                $raf->Seek($pos, 0) and $raf->Read($buff, $bytes);
+            }
+            if (length $buff) {
+                my $str = ($bytes > 1) ? "unused $bytes bytes" : 'pad byte';
+                $self->DumpTable($pos-$dataPos, \$buff, "[$str]", "t$index", 0x108);
+                ++$index;
+            }
+            $pos = $start;  # dumped unused data up to the start of this block
+        }
+        my $parms;
+        my $parmList = $$block{$start};
+        foreach $parms (@$parmList) {
+            my ($len, $msg, $tip, $flag, $tipNum) = @$parms;
+            next unless $len > 0;
+            $flag = 0 unless defined $flag;
+            # generate same name for all blocks indexed by this tooltip
+            my $name = $names[$tipNum];
+            my $idx = $index;
+            if ($name) {
+                # get index from existing ID
+                $idx = substr($name, 1);
+            } else {
+                $name = $names[$tipNum] = "t$index";
+                ++$index;
+            }
+            if ($flag == 4) {
+                $bkgStart = $start - $dataPos;
+                $bkgEnd = $bkgStart + $len;
+                $bkgSpan = "<span class='$name M'>";
+                push @{$self->{MSpanList}}, $name;
+                next;
+            }
+            $tip and $self->{TipList}->[$idx] = $tip;
+            my $end = $start + $len;
+            if ($start >= $dataPos and $end <= $dataEnd) {
+                $buff = substr($$dataPt, $start-$dataPos, $len);
+            } else {
+                $buff = '';
+                $raf->Seek($start, 0) and $raf->Read($buff, $len);
+            }
+            next unless length $buff;
+            # set flag to continue this line if next block is contiguous
+            if ($i+1 < @starts and $parms eq $$parmList[-1] and
+                ($end == $starts[$i+1] or ($end < $starts[$i+1] and $end >= $pos)))
+            {
+                my $nextFlag = $block->{$starts[$i+1]}->[0]->[3] || 0;
+                $flag |= 0x100 unless $flag & 0x01 or $nextFlag & 0x01;
+            }
+            $self->DumpTable($start-$dataPos, \$buff, $msg, $name,
+                             $flag, $pos-$dataPos);
+            $pos = $end if $pos < $end;
+        }
+    }
+    $self->Open('','');     # close all open elements
+    $raf->Seek($tell,0);
+
+    # write output HTML file
+    Write($outfile, $htmlHeader1, $title);
+    if ($self->{Cols}->[0]) {
+        Write($outfile, $htmlHeader2);
+        my $mspan = \@{$$self{MSpanList}};
+        for ($i=0; $i<@$mspan; ++$i) {
+            Write($outfile, qq(mspan[$i] = "$$mspan[$i]";\n));
+        }
+        my $tips = \@{$$self{TipList}};
+        for ($i=0; $i<@$tips; ++$i) {
+            Write($outfile, qq(t[$i] = "$$tips[$i]";\n)) if defined $$tips[$i];
+        }
+        delete $$self{TipList};
+        Write($outfile, $htmlHeader3, $self->{Cols}->[0]);
+        Write($outfile, '</pre></td><td valign="top">',
+                        $preMouse, $self->{Cols}->[1]);
+        Write($outfile, '</pre></td><td class=c2 valign="top">',
+                        $preMouse, $self->{Cols}->[2]);
+        Write($outfile, '</pre></td><td valign="top">',
+                        $preMouse, $self->{Cols}->[3]);
+        Write($outfile, "</pre></td></tr></table>\n");
+        $rtnVal = 1;
+    } else {
+        Write($outfile, "$title</title></head><body>\n",
+                        "No EXIF or TIFF information found in image\n");
+    }
+    Write($outfile, "</body></html>\n");
+    for ($i=0; $i<4; ++$i) {
+        $self->{Cols}->[$i] = '';   # free memory
+    }
+    return $rtnVal;
+}
+
+#------------------------------------------------------------------------------
+# Open or close a specified html element
+# Inputs: 0) HtmlDump object ref, 1) element id, 2) element string,
+#         3-N) list of column numbers (empty for all columns)
+# - element id may be '' to close all elements
+# - element string may be '' to close element by ID (or 0 to close without reopening)
+# - element id and string may both be 1 to reopen temporarily closed elements
+sub Open($$$;@)
+{
+    my ($self, $id, $element, @colNums) = @_;
+
+    # loop through specified columns
+    @colNums or @colNums = (0 .. $#{$self->{Open}});
+    my $col;
+    foreach $col (@colNums) {
+        # get information about open elements in this column
+        my $opHash = $self->{Open}->[$col];
+        my $opElem = $$opHash{Element};
+        if ($element) {
+            # next if already open
+            next if $$opElem{$id} and $$opElem{$id} eq $element;
+        } elsif ($id and not $$opElem{$id}) {
+            # next if already closed and nothing to reopen
+            next unless $element eq '' and @{$self->{Closed}->[$col]->{ID}};
+        }
+        my $opID = $$opHash{ID};
+        my $clHash = $self->{Closed}->[$col];
+        my $clID = $$clHash{ID};
+        my $clElem = $$clHash{Element};
+        # get reference to output column list (use temp list if available)
+        my $cols = $$self{TmpCols} || $$self{Cols};
+        # close everything down to this element if necessary
+        if ($$opElem{$id} or not $id) {
+            while (@$opID) {
+                my $tid = pop @$opID;
+                my $e = $$opElem{$tid};
+                $e =~ s/^<(\S+).*/<\/$1>/s;
+                $$cols[$col] .= $e;
+                if ($id eq $tid or not $id) {
+                    delete $$opElem{$tid};
+                    last if $id;
+                    next;
+                }
+                # add this to the temporarily closed list
+                # (because we really didn't want to close it)
+                push @$clID, $tid;
+                $$clElem{$tid} = $$opElem{$tid};
+                delete $$opElem{$tid};
+            }
+            unless ($id) {
+                # forget all temporarily closed elements
+                $clID = $$clHash{ID} = [ ];
+                $clElem = $$clHash{Element} = { };
+            }
+        } elsif ($$clElem{$id}) {
+            # delete from the list of temporarily closed elements
+            delete $$clElem{$id};
+            @$clID = grep !/^$id$/, @$clID;
+        }
+        next if $element eq '0'; # 0 = don't reopen temporarily closed elements
+
+        # re-open temporarily closed elements
+        while (@$clID) {
+            my $tid = pop @$clID;
+            $$cols[$col] .= $$clElem{$tid};
+            push @$opID, $tid;
+            $$opElem{$tid} = $$clElem{$tid};
+            delete $$clElem{$tid};
+        }
+        # open specified element
+        if ($element and $element ne '1') {
+            $$cols[$col] .= $element;
+            push @$opID, $id;
+            $$opElem{$id} = $element;
+        }
+    }
+}
+
+#------------------------------------------------------------------------------
+# Dump a block of data in HTML table form
+# Inputs: 0) HtmlDump object ref, 1) data position, 2) block pointer,
+#         3) message, 4) object name, 5) flag, 6) data end position
+sub DumpTable($$$;$$$$)
+{
+    my ($self, $pos, $blockPt, $msg, $name, $flag, $endPos) = @_;
+    my $len = length $$blockPt;
+    $endPos = 0 unless $endPos;
+    my ($f0, $dblRef, $id);
+    if (($endPos and $pos < $endPos) or $flag & 0x02) {
+        # display double-reference addresses in red
+        $f0 = "<span class=V>";
+        $dblRef = 1 if $endPos and $pos < $endPos;
+    } else {
+        $f0 = '';
+    }
+    my @c = ('','','','');
+    $$self{TmpCols} = \@c;
+    if ($name) {
+        if ($msg and $msg =~ /^\[/) {
+            $id = 'U';
+        } else {
+            if ($$self{A}) {
+                $id = 'X';
+                $$self{A} = 0;
+            } else {
+                $id = 'V';
+                $$self{A} = 1;
+            }
+            ++$id unless $dblRef;
+        }
+        $name = qq{<a name=$name class=$id>};
+        $msg and $msg = "$name$msg</a>";
+    } else {
+        $name = '';
+    }
+    # use base-relative offsets from now on
+    my $cols = 0;
+    my $p = $pos;
+    if ($$self{Cont}) {
+        $cols = $pos & 0x0f;
+        $c[1] .= ($cols == 8) ? '  ' : ' ';
+    } else {
+        my $addr = $pos < 0 ? sprintf("-%.4x",-$pos) : sprintf("%5.4x",$pos);
+        $self->Open('fgd', $f0, 0);
+        $self->Open('fgd', '', 3);
+        $c[0] .= "$addr";
+        $p -= $pos & 0x0f unless $flag & 0x01;
+        if ($p < $pos) {
+            $self->Open('bkg', '', 1, 2); # don't underline white space
+            $cols = $pos - $p;
+            my $n = 3 * $cols;
+            ++$n if $cols > 7;
+            $c[1] .= ' ' x $n;
+            $c[2] .= ' ' x $cols;
+            $p = $pos;
+        }
+    }
+    # loop through each column of hex numbers
+    for (;;) {
+        $self->Open('bkg', ($p>=$bkgStart and $p<$bkgEnd) ? $bkgSpan : '', 1, 2);
+        $self->Open('a', $name, 1, 2);
+        my $ch = substr($$blockPt,$p-$pos,1);
+        $c[1] .= sprintf("%.2x", ord($ch));
+        # make the character HTML-friendly
+        $ch =~ tr/\x00-\x1f\x7f-\xff/./;
+        $ch =~ s/&/&amp;/g;
+        $ch =~ s/>/&gt;/g;
+        $ch =~ s/</&lt;/g;
+        $c[2] .= $ch;
+        ++$p;
+        ++$cols;
+        # close necessary elements
+        if ($p >= $bkgEnd) {
+            # close without reopening if closing anchor later
+            my $arg = ($p - $pos >= $len) ? 0 : '';
+            $self->Open('bkg', $arg, 1, 2);
+        }
+        if ($dblRef and $p >= $endPos) {
+            $dblRef = 0;
+            ++$id;
+            $name =~ s/class=\w\b/class=$id/;
+            $f0 = '';
+            $self->Open('fgd', $f0, 0);
+        }
+        if ($p - $pos >= $len) {
+            $self->Open('a', '', 1, 2);     # close our anchor
+            last;
+        }
+        if ($cols < 16) {
+            $c[1] .= ($cols == 8 ? '  ' : ' ');
+            next;
+        } elsif ($flag & 0x01 and $cols < $len) {
+            $c[1] .= ' ';
+            next;   # put it all on one line
+        }
+        unless ($$self{Msg}) {
+            $c[3] .= $msg;
+            $msg = '';
+        }
+        $_ .= "\n" foreach @c;  # add CR to all lines
+        $$self{Msg} = 0;
+        # limit data length if specified
+        if ($$self{Limit}) {
+            my $div = ($flag & 0x08) ? 4 : 1;
+            my $lim = $$self{Limit} / (2 * $div) - 16;
+            if ($p - $pos > $lim and $len - $p + $pos > $lim) {
+                my $n = ($len - $p + $pos - $lim) & ~0x0f;
+                if ($n > 16) { # (no use just cutting out one line)
+                    $self->Open('bkg', '', 1, 2); # no underline
+                    my $note = "[snip $n bytes]";
+                    $note = (' ' x (24-length($note)/2)) . $note;
+                    $c[0] .= "  ...\n";
+                    $c[1] .= $note . (' ' x (48-length($note))) . "\n";
+                    $c[2] .= "     [snip]     \n";
+                    $c[3] .= "\n";
+                    $p += $n;
+                }
+            }
+        }
+        $c[0] .= ($p < 0 ? sprintf("-%.4x",-$p) : sprintf("%5.4x",$p));
+        $cols = 0;
+    }
+    if ($msg) {
+        $msg = " $msg" if $$self{Msg};
+        $c[3] .= $msg;
+    }
+    if ($flag & 0x100 and $cols < 16) {    # continue on same line?
+        $$self{Cont} = 1;
+        $$self{Msg} = 1 if $msg;
+    } else {
+        $_ .= "\n" foreach @c;
+        $$self{Msg} = 0;
+        $$self{Cont} = 0;
+    }
+    # add temporary column data to our real columns
+    my $i;
+    for ($i=0; $i<4; ++$i) {
+        $self->{Cols}->[$i] .= $c[$i];
+    }
+    delete $$self{TmpCols};
+}
+
+#------------------------------------------------------------------------------
+# Finish dumping of TIFF image data
+# Inputs: 0) HtmlDump object ref, 1) ExifTool object ref, 2) length of file
+# (this really belongs in Image::ExifTool::Exif, but is placed here so it
+#  is only compiled when needed)
+sub FinishTiffDump($$$)
+{
+    my ($self, $exifTool, $size) = @_;
+    my ($tag, $key, $start, $blockInfo, $i);
+
+    # list of all indirectly referenced TIFF data tags
+    my %offsetPair = (
+        StripOffsets      => 'StripByteCounts',
+        TileOffsets       => 'TileByteCounts',
+        FreeOffsets       => 'FreeByteCounts',
+        ThumbnailOffset   => 'ThumbnailLength',
+        PreviewImageStart => 'PreviewImageLength',
+        JpgFromRawStart   => 'JpgFromRawLength',
+        OtherImageStart   => 'OtherImageLength',
+        ImageOffset       => 'ImageByteCount',
+        AlphaOffset       => 'AlphaByteCount',
+    );
+
+    # add TIFF data to html dump
+    foreach $tag (keys %offsetPair) {
+        my $info = $exifTool->GetInfo($tag);
+        next unless %$info;
+        foreach $key (%$info) {
+            my $name = Image::ExifTool::GetTagName($key);
+            my $grp1 = $exifTool->GetGroup($key, 1);
+            my $info2 = $exifTool->GetInfo($offsetPair{$tag}, { Group1 => $grp1 });
+            next unless %$info2;
+            my ($key2) = keys %$info2;
+            my $offsets = $$info{$key};
+            my $byteCounts = $$info2{$key2};
+            # (long lists may be SCALAR references)
+            my @offsets = split ' ', (ref $offsets ? $$offsets : $offsets);
+            my @byteCounts = split ' ', (ref $byteCounts ? $$byteCounts : $byteCounts);
+            my $num = scalar @offsets;
+            my $li = 0;
+            my $padBytes = 0;
+            for ($i=0; @offsets and @byteCounts; ++$i) {
+                my $offset = shift @offsets;
+                my $byteCount = shift @byteCounts;
+                my $end = $offset + $byteCount;
+                if (@offsets and @byteCounts) {
+                    # show data as contiguous if only normal pad bytes between blocks
+                    if ($end & 0x01 and $end + 1 == $offsets[0]) {
+                        $end += 1;
+                        ++$padBytes;    # count them
+                    }
+                    if ($end == $offsets[0]) {
+                        # combine these two blocks
+                        $byteCounts[0] += $offsets[0] - $offset;
+                        $offsets[0] = $offset;
+                        next;
+                    }
+                }
+                my $msg = $exifTool->GetGroup($key, 1) . ':' . $tag;
+                $msg =~ s/(Offsets?|Start)$/ /;
+                if ($num > 1) {
+                    $msg .= "$li-" if $li != $i;
+                    $msg .= "$i ";
+                    $li = $i + 1;
+                }
+                $msg .= "data";
+                my $tip = "Size: $byteCount bytes";
+                $tip .= ", incl. $padBytes pad bytes" if $padBytes;
+                $self->Add($offset, $byteCount, "($msg)", $tip, 0x08);
+            }
+        }
+    }
+    # find offset of last dumped information, and dump any unknown trailer
+    my $last = 0;
+    my $block = $$self{Block};
+    foreach $start (keys %$block) {
+        foreach $blockInfo (@{$$block{$start}}) {
+            my $end = $start + $$blockInfo[0];
+            $last = $end if $last < $end;
+        }
+    }
+    my $diff = $size - $last;
+    if ($diff > 0 and ($last or $exifTool->Options('Unknown'))) {
+        if ($diff > 1 or $size & 0x01) {
+            $self->Add($last, $diff, "[unknown data]", "Size: $diff bytes", 0x08);
+        } else {
+            $self->Add($last, $diff, "[trailing pad byte]", undef, 0x08);
+        }
+    }
+}
+
+#------------------------------------------------------------------------------
+# utility routine to write to file or memory
+# Inputs: 0) file or scalar reference, 1-N) list of stuff to write
+# Returns: true on success
+sub Write($@)
+{
+    my $outfile = shift;
+    if (UNIVERSAL::isa($outfile,'GLOB')) {
+        return print $outfile @_;
+    } elsif (ref $outfile eq 'SCALAR') {
+        $$outfile .= join('', @_);
+        return 1;
+    }
+    return 0;
+}
+
+1;  # end
+
+__END__
+
+=head1 NAME
+
+Image::ExifTool::HtmlDump - Dump information in hex to HTML page
+
+=head1 SYNOPSIS
+
+    use Image::ExifTool::HtmlDump;
+    my $dump = new Image::ExifTool::HtmlDump;
+    $dump->Add($start, $size, $comment);
+    $dump->Print($dumpInfo, $raf, $dataPt, $dataPos, $outfile);
+
+=head1 DESCRIPTION
+
+This module contains code used to generate an HTML-based hex dump of
+information for debugging purposes.  This is code is called when the
+ExifTool 'HtmlDump' option is used.
+
+Currently, only EXIF and TIFF information is dumped.
+
+=head1 BUGS
+
+Due to a memory allocation bug in ActivePerl 5.8.x for Windows, this code
+may run extremely slowly when processing large files with this version of
+Perl.
+
+An HTML 4 compliant browser is needed to properly display the generated HTML
+page, but note that some of these browsers (like Mozilla) may not properly
+display linefeeds in the tool tips.
+
+=head1 AUTHOR
+
+Copyright 2003-2007, Phil Harvey (phil at owl.phy.queensu.ca)
+
+This library is free software; you can redistribute it and/or modify it
+under the same terms as Perl itself.
+
+=head1 SEE ALSO
+
+L<Image::ExifTool(3pm)|Image::ExifTool>
+
+=cut
+
Index: /gsdl/trunk/perllib/cpan/Image/ExifTool/ICC_Profile.pm
===================================================================
--- /gsdl/trunk/perllib/cpan/Image/ExifTool/ICC_Profile.pm	(revision 16842)
+++ /gsdl/trunk/perllib/cpan/Image/ExifTool/ICC_Profile.pm	(revision 16842)
@@ -0,0 +1,836 @@
+#------------------------------------------------------------------------------
+# File:         ICC_Profile.pm
+#
+# Description:  Read ICC Profile meta information
+#
+# Revisions:    11/16/04 - P. Harvey Created
+#
+# References:   1) http://www.color.org/icc_specs2.html (ICC.1:2003-09)
+#               2) http://www.color.org/icc_specs2.html (ICC.1:2001-04)
+#               3) http://developer.apple.com/documentation/GraphicsImaging/Reference/ColorSync_Manager/ColorSync_Manager.pdf
+#               4) http://www.color.org/privatetag2007-01.pdf
+#
+# Notes:        The ICC profile information is different: the format of each
+#               tag is embedded in the information instead of in the directory
+#               structure. This makes things a bit more complex because I need
+#               an extra level of logic to decode the variable-format tags.
+#------------------------------------------------------------------------------
+
+package Image::ExifTool::ICC_Profile;
+
+use strict;
+use vars qw($VERSION);
+use Image::ExifTool qw(:DataAccess :Utils);
+
+$VERSION = '1.13';
+
+sub ProcessICC($$);
+sub ProcessICC_Profile($$$);
+sub WriteICC_Profile($$;$);
+sub ValidateICC($);
+
+# illuminant type definitions
+my %illuminantType = (
+    1 => 'D50',
+    2 => 'D65',
+    3 => 'D93',
+    4 => 'F2',
+    5 => 'D55',
+    6 => 'A',
+    7 => 'Equi-Power (E)',
+    8 => 'F8',
+);
+my %profileClass = (
+    scnr => 'Input Device Profile',
+    mntr => 'Display Device Profile',
+    prtr => 'Output Device Profile',
+   'link'=> 'DeviceLink Profile',
+    spac => 'ColorSpace Conversion Profile',
+    abst => 'Abstract Profile',
+    nmcl => 'NamedColor Profile',
+);
+
+# ICC_Profile tag table
+%Image::ExifTool::ICC_Profile::Main = (
+    GROUPS => { 2 => 'Image' },
+    PROCESS_PROC => \&ProcessICC_Profile,
+    WRITE_PROC => \&WriteICC_Profile,
+    NOTES => q{
+        ICC profile information is used in many different file types including JPEG,
+        TIFF, PDF, PostScript, Photoshop, PNG, MIFF, PICT, QuickTime and some RAW
+        formats.  While the tags listed below are not individually writable, the
+        entire profile itself can be accessed via the extra 'ICC_Profile' tag, but
+        this tag is neither extracted nor written unless specified explicitly.
+    },
+    A2B0 => 'AToB0',
+    A2B1 => 'AToB1',
+    A2B2 => 'AToB2',
+    bXYZ => 'BlueMatrixColumn', # (called BlueColorant in ref 2)
+    bTRC => {
+        Name => 'BlueTRC',
+        Description => 'Blue Tone Reproduction Curve',
+    },
+    B2A0 => 'BToA0',
+    B2A1 => 'BToA1',
+    B2A2 => 'BToA2',
+    calt => {
+        Name => 'CalibrationDateTime',
+        Groups => { 2 => 'Time' },
+        PrintConv => '$self->ConvertDateTime($val)',
+    },
+    targ => 'CharTarget',
+    chad => 'ChromaticAdaptation',
+    chrm => {
+        Name => 'Chromaticity',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::ICC_Profile::Chromaticity',
+            Validate => '$type eq "chrm"',
+        },
+    },
+    clro => 'ColorantOrder',
+    clrt => {
+        Name => 'ColorantTable',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::ICC_Profile::ColorantTable',
+            Validate => '$type eq "clrt"',
+        },
+    },
+    cprt => 'ProfileCopyright',
+    crdi => 'CRDInfo', #2
+    dmnd => {
+        Name => 'DeviceMfgDesc',
+        Groups => { 2 => 'Camera' },
+    },
+    dmdd => {
+        Name => 'DeviceModelDesc',
+        Groups => { 2 => 'Camera' },
+    },
+    devs => {
+        Name => 'DeviceSettings', #2
+        Groups => { 2 => 'Camera' },
+    },
+    gamt => 'Gamut',
+    kTRC => {
+        Name => 'GrayTRC',
+        Description => 'Gray Tone Reproduction Curve',
+    },
+    gXYZ => 'GreenMatrixColumn', # (called GreenColorant in ref 2)
+    gTRC => {
+        Name => 'GreenTRC',
+        Description => 'Green Tone Reproduction Curve',
+    },
+    lumi => 'Luminance',
+    meas => {
+        Name => 'Measurement',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::ICC_Profile::Measurement',
+            Validate => '$type eq "meas"',
+        },
+    },
+    bkpt => 'MediaBlackPoint',
+    wtpt => 'MediaWhitePoint',
+    ncol => 'NamedColor', #2
+    ncl2 => 'NamedColor2',
+    resp => 'OutputResponse',
+    pre0 => 'Preview0',
+    pre1 => 'Preview1',
+    pre2 => 'Preview2',
+    desc => 'ProfileDescription',
+    pseq => 'ProfileSequenceDesc',
+    psd0 => 'PostScript2CRD0', #2
+    psd1 => 'PostScript2CRD1', #2
+    psd2 => 'PostScript2CRD2', #2
+    ps2s => 'PostScript2CSA', #2
+    ps2i => 'PS2RenteringIntent', #2
+    rXYZ => 'RedMatrixColumn', # (called RedColorant in ref 2)
+    rTRC => {
+        Name => 'RedTRC',
+        Description => 'Red Tone Reproduction Curve',
+    },
+    scrd => 'ScreeningDesc',
+    scrn => 'Screening',
+   'bfd '=> {
+        Name => 'UCRBG',
+        Description => 'Under Color Removal & Black Gen.',
+    },
+    tech => {
+        Name => 'Technology',
+        PrintConv => {
+            fscn => 'Film Scanner',
+            dcam => 'Digital Camera',
+            rscn => 'Reflective Scanner',
+            ijet => 'Ink Jet Printer',
+            twax => 'Thermal Wax Printer',
+            epho => 'Electrophotographic Printer',
+            esta => 'Electrostatic Printer',
+            dsub => 'Dye Sublimation Printer',
+            rpho => 'Photographic Paper Printer',
+            fprn => 'Film Writer',
+            vidm => 'Video Monitor',
+            vidc => 'Video Camera',
+            pjtv => 'Projection Television',
+           'CRT '=> 'Cathode Ray Tube Display',
+           'PMD '=> 'Passive Matrix Display',
+           'AMD '=> 'Active Matrix Display',
+            KPCD => 'Photo CD',
+            imgs => 'Photo Image Setter',
+            grav => 'Gravure',
+            offs => 'Offset Lithography',
+            silk => 'Silkscreen',
+            flex => 'Flexography',
+        },
+    },
+    vued => 'ViewingCondDesc',
+    view => {
+        Name => 'ViewingConditions',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::ICC_Profile::ViewingConditions',
+            Validate => '$type eq "view"',
+        },
+    },
+    # ColorSync custom tags (ref 3)
+    psvm => 'PS2CRDVMSize',
+    vcgt => 'VideoCardGamma',
+    mmod => 'MakeAndModel',
+    dscm => 'ProfileDescriptionML',
+    ndin => 'NativeDisplayInfo',
+    
+    # Microsoft custom tags (ref http://msdn2.microsoft.com/en-us/library/ms536870.aspx)
+    MS00 => 'WCSProfiles',
+
+    # the following entry represents the ICC profile header, and doesn't
+    # exist as a tag in the directory.  It is only in this table to provide
+    # a link so ExifTool can locate the header tags
+    Header => {
+        Name => 'ProfileHeader',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::ICC_Profile::Header',
+        },
+    },
+);
+
+# ICC profile header definition
+%Image::ExifTool::ICC_Profile::Header = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    GROUPS => { 0 => 'ICC_Profile', 1 => 'ICC-header', 2 => 'Image' },
+    4 => {
+        Name => 'ProfileCMMType',
+        Format => 'string[4]',
+    },
+    8 => {
+        Name => 'ProfileVersion',
+        Format => 'int16s',
+        PrintConv => '($val >> 8).".".(($val & 0xf0)>>4).".".($val & 0x0f)',
+    },
+    12 => {
+        Name => 'ProfileClass',
+        Format => 'string[4]',
+        PrintConv => \%profileClass,
+    },
+    16 => {
+        Name => 'ColorSpaceData',
+        Format => 'string[4]',
+    },
+    20 => {
+        Name => 'ProfileConnectionSpace',
+        Format => 'string[4]',
+    },
+    24 => {
+        Name => 'ProfileDateTime',
+        Groups => { 2 => 'Time' },
+        Format => 'int16u[6]',
+        ValueConv => 'sprintf("%.4d:%.2d:%.2d %.2d:%.2d:%.2d",split(" ",$val));',
+        PrintConv => '$self->ConvertDateTime($val)',
+    },
+    36 => {
+        Name => 'ProfileFileSignature',
+        Format => 'string[4]',
+    },
+    40 => {
+        Name => 'PrimaryPlatform',
+        Format => 'string[4]',
+        PrintConv => {
+            'APPL' => 'Apple Computer Inc.',
+            'MSFT' => 'Microsoft Corporation',
+            'SGI ' => 'Silicon Graphics Inc.',
+            'SUNW' => 'Sun Microsystems Inc.',
+            'TGNT' => 'Taligent Inc.',
+        },
+    },
+    44 => {
+        Name => 'CMMFlags',
+        Format => 'int32u',
+        PrintConv => q[
+            ($val & 0x01 ? "Embedded, " : "Not Embedded, ") .
+            ($val & 0x02 ? "Not Independent" : "Independent")
+        ],
+    },
+    48 => {
+        Name => 'DeviceManufacturer',
+        Format => 'string[4]',
+    },
+    52 => {
+        Name => 'DeviceModel',
+        Format => 'string[4]',
+    },
+    56 => {
+        Name => 'DeviceAttributes',
+        Format => 'int32u[2]',
+        PrintConv => q[
+            my @v = split ' ', $val;
+            ($v[1] & 0x01 ? "Transparency, " : "Reflective, ") .
+            ($v[1] & 0x02 ? "Matte, " : "Glossy, ") .
+            ($v[1] & 0x04 ? "Negative, " : "Positive, ") .
+            ($v[1] & 0x08 ? "B&W" : "Color");
+        ],
+    },
+    64 => {
+        Name => 'RenderingIntent',
+        Format => 'int32u',
+        PrintConv => {
+            0 => 'Perceptual',
+            1 => 'Media-Relative Colorimetric',
+            2 => 'Saturation',
+            3 => 'ICC-Absolute Colorimetric',
+        },
+    },
+    68 => {
+        Name => 'ConnectionSpaceIlluminant',
+        Format => 'fixed32s[3]',  # xyz
+    },
+    80 => {
+        Name => 'ProfileCreator',
+        Format => 'string[4]',
+    },
+    84 => {
+        Name => 'ProfileID',
+        Format => 'int8u[16]',
+        PrintConv => 'Image::ExifTool::ICC_Profile::HexID($val)',
+    },
+);
+
+# viewingConditionsType (view) definition
+%Image::ExifTool::ICC_Profile::ViewingConditions = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    GROUPS => { 0 => 'ICC_Profile', 1 => 'ICC-view', 2 => 'Image' },
+    8 => {
+        Name => 'ViewingCondIlluminant',
+        Format => 'fixed32s[3]',   # xyz
+    },
+    20 => {
+        Name => 'ViewingCondSurround',
+        Format => 'fixed32s[3]',   # xyz
+    },
+    32 => {
+        Name => 'ViewingCondIlluminantType',
+        Format => 'int32u',
+        PrintConv => \%illuminantType,
+    },
+);
+
+# measurementType (meas) definition
+%Image::ExifTool::ICC_Profile::Measurement = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    GROUPS => { 0 => 'ICC_Profile', 1 => 'ICC-meas', 2 => 'Image' },
+    8 => {
+        Name => 'MeasurementObserver',
+        Format => 'int32u',
+        PrintConv => {
+            1 => 'CIE 1931',
+            2 => 'CIE 1964',
+        },
+    },
+    12 => {
+        Name => 'MeasurementBacking',
+        Format => 'fixed32s[3]',   # xyz
+    },
+    24 => {
+        Name => 'MeasurementGeometry',
+        Format => 'int32u',
+        PrintConv => {
+            1 => '0/45 or 45/0',
+            2 => '0/d or d/0',
+        },
+    },
+    28 => {
+        Name => 'MeasurementFlare',
+        Format => 'fixed32u',
+        PrintConv => '$val*100 . "%"',  # change into a percent
+    },
+    32 => {
+        Name => 'MeasurementIlluminant',
+        Format => 'int32u',
+        PrintConv => \%illuminantType,
+    },
+);
+
+# chromaticity (chrm) definition
+%Image::ExifTool::ICC_Profile::Chromaticity = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    GROUPS => { 0 => 'ICC_Profile', 1 => 'ICC-chrm', 2 => 'Image' },
+    8 => {
+        Name => 'ChromaticityChannels',
+        Format => 'int16u',
+    },
+    10 => {
+        Name => 'ChromaticityColorant',
+        Format => 'int16u',
+        PrintConv => {
+            1 => 'ITU-R BT.709',
+            2 => 'SMPTE RP145-1994',
+            3 => 'EBU Tech.3213-E',
+            4 => 'P22',
+        },
+    },
+    # include definitions for 4 channels -- if there are
+    # fewer then the ProcessBinaryData logic won't print them.
+    # If there are more, oh well.
+    12 => {
+        Name => 'ChromaticityChannel1',
+        Format => 'fixed32u[2]',
+    },
+    20 => {
+        Name => 'ChromaticityChannel2',
+        Format => 'fixed32u[2]',
+    },
+    28 => {
+        Name => 'ChromaticityChannel3',
+        Format => 'fixed32u[2]',
+    },
+    36 => {
+        Name => 'ChromaticityChannel4',
+        Format => 'fixed32u[2]',
+    },
+);
+
+# colorantTable (clrt) definition
+%Image::ExifTool::ICC_Profile::ColorantTable = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    GROUPS => { 0 => 'ICC_Profile', 1 => 'ICC-clrt', 2 => 'Image' },
+    8 => {
+        Name => 'ColorantCount',
+        Format => 'int32u',
+    },
+    # include definitions for 3 colorants -- if there are
+    # fewer then the ProcessBinaryData logic won't print them.
+    # If there are more, oh well.
+    12 => {
+        Name => 'Colorant1Name',
+        Format => 'string[32]',
+    },
+    44 => {
+        Name => 'Colorant1Coordinates',
+        Format => 'int16u[3]',
+    },
+    50 => {
+        Name => 'Colorant2Name',
+        Format => 'string[32]',
+    },
+    82 => {
+        Name => 'Colorant2Coordinates',
+        Format => 'int16u[3]',
+    },
+    88 => {
+        Name => 'Colorant3Name',
+        Format => 'string[32]',
+    },
+    120 => {
+        Name => 'Colorant3Coordinates',
+        Format => 'int16u[3]',
+    },
+);
+
+
+#------------------------------------------------------------------------------
+# print ICC Profile ID in hex
+# Inputs: 1) string of numbers
+# Returns: string of hex digits
+sub HexID($)
+{
+    my $val = shift;
+    my @vals = split(' ', $val);
+    # return a simple zero if no MD5 done
+    return 0 unless grep(!/^0/, @vals);
+    $val = '';
+    foreach (@vals) { $val .= sprintf("%.2x",$_); }
+    return $val;
+}
+
+#------------------------------------------------------------------------------
+# get formatted value from ICC tag (which has the type embedded)
+# Inputs: 0) data reference, 1) offset to tag data, 2) tag data size
+# Returns: Formatted value or undefined if format not supported
+# Notes: The following types are handled by BinaryTables:
+#  chromaticityType, colorantTableType, measurementType, viewingConditionsType
+# The following types are not currently handled (most are large tables):
+#  curveType, lut16Type, lut8Type, lutAtoBType, lutBtoAType, namedColor2Type,
+#  parametricCurveType, profileSeqDescType, responseCurveSet16Type
+sub FormatICCTag($$$)
+{
+    my ($dataPt, $offset, $size) = @_;
+
+    my $type;
+    if ($size >= 8) {
+        # get data type from start of tag data
+        $type = substr($$dataPt, $offset, 4);
+    } else {
+        $type = 'err';
+    }
+    # colorantOrderType
+    if ($type eq 'clro' and $size >= 12) {
+        my $num = Get32u($dataPt, $offset+8);
+        if ($size >= $num + 12) {
+            my $pos = $offset + 12;
+            return join(' ',unpack("x$pos c$num", $$dataPt));
+        }
+    }
+    # dataType
+    if ($type eq 'data' and $size >= 12) {
+        my $form = Get32u($dataPt, $offset+8);
+        # format 0 is ASCII data
+        $form == 0 and return substr($$dataPt, $offset+12, $size-12);
+        # binary data and other data types treat as binary (ie. don't format)
+    }
+    # dateTimeType
+    if ($type eq 'dtim' and $size >= 20) {
+        return sprintf("%.4d:%.2d:%.2d %.2d:%.2d:%.2d",
+               Get16u($dataPt, $offset+8),  Get16u($dataPt, $offset+10),
+               Get16u($dataPt, $offset+12), Get16u($dataPt, $offset+14),
+               Get16u($dataPt, $offset+16), Get16u($dataPt, $offset+18));
+    }
+    # multiLocalizedUnicodeType (replaces textDescriptionType of ref 2)
+    if ($type eq 'mluc' and $size >= 28) {
+        # take first language in list (pray that it is ascii)
+        my $len = Get32u($dataPt, $offset + 20);
+        my $pos = Get32u($dataPt, $offset + 24);
+        if ($size >= $pos + $len) {
+            my $str = substr($$dataPt, $offset + $pos, $len);
+            $str =~ tr/\x00-\x1f\x80-\xff//d; # remove control characters and non-ascii
+            return $str;
+        }
+    }
+    # s15Fixed16ArrayType
+    if ($type eq 'sf32') {
+        return ReadValue($dataPt,$offset+8,'fixed32s',($size-8)/4,$size-8);
+    }
+    # signatureType
+    if ($type eq 'sig ' and $size >= 12) {
+        return substr($$dataPt, $offset+8, 4);
+    }
+    # textType
+    $type eq 'text' and return substr($$dataPt, $offset+8, $size-8);
+    # textDescriptionType (ref 2, replaced by multiLocalizedUnicodeType)
+    if ($type eq 'desc' and $size >= 12) {
+        my $len = Get32u($dataPt, $offset+8);
+        if ($size >= $len + 12) {
+            my $str = substr($$dataPt, $offset+12, $len);
+            $str =~ s/\0.*//s;   # truncate at null terminator
+            return $str;
+        }
+    }
+    # u16Fixed16ArrayType
+    if ($type eq 'uf32') {
+        return ReadValue($dataPt,$offset+8,'fixed32u',($size-8)/4,$size-8);
+    }
+    # uInt32ArrayType
+    if ($type eq 'ui32') {
+        return ReadValue($dataPt,$offset+8,'int32u',($size-8)/4,$size-8);
+    }
+    # uInt64ArrayType
+    if ($type eq 'ui64') {
+        return ReadValue($dataPt,$offset+8,'int64u',($size-8)/8,$size-8);
+    }
+    # uInt8ArrayType
+    if ($type eq 'ui08') {
+        return ReadValue($dataPt,$offset+8,'int8u',$size-8,$size-8);
+    }
+    # XYZType
+    if ($type eq 'XYZ ') {
+        my $str = '';
+        my $pos;
+        for ($pos=8; $pos+12<=$size; $pos+=12) {
+            $str and $str .= ', ';
+            $str .= ReadValue($dataPt,$offset+$pos,'fixed32s',3,$size-$pos);
+        }
+        return $str;
+    }
+    return undef;   # data type is not supported
+}
+
+#------------------------------------------------------------------------------
+# Write ICC profile file
+# Inputs: 0) ExifTool object reference, 1) Reference to directory information
+# Returns: 1 on success, 0 if this wasn't a valid ICC file,
+#          or -1 if a write error occurred
+sub WriteICC($$)
+{
+    my ($exifTool, $dirInfo) = @_;
+    # first make sure this is a valid ICC file (or no file at all)
+    my $raf = $$dirInfo{RAF};
+    my $buff;
+    return 0 if $raf->Read($buff, 24) and ValidateICC(\$buff);
+    # now write the new ICC
+    $buff = WriteICC_Profile($exifTool, $dirInfo);
+    if (defined $buff and length $buff) {
+        Write($$dirInfo{OutFile}, $buff) or return -1;
+    } else {
+        $exifTool->Error('No ICC information to write');
+    }
+    return 1;
+}
+
+#------------------------------------------------------------------------------
+# Write ICC data record
+# Inputs: 0) ExifTool object reference, 1) source dirInfo reference,
+#         2) tag table reference
+# Returns: ICC data block (may be empty if no ICC data)
+# Notes: Increments ExifTool CHANGED flag if changed
+sub WriteICC_Profile($$;$)
+{
+    my ($exifTool, $dirInfo, $tagTablePtr) = @_;
+    $exifTool or return 1;    # allow dummy access
+    my $dirName = $$dirInfo{DirName} || 'ICC_Profile';
+    # (don't write AsShotICCProfile or CurrentICCProfile here)
+    return undef unless $dirName eq 'ICC_Profile';
+    my $newValueHash = $exifTool->GetNewValueHash($Image::ExifTool::Extra{$dirName});
+    return undef unless Image::ExifTool::IsOverwriting($newValueHash);
+    my $val = Image::ExifTool::GetNewValues($newValueHash);
+    $val = '' unless defined $val;
+    ++$exifTool->{CHANGED};
+    return $val;
+}
+
+#------------------------------------------------------------------------------
+# Validate ICC data
+# Inputs: 0) ICC data reference
+# Returns: error string or undef on success
+sub ValidateICC($)
+{
+    my $valPtr = shift;
+    my $err;
+    length($$valPtr) < 24 and return 'Invalid ICC profile';
+    $profileClass{substr($$valPtr, 12, 4)} or $err = 'profile class';
+    my $col = substr($$valPtr, 16, 4); # ColorSpaceData
+    my $con = substr($$valPtr, 20, 4); # ConnectionSpace
+    my $match = '(XYZ |Lab |Luv |YCbr|Yxy |RGB |GRAY|HSV |HLS |CMYK|CMY |[2-9A-F]CLR)';
+    $col =~ /$match/ or $err = 'color space';
+    $con =~ /$match/ or $err = 'connection space';
+    return $err ? "Invalid ICC profile (bad $err)" : undef;
+}
+
+#------------------------------------------------------------------------------
+# Process ICC profile file
+# Inputs: 0) ExifTool object reference, 1) Reference to directory information
+# Returns: 1 if this was an ICC file
+sub ProcessICC($$)
+{
+    my ($exifTool, $dirInfo) = @_;
+    my $raf = $$dirInfo{RAF};
+    my $buff;
+    $raf->Read($buff, 24) == 24 or return 0;
+    # check to see if this is a valid ICC profile file
+    return 0 if ValidateICC(\$buff);
+    $exifTool->SetFileType();
+    # read the profile
+    my $size = unpack('N', $buff);
+    if ($size < 128 or $size & 0x80000000) {
+        $exifTool->Error("Bad ICC Profile length ($size)");
+        return 1;
+    }
+    $raf->Seek(0, 0);
+    unless ($raf->Read($buff, $size)) {
+        $exifTool->Error('Truncated ICC profile');
+        return 1;
+    }
+    my %dirInfo = (
+        DataPt => \$buff,
+        DataLen => $size,
+        DirStart => 0,
+        DirLen => $size,
+    );
+    my $tagTablePtr = GetTagTable('Image::ExifTool::ICC_Profile::Main');
+    return ProcessICC_Profile($exifTool, \%dirInfo, $tagTablePtr);
+}
+
+#------------------------------------------------------------------------------
+# Process ICC_Profile APP13 record
+# Inputs: 0) ExifTool object reference, 1) Reference to directory information
+#         2) Tag table reference (undefined to read ICC file)
+# Returns: 1 on success
+sub ProcessICC_Profile($$$)
+{
+    my ($exifTool, $dirInfo, $tagTablePtr) = @_;
+    my $dataPt = $$dirInfo{DataPt};
+    my $dirStart = $$dirInfo{DirStart};
+    my $dirLen = $$dirInfo{DirLen};
+    my $verbose = $exifTool->Options('Verbose');
+    my $raf;
+
+    # extract binary ICC_Profile data block if binary mode or requested
+    if ($exifTool->{OPTIONS}->{Binary} or $exifTool->{REQ_TAG_LOOKUP}->{icc_profile} and
+        # (don't extract from AsShotICCProfile or CurrentICCProfile)
+        (not $$dirInfo{Name} or $$dirInfo{Name} eq 'ICC_Profile'))
+    {
+        $exifTool->FoundTag('ICC_Profile', substr($$dataPt, $dirStart, $dirLen));
+    }
+
+    SetByteOrder('MM');     # ICC_Profile is always big-endian
+
+    # check length of table
+    my $len = Get32u($dataPt, $dirStart);
+    if ($len != $dirLen or $len < 128) {
+        $exifTool->Warn("Bad length ICC_Profile (length $len)");
+        return 0 if $len < 128 or $dirLen < $len;
+    }
+    my $pos = $dirStart + 128;  # position at start of table
+    my $numEntries = Get32u($dataPt, $pos);
+    if ($numEntries < 1 or $numEntries >= 0x100
+        or $numEntries * 12 + 132 > $dirLen)
+    {
+        $exifTool->Warn("Bad ICC_Profile table ($numEntries entries)");
+        return 0;
+    }
+
+    if ($verbose) {
+        $exifTool->VerboseDir('ICC_Profile', $numEntries, $dirLen);
+        my $fakeInfo = { Name=>'ProfileHeader', SubDirectory => { } };
+        $exifTool->VerboseInfo(undef, $fakeInfo);
+    }
+    # increment ICC dir count
+    my $dirCount = $exifTool->{DIR_COUNT}->{ICC} = ($exifTool->{DIR_COUNT}->{ICC} || 0) + 1;
+    $exifTool->{SET_GROUP1} = '+' . $dirCount if $dirCount > 1;
+    # process the header block
+    my %subdirInfo = (
+        Name     => 'ProfileHeader',
+        DataPt   => $dataPt,
+        DataLen  => $$dirInfo{DataLen},
+        DirStart => $dirStart,
+        DirLen   => 128,
+        Parent   => $$dirInfo{DirName},
+    );
+    my $newTagTable = GetTagTable('Image::ExifTool::ICC_Profile::Header');
+    $exifTool->ProcessDirectory(\%subdirInfo, $newTagTable);
+
+    $pos += 4;    # skip item count
+    my $index;
+    for ($index=0; $index<$numEntries; ++$index) {
+        my $tagID  = substr($$dataPt, $pos, 4);
+        my $offset = Get32u($dataPt, $pos + 4);
+        my $size   = Get32u($dataPt, $pos + 8);
+        $pos += 12;
+        my $tagInfo = $exifTool->GetTagInfo($tagTablePtr, $tagID);
+        # unknown tags aren't generated automatically by GetTagInfo()
+        # if the tagID's aren't numeric, so we must do this manually:
+        if (not $tagInfo and $exifTool->{OPTIONS}->{Unknown}) {
+            $tagInfo = { Unknown => 1 };
+            Image::ExifTool::AddTagToTable($tagTablePtr, $tagID, $tagInfo);
+        }
+        next unless defined $tagInfo;
+
+        if ($offset + $size > $dirLen) {
+            $exifTool->Warn("Bad ICC_Profile table (truncated)");
+            last;
+        }
+        my $valuePtr = $dirStart + $offset;
+
+        my $subdir = $$tagInfo{SubDirectory};
+        # format the value unless this is a subdirectory
+        my $value;
+        $value = FormatICCTag($dataPt, $valuePtr, $size) unless $subdir;
+        my $fmt;
+        $fmt = substr($$dataPt, $valuePtr, 4) if $size > 4;
+        $verbose and $exifTool->VerboseInfo($tagID, $tagInfo,
+            Table  => $tagTablePtr,
+            Index  => $index,
+            Value  => $value,
+            DataPt => $dataPt,
+            Size   => $size,
+            Start  => $valuePtr,
+            Format => "type '$fmt'",
+        );
+        if ($subdir) {
+            my $name = $$tagInfo{Name};
+            undef $newTagTable;
+            if ($$subdir{TagTable}) {
+                $newTagTable = GetTagTable($$subdir{TagTable});
+                unless ($newTagTable) {
+                    warn "Unknown tag table $$subdir{TagTable}\n";
+                    next;
+                }
+            } else {
+                warn "Must specify TagTable for SubDirectory $name\n";
+                next;
+            }
+            %subdirInfo = (
+                Name     => $name,
+                DataPt   => $dataPt,
+                DataPos  => $$dirInfo{DataPos},
+                DataLen  => $$dirInfo{DataLen},
+                DirStart => $valuePtr,
+                DirLen   => $size,
+                Parent   => $$dirInfo{DirName},
+            );
+            my $type = substr($$dataPt, $valuePtr, 4);
+            #### eval Validate ($type)
+            if (defined $$subdir{Validate} and not eval $$subdir{Validate}) {
+                $exifTool->Warn("Invalid $name data");
+            } else {
+                $exifTool->ProcessDirectory(\%subdirInfo, $newTagTable, $$subdir{ProcessProc});
+            }
+        } elsif (defined $value) {
+            $exifTool->FoundTag($tagInfo, $value);
+        } else {
+            $value = substr($$dataPt, $valuePtr, $size);
+            # treat unsupported formats as binary data
+            my $oldValueConv = $$tagInfo{ValueConv};
+            $$tagInfo{ValueConv} = '\$val' unless defined $$tagInfo{ValueConv};
+            $exifTool->FoundTag($tagInfo, $value);
+        }
+    }
+    delete $exifTool->{SET_GROUP1};
+    return 1;
+}
+
+
+1; # end
+
+
+__END__
+
+=head1 NAME
+
+Image::ExifTool::ICC_Profile - Read ICC Profile meta information
+
+=head1 SYNOPSIS
+
+This module is loaded automatically by Image::ExifTool when required.
+
+=head1 DESCRIPTION
+
+This module contains the definitions to read information from ICC profiles.
+ICC (International Color Consortium) profiles are used to translate color
+data created on one device into another device's native color space.
+
+=head1 AUTHOR
+
+Copyright 2003-2007, Phil Harvey (phil at owl.phy.queensu.ca)
+
+This library is free software; you can redistribute it and/or modify it
+under the same terms as Perl itself.
+
+=head1 REFERENCES
+
+=over 4
+
+=item L<http://www.color.org/icc_specs2.html>
+
+=item L<http://developer.apple.com/documentation/GraphicsImaging/Reference/ColorSync_Manager/ColorSync_Manager.pdf>
+
+=back
+
+=head1 SEE ALSO
+
+L<Image::ExifTool::TagNames/ICC_Profile Tags>,
+L<Image::ExifTool(3pm)|Image::ExifTool>
+
+=cut
Index: /gsdl/trunk/perllib/cpan/Image/ExifTool/ID3.pm
===================================================================
--- /gsdl/trunk/perllib/cpan/Image/ExifTool/ID3.pm	(revision 16842)
+++ /gsdl/trunk/perllib/cpan/Image/ExifTool/ID3.pm	(revision 16842)
@@ -0,0 +1,957 @@
+#------------------------------------------------------------------------------
+# File:         ID3.pm
+#
+# Description:  Read ID3 meta information
+#
+# Revisions:    09/12/2005 - P. Harvey Created
+#
+# References:   1) http://www.id3.org/
+#               2) http://www.mp3-tech.org/
+#------------------------------------------------------------------------------
+
+package Image::ExifTool::ID3;
+
+use strict;
+use vars qw($VERSION);
+use Image::ExifTool qw(:DataAccess :Utils);
+
+$VERSION = '1.11';
+
+sub ProcessID3v2($$$);
+sub ProcessPrivate($$$);
+
+# audio formats that we process after an ID3v2 header (in order)
+my @audioFormats = qw(APE MPC FLAC OGG MP3);
+
+# audio formats where the processing proc is in a different module
+my %audioModule = (
+    MP3 => 'ID3',
+    OGG => 'Vorbis',
+);
+
+my %warnedOnce;     # hash of warnings we issued
+
+# This table is just for documentation purposes
+%Image::ExifTool::ID3::Main = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData, # (not really)
+    NOTES => q{
+        ExifTool extracts ID3 information from MP3, MPEG, AIFF, OGG, FLAC, APE and
+        RealAudio files.
+    },
+    ID3v1 => {
+        Name => 'ID3v1',
+        SubDirectory => { TagTable => 'Image::ExifTool::ID3::v1' },
+    },
+    ID3v22 => {
+        Name => 'ID3v2_2',
+        SubDirectory => { TagTable => 'Image::ExifTool::ID3::v2_2' },
+    },
+    ID3v23 => {
+        Name => 'ID3v2_3',
+        SubDirectory => { TagTable => 'Image::ExifTool::ID3::v2_3' },
+    },
+    ID3v24 => {
+        Name => 'ID3v2_4',
+        SubDirectory => { TagTable => 'Image::ExifTool::ID3::v2_4' },
+    },
+);
+
+# Mapping for ID3v1 Genre numbers
+my %genre = (
+      0 => 'Blues',
+      1 => 'Classic Rock',
+      2 => 'Country',
+      3 => 'Dance',
+      4 => 'Disco',
+      5 => 'Funk',
+      6 => 'Grunge',
+      7 => 'Hip-Hop',
+      8 => 'Jazz',
+      9 => 'Metal',
+     10 => 'New Age',
+     11 => 'Oldies',
+     12 => 'Other',
+     13 => 'Pop',
+     14 => 'R&B',
+     15 => 'Rap',
+     16 => 'Reggae',
+     17 => 'Rock',
+     18 => 'Techno',
+     19 => 'Industrial',
+     20 => 'Alternative',
+     21 => 'Ska',
+     22 => 'Death Metal',
+     23 => 'Pranks',
+     24 => 'Soundtrack',
+     25 => 'Euro-Techno',
+     26 => 'Ambient',
+     27 => 'Trip-Hop',
+     28 => 'Vocal',
+     29 => 'Jazz+Funk',
+     30 => 'Fusion',
+     31 => 'Trance',
+     32 => 'Classical',
+     33 => 'Instrumental',
+     34 => 'Acid',
+     35 => 'House',
+     36 => 'Game',
+     37 => 'Sound Clip',
+     38 => 'Gospel',
+     39 => 'Noise',
+     40 => 'AlternRock',
+     41 => 'Bass',
+     42 => 'Soul',
+     43 => 'Punk',
+     44 => 'Space',
+     45 => 'Meditative',
+     46 => 'Instrumental Pop',
+     47 => 'Instrumental Rock',
+     48 => 'Ethnic',
+     49 => 'Gothic',
+     50 => 'Darkwave',
+     51 => 'Techno-Industrial',
+     52 => 'Electronic',
+     53 => 'Pop-Folk',
+     54 => 'Eurodance',
+     55 => 'Dream',
+     56 => 'Southern Rock',
+     57 => 'Comedy',
+     58 => 'Cult',
+     59 => 'Gangsta',
+     60 => 'Top 40',
+     61 => 'Christian Rap',
+     62 => 'Pop/Funk',
+     63 => 'Jungle',
+     64 => 'Native American',
+     65 => 'Cabaret',
+     66 => 'New Wave',
+     67 => 'Psychadelic',
+     68 => 'Rave',
+     69 => 'Showtunes',
+     70 => 'Trailer',
+     71 => 'Lo-Fi',
+     72 => 'Tribal',
+     73 => 'Acid Punk',
+     74 => 'Acid Jazz',
+     75 => 'Polka',
+     76 => 'Retro',
+     77 => 'Musical',
+     78 => 'Rock & Roll',
+     79 => 'Hard Rock',
+     # The following genres are Winamp extensions
+     80 => 'Folk',
+     81 => 'Folk-Rock',
+     82 => 'National Folk',
+     83 => 'Swing',
+     84 => 'Fast Fusion',
+     85 => 'Bebob',
+     86 => 'Latin',
+     87 => 'Revival',
+     88 => 'Celtic',
+     89 => 'Bluegrass',
+     90 => 'Avantgarde',
+     91 => 'Gothic Rock',
+     92 => 'Progressive Rock',
+     93 => 'Psychedelic Rock',
+     94 => 'Symphonic Rock',
+     95 => 'Slow Rock',
+     96 => 'Big Band',
+     97 => 'Chorus',
+     98 => 'Easy Listening',
+     99 => 'Acoustic',
+    100 => 'Humour',
+    101 => 'Speech',
+    102 => 'Chanson',
+    103 => 'Opera',
+    104 => 'Chamber Music',
+    105 => 'Sonata',
+    106 => 'Symphony',
+    107 => 'Booty Bass',
+    108 => 'Primus',
+    109 => 'Porn Groove',
+    110 => 'Satire',
+    111 => 'Slow Jam',
+    112 => 'Club',
+    113 => 'Tango',
+    114 => 'Samba',
+    115 => 'Folklore',
+    116 => 'Ballad',
+    117 => 'Power Ballad',
+    118 => 'Rhythmic Soul',
+    119 => 'Freestyle',
+    120 => 'Duet',
+    121 => 'Punk Rock',
+    122 => 'Drum Solo',
+    123 => 'Acapella',
+    124 => 'Euro-House',
+    125 => 'Dance Hall',
+    255 => 'None',
+    # ID3v2 adds some text short forms...
+    CR  => 'Cover',
+    RX  => 'Remix',
+);
+
+# Tags for ID3v1
+%Image::ExifTool::ID3::v1 = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    GROUPS => { 1 => 'ID3v1', 2 => 'Audio' },
+    PRIORITY => 0,  # let ID3v2 tags replace these if they come later
+    3 => {
+        Name => 'Title',
+        Format => 'string[30]',
+        ValueConv => 'Image::ExifTool::Latin2Charset($self,$val)',
+    },
+    33 => {
+        Name => 'Artist',
+        Groups => { 2 => 'Author' },
+        Format => 'string[30]',
+        ValueConv => 'Image::ExifTool::Latin2Charset($self,$val)',
+    },
+    63 => {
+        Name => 'Album',
+        Format => 'string[30]',
+        ValueConv => 'Image::ExifTool::Latin2Charset($self,$val)',
+    },
+    93 => {
+        Name => 'Year',
+        Groups => { 2 => 'Time' },
+        Format => 'string[4]',
+    },
+    97 => {
+        Name => 'Comment',
+        Format => 'string[30]',
+        ValueConv => 'Image::ExifTool::Latin2Charset($self,$val)',
+    },
+    127 => {
+        Name => 'Genre',
+        Notes => 'CR and RX are ID3v2 only',
+        Format => 'int8u',
+        PrintConv => \%genre,
+    },
+);
+
+# Tags for ID2v2.2
+%Image::ExifTool::ID3::v2_2 = (
+    PROCESS_PROC => \&Image::ExifTool::ID3::ProcessID3v2,
+    GROUPS => { 1 => 'ID3v2_2', 2 => 'Audio' },
+    NOTES => q{
+ExifTool extracts mainly text-based tags from ID3v2 information.  The tags
+in the tables below are those extracted by ExifTool, and don't represent a
+complete list of available ID3v2 tags.
+
+ID3 version 2.2 tags.  (These are the tags written by iTunes 5.0.)
+    },
+    CNT => 'PlayCounter',
+    COM => 'Comment',
+    IPL => 'InvolvedPeople',
+    PIC => {
+        Name => 'Picture',
+        Binary => 1,
+    },
+  # POP => 'Popularimeter',
+    TAL => 'Album',
+    TBP => 'BeatsPerMinute',
+    TCM => 'Composer',
+    TCO =>{
+        Name => 'Genre',
+        Notes => 'uses same lookup table as ID3v1 Genre',
+        PrintConv => 'Image::ExifTool::ID3::PrintGenre($val)',
+    },
+    TCP => 'Compilation', # not part of spec, but used by iTunes
+    TCR => { Name => 'Copyright', Groups => { 2 => 'Author' } },
+    TDA => { Name => 'Date', Groups => { 2 => 'Time' } },
+    TDY => 'PlaylistDelay',
+    TEN => 'EncodedBy',
+    TFT => 'FileType',
+    TIM => { Name => 'Time', Groups => { 2 => 'Time' } },
+    TKE => 'InitialKey',
+    TLA => 'Language',
+    TLE => 'Length',
+    TMT => 'Media',
+    TOA => { Name => 'OriginalArtist', Groups => { 2 => 'Author' } },
+    TOF => 'OriginalFilename',
+    TOL => 'OriginalLyricist',
+    TOR => 'OriginalReleaseYear',
+    TOT => 'OriginalAlbum',
+    TP1 => { Name => 'Artist', Groups => { 2 => 'Author' } },
+    TP2 => 'Band',
+    TP3 => 'Conductor',
+    TP4 => 'InterpretedBy',
+    TPA => 'PartOfSet',
+    TPB => 'Publisher',
+    TRC => 'ISRC', # (international standard recording code)
+    TRD => 'RecordingDates',
+    TRK => 'Track',
+    TSI => 'Size',
+    TSS => 'EncoderSettings',
+    TT1 => 'Grouping',
+    TT2 => 'Title',
+    TT3 => 'Subtitle',
+    TXT => 'Lyricist',
+    TXX => 'UserDefinedText',
+    TYE => { Name => 'Year', Groups => { 2 => 'Time' } },
+    ULT => 'Lyrics',
+    WAF => 'FileRUL',
+    WAR => { Name => 'ArtistURL', Groups => { 2 => 'Author' } },
+    WAS => 'SourceURL',
+    WCM => 'CommercialURL',
+    WCP => { Name => 'CopyrightURL', Groups => { 2 => 'Author' } },
+    WPB => 'PublisherURL',
+    WXX => 'UserDefinedURL',
+);
+
+# tags common to ID3v2.3 and ID3v2.4
+my %id3v2_common = (
+  # AENC => 'AudioEncryption', # Owner, preview start, preview length, encr data
+    APIC => {
+        Name => 'Picture',
+        Binary => 1,
+    },
+    COMM => 'Comment',
+  # COMR => 'Commercial',
+  # ENCR => 'EncryptionMethod',
+  # ETCO => 'EventTimingCodes',
+  # GEOB => 'GeneralEncapsulatedObject',
+  # GRID => 'GroupIdentification',
+  # LINK => 'LinkedInformation',
+    MCDI => { Name => 'MusicCDIdentifier', Binary => 1 },
+  # MLLT => 'MPEGLocationLookupTable',
+  # OWNE => 'Ownership', # enc(1), _price, 00, _date(8), Seller
+    PCNT => 'PlayCounter',
+  # POPM => 'Popularimeter', # _email, 00, rating(1), counter(4-N)
+  # POSS => 'PostSynchronization',
+    PRIV => {
+        Name => 'Private',
+        SubDirectory => { TagTable => 'Image::ExifTool::ID3::Private' },
+    },
+  # RBUF => 'RecommendedBufferSize',
+  # RVRB => 'Reverb',
+  # SYLT => 'SynchronizedLyricText',
+  # SYTC => 'SynchronizedTempoCodes',
+    TALB => 'Album',
+    TBPM => 'BeatsPerMinute',
+    TCOM => 'Composer',
+    TCON =>{
+        Name => 'Genre',
+        Notes => 'uses same lookup table as ID3v1 Genre',
+        PrintConv => 'Image::ExifTool::ID3::PrintGenre($val)',
+    },
+    TCOP => { Name => 'Copyright', Groups => { 2 => 'Author' } },
+    TDLY => 'PlaylistDelay',
+    TENC => 'EncodedBy',
+    TEXT => 'Lyricist',
+    TFLT => 'FileType',
+    TIT1 => 'Grouping',
+    TIT2 => 'Title',
+    TIT3 => 'Subtitle',
+    TKEY => 'InitialKey',
+    TLAN => 'Language',
+    TLEN => {
+        Name => 'Length',
+        ValueConv => '$val / 1000',
+        PrintConv => '"$val sec"',
+    },
+    TMED => 'Media',
+    TOAL => 'OriginalAlbum',
+    TOFN => 'OriginalFilename',
+    TOLY => 'OriginalLyricist',
+    TOPE => { Name => 'OriginalArtist', Groups => { 2 => 'Author' } },
+    TOWN => 'FileOwner',
+    TPE1 => { Name => 'Artist', Groups => { 2 => 'Author' } },
+    TPE2 => 'Band',
+    TPE3 => 'Conductor',
+    TPE4 => 'InterpretedBy',
+    TPOS => 'PartOfSet',
+    TPUB => 'Publisher',
+    TRCK => 'Track',
+    TRSN => 'InternetRadioStationName',
+    TRSO => 'InternetRadioStationOwner',
+    TSRC => 'ISRC', # (international standard recording code)
+    TSSE => 'EncoderSettings',
+    TXXX => 'UserDefinedText',
+  # UFID => 'UniqueFileID',
+    USER => 'TermsOfUse',
+    USLT => 'Lyrics',
+    WCOM => 'CommercialURL',
+    WCOP => 'CopyrightURL',
+    WOAF => 'FileRUL',
+    WOAR => { Name => 'ArtistURL', Groups => { 2 => 'Author' } },
+    WOAS => 'SourceURL',
+    WORS => 'InternetRadioStationURL',
+    WPAY => 'PaymentURL',
+    WPUB => 'PublisherURL',
+    WXXX => 'UserDefinedURL',
+);
+
+# Tags for ID3v2.3 (http://www.id3.org/id3v2.3.0)
+%Image::ExifTool::ID3::v2_3 = (
+    PROCESS_PROC => \&Image::ExifTool::ID3::ProcessID3v2,
+    GROUPS => { 1 => 'ID3v2_3', 2 => 'Audio' },
+    NOTES => 'ID3 version 2.3 tags',
+    %id3v2_common,  # include common tags
+  # EQUA => 'Equalization',
+    IPLS => 'InvolvedPeople',
+  # RVAD => 'RelativeVolumeAdjustment',
+    TDAT => { Name => 'Date', Groups => { 2 => 'Time' } },
+    TIME => { Name => 'Time', Groups => { 2 => 'Time' } },
+    TORY => 'OriginalReleaseYear',
+    TRDA => 'RecordingDates',
+    TSIZ => 'Size',
+    TYER => { Name => 'Year', Groups => { 2 => 'Time' } },
+);
+
+# Tags for ID3v2.4 (http://www.id3.org/id3v2.4.0-frames)
+%Image::ExifTool::ID3::v2_4 = (
+    PROCESS_PROC => \&Image::ExifTool::ID3::ProcessID3v2,
+    GROUPS => { 1 => 'ID3v2_4', 2 => 'Audio' },
+    NOTES => 'ID3 version 2.4 tags',
+    %id3v2_common,  # include common tags
+  # EQU2 => 'Equalization',
+  # RVA2 => 'RelativeVolumeAdjustment',
+  # SEEK => 'Seek',
+  # SIGN => 'Signature',
+    TDEN => { Name => 'EncodingTime',       Groups => { 2 => 'Time' } },
+    TDOR => { Name => 'OriginalReleaseTime',Groups => { 2 => 'Time' } },
+    TDRC => { Name => 'RecordingTime',      Groups => { 2 => 'Time' } },
+    TDRL => { Name => 'ReleaseTime',        Groups => { 2 => 'Time' } },
+    TDTG => { Name => 'TaggingTime',        Groups => { 2 => 'Time' } },
+    TIPL => 'InvolvedPeople',
+    TMCL => 'MusicianCredits',
+    TMOO => 'Mood',
+    TPRO => 'ProducedNotice',
+    TSOA => 'AlbumSortOrder',
+    TSOP => 'PerformerSortOrder',
+    TSOT => 'TitleSortOrder',
+    TSST => 'SetSubtitle',
+);
+
+# ID3 PRIV tags (ref PH)
+%Image::ExifTool::ID3::Private = (
+    PROCESS_PROC => \&Image::ExifTool::ID3::ProcessPrivate,
+    GROUPS => { 1 => 'ID3', 2 => 'Audio' },
+    NOTES => 'ID3 private (PRIV) tags.',
+    XMP => {
+        SubDirectory => {
+            DirName => 'XMP',
+            TagTable => 'Image::ExifTool::XMP::Main',
+        },
+    },
+    PeakValue => {
+        ValueConv => 'length($val)==4 ? unpack("V",$val) : \$val',
+    },
+    AverageLevel => {
+        ValueConv => 'length($val)==4 ? unpack("V",$val) : \$val',
+    },
+);
+
+# can't share tagInfo hashes between two tables, so we must make
+# copies of the necessary hashes
+{
+    my $tag;
+    foreach $tag (keys %id3v2_common) {
+        next unless ref $id3v2_common{$tag} eq 'HASH';
+        my %tagInfo = %{$id3v2_common{$tag}};
+        $Image::ExifTool::ID3::v2_4{$tag} = \%tagInfo;
+    }
+}
+
+#------------------------------------------------------------------------------
+# Process ID3 PRIV data
+# Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
+sub ProcessPrivate($$$)
+{
+    my ($exifTool, $dirInfo, $tagTablePtr) = @_;
+    my $dataPt = $$dirInfo{DataPt};
+    my ($tag, $start);
+    if ($$dataPt =~ /^(.*?)\0/) {
+        $tag = $1;
+        $start = length($tag) + 1;
+    } else {
+        $tag = '';
+        $start = 0;
+    }
+    unless ($$tagTablePtr{$tag}) {
+        $tag =~ tr{/ }{_}d; # translate '/' to '_' and remove spaces
+        $tag = 'private' unless $tag =~ /^[-\w]{1,24}$/;
+        unless ($$tagTablePtr{$tag}) {
+            Image::ExifTool::AddTagToTable($tagTablePtr, $tag,
+                { Name => ucfirst($tag), Binary => 1 });
+        }
+    }
+    my $key = $exifTool->HandleTag($tagTablePtr, $tag, undef,
+        Size  => length($$dataPt) - $start,
+        Start => $start,
+        DataPt => $dataPt,
+    );
+    # set group1 name
+    $exifTool->SetGroup1($key, $$exifTool{ID3_Ver}) if $key;
+}
+
+#------------------------------------------------------------------------------
+# Print ID3v2 Genre
+# Inputs: TCON or TCO frame data
+# Returns: Content type with decoded genre numbers
+sub PrintGenre($)
+{
+    my $val = shift;
+    # make sure that %genre has an entry for all numbers we are interested in
+    # (genre numbers are in brackets for ID3v2.2 and v2.3)
+    while ($val =~ /\((\d+)\)/g) {
+        $genre{$1} or $genre{$1} = "Unknown ($1)";
+    }
+    # (genre numbers are separated by nulls in ID3v2.4,
+    #  but nulls are converted to '/' by DecodeString())
+    while ($val =~ /(?:^|\/)(\d+)/g) {
+        $genre{$1} or $genre{$1} = "Unknown ($1)";
+    }
+    $val =~ s/\((\d+)\)/\($genre{$1}\)/g;
+    $val =~ s/(^|\/)(\d+)/$1$genre{$2}/g;
+    return $val;
+}
+
+#------------------------------------------------------------------------------
+# Decode ID3 string
+# Inputs: 0) ExifTool object reference, 1) string beginning with encoding byte
+# Returns: Decoded string in scalar context, or list of strings in list context
+sub DecodeString($$)
+{
+    my ($exifTool, $val) = @_;
+    return '' unless length $val;
+    my $enc = unpack('C', $val);
+    $val = substr($val, 1); # remove encoding byte
+    my @vals;
+    if ($enc == 0) {        # ISO 8859-1
+        $val =~ s/\0+$//;   # remove any null padding
+        $val = $exifTool->Latin2Charset($val);
+        @vals = split "\0", $val;
+    } elsif ($enc == 3) {   # UTF-8
+        $val =~ s/\0+$//;
+        $val = $exifTool->UTF82Charset($val);
+        @vals = split "\0", $val;
+    } elsif ($enc == 1 or $enc == 2) {  # UTF-16 with BOM, or UTF-16BE
+        my $bom = "\xfe\xff";
+        my %order = ( "\xfe\xff" => 'MM', "\xff\xfe", => 'II' );
+        for (;;) {
+            my $v;
+            # split string at null terminators on word boundaries
+            if ($val =~ s/((..)*?)\0\0//) {
+                $v = $1;
+            } else {
+                last unless length $val > 1;
+                $v = $val;
+                $val = '';
+            }
+            $bom = $1 if $v =~ s/^(\xfe\xff|\xff\xfe)//;
+            push @vals, $exifTool->Unicode2Charset($v, $order{$bom});
+        }
+    } else {
+        $val =~ s/\0+$//;
+        return "<Unknown encoding $enc> $val";
+    }
+    return @vals if wantarray;
+    return join('/',@vals);
+}
+
+#------------------------------------------------------------------------------
+# Issue one warning of each type
+# Inputs: 0) ExifTool object reference, 1) warning
+sub WarnOnce($$)
+{
+    my ($exifTool, $warn) = @_;
+    unless ($warnedOnce{$warn}) {
+        $warnedOnce{$warn} = 1;
+        $exifTool->Warn($warn);
+    }
+}
+#------------------------------------------------------------------------------
+# Process ID3v2 information
+# Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
+sub ProcessID3v2($$$)
+{
+    my ($exifTool, $dirInfo, $tagTablePtr) = @_;
+    my $dataPt  = $$dirInfo{DataPt};
+    my $offset  = $$dirInfo{DirStart};
+    my $size    = $$dirInfo{DirLen};
+    my $vers    = $$dirInfo{Version};
+    my $verbose = $exifTool->Options('Verbose');
+    my $len;    # frame data length
+
+    $verbose and $exifTool->VerboseDir($tagTablePtr->{GROUPS}->{1}, 0, $size);
+    undef %warnedOnce;
+
+    for (;;$offset+=$len) {
+        my ($id, $flags, $hi);
+        if ($vers < 0x0300) {
+            # version 2.2 frame header is 6 bytes
+            last if $offset + 6 > $size;
+            ($id, $hi, $len) = unpack("x${offset}a3Cn",$$dataPt);
+            last if $id eq "\0\0\0";
+            $len += $hi << 16;
+            $offset += 6;
+        } else {
+            # version 2.3/2.4 frame header is 10 bytes
+            last if $offset + 10 > $size;
+            ($id, $len, $flags) = unpack("x${offset}a4Nn",$$dataPt);
+            last if $id eq "\0\0\0\0";
+            $offset += 10;
+        }
+        last if $offset + $len > $size;
+        my $tagInfo = $exifTool->GetTagInfo($tagTablePtr, $id);
+        unless ($tagInfo) {
+            next unless $verbose or $exifTool->Options('Unknown');
+            $id =~ tr/-A-Za-z0-9_//dc;
+            $id = 'unknown' unless length $id;
+            unless ($$tagTablePtr{$id}) {
+                $tagInfo = { Name => "ID3_$id", Binary => 1 };
+                Image::ExifTool::AddTagToTable($tagTablePtr, $id, $tagInfo);
+            }
+        }
+        # decode v2.3 and v2.4 flags
+        my %flags;
+        if ($flags) {
+            if ($vers < 0x0400) {
+                # version 2.3 flags
+                $flags & 0x80 and $flags{Compress} = 1;
+                $flags & 0x40 and $flags{Encrypt}  = 1;
+                $flags & 0x20 and $flags{GroupID}  = 1;
+            } else {
+                # version 2.4 flags
+                $flags & 0x40 and $flags{GroupID}  = 1;
+                $flags & 0x08 and $flags{Compress} = 1;
+                $flags & 0x04 and $flags{Encrypt}  = 1;
+                $flags & 0x02 and $flags{Unsync}   = 1;
+                $flags & 0x01 and $flags{DataLen}  = 1;
+            }
+        }
+        if ($flags{Encrypt}) {
+            WarnOnce($exifTool, 'Encrypted frames currently not supported');
+            next;
+        }
+        # extract the value
+        my $val = substr($$dataPt, $offset, $len);
+
+        # handle the flags
+        if ($flags{Unsync}) {
+            # reverse the unsynchronization
+            $val =~ s/\xff\x00/\xff/g;
+        }
+        if ($flags{GroupID}) {
+            length($val) >= 1 or $exifTool->Warn("Short $id frame"), next;
+            # ignore the grouping identity byte
+            $val = substr($val, 1);
+        }
+        if ($flags{DataLen} or $flags{Compress}) {
+            length($val) >= 4 or $exifTool->Warn("Short $id frame"), next;
+            # ignore data length word
+            $val = substr($val, 4);
+        }
+        if ($flags{Compress}) {
+            if (eval 'require Compress::Zlib') {
+                my $inflate = Compress::Zlib::inflateInit();
+                my ($buff, $stat);
+                $inflate and ($buff, $stat) = $inflate->inflate($val);
+                if ($inflate and $stat == Compress::Zlib::Z_STREAM_END()) {
+                    $val = $buff;
+                } else {
+                    $exifTool->Warn("Error inflating $id frame");
+                    next;
+                }
+            } else {
+                WarnOnce($exifTool,'Install Compress::Zlib to decode compressed frames');
+                next;
+            }
+        }
+        $verbose and $exifTool->VerboseInfo($id, $tagInfo,
+            Table   => $tagTablePtr,
+            Value   => $val,
+            DataPt  => $dataPt,
+            DataPos => $$dirInfo{DataPos},
+            Size    => $len,
+            Start   => $offset,
+        );
+        next unless $tagInfo;
+#
+# decode data in this frame
+#
+        my $valLen = length($val);  # actual value length (after decompression, etc)
+        if ($id =~ /^(TXX|TXXX)$/) {
+            # two encoded strings separated by a null
+            my @vals = DecodeString($exifTool, $val);
+            foreach (0..1) { $vals[$_] = '' unless defined $vals[$_]; }
+            ($val = "($vals[0]) $vals[1]") =~ s/^\(\) //;
+        } elsif ($id =~ /^T/ or $id =~ /^(IPL|IPLS)$/) {
+            $val = DecodeString($exifTool, $val);
+        } elsif ($id =~ /^(WXX|WXXX)$/) {
+            # one encoded string and one Latin string separated by a null
+            my $enc = unpack('C', $val);
+            my $url;
+            if ($enc == 1 or $enc == 2) {
+                ($val, $url) = ($val =~ /^(.(?:..)*?)\0\0(.*)/);
+            } else {
+                ($val, $url) = ($val =~ /^(..*?)\0(.*)/);
+            }
+            unless (defined $val and defined $url) {
+                $exifTool->Warn("Invalid $id frame value");
+                next;
+            }               
+            $val = DecodeString($exifTool, $val);
+            $url =~ s/\0.*//;
+            $val = length($val) ? "($val) $url" : $url;
+        } elsif ($id =~ /^W/) {
+            $val =~ s/\0.*//;   # truncate at null
+        } elsif ($id =~ /^(COM|COMM|ULT|USLT)$/) {
+            $valLen > 4 or $exifTool->Warn("Short $id frame"), next;
+            substr($val, 1, 3) = '';    # remove language code
+            my @vals = DecodeString($exifTool, $val);
+            foreach (0..1) { $vals[$_] = '' unless defined $vals[$_]; }
+            $val = length($vals[0]) ? "($vals[0]) $vals[1]" : $vals[1];
+        } elsif ($id eq 'USER') {
+            $valLen > 4 or $exifTool->Warn('Short USER frame'), next;
+            substr($val, 1, 3) = '';    # remove language code
+            $val = DecodeString($exifTool, $val);
+        } elsif ($id =~ /^(CNT|PCNT)$/) {
+            $valLen >= 4 or $exifTool->Warn("Short $id frame"), next;
+            my $cnt = unpack('N', $val);
+            my $i;
+            for ($i=4; $i<$valLen; ++$i) {
+                $cnt = $cnt * 256 + unpack("x${i}C", $val);
+            }
+            $val = $cnt;
+        } elsif ($id =~ /^(PIC|APIC)$/) {
+            $valLen >= 4 or $exifTool->Warn("Short $id frame"), next;
+            my $enc = unpack('C', $val);
+            my $hdr = ($id eq 'PIC') ? '.{5}.*?\0' : '..*?\0..*?\0';
+            # remove header (encoding, image format or MIME type, picture type, description)
+            $val =~ s/$hdr//s or $exifTool->Warn("Invalid $id frame"), next;
+            $enc and $val =~ s/^\0//;   # remove 2nd terminator if Unicode encoding
+        } elsif ($id eq 'PRIV') {
+            # save version number to set group1 name for tag later
+            $exifTool->{ID3_Ver} = $tagTablePtr->{GROUPS}->{1};
+            $exifTool->HandleTag($tagTablePtr, $id, $val);
+            next;
+        } elsif (not $$tagInfo{Binary}) {
+            $exifTool->Warn("Don't know how to handle $id frame");
+            next;
+        }
+        $exifTool->FoundTag($tagInfo, $val);
+    }
+    undef %warnedOnce;
+}
+
+#------------------------------------------------------------------------------
+# Extract ID3 information from an audio file
+# Inputs: 0) ExifTool object reference, 1) dirInfo reference
+# Returns: 1 on success, 0 if this file didn't contain ID3 information
+# - also processes audio data if any ID3 information was found
+# - sets ExifTool DONE_ID3 to 1 when called, or to 2 if an ID3v1 trailer exists
+sub ProcessID3($$)
+{
+    my ($exifTool, $dirInfo) = @_;
+    
+    return 0 if $exifTool->{DONE_ID3};  # avoid infinite recursion
+    $exifTool->{DONE_ID3} = 1;
+
+    # allow this to be called with either RAF or DataPt
+    my $raf = $$dirInfo{RAF} || new File::RandomAccess($$dirInfo{DataPt});
+    my ($buff, %id3Header, %id3Trailer, $hBuff, $tBuff, $tBuffPos, $tagTablePtr);
+    my $rtnVal = 0;
+    my $hdrEnd = 0;
+
+    # read first 3 bytes of file
+    $raf->Seek(0, 0);
+    return 0 unless $raf->Read($buff, 3) == 3;
+#
+# identify ID3v2 header
+#
+    while ($buff =~ /^ID3/) {
+        $rtnVal = 1;
+        $raf->Read($hBuff, 7) == 7 or $exifTool->Warn('Short ID3 header'), last;
+        my ($vers, $flags, $size) = unpack('nCN', $hBuff);
+        $size & 0x80808080 and $exifTool->Warn('Invalid ID3 header'), last;
+        my $verStr = sprintf("2.%d.%d", $vers >> 8, $vers & 0xff);
+        if ($vers >= 0x0500) {
+            $exifTool->Warn("Unsupported ID3 version: $verStr");
+            last;
+        }
+        $size =  ($size & 0x0000007f) |
+                (($size & 0x00007f00) >> 1) |
+                (($size & 0x007f0000) >> 2) |
+                (($size & 0x7f000000) >> 3);
+        unless ($raf->Read($hBuff, $size) == $size) {
+            $exifTool->Warn('Truncated ID3 data');
+            last;
+        }
+        if ($flags & 0x80) {
+            # reverse the unsynchronization
+            $hBuff =~ s/\xff\x00/\xff/g;
+        }
+        my $pos = 10;
+        if ($flags & 0x40) {
+            # skip the extended header
+            $size >= 4 or $exifTool->Warn('Bad ID3 extended header'), last;
+            my $len = unpack('N', $hBuff);
+            if ($len > length($hBuff) - 4) {
+                $exifTool->Warn('Truncated ID3 extended header');
+                last;
+            }
+            $hBuff = substr($hBuff, $len + 4);
+            $pos += $len + 4;
+        }
+        if ($flags & 0x10) {
+            # ignore v2.4 footer (10 bytes long)
+            $raf->Seek(10, 1);
+        }
+        %id3Header = (
+            DataPt   => \$hBuff,
+            DataPos  => $pos,
+            DirStart => 0,
+            DirLen   => length($hBuff),
+            Version  => $vers,
+            DirName  => "ID3v$verStr",
+        );
+        if ($vers >= 0x0400) {
+            $tagTablePtr = GetTagTable('Image::ExifTool::ID3::v2_4');
+        } elsif ($vers >= 0x0300) {
+            $tagTablePtr = GetTagTable('Image::ExifTool::ID3::v2_3');
+        } else {
+            $tagTablePtr = GetTagTable('Image::ExifTool::ID3::v2_2');
+        }
+        $hdrEnd = $raf->Tell();
+        last;
+    }
+#
+# read ID3v1 trailer if it exists
+#
+    if ($raf->Seek(-128, 2) and $raf->Read($tBuff, 128) == 128 and $tBuff =~ /^TAG/) {
+        $exifTool->{DONE_ID3} = 2;  # set to 2 as flag that trailer exists
+        %id3Trailer = (
+            DataPt   => \$tBuff,
+            DataPos  => $raf->Tell() - 128,
+            DirStart => 0,
+            DirLen   => length($tBuff),
+        );
+        $rtnVal = 1;
+    }
+#
+# process the the information
+#
+    if ($rtnVal) {
+        # first process audio data if it exists
+        if ($$dirInfo{RAF}) {
+            my $type = $exifTool->{FILE_TYPE};  # save file type
+            # check current file type first
+            my @types = grep /^$type$/, @audioFormats;
+            push @types, grep(!/^$type$/, @audioFormats);
+            my $oldType = $exifTool->{FILE_TYPE};
+            foreach $type (@types) {
+                # seek to end of ID3 header
+                $raf->Seek($hdrEnd, 0);
+                # set type for this file if we are successful
+                $exifTool->{FILE_TYPE} = $type;
+                my $module = $audioModule{$type} || $type;
+                require "Image/ExifTool/$module.pm" or next;
+                my $func = "Image::ExifTool::${module}::Process$type";
+                # process the file
+                no strict 'refs';
+                &$func($exifTool, $dirInfo) and last;
+                use strict 'refs';
+            }
+            $exifTool->{FILE_TYPE} = $type;     # restore original file type
+        }
+        # set file type to MP3 if we didn't find audio data
+        $exifTool->SetFileType('MP3') unless $exifTool->{VALUE}->{FileType};
+        # process ID3v2 header if it exists
+        if (%id3Header) {
+            $exifTool->VPrint(0, "$id3Header{DirName}:\n");
+            $exifTool->ProcessDirectory(\%id3Header, $tagTablePtr);
+        }
+        # process ID3v1 trailer if it exists
+        if (%id3Trailer) {
+            $exifTool->VPrint(0, "ID3v1:\n");
+            SetByteOrder('MM');
+            $tagTablePtr = GetTagTable('Image::ExifTool::ID3::v1');
+            $exifTool->ProcessDirectory(\%id3Trailer, $tagTablePtr);
+        }
+    }
+    # return file pointer to start of file to read audio data if necessary
+    $raf->Seek(0, 0);
+    return $rtnVal;
+}
+
+#------------------------------------------------------------------------------
+# Extract ID3 information from an MP3 audio file
+# Inputs: 0) ExifTool object reference, 1) dirInfo reference
+# Returns: 1 on success, 0 if this wasn't a valid MP3 file
+sub ProcessMP3($$)
+{
+    my ($exifTool, $dirInfo) = @_;
+
+    # must first check for leading/trailing ID3 information
+    unless ($exifTool->{DONE_ID3}) {
+        require Image::ExifTool::ID3;
+        Image::ExifTool::ID3::ProcessID3($exifTool, $dirInfo) and return 1;
+    }
+    my $raf = $$dirInfo{RAF};
+    my $rtnVal = 0;
+    my $buff;
+#
+# extract information from first audio/video frame headers
+# (if found in the first 256 bytes)
+#
+    if ($raf->Read($buff, 256)) {
+        require Image::ExifTool::MPEG;
+        if ($buff =~ /\0\0\x01(\xb3|\xc0)/) {
+            # look for A/V headers in first 64kB
+            my $buf2;
+            $raf->Read($buf2, 65280) and $buff .= $buf2;
+            $rtnVal = 1 if Image::ExifTool::MPEG::ProcessMPEGAudioVideo($exifTool, \$buff);
+        } else {
+            # look for audio frame sync in first 256 bytes
+            $rtnVal = 1 if Image::ExifTool::MPEG::ProcessMPEGAudio($exifTool, \$buff);
+        }
+    }
+    return $rtnVal;
+}
+
+1;  # end
+
+__END__
+
+=head1 NAME
+
+Image::ExifTool::ID3 - Read ID3 meta information
+
+=head1 SYNOPSIS
+
+This module is used by Image::ExifTool
+
+=head1 DESCRIPTION
+
+This module contains definitions required by Image::ExifTool to extract ID3
+information from audio files.  ID3 information is found in MP3 and various
+other types of audio files.
+
+=head1 AUTHOR
+
+Copyright 2003-2007, Phil Harvey (phil at owl.phy.queensu.ca)
+
+This library is free software; you can redistribute it and/or modify it
+under the same terms as Perl itself.
+
+=head1 REFERENCES
+
+=over 4
+
+=item L<http://www.id3.org/>
+
+=item L<http://www.mp3-tech.org/>
+
+=back
+
+=head1 SEE ALSO
+
+L<Image::ExifTool::TagNames/ID3 Tags>,
+L<Image::ExifTool(3pm)|Image::ExifTool>
+
+=cut
+
Index: /gsdl/trunk/perllib/cpan/Image/ExifTool/IPTC.pm
===================================================================
--- /gsdl/trunk/perllib/cpan/Image/ExifTool/IPTC.pm	(revision 16842)
+++ /gsdl/trunk/perllib/cpan/Image/ExifTool/IPTC.pm	(revision 16842)
@@ -0,0 +1,1104 @@
+#------------------------------------------------------------------------------
+# File:         IPTC.pm
+#
+# Description:  Read IPTC meta information
+#
+# Revisions:    Jan. 08/03 - P. Harvey Created
+#               Feb. 05/04 - P. Harvey Added support for records other than 2
+#
+# References:   1) http://www.iptc.org/IIM/
+#------------------------------------------------------------------------------
+
+package Image::ExifTool::IPTC;
+
+use strict;
+use vars qw($VERSION $AUTOLOAD %iptcCharset);
+
+$VERSION = '1.23';
+
+%iptcCharset = (
+    "\x1b%G"  => 'UTF8',
+   # don't translate these (at least until we handle ISO 2022 shift codes)
+   # because the sets are only designated and not invoked 
+   # "\x1b,A"  => 'Latin',  # G0 = ISO 8859-1 (similar to Latin1, but codes 0x80-0x9f are missing)
+   # "\x1b-A"  => 'Latin',  # G1     "
+   # "\x1b.A"  => 'Latin',  # G2
+   # "\x1b/A"  => 'Latin',  # G3
+);
+
+sub ProcessIPTC($$$);
+sub WriteIPTC($$$);
+sub CheckIPTC($$$);
+sub PrintCodedCharset($);
+sub PrintInvCodedCharset($);
+
+my %fileFormat = (
+    0 => 'No ObjectData',
+    1 => 'IPTC-NAA Digital Newsphoto Parameter Record',
+    2 => 'IPTC7901 Recommended Message Format',
+    3 => 'Tagged Image File Format (Adobe/Aldus Image data)',
+    4 => 'Illustrator (Adobe Graphics data)',
+    5 => 'AppleSingle (Apple Computer Inc)',
+    6 => 'NAA 89-3 (ANPA 1312)',
+    7 => 'MacBinary II',
+    8 => 'IPTC Unstructured Character Oriented File Format (UCOFF)',
+    9 => 'United Press International ANPA 1312 variant',
+    10 => 'United Press International Down-Load Message',
+    11 => 'JPEG File Interchange (JFIF)',
+    12 => 'Photo-CD Image-Pac (Eastman Kodak)',
+    13 => 'Bit Mapped Graphics File [.BMP] (Microsoft)',
+    14 => 'Digital Audio File [.WAV] (Microsoft & Creative Labs)',
+    15 => 'Audio plus Moving Video [.AVI] (Microsoft)',
+    16 => 'PC DOS/Windows Executable Files [.COM][.EXE]',
+    17 => 'Compressed Binary File [.ZIP] (PKWare Inc)',
+    18 => 'Audio Interchange File Format AIFF (Apple Computer Inc)',
+    19 => 'RIFF Wave (Microsoft Corporation)',
+    20 => 'Freehand (Macromedia/Aldus)',
+    21 => 'Hypertext Markup Language [.HTML] (The Internet Society)',
+    22 => 'MPEG 2 Audio Layer 2 (Musicom), ISO/IEC',
+    23 => 'MPEG 2 Audio Layer 3, ISO/IEC',
+    24 => 'Portable Document File [.PDF] Adobe',
+    25 => 'News Industry Text Format (NITF)',
+    26 => 'Tape Archive [.TAR]',
+    27 => 'Tidningarnas Telegrambyra NITF version (TTNITF DTD)',
+    28 => 'Ritzaus Bureau NITF version (RBNITF DTD)',
+    29 => 'Corel Draw [.CDR]',
+);
+
+# main IPTC tag table
+# Note: ALL entries in main IPTC table (except PROCESS_PROC) must be SubDirectory
+# entries, each specifying a TagTable.
+%Image::ExifTool::IPTC::Main = (
+    GROUPS => { 2 => 'Image' },
+    PROCESS_PROC => \&ProcessIPTC,
+    WRITE_PROC => \&WriteIPTC,
+    1 => {
+        Name => 'IPTCEnvelope',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::IPTC::EnvelopeRecord',
+        },
+    },
+    2 => {
+        Name => 'IPTCApplication',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::IPTC::ApplicationRecord',
+        },
+    },
+    3 => {
+        Name => 'IPTCNewsPhoto',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::IPTC::NewsPhoto',
+        },
+    },
+    7 => {
+        Name => 'IPTCPreObjectData',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::IPTC::PreObjectData',
+        },
+    },
+    8 => {
+        Name => 'IPTCObjectData',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::IPTC::ObjectData',
+        },
+    },
+    9 => {
+        Name => 'IPTCPostObjectData',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::IPTC::PostObjectData',
+        },
+    },
+);
+
+# Record 1 -- EnvelopeRecord
+%Image::ExifTool::IPTC::EnvelopeRecord = (
+    GROUPS => { 2 => 'Other' },
+    WRITE_PROC => \&WriteIPTC,
+    CHECK_PROC => \&CheckIPTC,
+    WRITABLE => 1,
+    0 => {
+        Name => 'EnvelopeRecordVersion',
+        Format => 'int16u',
+    },
+    5 => {
+        Name => 'Destination',
+        Flags => 'List',
+        Groups => { 2 => 'Location' },
+        Format => 'string[0,1024]',
+    },
+    20 => {
+        Name => 'FileFormat',
+        Groups => { 2 => 'Image' },
+        Format => 'int16u',
+        PrintConv => \%fileFormat,
+    },
+    22 => {
+        Name => 'FileVersion',
+        Groups => { 2 => 'Image' },
+        Format => 'int16u',
+    },
+    30 => {
+        Name => 'ServiceIdentifier',
+        Format => 'string[0,10]',
+    },
+    40 => {
+        Name => 'EnvelopeNumber',
+        Format => 'digits[8]',
+    },
+    50 => {
+        Name => 'ProductID',
+        Flags => 'List',
+        Format => 'string[0,32]',
+    },
+    60 => {
+        Name => 'EnvelopePriority',
+        Format => 'digits[1]',
+    },
+    70 => {
+        Name => 'DateSent',
+        Groups => { 2 => 'Time' },
+        Format => 'digits[8]',
+        Shift => 'Time',
+        ValueConv => 'Image::ExifTool::Exif::ExifDate($val)',
+        ValueConvInv => 'Image::ExifTool::IPTC::IptcDate($val)',
+    },
+    80 => {
+        Name => 'TimeSent',
+        Groups => { 2 => 'Time' },
+        Format => 'string[11]',
+        Shift => 'Time',
+        ValueConv => 'Image::ExifTool::Exif::ExifTime($val)',
+        ValueConvInv => 'Image::ExifTool::IPTC::IptcTime($val)',
+    },
+    90 => {
+        Name => 'CodedCharacterSet',
+        Notes => q{
+            values are entered in the form "ESC X Y[, ...]".  The escape sequence for
+            UTF-8 character coding is "ESC % G", but this is displayed as "UTF8" for
+            convenience.  Either string may be used when writing.  The value of this tag
+            affects the decoding of string values in the Application and NewsPhoto
+            records
+        },
+        Format => 'string[0,32]',
+        # convert ISO 2022 escape sequences to a more readable format
+        PrintConv => \&PrintCodedCharset,
+        PrintConvInv => \&PrintInvCodedCharset,
+    },
+    100 => {
+        Name => 'UniqueObjectName',
+        Format => 'string[14,80]',
+    },
+    120 => {
+        Name => 'ARMIdentifier',
+        Format => 'int16u',
+    },
+    122 => {
+        Name => 'ARMVersion',
+        Format => 'int16u',
+    },
+);
+
+# Record 2 -- ApplicationRecord
+%Image::ExifTool::IPTC::ApplicationRecord = (
+    GROUPS => { 2 => 'Other' },
+    WRITE_PROC => \&WriteIPTC,
+    CHECK_PROC => \&CheckIPTC,
+    WRITABLE => 1,
+    0 => {
+        Name => 'ApplicationRecordVersion',
+        Format => 'int16u',
+    },
+    3 => {
+        Name => 'ObjectTypeReference',
+        Format => 'string[3,67]',
+    },
+    4 => {
+        Name => 'ObjectAttributeReference',
+        Flags => 'List',
+        Format => 'string[4,68]',
+    },
+    5 => {
+        Name => 'ObjectName',
+        Format => 'string[0,64]',
+    },
+    7 => {
+        Name => 'EditStatus',
+        Format => 'string[0,64]',
+    },
+    8 => {
+        Name => 'EditorialUpdate',
+        Format => 'digits[2]',
+    },
+    10 => {
+        Name => 'Urgency',
+        Format => 'digits[1]',
+    },
+    12 => {
+        Name => 'SubjectReference',
+        Flags => 'List',
+        Format => 'string[13,236]',
+    },
+    15 => {
+        Name => 'Category',
+        Format => 'string[0,3]',
+    },
+    20 => {
+        Name => 'SupplementalCategories',
+        Flags => 'List',
+        Format => 'string[0,32]',
+    },
+    22 => {
+        Name => 'FixtureIdentifier',
+        Format => 'string[0,32]',
+    },
+    25 => {
+        Name => 'Keywords',
+        Flags => 'List',
+        Format => 'string[0,64]',
+    },
+    26 => {
+        Name => 'ContentLocationCode',
+        Flags => 'List',
+        Groups => { 2 => 'Location' },
+        Format => 'string[3]',
+    },
+    27 => {
+        Name => 'ContentLocationName',
+        Flags => 'List',
+        Groups => { 2 => 'Location' },
+        Format => 'string[0,64]',
+    },
+    30 => {
+        Name => 'ReleaseDate',
+        Groups => { 2 => 'Time' },
+        Format => 'digits[8]',
+        Shift => 'Time',
+        ValueConv => 'Image::ExifTool::Exif::ExifDate($val)',
+        ValueConvInv => 'Image::ExifTool::IPTC::IptcDate($val)',
+    },
+    35 => {
+        Name => 'ReleaseTime',
+        Groups => { 2 => 'Time' },
+        Format => 'string[11]',
+        Shift => 'Time',
+        ValueConv => 'Image::ExifTool::Exif::ExifTime($val)',
+        ValueConvInv => 'Image::ExifTool::IPTC::IptcTime($val)',
+    },
+    37 => {
+        Name => 'ExpirationDate',
+        Groups => { 2 => 'Time' },
+        Format => 'digits[8]',
+        Shift => 'Time',
+        ValueConv => 'Image::ExifTool::Exif::ExifDate($val)',
+        ValueConvInv => 'Image::ExifTool::IPTC::IptcDate($val)',
+    },
+    38 => {
+        Name => 'ExpirationTime',
+        Groups => { 2 => 'Time' },
+        Format => 'string[11]',
+        Shift => 'Time',
+        ValueConv => 'Image::ExifTool::Exif::ExifTime($val)',
+        ValueConvInv => 'Image::ExifTool::IPTC::IptcTime($val)',
+    },
+    40 => {
+        Name => 'SpecialInstructions',
+        Format => 'string[0,256]',
+    },
+    42 => {
+        Name => 'ActionAdvised',
+        Format => 'digits[2]',
+        PrintConv => {
+            '' => '',
+            '01' => 'Object Kill',
+            '02' => 'Object Replace',
+            '03' => 'Ojbect Append',
+            '04' => 'Object Reference',
+        },
+    },
+    45 => {
+        Name => 'ReferenceService',
+        Flags => 'List',
+        Format => 'string[0,10]',
+    },
+    47 => {
+        Name => 'ReferenceDate',
+        Groups => { 2 => 'Time' },
+        Flags => 'List',
+        Format => 'digits[8]',
+        Shift => 'Time',
+        ValueConv => 'Image::ExifTool::Exif::ExifDate($val)',
+        ValueConvInv => 'Image::ExifTool::IPTC::IptcDate($val)',
+    },
+    50 => {
+        Name => 'ReferenceNumber',
+        Flags => 'List',
+        Format => 'digits[8]',
+    },
+    55 => {
+        Name => 'DateCreated',
+        Groups => { 2 => 'Time' },
+        Format => 'digits[8]',
+        Shift => 'Time',
+        ValueConv => 'Image::ExifTool::Exif::ExifDate($val)',
+        ValueConvInv => 'Image::ExifTool::IPTC::IptcDate($val)',
+    },
+    60 => {
+        Name => 'TimeCreated',
+        Groups => { 2 => 'Time' },
+        Format => 'string[11]',
+        Shift => 'Time',
+        ValueConv => 'Image::ExifTool::Exif::ExifTime($val)',
+        ValueConvInv => 'Image::ExifTool::IPTC::IptcTime($val)',
+    },
+    62 => {
+        Name => 'DigitalCreationDate',
+        Groups => { 2 => 'Time' },
+        Format => 'digits[8]',
+        Shift => 'Time',
+        ValueConv => 'Image::ExifTool::Exif::ExifDate($val)',
+        ValueConvInv => 'Image::ExifTool::IPTC::IptcDate($val)',
+    },
+    63 => {
+        Name => 'DigitalCreationTime',
+        Groups => { 2 => 'Time' },
+        Format => 'string[11]',
+        Shift => 'Time',
+        ValueConv => 'Image::ExifTool::Exif::ExifTime($val)',
+        ValueConvInv => 'Image::ExifTool::IPTC::IptcTime($val)',
+    },
+    65 => {
+        Name => 'OriginatingProgram',
+        Format => 'string[0,32]',
+    },
+    70 => {
+        Name => 'ProgramVersion',
+        Format => 'string[0,10]',
+    },
+    75 => {
+        Name => 'ObjectCycle',
+        Format => 'string[1]',
+        PrintConv => {
+            'a' => 'Morning',
+            'p' => 'Evening',
+            'b' => 'Both Morning and Evening',
+        },
+    },
+    80 => {
+        Name => 'By-line',
+        Flags => 'List',
+        Format => 'string[0,32]',
+        Groups => { 2 => 'Author' },
+    },
+    85 => {
+        Name => 'By-lineTitle',
+        Flags => 'List',
+        Format => 'string[0,32]',
+        Groups => { 2 => 'Author' },
+    },
+    90 => {
+        Name => 'City',
+        Format => 'string[0,32]',
+        Groups => { 2 => 'Location' },
+    },
+    92 => {
+        Name => 'Sub-location',
+        Format => 'string[0,32]',
+        Groups => { 2 => 'Location' },
+    },
+    95 => {
+        Name => 'Province-State',
+        Format => 'string[0,32]',
+        Groups => { 2 => 'Location' },
+    },
+    100 => {
+        Name => 'Country-PrimaryLocationCode',
+        Format => 'string[3]',
+        Groups => { 2 => 'Location' },
+    },
+    101 => {
+        Name => 'Country-PrimaryLocationName',
+        Format => 'string[0,64]',
+        Groups => { 2 => 'Location' },
+    },
+    103 => {
+        Name => 'OriginalTransmissionReference',
+        Format => 'string[0,32]',
+    },
+    105 => {
+        Name => 'Headline',
+        Format => 'string[0,256]',
+    },
+    110 => {
+        Name => 'Credit',
+        Groups => { 2 => 'Author' },
+        Format => 'string[0,32]',
+    },
+    115 => {
+        Name => 'Source',
+        Groups => { 2 => 'Author' },
+        Format => 'string[0,32]',
+    },
+    116 => {
+        Name => 'CopyrightNotice',
+        Groups => { 2 => 'Author' },
+        Format => 'string[0,128]',
+    },
+    118 => {
+        Name => 'Contact',
+        Flags => 'List',
+        Groups => { 2 => 'Author' },
+        Format => 'string[0,128]',
+    },
+    120 => {
+        Name => 'Caption-Abstract',
+        Format => 'string[0,2000]',
+    },
+    121 => { # (format not certain)
+        Name => 'LocalCaption',
+        Format => 'string[0,256]',
+        Notes => q{
+            I haven't found a reference for the format of tags 121, 184-188 and
+            225-232, so I have just make them writable as strings with
+            reasonable length.  Beware that if this is wrong, other utilities
+            won't be able to read these tags as written by ExifTool.
+        },
+    },
+    122 => {
+        Name => 'Writer-Editor',
+        Flags => 'List',
+        Groups => { 2 => 'Author' },
+        Format => 'string[0,32]',
+    },
+    125 => {
+        Name => 'RasterizedCaption',
+        Format => 'string[7360]',
+        Binary => 1,
+    },
+    130 => {
+        Name => 'ImageType',
+        Groups => { 2 => 'Image' },
+        Format => 'string[2]',
+    },
+    131 => {
+        Name => 'ImageOrientation',
+        Groups => { 2 => 'Image' },
+        Format => 'string[1]',
+        PrintConv => {
+            P => 'Portrait',
+            L => 'Landscape',
+            S => 'Square',
+        },
+    },
+    135 => {
+        Name => 'LanguageIdentifier',
+        Format => 'string[2,3]',
+    },
+    150 => {
+        Name => 'AudioType',
+        Format => 'string[2]',
+        PrintConv => {
+            '1A' => 'Mono Actuality',
+            '2A' => 'Stereo Actuality',
+            '1C' => 'Mono Question and Answer Session',
+            '2C' => 'Stereo Question and Answer Session',
+            '1M' => 'Mono Music',
+            '2M' => 'Stereo Music',
+            '1Q' => 'Mono Response to a Question',
+            '2Q' => 'Stereo Response to a Question',
+            '1R' => 'Mono Raw Sound',
+            '2R' => 'Stereo Raw Sound',
+            '1S' => 'Mono Scener',
+            '2S' => 'Stereo Scener',
+            '0T' => 'Text Only',
+            '1V' => 'Mono Voicer',
+            '2V' => 'Stereo Voicer',
+            '1W' => 'Mono Wrap',
+            '2W' => 'Stereo Wrap',
+        },
+    },
+    151 => {
+        Name => 'AudioSamplingRate',
+        Format => 'digits[6]',
+    },
+    152 => {
+        Name => 'AudioSamplingResolution',
+        Format => 'digits[2]',
+    },
+    153 => {
+        Name => 'AudioDuration',
+        Format => 'digits[6]',
+    },
+    154 => {
+        Name => 'AudioOutcue',
+        Format => 'string[0,64]',
+    },
+    184 => { # (format not certain)
+        Name => 'JobID',
+        Format => 'string[0,64]',
+    },
+    185 => { # (format not certain)
+        Name => 'MasterDocumentID',
+        Format => 'string[0,256]',
+    },
+    186 => { # (format not certain)
+        Name => 'ShortDocumentID',
+        Format => 'string[0,64]',
+    },
+    187 => { # (format not certain)
+        Name => 'UniqueDocumentID',
+        Format => 'string[0,128]',
+    },
+    188 => { # (format not certain)
+        Name => 'OwnerID',
+        Format => 'string[0,128]',
+    },
+    200 => {
+        Name => 'ObjectPreviewFileFormat',
+        Groups => { 2 => 'Image' },
+        Format => 'int16u',
+        PrintConv => \%fileFormat,
+    },
+    201 => {
+        Name => 'ObjectPreviewFileVersion',
+        Groups => { 2 => 'Image' },
+        Format => 'int16u',
+    },
+    202 => {
+        Name => 'ObjectPreviewData',
+        Groups => { 2 => 'Image' },
+        Format => 'string[0,256000]',
+        Binary => 1,
+    },
+    221 => {
+        Name => 'Prefs',
+        Groups => { 2 => 'Image' },
+        Format => 'string[0,64]',
+        Notes => 'PhotoMechanic preferences',
+        PrintConv => q{
+            $val =~ s[\s*(\d+):\s*(\d+):\s*(\d+):\s*(\S*)]
+                     [Tagged:$1, ColorClass:$2, Rating:$3, FrameNum:$4];
+            return $val;
+        },
+        PrintConvInv => q{
+            $val =~ s[Tagged:\s*(\d+).*ColorClass:\s*(\d+).*Rating:\s*(\d+).*FrameNum:\s*(\S*)]
+                     [$1:$2:$3:$4]is;
+            return $val;
+        },
+    },
+    225 => { # (format not certain)
+        Name => 'ClassifyState',
+        Format => 'string[0,64]',
+    },
+    228 => { # (format not certain)
+        Name => 'SimilarityIndex',
+        Format => 'string[0,32]',
+    },
+    230 => { # (format not certain)
+        Name => 'DocumentNotes',
+        Format => 'string[0,1024]',
+    },
+    231 => { # (format not certain)
+        Name => 'DocumentHistory',
+        Format => 'string[0,256]',
+    },
+    232 => { # (format not certain)
+        Name => 'ExifCameraInfo',
+        Format => 'string[0,4096]',
+    },
+);
+
+# Record 3 -- News photo
+%Image::ExifTool::IPTC::NewsPhoto = (
+    GROUPS => { 2 => 'Image' },
+    WRITE_PROC => \&WriteIPTC,
+    CHECK_PROC => \&CheckIPTC,
+    WRITABLE => 1,
+    0 => {
+        Name => 'NewsPhotoVersion',
+        Format => 'int16u',
+    },
+    10 => {
+        Name => 'IPTCPictureNumber',
+        Format => 'string[16]',
+        Notes => '4 numbers: 1-Manufacturer ID, 2-Equipment ID, 3-Date, 4-Sequence',
+        PrintConv => 'Image::ExifTool::IPTC::ConvertPictureNumber($val)',
+        PrintConvInv => 'Image::ExifTool::IPTC::InvConvertPictureNumber($val)',
+    },
+    20 => {
+        Name => 'IPTCImageWidth',
+        Format => 'int16u',
+    },
+    30 => {
+        Name => 'IPTCImageHeight',
+        Format => 'int16u',
+    },
+    40 => {
+        Name => 'IPTCPixelWidth',
+        Format => 'int16u',
+    },
+    50 => {
+        Name => 'IPTCPixelHeight',
+        Format => 'int16u',
+    },
+    55 => {
+        Name => 'SupplementalType',
+        Format => 'int8u',
+        PrintConv => {
+            0 => 'Main Image',
+            1 => 'Reduced Resolution Image',
+            2 => 'Logo',
+            3 => 'Rasterized Caption',
+        },
+    },
+    60 => {
+        Name => 'ColorRepresentation',
+        Format => 'int16u',
+        PrintHex => 1,
+        PrintConv => {
+            0x000 => 'No Image, Single Frame',
+            0x100 => 'Monochrome, Single Frame',
+            0x300 => '3 Components, Single Frame',
+            0x301 => '3 Components, Frame Sequential in Multiple Objects',
+            0x302 => '3 Components, Frame Sequential in One Object',
+            0x303 => '3 Components, Line Sequential',
+            0x304 => '3 Components, Pixel Sequential',
+            0x305 => '3 Components, Special Interleaving',
+            0x400 => '4 Components, Single Frame',
+            0x401 => '4 Components, Frame Sequential in Multiple Objects',
+            0x402 => '4 Components, Frame Sequential in One Object',
+            0x403 => '4 Components, Line Sequential',
+            0x404 => '4 Components, Pixel Sequential',
+            0x405 => '4 Components, Special Interleaving',
+        },
+    },
+    64 => {
+        Name => 'InterchangeColorSpace',
+        Format => 'int8u',
+        PrintConv => {
+            1 => 'X,Y,Z CIE',
+            2 => 'RGB SMPTE',
+            3 => 'Y,U,V (K) (D65)',
+            4 => 'RGB Device Dependent',
+            5 => 'CMY (K) Device Dependent',
+            6 => 'Lab (K) CIE',
+            7 => 'YCbCr',
+            8 => 'sRGB',
+        },
+    },
+    65 => {
+        Name => 'ColorSequence',
+        Format => 'int8u',
+    },
+    66 => {
+        Name => 'ICC_Profile',
+        # ...could add SubDirectory support to read into this (if anybody cares)
+        Writable => 0,
+        Binary => 1,
+    },
+    70 => {
+        Name => 'ColorCalibrationMatrix',
+        Writable => 0,
+        Binary => 1,
+    },
+    80 => {
+        Name => 'LookupTable',
+        Writable => 0,
+        Binary => 1,
+    },
+    84 => {
+        Name => 'NumIndexEntries',
+        Format => 'int16u',
+    },
+    85 => {
+        Name => 'ColorPalette',
+        Writable => 0,
+        Binary => 1,
+    },
+    86 => {
+        Name => 'IPTCBitsPerSample',
+        Format => 'int8u',
+    },
+    90 => {
+        Name => 'SampleStructure',
+        Format => 'int8u',
+        PrintConv => {
+            0 => 'OrthogonalConstangSampling',
+            1 => 'Orthogonal4-2-2Sampling',
+            2 => 'CompressionDependent',
+        },
+    },
+    100 => {
+        Name => 'ScanningDirection',
+        Format => 'int8u',
+        PrintConv => {
+            0 => 'L-R, Top-Bottom',
+            1 => 'R-L, Top-Bottom',
+            2 => 'L-R, Bottom-Top',
+            3 => 'R-L, Bottom-Top',
+            4 => 'Top-Bottom, L-R',
+            5 => 'Bottom-Top, L-R',
+            6 => 'Top-Bottom, R-L',
+            7 => 'Bottom-Top, R-L',
+        },
+    },
+    102 => {
+        Name => 'IPTCImageRotation',
+        Format => 'int8u',
+        PrintConv => {
+            0 => 0,
+            1 => 90,
+            2 => 180,
+            3 => 270,
+        },
+    },
+    110 => {
+        Name => 'DataCompressionMethod',
+        Format => 'int32u',
+    },
+    120 => {
+        Name => 'QuantizationMethod',
+        Format => 'int8u',
+        PrintConv => {
+            0 => 'Linear Reflectance/Transmittance',
+            1 => 'Linear Density',
+            2 => 'IPTC Ref B',
+            3 => 'Linear Dot Percent',
+            4 => 'AP Domestic Analogue',
+            5 => 'Compression Method Specific',
+            6 => 'Color Space Specific',
+            7 => 'Gamma Compensated',
+        },
+    },
+    125 => {
+        Name => 'EndPoints',
+        Writable => 0,
+        Binary => 1,
+    },
+    130 => {
+        Name => 'ExcursionTolerance',
+        Format => 'int8u',
+        PrintConv => {
+            0 => 'Not Allowed',
+            1 => 'Allowed',
+        },
+    },
+    135 => {
+        Name => 'BitsPerComponent',
+        Format => 'int8u',
+    },
+    140 => {
+        Name => 'MaximumDensityRange',
+        Format => 'int16u',
+    },
+    145 => {
+        Name => 'GammaCompensatedValue',
+        Format => 'int16u',
+    },
+);
+
+# Record 7 -- Pre-object Data
+%Image::ExifTool::IPTC::PreObjectData = (
+    # (not actually writable, but used in BuildTagLookup to recognize IPTC tables)
+    WRITE_PROC => \&WriteIPTC,
+    10 => {
+        Name => 'SizeMode',
+        Format => 'int8u',
+        PrintConv => {
+            0 => 'Size Not Known',
+            1 => 'Size Known',
+        },
+    },
+    20 => {
+        Name => 'MaxSubfileSize',
+        Format => 'int32u',
+    },
+    90 => {
+        Name => 'ObjectSizeAnnounced',
+        Format => 'int32u',
+    },
+    95 => {
+        Name => 'MaximumObjectSize',
+        Format => 'int32u',
+    },
+);
+
+# Record 8 -- ObjectData
+%Image::ExifTool::IPTC::ObjectData = (
+    WRITE_PROC => \&WriteIPTC,
+    10 => {
+        Name => 'SubFile',
+        Flags => 'List',
+        Binary => 1,
+    },
+);
+
+# Record 9 -- PostObjectData
+%Image::ExifTool::IPTC::PostObjectData = (
+    WRITE_PROC => \&WriteIPTC,
+    10 => {
+        Name => 'ConfirmedObjectSize',
+        Format => 'int32u',
+    },
+);
+
+
+#------------------------------------------------------------------------------
+# AutoLoad our writer routines when necessary
+#
+sub AUTOLOAD
+{
+    return Image::ExifTool::DoAutoLoad($AUTOLOAD, @_);
+}
+
+#------------------------------------------------------------------------------
+# Print conversion for CodedCharacterSet
+# Inputs: 0) value
+sub PrintCodedCharset($)
+{
+    my $val = shift;
+    return $iptcCharset{$val} if $iptcCharset{$val};
+    $val =~ s/(.)/ $1/g;
+    $val =~ s/ \x1b/, ESC/g;
+    $val =~ s/^,? //;
+    return $val;
+}
+        
+#------------------------------------------------------------------------------
+# Handle CodedCharacterSet
+# Inputs: 0) ExifTool ref, 1) CodedCharacterSet value
+# Returns: external character set if translation required (or 'bad' if unknown)
+sub HandleCodedCharset($$)
+{
+    my ($exifTool, $val) = @_;
+    my $xlat = $exifTool->Options('Charset');
+    if ($iptcCharset{$val}) {
+        # no need to translate if destination is the same
+        undef $xlat if $xlat eq $iptcCharset{$val};
+    } elsif ($val =~ /^\x1b\x25/) {
+        # some unknown character set involked
+        $xlat = 'bad';  # flag unsupported coding
+    } else {
+        # translate all other codes as Latin
+        undef $xlat if $xlat eq 'Latin';
+    }
+    return $xlat;
+}
+
+#------------------------------------------------------------------------------
+# Encode or decode coded string
+# Inputs: 0) ExifTool ref, 1) value ptr, 2) destination charset ('Latin','UTF8' or 'bad')
+#         3) flag set to decode (read) value from IPTC
+# Updates value on return
+sub TranslateCodedString($$$$)
+{
+    my ($exifTool, $valPtr, $xlatPtr, $read) = @_;
+    my $escaped;
+    if ($$xlatPtr eq 'bad') {
+        $exifTool->Warn('Some IPTC characters not converted (unsupported CodedCharacterSet)');
+        undef $$xlatPtr;
+    } elsif ($$xlatPtr eq 'Latin' xor $read) {
+        # don't yet support reading ISO 2022 shifted character sets
+        if (not $read or $$valPtr !~ /[\x14\x15\x1b]/) {
+            # convert from Latin to UTF-8
+            my $val = Image::ExifTool::Latin2Unicode($$valPtr,'n');
+            $$valPtr = Image::ExifTool::Unicode2UTF8($val,'n');
+        } elsif (not $$exifTool{WarnShift2022}) {
+            $exifTool->Warn('Some IPTC characters not converted (ISO 2022 shifting not supported)');
+            $$exifTool{WarnShift2022} = 1;
+        }
+    } else {
+        # convert from UTF-8 to Latin
+        my $val = Image::ExifTool::UTF82Unicode($$valPtr,'n',$exifTool);
+        $$valPtr = Image::ExifTool::Unicode2Latin($val,'n',$exifTool);
+    }
+}
+
+#------------------------------------------------------------------------------
+# get IPTC info
+# Inputs: 0) ExifTool object reference, 1) dirInfo reference
+#         2) reference to tag table
+# Returns: 1 on success, 0 otherwise
+sub ProcessIPTC($$$)
+{
+    my ($exifTool, $dirInfo, $tagTablePtr) = @_;
+    my $dataPt = $$dirInfo{DataPt};
+    my $pos = $$dirInfo{DirStart} || 0;
+    my $dirLen = $$dirInfo{DirLen} || 0;
+    my $dirEnd = $pos + $dirLen;
+    my $verbose = $exifTool->Options('Verbose');
+    my $success = 0;
+    my (%listTags, $lastRec, $recordPtr, $recordName);
+
+    # begin by assuming IPTC is Latin (so no translation if Charset is Latin)
+    my $xlat = $exifTool->Options('Charset');
+    undef $xlat if $xlat eq 'Latin';
+    
+    $verbose and $dirInfo and $exifTool->VerboseDir('IPTC', 0, $$dirInfo{DirLen});
+    if ($tagTablePtr eq \%Image::ExifTool::IPTC::Main) {
+        my $dirCount = ($exifTool->{DIR_COUNT}->{IPTC} || 0) + 1;
+        $exifTool->{DIR_COUNT}->{IPTC} = $dirCount;
+        $exifTool->{SET_GROUP1} = '+' . $dirCount if $dirCount > 1;
+    }
+    # quick check for improperly byte-swapped IPTC
+    if ($dirLen >= 4 and substr($$dataPt, $pos, 1) ne "\x1c" and
+                         substr($$dataPt, $pos + 3, 1) eq "\x1c")
+    {
+        $exifTool->Warn('IPTC data was improperly byte-swapped');
+        my $newData = pack('N*', unpack('V*', substr($$dataPt, $pos, $dirLen) . "\0\0\0"));
+        $dataPt = \$newData;
+        $pos = 0;
+        $dirEnd = $pos + $dirLen;
+        # NOTE: MUST NOT access $dirInfo DataPt, DirStart or DataLen after this!
+    }
+    while ($pos + 5 <= $dirEnd) {
+        my $buff = substr($$dataPt, $pos, 5);
+        my ($id, $rec, $tag, $len) = unpack("CCCn", $buff);
+        unless ($id == 0x1c) {
+            unless ($id) {
+                # scan the rest of the data an give warning unless all zeros
+                # (iMatch pads the IPTC block with nulls for some reason)
+                my $remaining = substr($$dataPt, $pos, $dirEnd - $pos);
+                last unless $remaining =~ /[^\0]/;
+            }
+            $exifTool->Warn(sprintf('Bad IPTC data tag (marker 0x%x)',$id));
+            last;
+        }
+        if (not defined $lastRec or $lastRec != $rec) {
+            my $tableInfo = $tagTablePtr->{$rec};
+            unless ($tableInfo) {
+                $exifTool->Warn("Unrecognized IPTC record: $rec");
+                last;   # stop now because we're probably reading garbage
+            }
+            my $tableName = $tableInfo->{SubDirectory}->{TagTable};
+            unless ($tableName) {
+                $exifTool->Warn("No table for IPTC record $rec!");
+                last;   # this shouldn't happen
+            }
+            $recordName = $$tableInfo{Name};
+            $recordPtr = Image::ExifTool::GetTagTable($tableName);
+            $exifTool->VPrint(0,$$exifTool{INDENT},"-- $recordName record --\n");
+            $lastRec = $rec;
+        }
+        $pos += 5;      # step to after field header
+        # handle extended IPTC entry if necessary
+        if ($len & 0x8000) {
+            my $n = $len & 0x7fff; # get num bytes in length field
+            if ($pos + $n > $dirEnd or $n > 8) {
+                $exifTool->VPrint(0, "Invalid extended IPTC entry (tag $tag)\n");
+                $success = 0;
+                last;
+            }
+            # determine length (a big-endian, variable sized int)
+            for ($len = 0; $n; ++$pos, --$n) {
+                $len = $len * 256 + ord(substr($$dataPt, $pos, 1));
+            }
+        }
+        if ($pos + $len > $dirEnd) {
+            $exifTool->VPrint(0, "Invalid IPTC entry (tag $tag, len $len)\n");
+            $success = 0;
+            last;
+        }
+        my $val = substr($$dataPt, $pos, $len);
+
+        # add tagInfo for all unknown tags:
+        unless ($$recordPtr{$tag}) {
+            # - no Format so format is auto-detected
+            # - no Name so name is generated automatically with decimal tag number
+            Image::ExifTool::AddTagToTable($recordPtr, $tag, { Unknown => 1 });
+        }
+
+        my $tagInfo = $exifTool->GetTagInfo($recordPtr, $tag);
+        my $format;
+        $format = $$tagInfo{Format} if $tagInfo;
+        # use logic to determine format if not specified
+        unless ($format) {
+            $format = 'int' if $len <= 4 and $len != 3 and $val =~ /[\0-\x08]/;
+        }
+        if ($format) {
+            if ($format =~ /^int/) {
+                if ($len <= 8) {    # limit integer conversion to 8 bytes long
+                    $val = 0;
+                    my $i;
+                    for ($i=0; $i<$len; ++$i) {
+                        $val = $val * 256 + ord(substr($$dataPt, $pos+$i, 1));
+                    }
+                }
+            } elsif ($format =~ /^string/) {
+                if ($rec == 1) {
+                    # handle CodedCharacterSet tag
+                    $xlat = HandleCodedCharset($exifTool, $val) if $tag == 90;
+                # translate characters if necessary and special characters exist
+                } elsif ($xlat and $rec < 7 and $val =~ /[\x80-\xff]/) {
+                    # translate to specified character set
+                    TranslateCodedString($exifTool, \$val, \$xlat, 1);
+                }
+            } elsif ($format !~ /^digits/) {
+                warn("Invalid IPTC format: $format");
+            }
+        }
+        $verbose and $exifTool->VerboseInfo($tag, $tagInfo,
+            Table   => $tagTablePtr,
+            Value   => $val,
+            DataPt  => $dataPt,
+            DataPos => $$dirInfo{DataPos},
+            Size    => $len,
+            Start   => $pos,
+            Extra   => ", $recordName record",
+        );
+        # prevent adding tags to list from another IPTC directory
+        if ($tagInfo) {
+            if ($$tagInfo{List}) {
+                $exifTool->{NO_LIST} = 1 unless $listTags{$tagInfo};
+                $listTags{$tagInfo} = 1;    # list the next one we see
+            }
+            $exifTool->FoundTag($tagInfo, $val);
+        }
+        delete $exifTool->{NO_LIST};
+        $success = 1;
+
+        $pos += $len;   # increment to next field
+    }
+    delete $exifTool->{SET_GROUP1};
+    return $success;
+}
+
+1; # end
+
+
+__END__
+
+=head1 NAME
+
+Image::ExifTool::IPTC - Read IPTC meta information
+
+=head1 SYNOPSIS
+
+This module is loaded automatically by Image::ExifTool when required.
+
+=head1 DESCRIPTION
+
+This module contains definitions required by Image::ExifTool to interpret
+IPTC (International Press Telecommunications Council) meta information in
+image files.
+
+=head1 AUTHOR
+
+Copyright 2003-2007, Phil Harvey (phil at owl.phy.queensu.ca)
+
+This library is free software; you can redistribute it and/or modify it
+under the same terms as Perl itself.
+
+=head1 REFERENCES
+
+=over 4
+
+=item L<http://www.iptc.org/IIM/>
+
+=back
+
+=head1 SEE ALSO
+
+L<Image::ExifTool::TagNames/IPTC Tags>,
+L<Image::ExifTool(3pm)|Image::ExifTool>
+
+=cut
Index: /gsdl/trunk/perllib/cpan/Image/ExifTool/JPEG.pm
===================================================================
--- /gsdl/trunk/perllib/cpan/Image/ExifTool/JPEG.pm	(revision 16842)
+++ /gsdl/trunk/perllib/cpan/Image/ExifTool/JPEG.pm	(revision 16842)
@@ -0,0 +1,327 @@
+#------------------------------------------------------------------------------
+# File:         JPEG.pm
+#
+# Description:  Definitions for uncommon JPEG segments
+#
+# Revisions:    10/06/2006 - P. Harvey Created
+#------------------------------------------------------------------------------
+
+package Image::ExifTool::JPEG;
+use strict;
+use vars qw($VERSION);
+
+$VERSION = '1.07';
+
+# (this main JPEG table is for documentation purposes only)
+%Image::ExifTool::JPEG::Main = (
+    NOTES => 'This table lists information extracted by ExifTool from JPEG images.',
+    APP0 => [{
+        Name => 'JFIF',
+        Condition => '$$valPt =~ /^JFIF\0/',
+        SubDirectory => { TagTable => 'Image::ExifTool::JFIF::Main' },
+      }, {
+        Name => 'JFXX',
+        Condition => '$$valPt =~ /^JFXX\0\x10/',
+        SubDirectory => { TagTable => 'Image::ExifTool::JFIF::Extension' },
+      }, {
+        Name => 'CIFF',
+        Condition => '$$valPt =~ /^(II|MM).{4}HEAPJPGM/s',
+        SubDirectory => { TagTable => 'Image::ExifTool::CanonRaw::Main' },
+    }],
+    APP1 => [{
+        Name => 'EXIF',
+        Condition => '$$valPt =~ /^Exif\0/',
+        SubDirectory => { TagTable => 'Image::ExifTool::Exif::Main' },
+      }, {
+        Name => 'XMP',
+        Condition => '$$valPt =~ /^http/ or $$valPt =~ /<exif:/',
+        SubDirectory => { TagTable => 'Image::ExifTool::XMP::Main' },
+    }],
+    APP2 => [{
+        Name => 'ICC_Profile',
+        Condition => '$$valPt =~ /^ICC_PROFILE\0/',
+        SubDirectory => { TagTable => 'Image::ExifTool::ICC_Profile::Main' },
+      }, {
+        Name => 'FPXR',
+        Condition => '$$valPt =~ /^FPXR\0/',
+        SubDirectory => { TagTable => 'Image::ExifTool::FlashPix::Main' },
+    }],
+    APP3 => {
+        Name => 'Meta',
+        Condition => '$$valPt =~ /^(Meta|META|Exif)\0\0/',
+        SubDirectory => { TagTable => 'Image::ExifTool::Kodak::Meta' },
+    },
+    APP5 => {
+        Name => 'RMETA',
+        Condition => '$$valPt =~ /^RMETA\0/',
+        SubDirectory => { TagTable => 'Image::ExifTool::Ricoh::RMETA' },
+    },
+    APP6 => {
+        Name => 'EPPIM',
+        Condition => '$$valPt =~ /^EPPIM\0/',
+        SubDirectory => { TagTable => 'Image::ExifTool::JPEG::EPPIM' },
+    },
+    APP8 => {
+        Name => 'SPIFF',
+        Condition => '$$valPt =~ /^SPIFF\0/',
+        SubDirectory => { TagTable => 'Image::ExifTool::JPEG::SPIFF' },
+    },
+    APP10 => {
+        Name => 'Comment',
+        Condition => '$$valPt =~ /^UNICODE\0/',
+        Notes => 'PhotoStudio Unicode comment',
+    },
+    APP12 => [{
+        Name => 'PictureInfo',
+        Condition => '$$valPt =~ /(\[picture info\]|Type=)/',
+        SubDirectory => { TagTable => 'Image::ExifTool::APP12::PictureInfo' },
+      }, {
+        Name => 'Ducky',
+        Condition => '$$valPt =~ /^Ducky/',
+        SubDirectory => { TagTable => 'Image::ExifTool::APP12::Ducky' },
+    }],
+    APP13 => [{
+        Name => 'Photoshop',
+        Condition => '$$valPt =~ /^(Photoshop 3.0\0|Adobe_Photoshop2.5)/',
+        SubDirectory => { TagTable => 'Image::ExifTool::Photoshop::Main' },
+    }, {
+        Name => 'Adobe_CM',
+        Condition => '$$valPt =~ /^Adobe_CM/',
+        SubDirectory => { TagTable => 'Image::ExifTool::JPEG::AdobeCM' },
+    }],
+    APP14 => {
+        Name => 'Adobe',
+        Condition => '$$valPt =~ /^Adobe/',
+        SubDirectory => { TagTable => 'Image::ExifTool::JPEG::Adobe' },
+    },
+    APP15 => {
+        Name => 'GraphicConverter',
+        Condition => '$$valPt =~ /^Q\s*(\d+)/',
+        SubDirectory => { TagTable => 'Image::ExifTool::JPEG::GraphConv' },
+    },
+    COM => {
+        Name => 'Comment',
+        # note: flag as writable for documentation, but it won't show up
+        # in the TagLookup as writable because there is no WRITE_PROC
+        Writable => '1',
+    },
+    SOF => {
+        Name => 'StartOfFrame',
+        SubDirectory => { TagTable => 'Image::ExifTool::JPEG::SOF' },
+    },
+    Trailer => [{
+        Name => 'AFCP',
+        Condition => '$$valPt =~ /AXS(!|\*).{8}$/s',
+        SubDirectory => { TagTable => 'Image::ExifTool::AFCP::Main' },
+      }, {
+        Name => 'CanonVRD',
+        Condition => '$$valPt =~ /CANON OPTIONAL DATA\0.{44}$/s',
+        SubDirectory => { TagTable => 'Image::ExifTool::CanonVRD::Main' },
+      }, {
+        Name => 'FotoStation',
+        Condition => '$$valPt =~ /\xa1\xb2\xc3\xd4$/',
+        SubDirectory => { TagTable => 'Image::ExifTool::FotoStation::Main' },
+      }, {
+        Name => 'PhotoMechanic',
+        Condition => '$$valPt =~ /cbipcbbl$/',
+        SubDirectory => { TagTable => 'Image::ExifTool::PhotoMechanic::Main' },
+      }, {
+        Name => 'MIE',
+        Condition => q{
+            $$valPt =~ /~\0\x04\0zmie~\0\0\x06.{4}[\x10\x18]\x04$/s or
+            $$valPt =~ /~\0\x04\0zmie~\0\0\x0a.{8}[\x10\x18]\x08$/s
+        },
+        SubDirectory => { TagTable => 'Image::ExifTool::MIE::Main' },
+      }, {
+        Name => 'PreviewImage',
+        Condition => '$$valPt =~ /^\xff\xd8\xff/',
+        Writable => 1,  # (for docs only)
+    }],
+);
+
+# EPPIM APP6 (Toshiba PrintIM) segment (ref PH, from PDR-M700 samples)
+%Image::ExifTool::JPEG::EPPIM = (
+    GROUPS => { 0 => 'APP6', 1 => 'EPPIM', 2 => 'Image' },
+    NOTES => q{
+        APP6 is used in by the Toshiba PDR-M700 to store a TIFF structure containing
+        PrintIM information.
+    },
+    0xc4a5 => {
+        Name => 'PrintIM',
+        # must set Writable here so this tag will be saved with MakerNotes option
+        Writable => 'undef',
+        Description => 'Print Image Matching',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::PrintIM::Main',
+        },
+    },
+);
+
+# SPIFF APP8 segment.  Refs:
+# 1) http://www.fileformat.info/format/spiff/
+# 2) http://www.jpeg.org/public/spiff.pdf
+%Image::ExifTool::JPEG::SPIFF = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    GROUPS => { 0 => 'APP8', 1 => 'SPIFF', 2 => 'Image' },
+    NOTES => q{
+        This information is found in APP8 of SPIFF-style JPEG images (the "official"
+        yet rarely used JPEG file format standard: Still Picture Interchange File
+        Format).
+    },
+    0 => {
+        Name => 'SPIFFVersion',
+        Format => 'int8u[2]',
+        PrintConv => '$val =~ tr/ /./; $val',
+    },
+    2 => {
+        Name => 'ProfileID',
+        PrintConv => {
+            0 => 'Not Specified',
+            1 => 'Continuous-tone Base',
+            2 => 'Continuous-tone Progressive',
+            3 => 'Bi-level Facsimile',
+            4 => 'Continuous-tone Facsimile',
+        },
+    },
+    3 => 'ColorComponents',
+    6 => {
+        Name => 'ImageHeight',
+        Notes => q{
+            at index 4 in specification, but there are 2 extra bytes here in my only
+            SPIFF sample, version 1.2
+        },
+        Format => 'int32u',
+    },
+    10 => {
+        Name => 'ImageWidth',
+        Format => 'int32u',
+    },
+    14 => {
+        Name => 'ColorSpace',
+        PrintConv => {
+            0 => 'Bi-level',
+            1 => 'YCbCr, ITU-R BT 709, video',
+            2 => 'No color space specified',
+            3 => 'YCbCr, ITU-R BT 601-1, RGB',
+            4 => 'YCbCr, ITU-R BT 601-1, video',
+            8 => 'Gray-scale',
+            9 => 'PhotoYCC',
+            10 => 'RGB',
+            11 => 'CMY',
+            12 => 'CMYK',
+            13 => 'YCCK',
+            14 => 'CIELab',
+        },
+    },
+    15 => 'BitsPerSample',
+    16 => {
+        Name => 'Compression',
+        PrintConv => {
+            0 => 'Uncompressed, interleaved, 8 bits per sample',
+            1 => 'Modified Huffman',
+            2 => 'Modified READ',
+            3 => 'Modified Modified READ',
+            4 => 'JBIG',
+            5 => 'JPEG',
+        },
+    },
+    17 => {
+        Name => 'ResolutionUnit',
+        PrintConv => {
+            0 => 'None',
+            1 => 'inches',
+            2 => 'cm',
+        },
+    },
+    18 => {
+        Name => 'YResolution',
+        Format => 'int32u',
+    },
+    22 => {
+        Name => 'XResolution',
+        Format => 'int32u',
+    },
+);
+
+# AdobeCM APP13 (no references)
+%Image::ExifTool::JPEG::AdobeCM = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    GROUPS => { 0 => 'APP13', 1 => 'AdobeCM', 2 => 'Image' },
+    NOTES => q{
+        The "Adobe_CM" APP13 segment presumably contains color management
+        information, but the meaning of the data is currently unknown.  If anyone
+        has an idea about what this means, please let me know.
+    },
+    FORMAT => 'int16u',
+    0 => 'AdobeCMType',
+);
+
+# Adobe APP14 refs:
+# http://partners.adobe.com/public/developer/en/ps/sdk/5116.DCT_Filter.pdf
+# http://java.sun.com/j2se/1.5.0/docs/api/javax/imageio/metadata/doc-files/jpeg_metadata.html#color
+%Image::ExifTool::JPEG::Adobe = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    GROUPS => { 0 => 'APP14', 1 => 'Adobe', 2 => 'Image' },
+    NOTES => 'The "Adobe" APP14 segment stores image encoding information for DCT filters.',
+    FORMAT => 'int16u',
+    0 => 'DCTEncodeVersion',
+    1 => {
+        Name => 'APP14Flags0',
+        PrintConv => { BITMASK => {
+            15 => 'Encoded with Blend=1 downsampling'
+        } },
+    },
+    2 => {
+        Name => 'APP14Flags1',
+        PrintConv => { BITMASK => { } },
+    },
+    3 => {
+        Name => 'ColorTransform',
+        Format => 'int8u',
+        PrintConv => {
+            0 => 'Unknown (RGB or CMYK)',
+            1 => 'YCbCr',
+            2 => 'YCCK',
+        },
+    },
+);
+
+# GraphicConverter APP15 (ref PH)
+%Image::ExifTool::JPEG::GraphConv = (
+    GROUPS => { 0 => 'APP15', 1 => 'GraphConv', 2 => 'Image' },
+    NOTES => 'APP15 is used by GraphicConverter to store JPEG quality.',
+    'Q' => 'Quality',
+);
+
+1;  # end
+
+__END__
+
+=head1 NAME
+
+Image::ExifTool::JPEG - Definitions for uncommon JPEG segments
+
+=head1 SYNOPSIS
+
+This module is used by Image::ExifTool
+
+=head1 DESCRIPTION
+
+This module contains definitions required by Image::ExifTool for some
+uncommon JPEG segments.  For speed reasons, definitions for more common JPEG
+segments are included in the Image::ExifTool module itself.
+
+=head1 AUTHOR
+
+Copyright 2003-2007, Phil Harvey (phil at owl.phy.queensu.ca)
+
+This library is free software; you can redistribute it and/or modify it
+under the same terms as Perl itself.
+
+=head1 SEE ALSO
+
+L<Image::ExifTool::TagNames/JPEG Tags>,
+L<Image::ExifTool(3pm)|Image::ExifTool>
+
+=cut
+
Index: /gsdl/trunk/perllib/cpan/Image/ExifTool/JVC.pm
===================================================================
--- /gsdl/trunk/perllib/cpan/Image/ExifTool/JVC.pm	(revision 16842)
+++ /gsdl/trunk/perllib/cpan/Image/ExifTool/JVC.pm	(revision 16842)
@@ -0,0 +1,126 @@
+#------------------------------------------------------------------------------
+# File:         JVC.pm
+#
+# Description:  JVC EXIF maker notes tags
+#
+# Revisions:    12/21/2005  - P. Harvey Created
+#------------------------------------------------------------------------------
+
+package Image::ExifTool::JVC;
+
+use strict;
+use vars qw($VERSION);
+use Image::ExifTool::Exif;
+
+$VERSION = '1.01';
+
+sub ProcessJVCText($$$);
+
+# JVC EXIF-based maker notes
+%Image::ExifTool::JVC::Main = (
+    WRITE_PROC => \&Image::ExifTool::Exif::WriteExif,
+    CHECK_PROC => \&Image::ExifTool::Exif::CheckExif,
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
+    NOTES => 'JVC EXIF maker note tags.',
+    #0x0001 - almost always '2', but '3' for GR-DV700 samples
+    0x0002 => 'CPUVersions', #PH
+    0x0003 => { #PH
+        Name => 'Quality',
+        PrintConv => {
+            0 => 'Low',
+            1 => 'Normal',
+            2 => 'Fine',
+        },
+    },
+);
+
+# JVC text-based maker notes
+%Image::ExifTool::JVC::Text = (
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
+    PROCESS_PROC => \&ProcessJVCText,
+    NOTES => 'JVC/Victor text-based maker note tags.',
+    VER => 'MakerNoteVersion', #PH
+    QTY => { #PH
+        Name => 'Quality',
+        PrintConv => {
+            STND => 'Normal',
+            STD  => 'Normal',
+            FINE => 'Fine',
+        },
+    },
+);
+
+#------------------------------------------------------------------------------
+# Process JVC text-based maker notes
+# Inputs: 0) ExifTool object reference
+#         1) Reference to directory information hash
+#         2) Pointer to tag table for this directory
+# Returns: 1 on success, otherwise returns 0 and sets a Warning
+sub ProcessJVCText($$$)
+{
+    my ($exifTool, $dirInfo, $tagTablePtr) = @_;
+    my $dataPt = $$dirInfo{DataPt};
+    my $dirStart = $$dirInfo{DirStart} || 0;
+    my $dataLen = $$dirInfo{DataLen};
+    my $dirLen = $$dirInfo{DirLen} || $dataLen - $dirStart;
+    my $verbose = $exifTool->Options('Verbose');
+
+    my $data = substr($$dataPt, $dirStart, $dirLen);
+    # validate text maker notes
+    unless ($data =~ /^VER:/) {
+        $exifTool->Warn('Bad JVC text maker notes');
+        return 0;
+    }
+    my $pos = 0;
+    while ($data =~ m/([A-Z]+):(.{3,4})/sg) {
+        my ($tag, $val) = ($1, $2);
+        my $tagInfo = $exifTool->GetTagInfo($tagTablePtr, $tag);
+        $exifTool->VerboseInfo($tag, $tagInfo,
+            Table  => $tagTablePtr,
+            Value  => $val,
+        ) if $verbose;
+        unless ($tagInfo) {
+            next unless $exifTool->{OPTIONS}->{Unknown};
+            $tagInfo = {
+                Name => "JVC_Text_$tag",
+                Unknown => 1,
+                PrintConv => 'length($val) > 60 ? substr($val,0,55) . "[...]" : $val',
+            };
+            # add tag information to table
+            Image::ExifTool::AddTagToTable($tagTablePtr, $tag, $tagInfo);
+        }
+        $exifTool->FoundTag($tagInfo, $val);
+    }
+    return 1;
+}
+
+1;  # end
+
+__END__
+
+=head1 NAME
+
+Image::ExifTool::JVC - JVC EXIF maker notes tags
+
+=head1 SYNOPSIS
+
+This module is loaded automatically by Image::ExifTool when required.
+
+=head1 DESCRIPTION
+
+This module contains routines used by Image::ExifTool to interpret JVC maker
+notes.
+
+=head1 AUTHOR
+
+Copyright 2003-2007, Phil Harvey (phil at owl.phy.queensu.ca)
+
+This library is free software; you can redistribute it and/or modify it
+under the same terms as Perl itself.
+
+=head1 SEE ALSO
+
+L<Image::ExifTool::TagNames/JVC Tags>,
+L<Image::ExifTool(3pm)|Image::ExifTool>
+
+=cut
Index: /gsdl/trunk/perllib/cpan/Image/ExifTool/Jpeg2000.pm
===================================================================
--- /gsdl/trunk/perllib/cpan/Image/ExifTool/Jpeg2000.pm	(revision 16842)
+++ /gsdl/trunk/perllib/cpan/Image/ExifTool/Jpeg2000.pm	(revision 16842)
@@ -0,0 +1,624 @@
+#------------------------------------------------------------------------------
+# File:         Jpeg2000.pm
+#
+# Description:  Read JPEG 2000 meta information
+#
+# Revisions:    02/11/2005 - P. Harvey Created
+#               06/22/2007 - PH Added write support (EXIF, IPTC and XMP only)
+#
+# References:   1) http://www.jpeg.org/public/fcd15444-2.pdf
+#               2) ftp://ftp.remotesensing.org/jpeg2000/fcd15444-1.pdf
+#------------------------------------------------------------------------------
+
+package Image::ExifTool::Jpeg2000;
+
+use strict;
+use vars qw($VERSION);
+use Image::ExifTool qw(:DataAccess :Utils);
+
+$VERSION = '1.12';
+
+sub ProcessJpeg2000Box($$$);
+
+my %resolutionUnit = (
+    -3 => 'km',
+    -2 => '100 m',
+    -1 => '10 m',
+     0 => 'm',
+     1 => '10 cm',
+     2 => 'cm',
+     3 => 'mm',
+     4 => '0.1 mm',
+     5 => '0.01 mm',
+     6 => 'um',
+);
+
+# map of where information is written in JPEG2000 image
+my %jp2Map = (
+    IPTC         => 'UUID-IPTC',
+    IFD0         => 'UUID-EXIF',
+    XMP          => 'UUID-XMP',
+  # jp2h         => 'JP2',  (not yet functional)
+  # ICC_Profile  => 'jp2h', (not yet functional)
+    IFD1         => 'IFD0',
+    EXIF         => 'IFD0', # to write EXIF as a block
+    ExifIFD      => 'IFD0',
+    GPS          => 'IFD0',
+    SubIFD       => 'IFD0',
+    GlobParamIFD => 'IFD0',
+    PrintIM      => 'IFD0',
+    InteropIFD   => 'ExifIFD',
+    MakerNotes   => 'ExifIFD',
+);
+
+# UUID's for writable UUID directories (by tag name)
+my %uuid = (
+    'UUID-EXIF'   => 'JpgTiffExif->JP2',
+    'UUID-IPTC'   => "\x33\xc7\xa4\xd2\xb8\x1d\x47\x23\xa0\xba\xf1\xa3\xe0\x97\xad\x38",
+    'UUID-XMP'    => "\xbe\x7a\xcf\xcb\x97\xa9\x42\xe8\x9c\x71\x99\x94\x91\xe3\xaf\xac",
+  # (can't yet write GeoJP2 information)
+  # 'UUID-GeoJP2' => "\xb1\x4b\xf8\xbd\x08\x3d\x4b\x43\xa5\xae\x8c\xd7\xd5\xa6\xce\x03",
+);
+
+# JPEG 2000 "box" (ie. segment) names
+%Image::ExifTool::Jpeg2000::Main = (
+    GROUPS => { 2 => 'Image' },
+    PROCESS_PROC => \&ProcessJpeg2000Box,
+    WRITE_PROC => \&ProcessJpeg2000Box,
+    NOTES => q{
+        The tags below are extracted from JPEG 2000 images, however ExifTool
+        currently writes only EXIF, IPTC and XMP tags in these images.
+    },
+   'jP  ' => 'JP2Signature', # (ref 1)
+   "jP\x1a\x1a" => 'JP2Signature', # (ref 2)
+    prfl => 'Profile',
+    ftyp => { Name => 'FileType', Priority => 0 },
+    rreq => 'ReaderRequirements',
+    jp2h => {
+        Name => 'JP2Header',
+        SubDirectory => { },
+    },
+        # JP2Header sub boxes...
+        ihdr => {
+            Name => 'ImageHeader',
+            SubDirectory => {
+                TagTable => 'Image::ExifTool::Jpeg2000::ImageHeader',
+            },
+        },
+        bpcc => 'BitsPerComponent',
+        colr => [
+            {
+                Name => 'ICC_Profile',
+                Condition => '$$valPt =~ /^(\x02|\x03)/',
+                SubDirectory => {
+                    TagTable => 'Image::ExifTool::ICC_Profile::Main',
+                    Start => '$valuePtr + 3',
+                },
+            },
+            {
+                Name => 'Colorspace',
+                Condition => '$$valPt =~ /^\x01/',
+                Format => 'binary',
+                ValueConv => 'unpack("x3N", $val)',
+                PrintConv => {
+                    16 => 'sRGB',
+                    17 => 'Grayscale',
+                    18 => 'sYCC',
+                },
+            },
+            {
+                Name => 'ColorSpecification',
+                Binary => 1,
+            },
+        ],
+        pclr => 'Palette',
+        cdef => 'ComponentDefinition',
+       'res '=> {
+            Name => 'Resolution',
+            SubDirectory => { },
+        },
+            # Resolution sub boxes...
+            resc => {
+                Name => 'CaptureResolution',
+                SubDirectory => {
+                    TagTable => 'Image::ExifTool::Jpeg2000::CaptureResolution',
+                },
+            },
+            resd => {
+                Name => 'DisplayResolution',
+                SubDirectory => {
+                    TagTable => 'Image::ExifTool::Jpeg2000::DisplayResolution',
+                },
+            },
+    jpch => {
+        Name => 'CodestreamHeader',
+        SubDirectory => { },
+    },
+        # CodestreamHeader sub boxes...
+       'lbl '=> {
+            Name => 'Label',
+            Format => 'string',
+        },
+        cmap => 'ComponentMapping',
+        roid => 'ROIDescription',
+    jplh => {
+        Name => 'CompositingLayerHeader',
+        SubDirectory => { },
+    },
+        # CompositingLayerHeader sub boxes...
+        cgrp => 'ColorGroup',
+        opct => 'Opacity',
+        creg => 'CodestreamRegistration',
+    dtbl => 'DataReference',
+    ftbl => {
+        Name => 'FragmentTable',
+        Subdirectory => { },
+    },
+        # FragmentTable sub boxes...
+        flst => 'FragmentList',
+    cref => 'Cross-Reference',
+    mdat => 'MediaData',
+    comp => 'Composition',
+    copt => 'CompositionOptions',
+    inst => 'InstructionSet',
+    asoc => 'Association',
+    nlst => 'NumberList',
+    bfil => 'BinaryFilter',
+    drep => 'DesiredReproductions',
+        # DesiredReproductions sub boxes...
+        gtso => 'GraphicsTechnologyStandardOutput',
+    chck => 'DigitalSignature',
+    mp7b => 'MPEG7Binary',
+    free => 'Free',
+    jp2c => 'ContiguousCodestream',
+    jp2i => {
+        Name => 'IntellectualProperty',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::XMP::Main',
+        },
+    },
+   'xml '=> {
+        Name => 'XML',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::XMP::Main',
+        },
+    },
+    uuid => [
+        {
+            Name => 'UUID-EXIF',
+            Condition => '$$valPt=~/^JpgTiffExif->JP2/',
+            SubDirectory => {
+                TagTable => 'Image::ExifTool::Exif::Main',
+                ProcessProc => \&Image::ExifTool::ProcessTIFF,
+                WriteProc => \&Image::ExifTool::WriteTIFF,
+                Start => '$valuePtr + 16',
+            },
+        },
+        {
+            Name => 'UUID-IPTC',
+            Condition => '$$valPt=~/^\x33\xc7\xa4\xd2\xb8\x1d\x47\x23\xa0\xba\xf1\xa3\xe0\x97\xad\x38/',
+            SubDirectory => {
+                TagTable => 'Image::ExifTool::IPTC::Main',
+                Start => '$valuePtr + 16',
+            },
+        },
+        {
+            Name => 'UUID-XMP',
+            # ref http://www.adobe.com/products/xmp/pdfs/xmpspec.pdf
+            Condition => '$$valPt=~/^\xbe\x7a\xcf\xcb\x97\xa9\x42\xe8\x9c\x71\x99\x94\x91\xe3\xaf\xac/',
+            SubDirectory => {
+                TagTable => 'Image::ExifTool::XMP::Main',
+                Start => '$valuePtr + 16',
+            },
+        },
+        {
+            Name => 'UUID-GeoJP2',
+            # ref http://www.remotesensing.org/jpeg2000/
+            Condition => '$$valPt=~/^\xb1\x4b\xf8\xbd\x08\x3d\x4b\x43\xa5\xae\x8c\xd7\xd5\xa6\xce\x03/',
+            SubDirectory => {
+                TagTable => 'Image::ExifTool::Exif::Main',
+                ProcessProc => \&Image::ExifTool::ProcessTIFF,
+                Start => '$valuePtr + 16',
+            },
+        },
+        {
+            Name => 'UUID-Unknown',
+        },
+    ],
+    uinf => {
+        Name => 'UUIDInfo',
+        SubDirectory => { },
+    },
+        # UUIDInfo sub boxes...
+        ulst => 'UUIDList',
+       'url '=> {
+            Name => 'URL',
+            Format => 'string',
+        },
+);
+
+%Image::ExifTool::Jpeg2000::ImageHeader = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    GROUPS => { 2 => 'Image' },
+    0 => {
+        Name => 'ImageHeight',
+        Format => 'int32u',
+    },
+    4 => {
+        Name => 'ImageWidth',
+        Format => 'int32u',
+    },
+    8 => {
+        Name => 'NumberOfComponents',
+        Format => 'int16u',
+    },
+    10 => {
+        Name => 'BitsPerComponent',
+        PrintConv => q{
+            $val == 0xff and return 'Variable';
+            my $sign = ($val & 0x80) ? 'Signed' : 'Unsigned';
+            return (($val & 0x7f) + 1) . " Bits, $sign";
+        },
+    },
+    11 => {
+        Name => 'Compression',
+        PrintConv => {
+            0 => 'Uncompressed',
+            1 => 'Modified Huffman',
+            2 => 'Modified READ',
+            3 => 'Modified Modified READ',
+            4 => 'JBIG',
+            5 => 'JPEG',
+            6 => 'JPEG-LS',
+            7 => 'JPEG 2000',
+            8 => 'JBIG2',
+        },
+    },
+);
+
+%Image::ExifTool::Jpeg2000::CaptureResolution = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    GROUPS => { 2 => 'Image' },
+    FORMAT => 'int8s',
+    0 => {
+        Name => 'CaptureYResolution',
+        Format => 'rational32u',
+    },
+    4 => {
+        Name => 'CaptureXResolution',
+        Format => 'rational32u',
+    },
+    8 => {
+        Name => 'CaptureYResolutionUnit',
+        SeparateTable => 'ResolutionUnit',
+        PrintConv => \%resolutionUnit,
+    },
+    9 => {
+        Name => 'CaptureXResolutionUnit',
+        SeparateTable => 'ResolutionUnit',
+        PrintConv => \%resolutionUnit,
+    },
+);
+
+%Image::ExifTool::Jpeg2000::DisplayResolution = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    GROUPS => { 2 => 'Image' },
+    FORMAT => 'int8s',
+    0 => {
+        Name => 'DisplayYResolution',
+        Format => 'rational32u',
+    },
+    4 => {
+        Name => 'DisplayXResolution',
+        Format => 'rational32u',
+    },
+    8 => {
+        Name => 'DisplayYResolutionUnit',
+        SeparateTable => 'ResolutionUnit',
+        PrintConv => \%resolutionUnit,
+    },
+    9 => {
+        Name => 'DisplayXResolutionUnit',
+        SeparateTable => 'ResolutionUnit',
+        PrintConv => \%resolutionUnit,
+    },
+);
+
+#------------------------------------------------------------------------------
+# Create new JPEG 2000 boxes when writing
+# (Currently only supports adding certain UUID boxes)
+# Inputs: 0) ExifTool object ref, 1) Output file or scalar ref
+# Returns: 1 on success
+sub CreateNewBoxes($$)
+{
+    my ($exifTool, $outfile) = @_;
+    my $addDirs = $$exifTool{AddJp2Dirs};
+    delete $$exifTool{AddJp2Dirs};
+    my $dirName;
+    foreach $dirName (sort keys %$addDirs) {
+        next unless $uuid{$dirName};
+        my $tagInfo;
+        foreach $tagInfo (@{$Image::ExifTool::Jpeg2000::Main{uuid}}) {
+            next unless $$tagInfo{Name} eq $dirName;
+            my $subdir = $$tagInfo{SubDirectory};
+            my $tagTable = GetTagTable($$subdir{TagTable});
+            my %dirInfo = (
+                DirName => $dirName,
+                Parent => 'JP2',
+            );
+            my $newdir = $exifTool->WriteDirectory(\%dirInfo, $tagTable, $$subdir{WriteProc});
+            if (defined $newdir and length $newdir) {
+                my $boxhdr = pack('N', length($newdir) + 24) . 'uuid' . $uuid{$dirName};
+                Write($outfile, $boxhdr, $newdir) or return 0;
+                last;
+            }
+        }
+    }
+    return 1;
+}
+
+#------------------------------------------------------------------------------
+# Process JPEG 2000 box
+# Inputs: 0) ExifTool object reference, 1) dirInfo reference, 2) Pointer to tag table
+# Returns: 1 on success when reading, or -1 on write error
+#          (or JP2 box or undef when writing from buffer)
+sub ProcessJpeg2000Box($$$)
+{
+    my ($exifTool, $dirInfo, $tagTablePtr) = @_;
+    my $dataPt = $$dirInfo{DataPt};
+    my $dataLen = $$dirInfo{DataLen};
+    my $dataPos = $$dirInfo{DataPos};
+    my $dirLen = $$dirInfo{DirLen} || 0;
+    my $dirStart = $$dirInfo{DirStart} || 0;
+    my $raf = $$dirInfo{RAF};
+    my $outfile = $$dirInfo{OutFile};
+    my $dirEnd = $dirStart + $dirLen;
+    my ($err, $outBuff, $verbose);
+
+    if ($outfile) {
+        unless ($raf) {
+            # buffer output to be used for return value
+            $outBuff = '';
+            $outfile = \$outBuff;
+        }
+    } else {
+        # (must not set verbose flag when writing!)
+        $verbose = $exifTool->{OPTIONS}->{Verbose};
+    }
+    # loop through all contained boxes
+    my ($pos, $boxLen);
+    for ($pos=$dirStart; ; $pos+=$boxLen) {
+        my ($boxID, $buff, $valuePtr);
+        if ($raf) {
+            $dataPos = $raf->Tell();
+            my $n = $raf->Read($buff,8);
+            unless ($n == 8) {
+                $n and $err = '', last;
+                if ($outfile) {
+                    CreateNewBoxes($exifTool, $outfile) or $err = 1;
+                }
+                last;
+            }
+            $dataPt = \$buff;
+            $dirLen = 8;
+            $pos = 0;
+        } elsif ($pos >= $dirEnd - 8) {
+            $err = '' unless $pos == $dirEnd;
+            last;
+        }
+        $boxLen = unpack("x$pos N",$$dataPt);
+        $boxID = substr($$dataPt, $pos+4, 4);
+        $pos += 8;
+        if ($boxLen == 1) {
+            if (not $raf and $pos < $dirLen - 8) {
+                $err = 'JPEG 2000 format error';
+            } else {
+                $err = "Can't currently handle huge JPEG 2000 boxes";
+            }
+            last;
+        } elsif ($boxLen == 0) {
+            if ($raf) {
+                if ($outfile) {
+                    CreateNewBoxes($exifTool, $outfile) or $err = 1;
+                    # copy over the rest of the file
+                    Write($outfile, $$dataPt) or $err = 1;
+                    while ($raf->Read($buff, 65536)) {
+                        Write($outfile, $buff) or $err = 1;
+                    }
+                }
+                last;   # (ignore the rest of the file when reading)
+            }
+            $boxLen = $dirLen - $pos;
+        } else {
+            $boxLen -= 8;
+        }
+        $boxLen < 0 and $err = 'Invalid JPEG 2000 box length', last;
+        my $tagInfo = $exifTool->GetTagInfo($tagTablePtr, $boxID);
+        unless (defined $tagInfo or $verbose) {
+            # no need to process this box
+            if ($raf) {
+                if ($outfile) {
+                    Write($outfile, $$dataPt) or $err = 1;
+                    $raf->Read($buff,$boxLen) == $boxLen or $err = '', last;
+                    Write($outfile, $buff) or $err = 1;
+                } else {
+                    $raf->Seek($boxLen, 1) or $err = 'Seek error', last;
+                }
+            } elsif ($outfile) {
+                Write($outfile, substr($$dataPt, $pos-8, $boxLen+8)) or $err = '', last;
+            }
+            next;
+        }
+        if ($raf) {
+            # read the box data
+            $dataPos = $raf->Tell();
+            $raf->Read($buff,$boxLen) == $boxLen or $err = '', last;
+            $valuePtr = 0;
+            $dataLen = $boxLen;
+        } elsif ($boxLen + $pos > $dirStart + $dirLen) {
+            $err = '';
+            last;
+        } else {
+            $valuePtr = $pos;
+        }
+        if (defined $tagInfo and not $tagInfo) {
+            # GetTagInfo() required the value for a Condition
+            my $tmpVal = substr($$dataPt, $valuePtr, $boxLen < 48 ? $boxLen : 48);
+            $tagInfo = $exifTool->GetTagInfo($tagTablePtr, $boxID, \$tmpVal);
+        }
+        # delete all UUID boxes if deleting all information
+        if ($outfile and $boxID eq 'uuid' and $exifTool->{DEL_GROUP}->{'*'}) {
+            $exifTool->VPrint(0, "  Deleting $$tagInfo{Name}\n");
+            ++$exifTool->{CHANGED};
+            next;
+        }
+        if ($verbose) {
+            $exifTool->VerboseInfo($boxID, $tagInfo,
+                Table  => $tagTablePtr,
+                DataPt => $dataPt,
+                Size   => $boxLen,
+                Start  => $valuePtr,
+            );
+            next unless $tagInfo;
+        }
+        if ($$tagInfo{SubDirectory}) {
+            my $subdir = $$tagInfo{SubDirectory};
+            my $subdirStart = $valuePtr;
+            if (defined $$subdir{Start}) {
+                #### eval Start ($valuePtr)
+                $subdirStart = eval($$subdir{Start});
+            }
+            my $subdirLen = $boxLen - ($subdirStart - $valuePtr);
+            my %subdirInfo = (
+                Parent => 'JP2',
+                DataPt => $dataPt,
+                DataPos => $dataPos,
+                DataLen => $dataLen,
+                DirStart => $subdirStart,
+                DirLen => $subdirLen,
+                DirName => $$tagInfo{Name},
+                OutFile => $outfile,
+                Base => $dataPos + $subdirStart,
+            );
+            my $subTable = GetTagTable($$subdir{TagTable}) || $tagTablePtr;
+            if ($outfile) {
+                # remove this directory from our create list
+                delete $exifTool->{AddJp2Dirs}->{$$tagInfo{Name}};
+                my $newdir;
+                # only edit writable UUID boxes
+                if ($uuid{$$tagInfo{Name}}) {
+                    $newdir = $exifTool->WriteDirectory(\%subdirInfo, $subTable, $$subdir{WriteProc});
+                    next if defined $newdir and not length $newdir; # next if deleting the box
+                }
+                # use old box data if not changed
+                defined $newdir or $newdir = substr($$dataPt, $subdirStart, $subdirLen);
+                my $prefixLen = $subdirStart - $valuePtr;
+                my $boxhdr = pack('N', length($newdir) + 8 + $prefixLen) . $boxID;
+                $boxhdr .= substr($$dataPt, $valuePtr, $prefixLen) if $prefixLen;
+                Write($outfile, $boxhdr, $newdir) or $err = 1;
+            } elsif (not $exifTool->ProcessDirectory(\%subdirInfo, $subTable, $$subdir{ProcessProc})) {
+                if ($subTable eq $tagTablePtr) {
+                    $err = 'JPEG 2000 format error';
+                } else {
+                    $err = "Unrecognized $$tagInfo{Name} box";
+                }
+                last;
+            }
+        } elsif ($$tagInfo{Format} and not $outfile) {
+            # only save tag values if Format was specified
+            my $val = ReadValue($dataPt, $valuePtr, $$tagInfo{Format}, undef, $boxLen);
+            $exifTool->FoundTag($tagInfo, $val) if defined $val;
+        } elsif ($outfile) {
+            my $boxhdr = pack('N', $boxLen + 8) . $boxID;
+            Write($outfile, $boxhdr, substr($$dataPt, $valuePtr, $boxLen)) or $err = 1;
+        }
+    }
+    if (defined $err) {
+        $err or $err = 'Truncated JPEG 2000 box';
+        if ($outfile) {
+            $exifTool->Error($err) unless $err eq '1';
+            return $raf ? -1 : undef;
+        }
+        $exifTool->Warn($err);
+    }
+    return $outBuff if $outfile and not $raf;
+    return 1;
+}
+
+#------------------------------------------------------------------------------
+# Read/write meta information from a JPEG 2000 image
+# Inputs: 0) ExifTool object reference, 1) dirInfo reference
+# Returns: 1 on success, 0 if this wasn't a valid JPEG 2000 file, or -1 on write error
+sub ProcessJP2($$)
+{
+    my ($exifTool, $dirInfo) = @_;
+    my $raf = $$dirInfo{RAF};
+    my $outfile = $$dirInfo{OutFile};
+    my $rtnVal = 0;
+    my $hdr;
+
+    # check to be sure this is a valid JPG2000 file
+    return 0 unless $raf->Read($hdr,12) == 12;
+    return 0 unless $hdr eq "\x00\x00\x00\x0cjP  \x0d\x0a\x87\x0a" or     # (ref 1)
+                    $hdr eq "\x00\x00\x00\x0cjP\x1a\x1a\x0d\x0a\x87\x0a"; # (ref 2)
+
+    if ($outfile) {
+        Write($outfile, $hdr) or return -1;
+        $exifTool->InitWriteDirs(\%jp2Map);
+        # save list of directories to create
+        my %addDirs = %{$$exifTool{ADD_DIRS}};
+        $$exifTool{AddJp2Dirs} = \%addDirs;
+    } else {
+        $exifTool->SetFileType();
+    }
+    SetByteOrder('MM'); # JPEG 2000 files are big-endian
+    my %dirInfo = (
+        RAF => $raf,
+        DirName => 'JP2',
+        OutFile => $$dirInfo{OutFile},
+    );
+    my $tagTablePtr = GetTagTable('Image::ExifTool::Jpeg2000::Main');
+    return $exifTool->ProcessDirectory(\%dirInfo, $tagTablePtr);
+}
+
+1;  # end
+
+__END__
+
+=head1 NAME
+
+Image::ExifTool::Jpeg2000 - Read JPEG 2000 meta information
+
+=head1 SYNOPSIS
+
+This module is used by Image::ExifTool
+
+=head1 DESCRIPTION
+
+This module contains routines required by Image::ExifTool to read JPEG 2000
+files.
+
+=head1 AUTHOR
+
+Copyright 2003-2007, Phil Harvey (phil at owl.phy.queensu.ca)
+
+This library is free software; you can redistribute it and/or modify it
+under the same terms as Perl itself.
+
+=head1 REFERENCES
+
+=over 4
+
+=item L<http://www.jpeg.org/public/fcd15444-2.pdf>
+
+=item L<ftp://ftp.remotesensing.org/jpeg2000/fcd15444-1.pdf>
+
+=back
+
+=head1 SEE ALSO
+
+L<Image::ExifTool::TagNames/Jpeg2000 Tags>,
+L<Image::ExifTool(3pm)|Image::ExifTool>
+
+=cut
+
Index: /gsdl/trunk/perllib/cpan/Image/ExifTool/Kodak.pm
===================================================================
--- /gsdl/trunk/perllib/cpan/Image/ExifTool/Kodak.pm	(revision 16842)
+++ /gsdl/trunk/perllib/cpan/Image/ExifTool/Kodak.pm	(revision 16842)
@@ -0,0 +1,1129 @@
+#------------------------------------------------------------------------------
+# File:         Kodak.pm
+#
+# Description:  Kodak EXIF maker notes and APP3 "Meta" tags
+#
+# Revisions:    03/28/2005  - P. Harvey Created
+#
+# References:   1) http://search.cpan.org/dist/Image-MetaData-JPEG/
+#               2) http://www.ozhiker.com/electronics/pjmt/jpeg_info/meta.html
+#
+# Notes:        There really isn't much public information about Kodak formats.
+#               The only source I could find was Image::MetaData::JPEG, which
+#               didn't provide information about decoding the tag values.  So
+#               this module represents a lot of work downloading sample images
+#               (about 100MB worth!), and testing with my daughter's CX4200.
+#------------------------------------------------------------------------------
+
+package Image::ExifTool::Kodak;
+
+use strict;
+use vars qw($VERSION);
+use Image::ExifTool::Exif;
+
+$VERSION = '1.10';
+
+sub ProcessKodakIFD($$$);
+sub WriteKodakIFD($$$);
+
+# Kodak type 1 maker notes (ref 1)
+%Image::ExifTool::Kodak::Main = (
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
+    CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
+    NOTES => q{
+        The table below contains the most common set of Kodak tags.  The following
+        Kodak camera models have been tested and found to use these tags: C360,
+        C663, C875, CX6330, CX6445, CX7330, CX7430, CX7525, CX7530, DC4800, DC4900,
+        DX3500, DX3600, DX3900, DX4330, DX4530, DX4900, DX6340, DX6440, DX6490,
+        DX7440, DX7590, DX7630, EasyShare-One, LS420, LS443, LS633, LS743, LS753,
+        V530, V550, V570, V603, V610, V705, Z650, Z700, Z710, Z730, Z740, Z760 and
+        Z7590.
+    },
+    WRITABLE => 1,
+    FIRST_ENTRY => 8,
+    0x00 => {
+        Name => 'KodakModel',
+        Format => 'string[8]',
+    },
+    0x09 => {
+        Name => 'Quality',
+        PrintConv => { #PH
+            1 => 'Fine',
+            2 => 'Normal',
+        },
+    },
+    0x0a => {
+        Name => 'BurstMode',
+        PrintConv => { 0 => 'Off', 1 => 'On' },
+    },
+    0x0c => {
+        Name => 'KodakImageWidth',
+        Format => 'int16u',
+    },
+    0x0e => {
+        Name => 'KodakImageHeight',
+        Format => 'int16u',
+    },
+    0x10 => {
+        Name => 'YearCreated',
+        Groups => { 2 => 'Time' },
+        Format => 'int16u',
+    },
+    0x12 => {
+        Name => 'MonthDayCreated',
+        Groups => { 2 => 'Time' },
+        Format => 'int8u[2]',
+        ValueConv => 'sprintf("%.2d:%.2d",split(" ", $val))',
+        ValueConvInv => '$val=~tr/:./ /;$val',
+    },
+    0x14 => {
+        Name => 'TimeCreated',
+        Groups => { 2 => 'Time' },
+        Format => 'int8u[4]',
+        Shift => 'Time',
+        ValueConv => 'sprintf("%.2d:%.2d:%.2d.%.2d",split(" ", $val))',
+        ValueConvInv => '$val=~tr/:./ /;$val',
+    },
+    0x18 => {
+        Name => 'BurstMode2',
+        Format => 'int16u',
+        Unknown => 1, # not sure about this tag (or other 'Unknown' tags)
+    },
+    0x1b => {
+        Name => 'ShutterMode',
+        PrintConv => { #PH
+            0 => 'Auto',
+            8 => 'Aperture Priority',
+            32 => 'Manual?',
+        },
+    },
+    0x1c => {
+        Name => 'MeteringMode',
+        PrintConv => { #PH
+            0 => 'Multi-pattern',
+            1 => 'Center-Weighted',
+            2 => 'Spot',
+        },
+    },
+    0x1d => 'SequenceNumber',
+    0x1e => {
+        Name => 'FNumber',
+        Format => 'int16u',
+        ValueConv => '$val / 100',
+        ValueConvInv => 'int($val * 100 + 0.5)',
+    },
+    0x20 => {
+        Name => 'ExposureTime',
+        Format => 'int32u',
+        ValueConv => '$val / 1e5',
+        ValueConvInv => '$val * 1e5',
+        PrintConv => 'Image::ExifTool::Exif::PrintExposureTime($val)',
+        PrintConvInv => 'eval $val',
+    },
+    0x24 => {
+        Name => 'ExposureCompensation',
+        Format => 'int16s',
+        ValueConv => '$val / 1000',
+        ValueConvInv => '$val * 1000',
+        PrintConv => '$val > 0 ? "+$val" : $val',
+        PrintConvInv => 'eval $val',
+    },
+    0x26 => {
+        Name => 'VariousModes',
+        Format => 'int16u',
+        Unknown => 1,
+    },
+    0x28 => {
+        Name => 'Distance1',
+        Format => 'int32u',
+        Unknown => 1,
+    },
+    0x2c => {
+        Name => 'Distance2',
+        Format => 'int32u',
+        Unknown => 1,
+    },
+    0x30 => {
+        Name => 'Distance3',
+        Format => 'int32u',
+        Unknown => 1,
+    },
+    0x34 => {
+        Name => 'Distance4',
+        Format => 'int32u',
+        Unknown => 1,
+    },
+    0x38 => {
+        Name => 'FocusMode',
+        PrintConv => {
+            0 => 'Normal',
+            2 => 'Macro',
+        },
+    },
+    0x3a => {
+        Name => 'VariousModes2',
+        Format => 'int16u',
+        Unknown => 1,
+    },
+    0x3c => {
+        Name => 'PanoramaMode',
+        Format => 'int16u',
+        Unknown => 1,
+    },
+    0x3e => {
+        Name => 'SubjectDistance',
+        Format => 'int16u',
+        Unknown => 1,
+    },
+    0x40 => {
+        Name => 'WhiteBalance',
+        Priority => 0,
+        PrintConv => { #PH
+            0 => 'Auto',
+            1 => 'Flash?',
+            2 => 'Tungsten',
+            3 => 'Daylight',
+        },
+    },
+    0x5c => {
+        Name => 'FlashMode',
+        Flags => 'PrintHex',
+        # various models express this number differently
+        PrintConv => { #PH
+            0x00 => 'Auto',
+            0x01 => 'Fill Flash',
+            0x02 => 'Off',
+            0x03 => 'Red-Eye',
+            0x10 => 'Fill Flash',
+            0x20 => 'Off',
+            0x40 => 'Red-Eye?',
+        },
+    },
+    0x5d => {
+        Name => 'FlashFired',
+        PrintConv => { 0 => 'No', 1 => 'Yes' },
+    },
+    0x5e => {
+        Name => 'ISOSetting',
+        Format => 'int16u',
+        PrintConv => '$val ? $val : "Auto"',
+        PrintConvInv => '$val=~/^\d+$/ ? $val : 0',
+    },
+    0x60 => {
+        Name => 'ISO',
+        Format => 'int16u',
+    },
+    0x62 => {
+        Name => 'TotalZoom',
+        Format => 'int16u',
+        ValueConv => '$val / 100',
+        ValueConvInv => '$val * 100',
+    },
+    0x64 => {
+        Name => 'DateTimeStamp',
+        Format => 'int16u',
+        PrintConv => '$val ? "Mode $val" : "Off"',
+        PrintConvInv => '$val=~tr/0-9//dc; $val ? $val : 0',
+    },
+    0x66 => {
+        Name => 'ColorMode',
+        Format => 'int16u',
+        Flags => 'PrintHex',
+        # various models express this number differently
+        PrintConv => { #PH
+            0x01 => 'B&W',
+            0x02 => 'Sepia',
+            0x03 => 'B&W Yellow Filter',
+            0x04 => 'B&W Red Filter',
+            0x20 => 'Saturated Color',
+            0x40 => 'Neutral Color',
+            0x100 => 'Saturated Color',
+            0x200 => 'Neutral Color',
+            0x2000 => 'B&W',
+            0x4000 => 'Sepia',
+        },
+    },
+    0x68 => {
+        Name => 'DigitalZoom',
+        Format => 'int16u',
+        ValueConv => '$val / 100',
+        ValueConvInv => '$val * 100',
+    },
+    0x6b => {
+        Name => 'Sharpness',
+        Format => 'int8s',
+        PrintConv => 'Image::ExifTool::Exif::PrintParameter($val)',
+        PrintConvInv => '$val=~/normal/i ? 0 : $val',
+    },
+);
+
+# Kodak type 2 maker notes (ref PH)
+%Image::ExifTool::Kodak::Type2 = (
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
+    CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
+    NOTES => q{
+        These tags are used by the Kodak DC220, DC260, DC265 and DC290,
+        Hewlett-Packard PhotoSmart 618, C500 and C912, Pentax EI-200 and EI-2000,
+        and Minolta EX1500Z.
+    },
+    WRITABLE => 1,
+    FIRST_ENTRY => 0,
+    0x08 => {
+        Name => 'KodakMaker',
+        Format => 'string[32]',
+    },
+    0x28 => {
+        Name => 'KodakModel',
+        Format => 'string[32]',
+    },
+    0x6c => {
+        Name => 'KodakImageWidth',
+        Format => 'int32u',
+    },
+    0x70 => {
+        Name => 'KodakImageHeight',
+        Format => 'int32u',
+    },
+);
+
+# Kodak type 3 maker notes (ref PH)
+%Image::ExifTool::Kodak::Type3 = (
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
+    CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
+    NOTES => 'These tags are used by the DC240, DC280, DC3400 and DC5000.',
+    WRITABLE => 1,
+    FIRST_ENTRY => 0,
+    0x0c => {
+        Name => 'YearCreated',
+        Groups => { 2 => 'Time' },
+        Format => 'int16u',
+    },
+    0x0e => {
+        Name => 'MonthDayCreated',
+        Groups => { 2 => 'Time' },
+        Format => 'int8u[2]',
+        ValueConv => 'sprintf("%.2d:%.2d",split(" ", $val))',
+        ValueConvInv => '$val=~tr/:./ /;$val',
+    },
+    0x10 => {
+        Name => 'TimeCreated',
+        Groups => { 2 => 'Time' },
+        Format => 'int8u[4]',
+        Shift => 'Time',
+        ValueConv => 'sprintf("%2d:%.2d:%.2d.%.2d",split(" ", $val))',
+        ValueConvInv => '$val=~tr/:./ /;$val',
+    },
+    0x1e => {
+        Name => 'OpticalZoom',
+        Format => 'int16u',
+        ValueConv => '$val / 100',
+        ValueConvInv => '$val * 100',
+    },
+    0x37 => {
+        Name => 'Sharpness',
+        Format => 'int8s',
+        PrintConv => 'Image::ExifTool::Exif::PrintParameter($val)',
+        PrintConvInv => '$val=~/normal/i ? 0 : $val',
+    },
+    0x38 => {
+        Name => 'ExposureTime',
+        Format => 'int32u',
+        ValueConv => '$val / 1e5',
+        ValueConvInv => '$val * 1e5',
+        PrintConv => 'Image::ExifTool::Exif::PrintExposureTime($val)',
+        PrintConvInv => 'eval $val',
+    },
+    0x3c => {
+        Name => 'FNumber',
+        Format => 'int16u',
+        ValueConv => '$val / 100',
+        ValueConvInv => 'int($val * 100 + 0.5)',
+    },
+    0x4e => {
+        Name => 'ISO',
+        Format => 'int16u',
+    },
+);
+
+# Kodak type 4 maker notes (ref PH)
+%Image::ExifTool::Kodak::Type4 = (
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
+    CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
+    NOTES => 'These tags are used by the DC200 and DC215.',
+    WRITABLE => 1,
+    FIRST_ENTRY => 0,
+    0x20 => {
+        Name => 'OriginalFileName',
+        Format => 'string[12]',
+    },
+);
+
+# Kodak type 5 maker notes (ref PH)
+%Image::ExifTool::Kodak::Type5 = (
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    NOTES => q{
+        These tags are used by the CX4200, CX4210, CX4230, CX4300, CX4310, CX6200
+        and CX6230.
+    },
+    WRITABLE => 1,
+    FIRST_ENTRY => 0,
+    0x14 => {
+        Name => 'ExposureTime',
+        Format => 'int32u',
+        ValueConv => '$val / 1e5',
+        ValueConvInv => '$val * 1e5',
+        PrintConv => 'Image::ExifTool::Exif::PrintExposureTime($val)',
+        PrintConvInv => 'eval $val',
+    },
+    0x1a => {
+        Name => 'WhiteBalance',
+        PrintConv => {
+            1 => 'Daylight',
+            2 => 'Flash',
+            3 => 'Tungsten',
+        },
+    },
+    0x1c => {
+        Name => 'FNumber',
+        Format => 'int16u',
+        ValueConv => '$val / 100',
+        ValueConvInv => 'int($val * 100 + 0.5)',
+    },
+    0x1e => {
+        Name => 'ISO',
+        Format => 'int16u',
+    },
+    0x20 => {
+        Name => 'OpticalZoom',
+        Format => 'int16u',
+        ValueConv => '$val / 100',
+        ValueConvInv => '$val * 100',
+    },
+    0x22 => {
+        Name => 'DigitalZoom',
+        Format => 'int16u',
+        ValueConv => '$val / 100',
+        ValueConvInv => '$val * 100',
+    },
+    0x27 => {
+        Name => 'FlashMode',
+        PrintConv => {
+            0 => 'Auto',
+            1 => 'On',
+            2 => 'Off',
+            3 => 'Red-Eye',
+        },
+    },
+    0x2a => {
+        Name => 'ImageRotated',
+        PrintConv => { 0 => 'No', 1 => 'Yes' },
+    },
+    0x2b => {
+        Name => 'Macro',
+        PrintConv => { 0 => 'On', 1 => 'Off' },
+    },
+);
+
+# Kodak type 6 maker notes (ref PH)
+%Image::ExifTool::Kodak::Type6 = (
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    NOTES => 'These tags are used by the DX3215 and DX3700.',
+    WRITABLE => 1,
+    FIRST_ENTRY => 0,
+    0x10 => {
+        Name => 'ExposureTime',
+        Format => 'int32u',
+        ValueConv => '$val / 1e5',
+        ValueConvInv => '$val * 1e5',
+        PrintConv => 'Image::ExifTool::Exif::PrintExposureTime($val)',
+        PrintConvInv => 'eval $val',
+    },
+    0x14 => {
+        Name => 'ISOSetting',
+        Format => 'int32u',
+        Unknown => 1,
+    },
+    0x18 => {
+        Name => 'FNumber',
+        Format => 'int16u',
+        ValueConv => '$val / 100',
+        ValueConvInv => 'int($val * 100 + 0.5)',
+    },
+    0x1a => {
+        Name => 'ISO',
+        Format => 'int16u',
+    },
+    0x1c => {
+        Name => 'OpticalZoom',
+        Format => 'int16u',
+        ValueConv => '$val / 100',
+        ValueConvInv => '$val * 100',
+    },
+    0x1e => {
+        Name => 'DigitalZoom',
+        Format => 'int16u',
+        ValueConv => '$val / 100',
+        ValueConvInv => '$val * 100',
+    },
+    0x22 => {
+        Name => 'Flash',
+        Format => 'int16u',
+        PrintConv => {
+            0 => 'No Flash',
+            1 => 'Fired',
+        },
+    },
+);
+
+# Kodak type 7 maker notes (ref PH)
+%Image::ExifTool::Kodak::Type7 = (
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
+    CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
+    WRITABLE => 1,
+    FIRST_ENTRY => 0,
+    NOTES => q{
+        The maker notes of models such as the C340, C433, CC533, LS755, V803 and
+        V1003 seem to start with the camera serial number.  The C310, C315, C330,
+        C643, C743, CD33, CD43, CX7220 and CX7300 maker notes are also decoded using
+        this table, although the strings for these cameras don't conform to the
+        usual Kodak serial number format, and instead have the model name followed
+        by 8 digits.
+    },
+    0 => { # (not confirmed)
+        Name => 'SerialNumber',
+        Format => 'string[16]',
+        ValueConv => '$val=~s/\s+$//; $val', # remove trailing whitespace
+        ValueConvInv => '$val',
+    },
+);
+
+# Kodak IFD-format maker notes (ref PH)
+%Image::ExifTool::Kodak::IFD = (
+    WRITE_PROC => \&Image::ExifTool::Exif::WriteExif,
+    CHECK_PROC => \&Image::ExifTool::Exif::CheckExif,
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
+    NOTES => q{
+        Newer Kodak models such as the P712, P850, P880, Z612 and Z712 use standard
+        TIFF IFD format for the maker notes.  There is a large amount of information
+        stored in these maker notes (with apparently much duplication), but
+        relatively few tags have so far been decoded.
+    },
+    0xfc00 => {
+        Name => 'SubIFD0',
+        Groups => { 1 => 'MakerNotes' },    # SubIFD needs group 1 set
+        Flags => 'SubIFD',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::Kodak::SubIFD0',
+            Start => '$val',
+        },
+    },
+    # SubIFD1 and higher data is preceded by a TIFF byte order mark to indicate
+    # the byte ordering used
+    0xfc01 => {
+        Name => 'SubIFD1',
+        Groups => { 1 => 'MakerNotes' },    # SubIFD needs group 1 set
+        Flags => 'SubIFD',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::Kodak::SubIFD1',
+            Start => '$val',
+            Base => '$start',
+        },
+    },
+    0xfc02 => {
+        Name => 'SubIFD2',
+        Groups => { 1 => 'MakerNotes' },    # SubIFD needs group 1 set
+        Flags => 'SubIFD',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::Kodak::SubIFD2',
+            Start => '$val',
+            Base => '$start',
+        },
+    },
+    0xfc03 => {
+        Name => 'SubIFD3',
+        Groups => { 1 => 'MakerNotes' },    # SubIFD needs group 1 set
+        Flags => 'SubIFD',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::Kodak::SubIFD3',
+            Start => '$val',
+            Base => '$start',
+        },
+    },
+    # (SubIFD4 has the pointer zeroed in my samples, but support it
+    # in case it is used by future models -- ignored if pointer is zero)
+    0xfc04 => {
+        Name => 'SubIFD4',
+        Groups => { 1 => 'MakerNotes' },    # SubIFD needs group 1 set
+        Flags => 'SubIFD',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::Kodak::SubIFD4',
+            Start => '$val',
+            Base => '$start',
+        },
+    },
+    0xfc05 => {
+        Name => 'SubIFD5',
+        Groups => { 1 => 'MakerNotes' },    # SubIFD needs group 1 set
+        Flags => 'SubIFD',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::Kodak::SubIFD5',
+            Start => '$val',
+            Base => '$start',
+        },
+    },
+    0xff00 => {
+        Name => 'CameraInfo',
+        Groups => { 1 => 'MakerNotes' },    # SubIFD needs group 1 set
+        Flags => 'SubIFD',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::Kodak::CameraInfo',
+            Start => '$val',
+        },
+    },
+);
+
+# Kodak SubIFD0 tags (ref PH)
+%Image::ExifTool::Kodak::SubIFD0 = (
+    WRITE_PROC => \&Image::ExifTool::Exif::WriteExif,
+    CHECK_PROC => \&Image::ExifTool::Exif::CheckExif,
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
+    NOTES => 'SubIFD0 through SubIFD5 tags are used by the Z612 and Z712.',
+    0xfa02 => {
+        Name => 'SceneMode',
+        Writable => 'int16u',
+        PrintConv => {
+            1 => 'Sport',
+            3 => 'Portrait',
+            4 => 'Landscape',
+            6 => 'Beach',
+            7 => 'Night Portrait',
+            8 => 'Night Landscape',
+            9 => 'Snow',
+            10 => 'Text',
+            11 => 'Fireworks',
+            12 => 'Macro',
+            13 => 'Museum',
+            16 => 'Children',
+            17 => 'Program',
+            18 => 'Aperture Priority',
+            19 => 'Shutter Priority',
+            20 => 'Manual',
+            25 => 'Back Light',
+            28 => 'Candlelight',
+            29 => 'Sunset',
+            31 => 'Panorama Left-Right',
+            32 => 'Panorama Right-Left',
+            33 => 'Smart Scene',
+            34 => 'High ISO',
+        },
+    },
+    # 0xfa04 - values: 0 (normally), 2 (panorama shots)
+    # 0xfa0f - values: 0 (normally), 1 (macro?)
+    # 0xfa11 - some sort of FNumber (x 100)
+    0xfa19 => {
+        Name => 'SerialNumber', # (verified with Z712 - PH)
+        Writable => 'string',
+    },
+    0xfa1d => {
+        Name => 'KodakImageWidth',
+        Writable => 'int16u',
+    },
+    0xfa1e => {
+        Name => 'KodakImageHeight',
+        Writable => 'int16u',
+    },
+    0xfa20 => {
+        Name => 'SensorWidth',
+        Writable => 'int16u',
+    },
+    0xfa21 => {
+        Name => 'SensorHeight',
+        Writable => 'int16u',
+    },
+    0xfa23 => {
+        Name => 'FNumber',
+        Writable => 'int16u',
+        Priority => 0,
+        ValueConv => '$val / 100',
+        ValueConvInv => '$val * 100',
+    },
+    0xfa24 => {
+        Name => 'ExposureTime',
+        Writable => 'int32u',
+        Priority => 0,
+        ValueConv => '$val / 1e5',
+        ValueConvInv => '$val * 1e5',
+        PrintConv => 'Image::ExifTool::Exif::PrintExposureTime($val)',
+        PrintConvInv => 'eval $val',
+    },
+    0xfa2e => {
+        Name => 'ISO',
+        Writable => 'int16u',
+        Priority => 0,
+    },
+    0xfa3d => {
+        Name => 'OpticalZoom',
+        Writable => 'int16u',
+        ValueConv => '$val / 100',
+        ValueConvInv => '$val * 100',
+        PrintConv => 'sprintf("%.2f",$val)',
+        PrintConvInv => '$val=~s/ ?x//; $val',
+    },
+    0xfa46 => {
+        Name => 'ISO',
+        Writable => 'int16u',
+        Priority => 0,
+    },
+    # 0xfa4c - related to focal length (1=wide, 32=full zoom)
+    0xfa51 => {
+        Name => 'KodakImageWidth',
+        Writable => 'int16u',
+    },
+    0xfa52 => {
+        Name => 'KodakImageHeight',
+        Writable => 'int16u',
+    },
+    0xfa54 => {
+        Name => 'ThumbnailWidth',
+        Writable => 'int16u',
+    },
+    0xfa55 => {
+        Name => 'ThumbnailHeight',
+        Writable => 'int16u',
+    },
+    0xfa57 => {
+        Name => 'PreviewWidth',
+        Writable => 'int16u',
+    },
+    0xfa58 => {
+        Name => 'PreviewHeight',
+        Writable => 'int16u',
+    },
+);
+
+# Kodak SubIFD1 tags (ref PH)
+%Image::ExifTool::Kodak::SubIFD1 = (
+    PROCESS_PROC => \&ProcessKodakIFD,
+    WRITE_PROC => \&WriteKodakIFD,
+    CHECK_PROC => \&Image::ExifTool::Exif::CheckExif,
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
+    0x0027 => {
+        Name => 'ISO',
+        Writable => 'int16u',
+        Priority => 0,
+    },
+    0x0028 => {
+        Name => 'ISO',
+        Writable => 'int16u',
+        Priority => 0,
+    },
+);
+
+my %sceneModeUsed = (
+    0 => 'Program',
+    2 => 'Aperture Priority',
+    3 => 'Shutter Priority',
+    4 => 'Manual',
+    5 => 'Portrait',
+    6 => 'Sport',
+    7 => 'Children',
+    8 => 'Museum',
+    10 => 'High ISO',
+    11 => 'Text',
+    12 => 'Macro',
+    13 => 'Back Light',
+    16 => 'Landscape',
+    17 => 'Night Landscape',
+    18 => 'Night Portrait',
+    19 => 'Snow',
+    20 => 'Beach',
+    21 => 'Fireworks',
+    22 => 'Sunset',
+    23 => 'Candlelight',
+    28 => 'Panorama',
+);
+
+# Kodak SubIFD2 tags (ref PH)
+%Image::ExifTool::Kodak::SubIFD2 = (
+    PROCESS_PROC => \&ProcessKodakIFD,
+    WRITE_PROC => \&WriteKodakIFD,
+    CHECK_PROC => \&Image::ExifTool::Exif::CheckExif,
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
+    0x6002 => {
+        Name => 'SceneModeUsed',
+        Writable => 'int32u',
+        PrintConv => \%sceneModeUsed,
+    },
+    0x6006 => {
+        Name => 'OpticalZoom',
+        Writable => 'int32u',
+        ValueConv => '$val / 100',
+        ValueConvInv => '$val * 100',
+        PrintConv => 'sprintf("%.2f",$val)',
+        PrintConvInv => '$val=~s/ ?x//; $val',
+    },
+    # 0x6009 - some sort of FNumber (x 100)
+    0x6103 => {
+        Name => 'MaxAperture',
+        Writable => 'int32u',
+        ValueConv => '$val / 100',
+        ValueConvInv => '$val * 100',
+    },
+    0xf002 => {
+        Name => 'SceneModeUsed',
+        Writable => 'int32u',
+        PrintConv => \%sceneModeUsed,
+    },
+    0xf006 => {
+        Name => 'OpticalZoom',
+        Writable => 'int32u',
+        ValueConv => '$val / 100',
+        ValueConvInv => '$val * 100',
+        PrintConv => 'sprintf("%.2f",$val)',
+        PrintConvInv => '$val=~s/ ?x//; $val',
+    },
+    # 0xf009 - some sort of FNumber (x 100)
+    0xf103 => {
+        Name => 'FNumber',
+        Writable => 'int32u',
+        Priority => 0,
+        ValueConv => '$val / 100',
+        ValueConvInv => '$val * 100',
+    },
+    0xf104 => {
+        Name => 'ExposureTime',
+        Writable => 'int32u',
+        Priority => 0,
+        ValueConv => '$val / 1e6',
+        ValueConvInv => '$val * 1e6',
+        PrintConv => 'Image::ExifTool::Exif::PrintExposureTime($val)',
+        PrintConvInv => 'eval $val',
+    },
+    0xf105 => {
+        Name => 'ISO',
+        Writable => 'int32u',
+        Priority => 0,
+        ValueConv => '$val / 10',
+        ValueConvInv => '$val * 10',
+    },
+);
+
+# Kodak SubIFD3 tags (ref PH)
+%Image::ExifTool::Kodak::SubIFD3 = (
+    PROCESS_PROC => \&ProcessKodakIFD,
+    WRITE_PROC => \&WriteKodakIFD,
+    CHECK_PROC => \&Image::ExifTool::Exif::CheckExif,
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
+    0x1000 => {
+        Name => 'OpticalZoom',
+        Writable => 'int16u',
+        ValueConv => '$val / 100',
+        ValueConvInv => '$val * 100',
+        PrintConv => 'sprintf("%.2f",$val)',
+        PrintConvInv => '$val=~s/ ?x//; $val',
+    },
+    # 0x1002 - related to focal length (1=wide, 32=full zoom)
+    # 0x1006 - pictures remaining? (gradually decreases as pictures are taken)
+);
+
+# Kodak SubIFD4 tags (ref PH)
+%Image::ExifTool::Kodak::SubIFD4 = (
+    PROCESS_PROC => \&ProcessKodakIFD,
+    WRITE_PROC => \&WriteKodakIFD,
+    CHECK_PROC => \&Image::ExifTool::Exif::CheckExif,
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
+);
+
+# Kodak SubIFD5 tags (ref PH)
+%Image::ExifTool::Kodak::SubIFD5 = (
+    PROCESS_PROC => \&ProcessKodakIFD,
+    WRITE_PROC => \&WriteKodakIFD,
+    CHECK_PROC => \&Image::ExifTool::Exif::CheckExif,
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
+    0x000f => {
+        Name => 'OpticalZoom',
+        Writable => 'int16u',
+        ValueConv => '$val / 100',
+        ValueConvInv => '$val * 100',
+        PrintConv => 'sprintf("%.2f",$val)',
+        PrintConvInv => '$val=~s/ ?x//; $val',
+    },
+);
+
+# Decoded from P712, P850 and P880 samples (ref PH)
+%Image::ExifTool::Kodak::CameraInfo = (
+    WRITE_PROC => \&Image::ExifTool::Exif::WriteExif,
+    CHECK_PROC => \&Image::ExifTool::Exif::CheckExif,
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
+    NOTES => 'These tags are used by the  P712, P850 and P880.',
+    0xf900 => {
+        Name => 'SensorWidth',
+        Writable => 'int16u',
+        Notes => 'effective sensor size',
+    },
+    0xf901 => {
+        Name => 'SensorHeight',
+        Writable => 'int16u',
+    },
+    0xf902 => {
+        Name => 'BayerPattern',
+        Writable => 'string',
+    },
+    0xf903 => {
+        Name => 'SensorFullWidth',
+        Writable => 'int16u',
+        Notes => 'includes black border?',
+    },
+    0xf904 => {
+        Name => 'SensorFullHeight',
+        Writable => 'int16u',
+    },
+    0xf907 => {
+        Name => 'KodakImageWidth',
+        Writable => 'int16u',
+    },
+    0xf908 => {
+        Name => 'KodakImageHeight',
+        Writable => 'int16u',
+    },
+    0xfa00 => {
+        Name => 'KodakInfoType',
+        Writable => 'string',
+    },
+    0xfa04 => {
+        Name => 'SerialNumber', # (unverified)
+        Writable => 'string',
+    },
+    0xfd04 => {
+        Name => 'FNumber',
+        Writable => 'int16u',
+        Priority => 0,
+        ValueConv => '$val / 100',
+        ValueConvInv => '$val * 100',
+    },
+    0xfd05 => {
+        Name => 'ExposureTime',
+        Writable => 'int32u',
+        Priority => 0,
+        ValueConv => '$val / 1e6',
+        ValueConvInv => '$val * 1e6',
+        PrintConv => 'Image::ExifTool::Exif::PrintExposureTime($val)',
+        PrintConvInv => 'eval $val',
+    },
+    0xfd06 => {
+        Name => 'ISO',
+        Writable => 'int16u',
+        Priority => 0,
+    },
+);
+
+# treat unknown maker notes as binary data (allows viewing with -U)
+%Image::ExifTool::Kodak::Unknown = (
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    FIRST_ENTRY => 0,
+);
+
+# Kodak APP3 "Meta" tags (ref 2)
+%Image::ExifTool::Kodak::Meta = (
+    GROUPS => { 0 => 'Meta', 1 => 'MetaIFD', 2 => 'Image'},
+    NOTES => q{
+        These tags are found in the APP3 "Meta" segment of JPEG images from Kodak
+        cameras such as the DC280, DC3400, DC5000 and MC3.  The structure of this
+        segment is similar to the APP1 "Exif" segment, but a different set of tags
+        is used.
+    },
+    0xc350 => 'FilmProductCode',
+    0xc351 => 'ImageSourceEK',
+    0xc352 => 'CaptureConditionsPAR',
+    0xc353 => {
+        Name => 'CameraOwner',
+        PrintConv => 'Image::ExifTool::Exif::ConvertExifText($self,$val)',
+    },
+    0xc354 => {
+        Name => 'SerialNumber',
+        Groups => { 2 => 'Camera' },
+    },
+    0xc355 => 'UserSelectGroupTitle',
+    0xc356 => 'DealerIDNumber',
+    0xc357 => 'CaptureDeviceFID',
+    0xc358 => 'EnvelopeNumber',
+    0xc359 => 'FrameNumber',
+    0xc35a => 'FilmCategory',
+    0xc35b => 'FilmGencode',
+    0xc35c => 'ModelAndVersion',
+    0xc35d => 'FilmSize',
+    0xc35e => 'SBA_RGBShifts',
+    0xc35f => 'SBAInputImageColorspace',
+    0xc360 => 'SBAInputImageBitDepth',
+    0xc361 => 'SBAExposureRecord',
+    0xc362 => 'UserAdjSBA_RGBShifts',
+    0xc363 => 'ImageRotationStatus',
+    0xc364 => 'RollGuidElements',
+    0xc365 => 'MetadataNumber',
+    0xc366 => 'EditTagArray',
+    0xc367 => 'Magnification',
+    0xc36c => 'NativeXResolution',
+    0xc36d => 'NativeYResolution',
+    0xc36e => {
+        Name => 'KodakEffectsIFD',
+        Flags => 'SubIFD',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::Kodak::SpecialEffects',
+            Start => '$val',
+        },
+    },
+    0xc36f => {
+        Name => 'KodakBordersIFD',
+        Flags => 'SubIFD',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::Kodak::Borders',
+            Start => '$val',
+        },
+    },
+    0xc37a => 'NativeResolutionUnit',
+    0xc418 => 'SourceImageDirectory',
+    0xc419 => 'SourceImageFileName',
+    0xc41a => 'SourceImageVolumeName',
+    0xc46c => 'PrintQuality',
+    0xc46e => 'ImagePrintStatus',
+);
+
+# Kodak APP3 "Meta" Special Effects sub-IFD (ref 2)
+%Image::ExifTool::Kodak::SpecialEffects = (
+    GROUPS => { 0 => 'Meta', 1 => 'KodakEffectsIFD', 2 => 'Image'},
+    0 => 'DigitalEffectsVersion',
+    1 => {
+        Name => 'DigitalEffectsName',
+        PrintConv => 'Image::ExifTool::Exif::ConvertExifText($self,$val)',
+    },
+    2 => 'DigitalEffectsType',
+);
+
+# Kodak APP3 "Meta" Borders sub-IFD (ref 2)
+%Image::ExifTool::Kodak::Borders = (
+    GROUPS => { 0 => 'Meta', 1 => 'KodakBordersIFD', 2 => 'Image'},
+    0 => 'BordersVersion',
+    1 => {
+        Name => 'BorderName',
+        PrintConv => 'Image::ExifTool::Exif::ConvertExifText($self,$val)',
+    },
+    2 => 'BorderID',
+    3 => 'BorderLocation',
+    4 => 'BorderType',
+    8 => 'WatermarkType',
+);
+
+# Kodak composite tags
+%Image::ExifTool::Kodak::Composite = (
+    DateCreated => {
+        Groups => { 2 => 'Time' },
+        Require => {
+            0 => 'Kodak:YearCreated',
+            1 => 'Kodak:MonthDayCreated',
+        },
+        ValueConv => '"$val[0]:$val[1]"',
+    },
+);
+
+# add our composite tags
+Image::ExifTool::AddCompositeTags('Image::ExifTool::Kodak');
+
+#------------------------------------------------------------------------------
+# Process Kodak IFD (with leading byte order mark)
+# Inputs: 0) ExifTool object ref, 1) dirInfo hash ref, 2) tag table ref
+# Returns: 1 on success, otherwise returns 0 and sets a Warning
+sub ProcessKodakIFD($$$)
+{
+    my ($exifTool, $dirInfo, $tagTablePtr) = @_;
+    my $dirStart = $$dirInfo{DirStart} || 0;
+    return 1 if $dirStart <= 0 or $dirStart + 2 > $$dirInfo{DataLen};
+    my $byteOrder = substr(${$$dirInfo{DataPt}}, $dirStart, 2);
+    return 1 unless Image::ExifTool::SetByteOrder($byteOrder);
+    $$dirInfo{DirStart} += 2;   # skip byte order mark
+    $$dirInfo{DirLen} -= 2;
+    if ($exifTool->{HTML_DUMP}) {
+        my $base = $$dirInfo{Base} + $$dirInfo{DataPos};
+        $exifTool->HtmlDump($dirStart+$base, 2, "Byte Order Mark");
+    }
+    return Image::ExifTool::Exif::ProcessExif($exifTool, $dirInfo, $tagTablePtr);
+}
+
+#------------------------------------------------------------------------------
+# Write Kodak IFD (with leading byte order mark)
+# Inputs: 0) ExifTool object ref, 1) source dirInfo ref, 2) tag table ref
+# Returns: Exif data block (may be empty if no Exif data) or undef on error
+sub WriteKodakIFD($$$)
+{
+    my ($exifTool, $dirInfo, $tagTablePtr) = @_;
+    my $dirStart = $$dirInfo{DirStart} || 0;
+    return '' if $dirStart <= 0 or $dirStart + 2 > $$dirInfo{DataLen};
+    my $byteOrder = substr(${$$dirInfo{DataPt}}, $dirStart, 2);
+    return '' unless Image::ExifTool::SetByteOrder($byteOrder);
+    $$dirInfo{DirStart} += 2;   # skip byte order mark
+    $$dirInfo{DirLen} -= 2;
+    my $buff = Image::ExifTool::Exif::WriteExif($exifTool, $dirInfo, $tagTablePtr);
+    return $buff unless defined $buff and length $buff;
+    # apply one-time fixup for length of byte order mark
+    if ($$dirInfo{Fixup}) {
+        $dirInfo->{Fixup}->{Shift} += 2;
+        $$dirInfo{Fixup}->ApplyFixup(\$buff);
+        delete $$dirInfo{Fixup};
+    }
+    return Image::ExifTool::GetByteOrder() . $buff;
+}
+
+1;  # end
+
+__END__
+
+=head1 NAME
+
+Image::ExifTool::Kodak - Kodak EXIF maker notes and APP3 "Meta" tags
+
+=head1 SYNOPSIS
+
+This module is loaded automatically by Image::ExifTool when required.
+
+=head1 DESCRIPTION
+
+This module contains definitions required by Image::ExifTool to
+interpret Kodak maker notes EXIF meta information.
+
+=head1 AUTHOR
+
+Copyright 2003-2007, Phil Harvey (phil at owl.phy.queensu.ca)
+
+This library is free software; you can redistribute it and/or modify it
+under the same terms as Perl itself.
+
+=head1 REFERENCES
+
+=over 4
+
+=item L<Image::MetaData::JPEG|Image::MetaData::JPEG>
+
+=item L<http://www.ozhiker.com/electronics/pjmt/jpeg_info/meta.html>
+
+=item (...plus lots of testing with my daughter's CX4200!)
+
+=back
+
+=head1 SEE ALSO
+
+L<Image::ExifTool::TagNames/Kodak Tags>,
+L<Image::ExifTool(3pm)|Image::ExifTool>
+
+=cut
Index: /gsdl/trunk/perllib/cpan/Image/ExifTool/KyoceraRaw.pm
===================================================================
--- /gsdl/trunk/perllib/cpan/Image/ExifTool/KyoceraRaw.pm	(revision 16842)
+++ /gsdl/trunk/perllib/cpan/Image/ExifTool/KyoceraRaw.pm	(revision 16842)
@@ -0,0 +1,171 @@
+#------------------------------------------------------------------------------
+# File:         KyoceraRaw.pm
+#
+# Description:  Read Kyocera RAW meta information
+#
+# Revisions:    02/17/2006 - P. Harvey Created
+#
+# References:   1) http://www.cybercom.net/~dcoffin/dcraw/
+#------------------------------------------------------------------------------
+
+package Image::ExifTool::KyoceraRaw;
+
+use strict;
+use vars qw($VERSION);
+use Image::ExifTool qw(:DataAccess);
+
+$VERSION = '1.00';
+
+sub ProcessRAW($$);
+
+# utility to reverse order of characters in a string
+sub ReverseString($) { pack('C*',reverse unpack('C*',shift)) }
+
+# Contax N Digital tags (ref PH)
+%Image::ExifTool::KyoceraRaw::Main = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
+    NOTES => 'Tags for Kyocera Contax N Digital RAW images.',
+    0x01 => {
+        Name => 'FirmwareVersion',
+        Format => 'string[10]',
+        ValueConv => \&ReverseString,
+    },
+    0x0c => {
+        Name => 'Model',
+        Format => 'string[12]',
+        ValueConv => \&ReverseString,
+    },
+    0x19 => { #1
+        Name => 'Make',
+        Format => 'string[7]',
+        ValueConv => \&ReverseString,
+    },
+    0x21 => { #1
+        Name => 'DateTimeOriginal',
+        Groups => { 2 => 'Time' },
+        Format => 'string[20]',
+        ValueConv => \&ReverseString,
+    },
+    0x34 => {
+        Name => 'ISO',
+        Groups => { 2 => 'Image' },
+        Format => 'int32u',
+        PrintConv => {
+            7 => 25,
+            8 => 32,
+            9 => 40,
+            10 => 50,
+            11 => 64,
+            12 => 80,
+            13 => 100,
+            14 => 125,
+            15 => 160,
+            16 => 200,
+            17 => 250,
+            18 => 320,
+            19 => 400,
+        },
+    },
+    0x38 => {
+        Name => 'ExposureTime',
+        Groups => { 2 => 'Image' },
+        Format => 'int32u',
+        ValueConv => '2**($val / 8) / 16000',
+        PrintConv => 'Image::ExifTool::Exif::PrintExposureTime($val)',
+    },
+    0x3c => { #1
+        Name => 'WB_RGGBLevels',
+        Groups => { 2 => 'Image' },
+        Format => 'int32u[4]',
+    },
+    0x58 => {
+        Name => 'FNumber',
+        Groups => { 2 => 'Image' },
+        Format => 'int32u',
+        ValueConv => '2**($val/16)',
+        PrintConv => 'sprintf("%.2g",$val)',
+    },
+    0x68 => {
+        Name => 'MaxAperture',
+        Format => 'int32u',
+        ValueConv => '2**($val/16)',
+        PrintConv => 'sprintf("%.2g",$val)',
+    },
+    0x70 => {
+        Name => 'FocalLength',
+        Format => 'int32u',
+        PrintConv => '"${val}mm"',
+    },
+    0x7c => {
+        Name => 'Lens',
+        Format => 'string[32]',
+    },
+);
+
+#------------------------------------------------------------------------------
+# Extract information from Kyocera RAW image
+# Inputs: 0) ExifTool object reference, 1) dirInfo reference
+# Returns: 1 if this was a valid Kyocera RAW image
+sub ProcessRAW($$)
+{
+    my ($exifTool, $dirInfo) = @_;
+    my $raf = $$dirInfo{RAF};
+    my $size = 156; # size of header
+    my $buff;
+
+    $raf->Read($buff, $size) == $size or return 0;
+    # validate Make string ('KYOCERA' reversed)
+    substr($buff, 0x19, 7) eq 'ARECOYK' or return 0;
+    $exifTool->SetFileType();
+    SetByteOrder('MM');
+    my %dirInfo = (
+        DataPt => \$buff,
+        DataPos => 0,
+        DataLen => $size,
+        DirStart => 0,
+        DirLen => $size,
+    );
+    my $tagTablePtr = Image::ExifTool::GetTagTable('Image::ExifTool::KyoceraRaw::Main');
+    $exifTool->ProcessDirectory(\%dirInfo, $tagTablePtr);
+    return 1;
+}
+
+1;  # end
+
+__END__
+
+=head1 NAME
+
+Image::ExifTool::KyoceraRaw - Read Kyocera RAW meta information
+
+=head1 SYNOPSIS
+
+This module is loaded automatically by Image::ExifTool when required.
+
+=head1 DESCRIPTION
+
+This module contains definitions required by Image::ExifTool to read
+meta information from Kyocera Contax N Digital RAW images.
+
+=head1 AUTHOR
+
+Copyright 2003-2007, Phil Harvey (phil at owl.phy.queensu.ca)
+
+This library is free software; you can redistribute it and/or modify it
+under the same terms as Perl itself.
+
+=head1 REFERENCES
+
+=over 4
+
+=item L<http://www.cybercom.net/~dcoffin/dcraw/>
+
+=back
+
+=head1 SEE ALSO
+
+L<Image::ExifTool::TagNames/KyoceraRaw Tags>,
+L<Image::ExifTool(3pm)|Image::ExifTool>
+
+=cut
Index: /gsdl/trunk/perllib/cpan/Image/ExifTool/Leaf.pm
===================================================================
--- /gsdl/trunk/perllib/cpan/Image/ExifTool/Leaf.pm	(revision 16842)
+++ /gsdl/trunk/perllib/cpan/Image/ExifTool/Leaf.pm	(revision 16842)
@@ -0,0 +1,514 @@
+#------------------------------------------------------------------------------
+# File:         Leaf.pm
+#
+# Description:  Read Creo Leaf EXIF meta information
+#
+# Revisions:    09/28/2005  - P. Harvey Created
+#------------------------------------------------------------------------------
+
+package Image::ExifTool::Leaf;
+
+use strict;
+use vars qw($VERSION);
+use Image::ExifTool qw(:DataAccess :Utils);
+use Image::ExifTool::Exif;
+
+$VERSION = '1.01';
+
+sub ProcessLeaf($$$);
+
+%Image::ExifTool::Leaf::Main = (
+    PROCESS_PROC => \&ProcessLeaf,
+    GROUPS => { 0 => 'Leaf', 2 => 'Camera' },
+    NOTES => q{
+These tags are found in .MOS images from Leaf digital camera backs as
+written by Creo Leaf Capture.  They exist within the Leaf-specific directory
+structure of EXIF tag 0x8606. The tables below list observed Leaf tags,
+however ExifTool will extract any tags found in the Leaf directories even if
+they don't appear in these tables.
+    },
+    icc_camera_profile => {
+        Name => 'ICC_Profile',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::ICC_Profile::Main',
+        },
+    },
+    icc_rgb_ws_profile => {
+        Name => 'RGB_Profile',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::ICC_Profile::Main',
+        },
+    },
+    camera_profile => {
+        Name => 'CameraProfile',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::Leaf::CameraProfile',
+        },
+    },
+    JPEG_preview_data => {
+        %Image::ExifTool::previewImageTagInfo,
+    },
+    JPEG_preview_info => 'PreviewInfo',
+    icc_camera_to_tone_space_flow => {
+        Name => 'ToneSpaceFlow',
+        Description => 'ICC To Tone Space Flow',
+        Format => 'int16u',
+    },
+    icc_camera_to_tone_matrix => {
+        Name => 'ToneMatrix',
+        Description => 'ICC To Tone Matrix',
+        Format => 'int8u',
+        Binary => 1,
+    },
+    PDA_histogram_data => {
+        Name => 'PDAHistogram',
+        Binary => 1,
+    },
+    pattern_ratation_angle => {
+        Name => 'PatternAngle',
+        Description => 'Pattern Rotation Angle',
+        Format => 'int16u',
+        Notes => '"ratation" is not a typo',
+    },
+    back_serial_number => {
+        Name => 'BackSerial',
+        Description => 'Back Serial Number',
+        PrintConv => '$val =~ s/ .*//; $val',
+    },
+    image_offset => { Format => 'int16u' },
+);
+
+%Image::ExifTool::Leaf::CameraProfile = (
+    PROCESS_PROC => \&ProcessLeaf,
+    GROUPS => { 0 => 'Leaf', 2 => 'Camera' },
+    CamProf_version => 'CameraProfileVersion',
+    CamProf_name    => 'CameraName',
+    CamProf_type    => 'CameraType',
+    CamProf_back_type => 'CameraBackType',
+    CamProf_back_type => {
+        Name => 'CameraBackType',
+    },
+    CamProf_capture_profile => {
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::Leaf::CaptureProfile',
+        },
+    },
+    CamProf_image_profile => {
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::Leaf::ImageProfile',
+        },
+    },
+);
+
+%Image::ExifTool::Leaf::CaptureProfile = (
+    PROCESS_PROC => \&ProcessLeaf,
+    GROUPS => { 0 => 'Leaf', 2 => 'Image' },
+    CaptProf_version        => {},
+    CaptProf_name           => {},
+    CaptProf_type           => {},
+    CaptProf_back_type      => {},
+    CaptProf_serial_number  => {
+        Name => 'CaptureSerial',
+        Description => 'Capture Serial Number',
+        PrintConv => '$val =~ s/ .*//; $val',
+    },
+    CaptProf_image_offset       => {},
+    CaptProf_luminance_consts   => {},
+    CaptProf_xy_offset_info     => 'XYOffsetInfo',
+    CaptProf_color_matrix       => {},
+    CaptProf_reconstruction_type=> {},
+    CaptProf_image_fields       => {},
+    CaptProf_image_bounds       => {},
+    CaptProf_number_of_planes   => {},
+    CaptProf_raw_data_rotation  => {},
+    CaptProf_color_averages     => {},
+    CaptProf_mosaic_pattern     => {},
+    CaptProf_dark_correction_type=>{},
+    CaptProf_right_dark_rect    => {},
+    CaptProf_left_dark_rect     => {},
+    CaptProf_center_dark_rect   => {},
+    CaptProf_CCD_rect           => {},
+    CaptProf_CCD_valid_rect     => {},
+    CaptProf_CCD_video_rect     => {},
+);
+
+%Image::ExifTool::Leaf::ImageProfile = (
+    PROCESS_PROC => \&ProcessLeaf,
+    GROUPS => { 0 => 'Leaf', 2 => 'Image' },
+    ImgProf_version         => {},
+    ImgProf_name            => {},
+    ImgProf_type            => {},
+    ImgProf_back_type       => {},
+    ImgProf_shoot_setup => {
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::Leaf::ShootSetup',
+        },
+    },
+    ImgProf_image_status    => {},
+    ImgProf_rotation_angle  => {},
+);
+
+%Image::ExifTool::Leaf::ShootSetup = (
+    PROCESS_PROC => \&ProcessLeaf,
+    GROUPS => { 0 => 'Leaf', 2 => 'Image' },
+    ShootObj_version        => {},
+    ShootObj_name           => {},
+    ShootObj_type           => {},
+    ShootObj_back_type      => {},
+    ShootObj_capture_setup => {
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::Leaf::CaptureSetup',
+        },
+    },
+    ShootObj_color_setup => {
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::Leaf::ColorSetup',
+        },
+    },
+    ShootObj_save_setup => {
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::Leaf::SaveSetup',
+        },
+    },
+    ShootObj_camera_setup => {
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::Leaf::CameraSetup',
+        },
+    },
+    ShootObj_look_header => {
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::Leaf::LookHeader',
+        },
+    },
+);
+
+%Image::ExifTool::Leaf::CaptureSetup = (
+    PROCESS_PROC => \&ProcessLeaf,
+    GROUPS => { 0 => 'Leaf', 2 => 'Image' },
+    CaptureObj_version      => {},
+    CaptureObj_name         => {},
+    CaptureObj_type         => {},
+    CaptureObj_back_type    => {},
+    CaptureObj_neutals => {
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::Leaf::Neutrals',
+        },
+    },
+    CaptureObj_selection => {
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::Leaf::Selection',
+        },
+    },
+    CaptureObj_tone_curve => {
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::Leaf::ToneCurve',
+        },
+    },
+    CaptureObj_sharpness => {
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::Leaf::Sharpness',
+        },
+    },
+    CaptureObj_single_quality => {},
+    CaptureObj_Multi_quality  => {},
+);
+
+%Image::ExifTool::Leaf::Neutrals = (
+    PROCESS_PROC => \&ProcessLeaf,
+    GROUPS => { 0 => 'Leaf', 2 => 'Image' },
+    NeutObj_version         => {},
+    NeutObj_name            => {},
+    NeutObj_type            => {},
+    NeutObj_back_type       => {},
+    NeutObj_neutrals        => {},
+    NeutObj_color_casts     => {},
+    NeutObj_shadow_end_points    => {},
+    NeutObj_highlight_end_points => {},
+);
+
+%Image::ExifTool::Leaf::Selection = (
+    PROCESS_PROC => \&ProcessLeaf,
+    GROUPS => { 0 => 'Leaf', 2 => 'Image' },
+    SelObj_version          => {},
+    SelObj_name             => {},
+    SelObj_type             => {},
+    SelObj_back_type        => {},
+    SelObj_rect             => {},
+    SelObj_resolution       => {},
+    SelObj_scale            => {},
+    SelObj_locks            => {},
+    SelObj_orientation      => {},
+);
+
+%Image::ExifTool::Leaf::ToneCurve = (
+    PROCESS_PROC => \&ProcessLeaf,
+    GROUPS => { 0 => 'Leaf', 2 => 'Image' },
+    ToneObj_version         => {},
+    ToneObj_name            => {},
+    ToneObj_type            => {},
+    ToneObj_back_type       => {},
+    ToneObj_npts            => {},
+    ToneObj_tones           => {},
+    ToneObj_gamma           => {},
+);
+
+%Image::ExifTool::Leaf::Sharpness = (
+    PROCESS_PROC => \&ProcessLeaf,
+    GROUPS => { 0 => 'Leaf', 2 => 'Image' },
+    SharpObj_version        => {},
+    SharpObj_name           => {},
+    SharpObj_type           => {},
+    SharpObj_back_type      => {},
+    SharpObj_sharp_method   => {},
+    SharpObj_data_len       => {},
+    SharpObj_sharp_info     => {},
+);
+
+%Image::ExifTool::Leaf::ColorSetup = (
+    PROCESS_PROC => \&ProcessLeaf,
+    GROUPS => { 0 => 'Leaf', 2 => 'Image' },
+    ColorObj_version        => {},
+    ColorObj_name           => {},
+    ColorObj_type           => {},
+    ColorObj_back_type      => {},
+    ColorObj_has_ICC        => {},
+    ColorObj_input_profile  => {},
+    ColorObj_output_profile => {},
+    ColorObj_color_mode     => {},
+    ColorObj_color_type     => {},
+);
+
+%Image::ExifTool::Leaf::SaveSetup = (
+    PROCESS_PROC => \&ProcessLeaf,
+    GROUPS => { 0 => 'Leaf', 2 => 'Other' },
+    SaveObj_version         => {},
+    SaveObj_name            => {},
+    SaveObj_type            => {},
+    SaveObj_back_type       => {},
+    SaveObj_leaf_auto_active=> {},
+    SaveObj_leaf_hot_folder => {},
+    SaveObj_leaf_output_file_type => {},
+    SaveObj_leaf_auto_base_name   => {},
+    SaveObj_leaf_save_selection   => {},
+    SaveObj_leaf_open_proc_HDR    => {},
+    SaveObj_std_auto_active       => {},
+    SaveObj_std_hot_folder        => {},
+    SaveObj_std_output_file_type  => {},
+    SaveObj_std_output_color_mode => {},
+    SaveObj_std_output_bit_depth  => {},
+    SaveObj_std_base_name         => {},
+    SaveObj_std_save_selection    => {},
+    SaveObj_std_oxygen            => {},
+    SaveObj_std_open_in_photoshop => {},
+    SaveObj_std_scaled_output     => {},
+    SaveObj_std_sharpen_output    => {},
+);
+
+%Image::ExifTool::Leaf::CameraSetup = (
+    PROCESS_PROC => \&ProcessLeaf,
+    GROUPS => { 0 => 'Leaf', 2 => 'Camera' },
+    CameraObj_version       => {},
+    CameraObj_name          => {},
+    CameraObj_type          => {},
+    CameraObj_back_type     => {},
+    CameraObj_ISO_speed     => {},
+    CameraObj_strobe        => {},
+    CameraObj_camera_type   => {},
+    CameraObj_lens_type     => {},
+    CameraObj_lens_ID       => {},
+);
+
+%Image::ExifTool::Leaf::LookHeader = (
+    PROCESS_PROC => \&ProcessLeaf,
+    GROUPS => { 0 => 'Leaf', 2 => 'Other' },
+    LookHead_version        => {},
+    LookHead_name           => {},
+    LookHead_type           => {},
+    LookHead_back_type      => {},
+);
+
+# tag table for any unknown Leaf directories
+%Image::ExifTool::Leaf::Unknown = (
+    PROCESS_PROC => \&ProcessLeaf,
+    GROUPS => { 0 => 'Leaf', 2 => 'Unknown' },
+);
+
+# table for Leaf SubIFD entries
+%Image::ExifTool::Leaf::SubIFD = (
+    GROUPS => { 0 => 'EXIF', 1 => 'LeafSubIFD', 2 => 'Image'},
+    WRITE_PROC => \&Image::ExifTool::Exif::WriteExif,
+    NOTES => q{
+Leaf also writes a TIFF-format sub-IFD inside IFD0 of a MOS image.  No tags
+in this sub-IFD are currently known, except for tag 0x8606 which really
+shouldn't be here anyway (so it doesn't appear in the table below) because
+it duplicates a reference to the same data of tag 0x8606 in IFD0.
+    },
+);
+
+# prepare Leaf tables by generating tag 'Name' and table 'NOTES'
+{
+    my @tableList = ( 'Image::ExifTool::Leaf::Main' );
+    my ($tag, %doneTable);
+    # keep prefix in tag name of common tags
+    my %keepPrefix = ( Version=>1, Name=>1, Type=>1, BackType=>1 );
+    while (@tableList) {
+        my $table = shift @tableList;
+        next if $doneTable{$table};
+        my $prefix = ($table =~ /::Main$/) ? undef : '';
+        $doneTable{$table} = 1;
+        no strict 'refs';
+        $table = \%$table;
+        use strict 'refs';
+        foreach $tag (keys %$table) {
+            my $tagInfo = $$table{$tag};
+            next unless ref $tagInfo eq 'HASH';
+            next if $tag eq 'GROUPS';
+            if (defined $prefix and not $prefix) {
+                ($prefix = $tag) =~ s/_.*//;
+            }
+            unless ($$tagInfo{Name}) {
+                my $name;
+                ($name = $tag) =~ s/_(.)/\U$1/g;
+                if ($prefix) {
+                    $name =~ s/^$prefix//;
+                    $name = $prefix . $name if $keepPrefix{$name};
+                }
+                $$tagInfo{Name} = ucfirst($name);
+            }
+            next unless $$tagInfo{SubDirectory};
+            my $subTable = $tagInfo->{SubDirectory}->{TagTable};
+            next unless $subTable =~ /::Leaf::/;
+            push @tableList, $subTable;
+        }
+        next unless $prefix;
+        $$table{NOTES} = "All B<Tag ID>'s in the following table have a " .
+                         "leading '${prefix}_' which\nhas been removed.\n";
+    }
+}
+
+#------------------------------------------------------------------------------
+# Process Leaf information
+# Inputs: 0) ExifTool object reference
+#         1) Reference to directory information hash
+#         2) Pointer to tag table for this directory
+# Returns: 1 on success, otherwise returns 0 and sets a Warning
+sub ProcessLeaf($$$)
+{
+    my ($exifTool, $dirInfo, $tagTablePtr) = @_;
+    my $dataPt = $$dirInfo{DataPt};
+    my $dirStart = $$dirInfo{DirStart} || 0;
+    my $dirLen = $$dirInfo{DirLen} || $$dirInfo{DataLen} - $dirStart;
+    my $dirEnd = $dirStart + $dirLen;
+    my $verbose = $exifTool->Options('Verbose');
+    my $pos = $dirStart;
+    my $hdrLen = 52;    # header length for PKTS information
+    my $success;
+
+    $verbose and $exifTool->VerboseDir('Leaf');
+    for (;;) {
+        last if $pos + $hdrLen > $dirEnd;
+        my $header = substr($$dataPt, $pos, $hdrLen);
+        last unless substr($header, 0, 4) eq 'PKTS';
+        $success = 1;
+        my $size = Get32u(\$header, 48);
+        $pos += $hdrLen;
+        if ($pos + $size > $dirEnd) {
+            $exifTool->Warn('Truncated Leaf data');
+            last;
+        }
+        my $tag = substr($header, 8, 40);
+        $tag =~ s/\0.*//;
+        next unless $tag;
+        my $tagInfo = $exifTool->GetTagInfo($tagTablePtr, $tag);
+        # generate tag info for unknown tags
+        my $val;
+        if ($tagInfo and $$tagInfo{Format}) {
+            $val = ReadValue($dataPt, $pos, $$tagInfo{Format}, undef, $size);
+        } else {
+            $val = substr($$dataPt, $pos, $size);
+        }
+        unless ($tagInfo) {
+            my $name = ucfirst($tag);
+            $name =~ s/_(.)/\U$1/g;
+            if ($val =~ /^PKTS\0\0\0\x01/) {
+                # also unpack unknown directories
+                $tagInfo = {
+                    Name => $name,
+                    SubDirectory => { TagTable => 'Image::ExifTool::Leaf::Unknown' },
+                };
+            } elsif ($tagTablePtr ne \%Image::ExifTool::Leaf::Main or
+                     $exifTool->Options('Unknown'))
+            {
+                $tagInfo = {
+                    Name => $name,
+                    Writable => 0,
+                    PrintConv => 'length($val) > 60 ? substr($val,0,55) . "[...]" : $val',
+                };
+                # make tags in main table unknown because they tend to be binary
+                $$tagInfo{Unknown} = 1 if $tagTablePtr eq \%Image::ExifTool::Leaf::Main;
+            }
+            $tagInfo and Image::ExifTool::AddTagToTable($tagTablePtr, $tag, $tagInfo);
+        }
+        if ($verbose) {
+            $exifTool->VerboseInfo($tag, $tagInfo,
+                Table   => $tagTablePtr,
+                Value   => $val,
+                DataPt  => $dataPt,
+                DataPos => $$dirInfo{DataPos},
+                Size    => $size,
+                Start   => $pos,
+            );
+        }
+        if ($tagInfo) {
+            if ($$tagInfo{SubDirectory}) {
+                my %subdirInfo = (
+                    DataPt => $dataPt,
+                    DirLen => $size,
+                    DirStart => $pos,
+                    DataPos => $$dirInfo{DataPos},
+                    DirName => 'Leaf PKTS',
+                );
+                my $subTable = GetTagTable($tagInfo->{SubDirectory}->{TagTable});
+                $exifTool->ProcessDirectory(\%subdirInfo, $subTable);
+            } else {
+                $val =~ tr/\n/ /;   # remove newlines from value
+                $exifTool->FoundTag($tagInfo, $val);
+            }
+        }
+        $pos += $size;
+    }
+    $success or $exifTool->Warn('Bad format Leaf data');
+    return $success;
+}
+
+1;  # end
+
+__END__
+
+=head1 NAME
+
+Image::ExifTool::Leaf - Read Creo Leaf EXIF meta information
+
+=head1 SYNOPSIS
+
+This module is loaded automatically by Image::ExifTool when required.
+
+=head1 DESCRIPTION
+
+This module contains definitions required by Image::ExifTool to interpret
+meta information from Leaf digital camera backs written by Creo Leaf
+Capture.
+
+=head1 AUTHOR
+
+Copyright 2003-2007, Phil Harvey (phil at owl.phy.queensu.ca)
+
+This library is free software; you can redistribute it and/or modify it
+under the same terms as Perl itself.
+
+=head1 SEE ALSO
+
+L<Image::ExifTool::TagNames/Leaf Tags>,
+L<Image::ExifTool(3pm)|Image::ExifTool>
+
+=cut
Index: /gsdl/trunk/perllib/cpan/Image/ExifTool/MIE.pm
===================================================================
--- /gsdl/trunk/perllib/cpan/Image/ExifTool/MIE.pm	(revision 16842)
+++ /gsdl/trunk/perllib/cpan/Image/ExifTool/MIE.pm	(revision 16842)
@@ -0,0 +1,2518 @@
+#------------------------------------------------------------------------------
+# File:         MIE.pm
+#
+# Description:  Read/write MIE meta information
+#
+# Revisions:    11/18/2005 - P. Harvey Created
+#------------------------------------------------------------------------------
+
+package Image::ExifTool::MIE;
+
+use strict;
+use vars qw($VERSION %tableDefaults);
+use Image::ExifTool qw(:DataAccess :Utils);
+use Image::ExifTool::Exif;
+use Image::ExifTool::GPS;
+
+$VERSION = '1.15';
+
+sub ProcessMIE($$);
+sub ProcessMIEGroup($$$);
+sub WriteMIEGroup($$$);
+sub CheckMIE($$$);
+sub GetLangInfo($$);
+
+# local variables
+my $hasZlib;        # 1=Zlib available, 0=no Zlib
+my %mieCode;        # reverse lookup for MIE format names
+my $doneMieMap;     # flag indicating we added user-defined groups to %mieMap
+
+# MIE format codes
+my %mieFormat = (
+    0x00 => 'undef',
+    0x10 => 'MIE',
+    0x18 => 'MIE',
+    0x20 => 'string', # ASCII (ISO 8859-1)
+    0x28 => 'utf8',
+    0x29 => 'utf16',
+    0x2a => 'utf32',
+    0x30 => 'string_list',
+    0x38 => 'utf8_list',
+    0x39 => 'utf16_list',
+    0x3a => 'utf32_list',
+    0x40 => 'int8u',
+    0x41 => 'int16u',
+    0x42 => 'int32u',
+    0x43 => 'int64u',
+    0x48 => 'int8s',
+    0x49 => 'int16s',
+    0x4a => 'int32s',
+    0x4b => 'int64s',
+    0x52 => 'rational32u',
+    0x53 => 'rational64u',
+    0x5a => 'rational32s',
+    0x5b => 'rational64s',
+    0x61 => 'fixed16u',
+    0x62 => 'fixed32u',
+    0x69 => 'fixed16s',
+    0x6a => 'fixed32s',
+    0x72 => 'float',
+    0x73 => 'double',
+    0x80 => 'free',
+);
+
+# map of MIE directory locations
+my %mieMap = (
+   'MIE-Meta'       => 'MIE',
+   'MIE-Audio'      => 'MIE-Meta',
+   'MIE-Camera'     => 'MIE-Meta',
+   'MIE-Doc'        => 'MIE-Meta',
+   'MIE-Geo'        => 'MIE-Meta',
+   'MIE-Image'      => 'MIE-Meta',
+   'MIE-MakerNotes' => 'MIE-Meta',
+   'MIE-Preview'    => 'MIE-Meta',
+   'MIE-Thumbnail'  => 'MIE-Meta',
+   'MIE-Video'      => 'MIE-Meta',
+   'MIE-Flash'      => 'MIE-Camera',
+   'MIE-Lens'       => 'MIE-Camera',
+   'MIE-Orient'     => 'MIE-Camera',
+   'MIE-Extender'   => 'MIE-Lens',
+   'MIE-GPS'        => 'MIE-Geo',
+   'MIE-UTM'        => 'MIE-Geo',
+    EXIF            => 'MIE-Meta',
+    XMP             => 'MIE-Meta',
+    IPTC            => 'MIE-Meta',
+    ICC_Profile     => 'MIE-Meta',
+    ID3             => 'MIE-Meta',
+    IFD0            => 'EXIF',
+    IFD1            => 'IFD0',
+    ExifIFD         => 'IFD0',
+    GPS             => 'IFD0',
+    SubIFD          => 'IFD0',
+    GlobParamIFD    => 'IFD0',
+    PrintIM         => 'IFD0',
+    InteropIFD      => 'ExifIFD',
+    MakerNotes      => 'ExifIFD',
+);
+
+# convenience variables for common tagInfo entries
+my %binaryConv = (
+    Writable => 'undef',
+    Binary => 1,
+);
+my %dateInfo = (
+    Groups => { 2 => 'Time' },
+    Shift => 'Time',
+    PrintConv => '$self->ConvertDateTime($val)',
+    PrintConvInv => '$val',
+);
+my %noYes = ( 0 => 'No', 1 => 'Yes' );
+my %offOn = ( 0 => 'Off', 1 => 'On' );
+
+# default entries for MIE tag tables
+%tableDefaults = (
+    PROCESS_PROC => \&ProcessMIE,
+    WRITE_PROC   => \&ProcessMIE,
+    CHECK_PROC   => \&CheckMIE,
+    LANG_INFO    => \&GetLangInfo,
+    WRITABLE     => 'string',
+    PREFERRED    => 1,
+);
+
+# MIE info
+%Image::ExifTool::MIE::Main = (
+    %tableDefaults,
+    GROUPS => { 1 => 'MIE-Main' },
+    WRITE_GROUP => 'MIE-Main',
+    NOTES => q{
+        MIE is a flexible format which may be used as a stand-alone meta information
+        format, for encapsulation of other files and information, or as a trailer
+        appended to other file formats.  The tables below represent currently
+        defined MIE tags, however ExifTool will also extract any other information
+        present in a MIE file.
+
+        When writing MIE information, some special features are supported:
+
+        1) String values may be written as ASCII (ISO 8859-1) or UTF-8.  ExifTool
+        automatically detects the presence of wide characters and treats the string
+        appropriately. Internally, UTF-8 text may be converted to UTF-16 or UTF-32
+        and stored in this format in the file if it is more compact.
+
+        2) All MIE string-value tags support localized text.  Localized values are
+        written by adding a language/country code to the tag name in the form
+        C<TAG-xx_YY>, where C<TAG> is the tag name, C<xx> is a 2-character lower
+        case ISO 639-1 language code, and C<YY> is a 2-character upper case ISO
+        3166-1 alpha 2 country code (ie. C<Title-en_US>).  But as usual, the user
+        interface is case-insensitive, and ExifTool will write the correct case to
+        the file.
+
+        3) Some numerical MIE tags allow units of measurement to be specified.  For
+        these tags, units may be added in brackets immediately following the value
+        (ie. C<55(mi/h)>).  If no units are specified, the default units are
+        written.
+    },
+   '0Type' => {
+        Name => 'SubfileType',
+        Notes => q{
+            Currently defined types are ACR, AI, AIFF, APE, ARW, ASF, AVI, BMP, CR2,
+            CRW, DCM, DNG, EPS, ERF, FLAC, FPX, GIF, ICC, JNG, JP2, JPEG, MIE, MIFF MNG,
+            MOS, MOV, MP3, MP4, MPC, MPEG, MRW, NEF, OGG, ORF, PBM, PDF, PEF, PGM, PICT,
+            PNG, PPM, PS, PSD, QTIF, RA, RAF, RAW, RIFF, RM, SR2, SRF, SWF, TIFF, VRD,
+            WAV, WDP, WMA, WMV, X3F and XMP.  Other types should use the common file
+            extension.
+        },
+    },
+   '0Vers' => {
+        Name => 'MIEVersion',
+        Notes => 'version 1.1 is assumed if not specified',
+    },
+   '1Directory' => { Name => 'SubfileDirectory' },
+   '1Name'      => { Name => 'SubfileName' },
+   '2MIME'      => { Name => 'SubfileMIMEType' },
+    Meta => {
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::MIE::Meta',
+            DirName => 'MIE-Meta',
+        },
+    },
+    data => {
+        Name => 'SubfileData',
+        Notes => 'the subfile data',
+        %binaryConv,
+    },
+    rsrc => {
+        Name => 'SubfileResource',
+        Notes => 'subfile resource fork if it exists',
+        %binaryConv,
+    },
+    zmd5 => {
+        Name => 'MD5Digest',
+        Notes => q{
+            16-byte MD5 digest written in binary form or as a 32-character hex-encoded
+            ASCII string. Value is an MD5 digest of the entire 0MIE group as it would be
+            with the digest value itself set to all null bytes
+        },
+    },
+    zmie => {
+        Name => 'TrailerSignature',
+        Writable => 'undef',
+        Notes => q{
+            used as the last element in the main "0MIE" group to identify a MIE trailer
+            when appended to another type of file.  ExifTool will create this tag if set
+            to any value, but always with an empty data block
+        },
+        ValueConvInv => '""',   # data block must be empty
+    },
+);
+
+# MIE meta information group
+%Image::ExifTool::MIE::Meta = (
+    %tableDefaults,
+    GROUPS => { 1 => 'MIE-Meta', 2 => 'Image' },
+    WRITE_GROUP => 'MIE-Meta',
+    Audio => {
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::MIE::Audio',
+            DirName => 'MIE-Audio',
+        },
+    },
+    Camera => {
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::MIE::Camera',
+            DirName => 'MIE-Camera',
+        },
+    },
+    Document => {
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::MIE::Doc',
+            DirName => 'MIE-Doc',
+        },
+    },
+    EXIF => {
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::Exif::Main',
+            ProcessProc => \&Image::ExifTool::ProcessTIFF,
+            WriteProc => \&Image::ExifTool::WriteTIFF,
+        },
+    },
+    Geo => {
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::MIE::Geo',
+            DirName => 'MIE-Geo',
+        },
+    },
+    ICCProfile  => {
+        Name => 'ICC_Profile',
+        SubDirectory => { TagTable => 'Image::ExifTool::ICC_Profile::Main' },
+    },
+    ID3  => { SubDirectory => { TagTable => 'Image::ExifTool::ID3::Main' } },
+    IPTC => { SubDirectory => { TagTable => 'Image::ExifTool::IPTC::Main' } },
+    Image => {
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::MIE::Image',
+            DirName => 'MIE-Image',
+        },
+    },
+    MakerNotes => {
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::MIE::MakerNotes',
+            DirName => 'MIE-MakerNotes',
+        },
+    },
+    Preview => {
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::MIE::Preview',
+            DirName => 'MIE-Preview',
+        },
+    },
+    Thumbnail => {
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::MIE::Thumbnail',
+            DirName => 'MIE-Thumbnail',
+        },
+    },
+    Video => {
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::MIE::Video',
+            DirName => 'MIE-Video',
+        },
+    },
+    XMP => { SubDirectory => { TagTable => 'Image::ExifTool::XMP::Main' } },
+);
+
+# MIE document information
+%Image::ExifTool::MIE::Doc = (
+    %tableDefaults,
+    GROUPS => { 1 => 'MIE-Doc', 2 => 'Document' },
+    WRITE_GROUP => 'MIE-Doc',
+    NOTES => 'Information describing the main document, image or file.',
+    Author      => { Groups => { 2 => 'Author' } },
+    Comment     => { },
+    Contributors=> { Groups => { 2 => 'Author' }, List => 1 },
+    Copyright   => { Groups => { 2 => 'Author' } },
+    CreateDate  => { %dateInfo },
+    EMail       => { Groups => { 2 => 'Author' } },
+    Keywords    => { List => 1 },
+    ModifyDate  => { %dateInfo },
+    OriginalDate=> { Name => 'DateTimeOriginal', %dateInfo },
+    Phone       => { Name => 'PhoneNumber', Groups => { 2 => 'Author' } },
+    References  => { List => 1 },
+    Software    => { },
+    Title       => { },
+    URL         => { },
+);
+
+# MIE geographic information
+%Image::ExifTool::MIE::Geo = (
+    %tableDefaults,
+    GROUPS => { 1 => 'MIE-Geo', 2 => 'Location' },
+    WRITE_GROUP => 'MIE-Geo',
+    NOTES => 'Information related to geographic location.',
+    Address     => { },
+    City        => { },
+    Country     => { },
+    GPS => {
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::MIE::GPS',
+            DirName => 'MIE-GPS',
+        },
+    },
+    PostalCode  => { },
+    State       => { Notes => 'state or province' },
+    UTM => {
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::MIE::UTM',
+            DirName => 'MIE-UTM',
+        },
+    },
+);
+
+# MIE GPS information
+%Image::ExifTool::MIE::GPS = (
+    %tableDefaults,
+    GROUPS => { 1 => 'MIE-GPS', 2 => 'Location' },
+    WRITE_GROUP => 'MIE-GPS',
+    Altitude   => {
+        Name => 'GPSAltitude',
+        Writable => 'rational64s',
+        Units => [ qw(m ft) ],
+        Notes => q{'m' above sea level unless 'ft' specified},
+    },
+    Bearing => {
+        Name => 'GPSDestBearing',
+        Writable => 'rational64s',
+        Units => [ qw(deg deg{mag}) ],
+        Notes => q{'deg' CW from true north unless 'deg{mag}' specified},
+    },
+    Datum   => { Name => 'GPSMapDatum', Notes => 'WGS-84 assumed if not specified' },
+    Differential => {
+        Name => 'GPSDifferential',
+        Writable => 'int8u',
+        PrintConv => {
+            0 => 'No Correction',
+            1 => 'Differential Corrected',
+        },
+    },
+    Distance => {
+        Name => 'GPSDestDistance',
+        Writable => 'rational64s',
+        Units => [ qw(km mi nmi) ],
+        Notes => q{'km' unless 'mi' or 'nmi' specified},
+    },
+    Heading  => {
+        Name => 'GPSTrack',
+        Writable => 'rational64s',
+        Units => [ qw(deg deg{mag}) ],
+        Notes => q{'deg' CW from true north unless 'deg{mag}' specified},
+    },
+    Latitude => {
+        Name => 'GPSLatitude',
+        Writable => 'rational64s',
+        Count => -1,
+        Notes => q{
+            1 to 3 numbers: degrees, minutes then seconds.  South latitudes are stored
+            as all negative numbers, but may be entered as positive numbers with a
+            trailing 'S' for convenience.  For example, these are all equivalent: "-40
+            -30", "-40.5", "40 30 0.00 S"
+        },
+        ValueConv    => 'Image::ExifTool::GPS::ToDegrees($val, 1)',
+        ValueConvInv => 'Image::ExifTool::GPS::ToDMS($self, $val, 0)',
+        PrintConv    => 'Image::ExifTool::GPS::ToDMS($self, $val, 1, "N")',
+        PrintConvInv => 'Image::ExifTool::GPS::ToDegrees($val, 1)',
+    },
+    Longitude => {
+        Name => 'GPSLongitude',
+        Writable => 'rational64s',
+        Count => -1,
+        Notes => q{
+            1 to 3 numbers: degrees, minutes then seconds.  West longitudes are
+            negative, but may be entered as positive numbers with a trailing 'W'
+        },
+        ValueConv    => 'Image::ExifTool::GPS::ToDegrees($val, 1)',
+        ValueConvInv => 'Image::ExifTool::GPS::ToDMS($self, $val, 0)',
+        PrintConv    => 'Image::ExifTool::GPS::ToDMS($self, $val, 1, "E")',
+        PrintConvInv => 'Image::ExifTool::GPS::ToDegrees($val, 1)',
+    },
+    MeasureMode => {
+        Name => 'GPSMeasureMode',
+        Writable => 'int8u',
+        PrintConv => { 2 => '2-D', 3 => '3-D' },
+    },
+    Satellites => 'GPSSatellites',
+    Speed => {
+        Name => 'GPSSpeed',
+        Writable => 'rational64s',
+        Units => [ qw(km/h mi/h m/s kn) ],
+        Notes => q{'km/h' unless 'mi/h', 'm/s' or 'kn' specified},
+    },
+    DateTime => { Name => 'GPSDateTime', %dateInfo },
+);
+
+# MIE UTM information
+%Image::ExifTool::MIE::UTM = (
+    %tableDefaults,
+    GROUPS => { 1 => 'MIE-UTM', 2 => 'Location' },
+    WRITE_GROUP => 'MIE-UTM',
+    Datum    => { Name => 'UTMMapDatum', Notes => 'WGS-84 assumed if not specified' },
+    Easting  => { Name => 'UTMEasting' },
+    Northing => { Name => 'UTMNorthing' },
+    Zone     => { Name => 'UTMZone', Writable => 'int8s' },
+);
+
+# MIE image information
+%Image::ExifTool::MIE::Image = (
+    %tableDefaults,
+    GROUPS => { 1 => 'MIE-Image', 2 => 'Image' },
+    WRITE_GROUP => 'MIE-Image',
+   '0Type'          => { Name => 'FullSizeImageType', Notes => 'JPEG if not specified' },
+   '1Name'          => { Name => 'FullSizeImageName' },
+    BitDepth        => { Name => 'BitDepth', Writable => 'int16u' },
+    ColorSpace      => { Notes => 'standard ColorSpace values are "sRGB" and "Adobe RGB"' },
+    Components      => {
+        Name => 'ComponentsConfiguration',
+        Notes => 'string composed of R, G, B, Y, Cb and Cr',
+    },
+    Compression     => { Name => 'CompressionRatio', Writable => 'rational32u' },
+    ImageSize       => {
+        Writable => 'int16u',
+        Count => -1,
+        Notes => '2 or 3 values, for number of XY or XYZ pixels',
+        PrintConv => '$val=~tr/ /x/;$val',
+        PrintConvInv => '$val=~tr/x/ /;$val',
+    },
+    Resolution      => {
+        Writable => 'rational64u',
+        Units => [ qw(/in /cm /deg /arcmin /arcsec), '' ],
+        Count => -1,
+        Notes => q{
+            1 to 3 values.  A single value for equal resolution in all directions, or
+            separate X, Y and Z values if necessary.  Units are '/in' unless '/cm',
+            '/deg', '/arcmin', '/arcsec' or '' specified
+        },
+        PrintConv => '$val=~tr/ /x/;$val',
+        PrintConvInv => '$val=~tr/x/ /;$val',
+    },
+    data => {
+        Name => 'FullSizeImage',
+        %binaryConv,
+        ValueConv => '$self->ValidateImage(\$val,$tag)',
+        ValueConvInv => '$val',
+    },
+);
+
+# MIE preview image
+%Image::ExifTool::MIE::Preview = (
+    %tableDefaults,
+    GROUPS => { 1 => 'MIE-Preview', 2 => 'Image' },
+    WRITE_GROUP => 'MIE-Preview',
+   '0Type'  => { Name => 'PreviewImageType', Notes => 'JPEG if not specified' },
+   '1Name'  => { Name => 'PreviewImageName' },
+    ImageSize => {
+        Name => 'PreviewImageSize',
+        Writable => 'int16u',
+        Count => -1,
+        Notes => '2 or 3 values, for number of XY or XYZ pixels',
+        PrintConv => '$val=~tr/ /x/;$val',
+        PrintConvInv => '$val=~tr/x/ /;$val',
+    },
+    data => {
+        Name => 'PreviewImage',
+        %binaryConv,
+        ValueConv => '$self->ValidateImage(\$val,$tag)',
+        ValueConvInv => '$val',
+    },
+);
+
+# MIE thumbnail image
+%Image::ExifTool::MIE::Thumbnail = (
+    %tableDefaults,
+    GROUPS => { 1 => 'MIE-Thumbnail', 2 => 'Image' },
+    WRITE_GROUP => 'MIE-Thumbnail',
+   '0Type'  => { Name => 'ThumbnailImageType', Notes => 'JPEG if not specified' },
+   '1Name'  => { Name => 'ThumbnailImageName' },
+    ImageSize => {
+        Name => 'ThumbnailImageSize',
+        Writable => 'int16u',
+        Count => -1,
+        Notes => '2 or 3 values, for number of XY or XYZ pixels',
+        PrintConv => '$val=~tr/ /x/;$val',
+        PrintConvInv => '$val=~tr/x/ /;$val',
+    },
+    data => {
+        Name => 'ThumbnailImage',
+        %binaryConv,
+        ValueConv => '$self->ValidateImage(\$val,$tag)',
+        ValueConvInv => '$val',
+    },
+);
+
+# MIE audio information
+%Image::ExifTool::MIE::Audio = (
+    %tableDefaults,
+    GROUPS => { 1 => 'MIE-Audio', 2 => 'Audio' },
+    WRITE_GROUP => 'MIE-Audio',
+    NOTES => q{
+        For the Audio group (and any other group containing a 'data' element), tags
+        refer to the contained data if present, otherwise they refer to the main
+        SubfileData.  The C<0Type> and C<1Name> elements should exist only if C<data>
+        is present.
+    },
+   '0Type'      => { Name => 'RelatedAudioFileType', Notes => 'MP3 if not specified' },
+   '1Name'      => { Name => 'RelatedAudioFileName' },
+    SampleBits  => { Writable => 'int16u' },
+    Channels    => { Writable => 'int8u' },
+    Compression => { Name => 'AudioCompression' },
+    Duration    => { Writable => 'rational64u' },
+    SampleRate  => { Writable => 'int32u' },
+    data        => { Name => 'RelatedAudioFile', %binaryConv },
+);
+
+# MIE video information
+%Image::ExifTool::MIE::Video = (
+    %tableDefaults,
+    GROUPS => { 1 => 'MIE-Video', 2 => 'Video' },
+    WRITE_GROUP => 'MIE-Video',
+   '0Type'      => { Name => 'RelatedVideoFileType', Notes => 'MOV if not specified' },
+   '1Name'      => { Name => 'RelatedVideoFileName' },
+    Codec       => { },
+    Duration    => { Writable => 'rational64u' },
+    data        => { Name => 'RelatedVideoFile', %binaryConv },
+);
+
+# MIE camera information
+%Image::ExifTool::MIE::Camera = (
+    %tableDefaults,
+    GROUPS => { 1 => 'MIE-Camera', 2 => 'Camera' },
+    WRITE_GROUP => 'MIE-Camera',
+    Brightness      => { Writable => 'int8s' },
+    ColorTemperature=> { Writable => 'int32u' },
+    ColorBalance    => {
+        Writable => 'rational64u',
+        Count => 3,
+        Notes => 'RGB scaling factors',
+    },
+    Contrast        => { Writable => 'int8s' },
+    DigitalZoom     => { Writable => 'rational64u' },
+    ExposureComp    => { Name => 'ExposureCompensation', Writable => 'rational64s' },
+    ExposureMode    => { },
+    ExposureTime    => {
+        Writable => 'rational64u',
+        PrintConv => 'Image::ExifTool::Exif::PrintExposureTime($val)',
+        PrintConvInv => 'eval $val',
+    },
+    Flash => {
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::MIE::Flash',
+            DirName => 'MIE-Flash',
+        },
+    },
+    FirmwareVersion => { },
+    FocusMode       => { },
+    ISO             => { Writable => 'int16u' },
+    ISOSetting      => {
+        Writable => 'int16u',
+        Notes => '0 = Auto, otherwise manual ISO speed setting',
+    },
+    ImageNumber     => { Writable => 'int32u' },
+    ImageQuality    => { Notes => 'Economy, Normal, Fine, Super Fine or Raw' },
+    ImageStabilization => { Writable => 'int8u', %offOn },
+    Lens => {
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::MIE::Lens',
+            DirName => 'MIE-Lens',
+        },
+    },
+    Make            => { },
+    MeasuredEV      => { Writable => 'rational64s' },
+    Model           => { },
+    OwnerName       => { },
+    Orientation     => {
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::MIE::Orient',
+            DirName => 'MIE-Orient',
+        },
+    },
+    Saturation      => { Writable => 'int8s' },
+    SensorSize      => {
+        Writable => 'rational64u',
+        Count => 2,
+        Notes => 'width and height of active sensor area in mm',
+    },
+    SerialNumber    => { },
+    Sharpness       => { Writable => 'int8s' },
+    ShootingMode    => { },
+);
+
+# Camera orientation information
+%Image::ExifTool::MIE::Orient = (
+    %tableDefaults,
+    GROUPS => { 1 => 'MIE-Orient', 2 => 'Camera' },
+    WRITE_GROUP => 'MIE-Orient',
+    NOTES => 'These tags describe the camera orientation.',
+    Azimuth     => {
+        Writable => 'rational64s',
+        Units => [ qw(deg deg{mag}) ],
+        Notes => q{'deg' CW from true north unless 'deg{mag}' specified},
+    },
+    Declination => { Writable => 'rational64s' },
+    Elevation   => { Writable => 'rational64s' },
+    RightAscension => { Writable => 'rational64s' },
+    Rotation => {
+        Writable => 'rational64s',
+        Notes => 'CW rotation angle of camera about lens axis',
+    },
+);
+
+# MIE camera lens information
+%Image::ExifTool::MIE::Lens = (
+    %tableDefaults,
+    GROUPS => { 1 => 'MIE-Lens', 2 => 'Camera' },
+    WRITE_GROUP => 'MIE-Lens',
+    NOTES => q{
+        All recorded lens parameters (focal length, aperture, etc) include the
+        effects of the extender if present.
+    },
+    Extender => {
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::MIE::Extender',
+            DirName => 'MIE-Extender',
+        },
+    },
+    FNumber         => { Writable => 'rational64u' },
+    FocalLength     => { Writable => 'rational64u', Notes => 'all focal lengths in mm' },
+    FocusDistance   => {
+        Writable => 'rational64u',
+        Units => [ qw(m ft) ],
+        Notes => q{'m' unless 'ft' specified},
+    },
+    Make            => { Name => 'LensMake' },
+    MaxAperture     => { Writable => 'rational64u' },
+    MaxApertureAtMaxFocal => { Writable => 'rational64u' },
+    MaxFocalLength  => { Writable => 'rational64u' },
+    MinAperture     => { Writable => 'rational64u' },
+    MinFocalLength  => { Writable => 'rational64u' },
+    Model           => { Name => 'LensModel' },
+    OpticalZoom     => { Writable => 'rational64u' },
+    SerialNumber    => { Name => 'LensSerialNumber' },
+);
+
+# MIE lens extender information
+%Image::ExifTool::MIE::Extender = (
+    %tableDefaults,
+    GROUPS => { 1 => 'MIE-Extender', 2 => 'Camera' },
+    WRITE_GROUP => 'MIE-Extender',
+    Magnification   => { Name => 'ExtenderMagnification', Writable => 'rational64s' },
+    Make            => { Name => 'ExtenderMake' },
+    Model           => { Name => 'ExtenderModel' },
+    SerialNumber    => { Name => 'ExtenderSerialNumber' },
+);
+
+# MIE camera flash information
+%Image::ExifTool::MIE::Flash = (
+    %tableDefaults,
+    GROUPS => { 1 => 'MIE-Flash', 2 => 'Camera' },
+    WRITE_GROUP => 'MIE-Flash',
+    ExposureComp    => { Name => 'FlashExposureComp', Writable => 'rational64s' },
+    Fired           => { Name => 'FlashFired', Writable => 'int8u', PrintConv => \%noYes },
+    GuideNumber     => { Name => 'FlashGuideNumber' },
+    Make            => { Name => 'FlashMake' },
+    Mode            => { Name => 'FlashMode' },
+    Model           => { Name => 'FlashModel' },
+    SerialNumber    => { Name => 'FlashSerialNumber' },
+    Type            => { Name => 'FlashType', Notes => '"Internal" or "External"' },
+);
+
+# MIE maker notes information
+%Image::ExifTool::MIE::MakerNotes = (
+    %tableDefaults,
+    GROUPS => { 1 => 'MIE-MakerNotes' },
+    WRITE_GROUP => 'MIE-MakerNotes',
+    NOTES => q{
+        MIE maker notes are contained within separate groups for each manufacturer
+        to avoid name conflicts.  Currently no specific manufacturer information has
+        been defined.
+    },
+    Canon       => { SubDirectory => { TagTable => 'Image::ExifTool::MIE::Unknown' } },
+    Casio       => { SubDirectory => { TagTable => 'Image::ExifTool::MIE::Unknown' } },
+    FujiFilm    => { SubDirectory => { TagTable => 'Image::ExifTool::MIE::Unknown' } },
+    Kodak       => { SubDirectory => { TagTable => 'Image::ExifTool::MIE::Unknown' } },
+    KonicaMinolta=>{ SubDirectory => { TagTable => 'Image::ExifTool::MIE::Unknown' } },
+    Nikon       => { SubDirectory => { TagTable => 'Image::ExifTool::MIE::Unknown' } },
+    Olympus     => { SubDirectory => { TagTable => 'Image::ExifTool::MIE::Unknown' } },
+    Panasonic   => { SubDirectory => { TagTable => 'Image::ExifTool::MIE::Unknown' } },
+    Pentax      => { SubDirectory => { TagTable => 'Image::ExifTool::MIE::Unknown' } },
+    Ricoh       => { SubDirectory => { TagTable => 'Image::ExifTool::MIE::Unknown' } },
+    Sigma       => { SubDirectory => { TagTable => 'Image::ExifTool::MIE::Unknown' } },
+    Sony        => { SubDirectory => { TagTable => 'Image::ExifTool::MIE::Unknown' } },
+);
+
+%Image::ExifTool::MIE::Unknown = (
+    PROCESS_PROC => \&ProcessMIE,
+    GROUPS => { 1 => 'MIE-Unknown' },
+);
+
+#------------------------------------------------------------------------------
+# Add user-defined MIE groups to %mieMap
+# Inputs: none;  Returns: nothing, but sets $doneMieMap flag
+sub UpdateMieMap()
+{
+    $doneMieMap = 1;    # set flag so we only do this once
+    return unless %Image::ExifTool::UserDefined;
+    my ($tableName, @tables, %doneTable, $tagID);
+    # get list of top-level MIE tables with user-defined tags
+    foreach $tableName (keys %Image::ExifTool::UserDefined) {
+        next unless $tableName =~ /^Image::ExifTool::MIE::/;
+        my $userTable = $Image::ExifTool::UserDefined{$tableName};
+        my $tagTablePtr = GetTagTable($tableName) or next;
+        # copy the WRITE_GROUP from the actual table
+        $$userTable{WRITE_GROUP} = $$tagTablePtr{WRITE_GROUP};
+        # add to list of tables to process
+        $doneTable{$tableName} = 1;
+        push @tables, [$tableName, $userTable];
+    }
+    # recursively add all user-defined groups to MIE map
+    while (@tables) {
+        my ($tableName, $tagTablePtr) = @{shift @tables};
+        my $parent = $$tagTablePtr{WRITE_GROUP};
+        $parent or warn("No WRITE_GROUP for $tableName\n"), next;
+        $mieMap{$parent} or warn("$parent is not in MIE map\n"), next;
+        foreach $tagID (TagTableKeys($tagTablePtr)) {
+            my $tagInfo = $$tagTablePtr{$tagID};
+            next unless ref $tagInfo eq 'HASH' and $$tagInfo{SubDirectory};
+            my $subTableName = $tagInfo->{SubDirectory}->{TagTable};
+            my $subTablePtr = GetTagTable($subTableName) or next;
+            # only care about MIE tables
+            next unless $$subTablePtr{PROCESS_PROC} and
+                        $$subTablePtr{PROCESS_PROC} eq \&ProcessMIE;
+            my $group = $$subTablePtr{WRITE_GROUP};
+            $group or warn("No WRITE_GROUP for $subTableName\n"), next;
+            if ($mieMap{$group} and $mieMap{$group} ne $parent) {
+                warn("$group already has different parent ($mieMap{$group})\n"), next;
+            }
+            $mieMap{$group} = $parent;  # add to map
+            # process tables within this one too
+            $doneTable{$subTableName} and next;
+            $doneTable{$subTableName} = 1;
+            push @tables, [$subTableName, $subTablePtr];
+        }
+    }
+}
+
+#------------------------------------------------------------------------------
+# Get localized version of tagInfo hash
+# Inputs: 0) tagInfo hash ref, 1) locale code (ie. "en_CA")
+# Returns: new tagInfo hash ref, or undef if invalid
+sub GetLangInfo($$)
+{
+    my ($tagInfo, $langCode) = @_;
+    # check for properly formatted language code
+    return undef unless $langCode =~ /^[a-z]{2}([-_])[A-Z]{2}$/;
+    # use '_' as a separator, but recognize '_' or '-'
+    $langCode =~ tr/-/_/ if $1 eq '-';
+    # can only set locale on string types
+    return undef if $$tagInfo{Writable} and $$tagInfo{Writable} ne 'string';
+    return Image::ExifTool::GetLangInfo($tagInfo, $langCode);
+}
+
+#------------------------------------------------------------------------------
+# return true if we have Zlib::Compress
+# Inputs: 0) ExifTool object ref, 1) verb for what you want to do with the info
+# Returns: 1 if Zlib available, 0 otherwise
+sub HasZlib($$)
+{
+    unless (defined $hasZlib) {
+        $hasZlib = eval 'require Compress::Zlib';
+        unless ($hasZlib) {
+            $hasZlib = 0;
+            $_[0]->Warn("Install Compress::Zlib to $_[1] compressed information");
+        }
+    }
+    return $hasZlib;
+}
+
+#------------------------------------------------------------------------------
+# Get format code for MIE group element with current byte order
+# Inputs: 0) [optional] true to convert result to chr()
+# Returns: format code
+sub MIEGroupFormat(;$)
+{
+    my $chr = shift;
+    my $format = GetByteOrder() eq 'MM' ? 0x10 : 0x18;
+    return $chr ? chr($format) : $format;
+}
+
+#------------------------------------------------------------------------------
+# Does a string contain valid UTF-8 characters?
+# Inputs: 0) string
+# Returns: 0=regular ASCII, -1=invalid UTF-8, 1=valid UTF-8 with maximum 16-bit
+#          wide characters, 2=valid UTF-8 requiring 32-bit wide characters
+# Notes: Changes current string position
+sub IsUTF8($)
+{
+    my $rtnVal = 0;
+    pos($_[0]) = 0; # start at beginning of string
+    for (;;) {
+        last unless $_[0] =~ /([\x80-\xff])/g;
+        my $ch = ord($1);
+        # minimum lead byte for 2-byte sequence is 0xc2 (overlong sequences
+        # not allowed), 0xf8-0xfd are restricted by RFC 3629 (no 5 or 6 byte
+        # sequences), and 0xfe and 0xff are not valid in UTF-8 strings
+        return -1 if $ch < 0xc2 or $ch >= 0xf8;
+        # determine number of bytes remaining in sequence
+        my $n = 1;
+        $n += ($ch >= 0xf0) ? 2 : 1 if $ch >= 0xe0;
+        return -1 unless $_[0] =~ /\G[\x80-\xbf]{$n}/g;
+        # character code is greater than 0xffff if more than 2 extra bytes were
+        # required in the UTF-8 character
+        $rtnVal |= ($n > 2) ? 2 : 1;
+    }
+    return $rtnVal;
+}
+
+#------------------------------------------------------------------------------
+# ReadValue() with added support for UTF formats (utf8, utf16 and utf32)
+# Inputs: 0) data reference, 1) value offset, 2) format string,
+#         3) number of values (or undef to use all data)
+#         4) valid data length relative to offset
+# Returns: converted value, or undefined if data isn't there
+#          or list of values in list context
+sub ReadMIEValue($$$$$)
+{
+    my ($dataPt, $offset, $format, $count, $size) = @_;
+    my $val;
+    if ($format =~ /^utf(8|16|32)/) {
+        if ($1 == 8) {
+            # return UTF8 string
+            $val = substr($$dataPt, $offset, $size);
+        } else {
+            # convert to UTF8
+            my $fmt;
+            if (GetByteOrder() eq 'MM') {
+                $fmt = ($1 == 16) ? 'n' : 'N';
+            } else {
+                $fmt = ($1 == 16) ? 'v' : 'V';
+            }
+            my @unpk = unpack("x$offset$fmt$size",$$dataPt);
+            if ($] >= 5.006001) {
+                $val = pack('C0U*', @unpk);
+            } else {
+                $val = Image::ExifTool::PackUTF8(@unpk);
+            }
+        }
+        # truncate at null unless this is a list
+        $val =~ s/\0.*//s unless $format =~ /_list$/;
+        return $val;
+    } else {
+        # don't modify string lists
+        $format = 'undef' if $format eq 'string_list' or $format eq 'free';
+        return ReadValue($dataPt, $offset, $format, $count, $size);
+    }
+}
+
+#------------------------------------------------------------------------------
+# validate raw values for writing
+# Inputs: 0) ExifTool object ref, 1) tagInfo hash ref, 2) raw value ref
+# Returns: error string or undef (and possibly changes value) on success
+sub CheckMIE($$$)
+{
+    my ($exifTool, $tagInfo, $valPtr) = @_;
+    my $format = $$tagInfo{Writable} || $tagInfo->{Table}->{WRITABLE};
+    my $err;
+
+    return 'No writable format' if not $format or $format eq '1';
+    # handle units if supported by this tag
+    my $ulist = $$tagInfo{Units};
+    if ($ulist and $$valPtr =~ /(.*)\((.*)\)$/) {
+        my ($val, $units) = ($1, $2);
+        ($units) = grep /^$units$/i, @$ulist;
+        defined $units or return 'Allowed units: (' . join('|', @$ulist) . ')';
+        $err = Image::ExifTool::CheckValue(\$val, $format, $$tagInfo{Count});
+        # add units back onto value
+        $$valPtr = "$val($units)" unless $err;
+    } elsif ($format !~ /^(utf|string|undef)/ and $$valPtr =~ /\)$/) {
+        return 'Units not supported';
+    } else {
+        $err = Image::ExifTool::CheckValue($valPtr, $format, $$tagInfo{Count});
+    }
+    return $err;
+}
+
+#------------------------------------------------------------------------------
+# Rewrite a MIE directory
+# Inputs: 0) ExifTool object reference, 1) DirInfo reference, 2) tag table ptr
+# Returns: undef on success, otherwise error message (empty message if nothing to write)
+sub WriteMIEGroup($$$)
+{
+    my ($exifTool, $dirInfo, $tagTablePtr) = @_;
+    my $outfile = $$dirInfo{OutFile};
+    my $dirName = $$dirInfo{DirName};
+    my $toWrite = $$dirInfo{ToWrite} || '';
+    my $raf = $$dirInfo{RAF};
+    my $verbose = $exifTool->Options('Verbose');
+    my $optCompress = $exifTool->Options('Compress');
+    my $out = $exifTool->Options('TextOut');
+    my ($msg, $err, $ok, $sync, $delGroup);
+    my $tag = '';
+    my $deletedTag = '';
+
+    # count each MIE directory found and make name for this specific instance
+    my ($grp1, %isWriting);
+    my $cnt = $exifTool->{MIE_COUNT};
+    my $grp = $tagTablePtr->{GROUPS}->{1};
+    my $n = $$cnt{'MIE-Main'} || 0;
+    if ($grp eq 'MIE-Main') {
+        $$cnt{$grp} = ++$n;
+        ($grp1 = $grp) =~ s/MIE-/MIE$n-/;
+    } else {
+        ($grp1 = $grp) =~ s/MIE-/MIE$n-/;
+        my $m = $$cnt{$grp1} = ($$cnt{$grp1} || 0) + 1;
+        $isWriting{"$grp$m"} = 1;   # ie. 'MIE-Doc2'
+        $isWriting{$grp1} = 1;      # ie. 'MIE1-Doc'
+        $grp1 .= $m;
+    }
+    # build lookup for all valid group names for this MIE group
+    $isWriting{$grp} = 1;           # ie. 'MIE-Doc'
+    $isWriting{$grp1} = 1;          # ie. 'MIE1-Doc2'
+    $isWriting{"MIE$n"} = 1;        # ie. 'MIE1'
+
+    # determine if we are deleting this group
+    if (%{$exifTool->{DEL_GROUP}}) {
+        $delGroup = 1 if $exifTool->{DEL_GROUP}->{MIE} or
+                         $exifTool->{DEL_GROUP}->{$grp} or
+                         $exifTool->{DEL_GROUP}->{$grp1} or
+                         $exifTool->{DEL_GROUP}->{"MIE$n"};
+    }
+
+    # prepare lookups and lists for writing
+    my $newTags = $exifTool->GetNewTagInfoHash($tagTablePtr);
+    my ($addDirs, $editDirs) = $exifTool->GetAddDirHash($tagTablePtr, $dirName);
+    my @editTags = sort keys %$newTags, keys %$editDirs;
+    $verbose and print $out $raf ? 'Writing' : 'Creating', " $grp1:\n";
+
+    # loop through elements in MIE group
+    MieElement: for (;;) {
+        my ($format, $tagLen, $valLen, $units, $oldHdr, $buff);
+        my $lastTag = $tag;
+        if ($raf) {
+            # read first 4 bytes of element header
+            my $n = $raf->Read($oldHdr, 4);
+            if ($n != 4) {
+                last if $n or defined $sync;
+                undef $raf; # all done reading
+                $ok = 1;
+            }
+        }
+        if ($raf) {
+            ($sync, $format, $tagLen, $valLen) = unpack('aC3', $oldHdr);
+            $sync eq '~' or $msg = 'Invalid sync byte', last;
+
+            # read tag name
+            if ($tagLen) {
+                $raf->Read($tag, $tagLen) == $tagLen or last;
+                $oldHdr .= $tag;    # add tag to element header
+                $exifTool->Warn("MIE tag '$tag' out of sequence") if $tag lt $lastTag;
+                # separate units from tag name if they exist
+                $units = $1 if $tag =~ s/\((.*)\)$//;
+            } else {
+                $tag = '';
+            }
+
+            # get multi-byte value length if necessary
+            if ($valLen > 252) {
+                # calculate number of bytes in extended DataLength
+                my $n = 1 << (256 - $valLen);
+                $raf->Read($buff, $n) == $n or last;
+                $oldHdr .= $buff;   # add to old header
+                my $fmt = 'int' . ($n * 8) . 'u';
+                $valLen = ReadValue(\$buff, 0, $fmt, 1, $n);
+                if ($valLen > 0x7fffffff) {
+                    $msg = "Can't write $tag (DataLength > 2GB not yet supported)";
+                    last;
+                }
+            }
+            # don't rewrite free bytes or information in deleted groups
+            if ($format eq 0x80 or ($delGroup and $tagLen and ($format & 0xf0) != 0x10)) {
+                $raf->Seek($valLen, 1) or $msg = 'Seek error', last;
+                if ($verbose > 1) {
+                    my $free = ($format eq 0x80) ? ' free' : '';
+                    print $out "    - $grp1:$tag ($valLen$free bytes)\n";
+                }
+                ++$exifTool->{CHANGED} if $delGroup;
+                next;
+            }
+        } else {
+            # no more elements to read
+            $tagLen = $valLen = 0;
+            $tag = '';
+        }
+#
+# write necessary new tags and process directories
+#
+        while (@editTags) {
+            last if $tagLen and $editTags[0] gt $tag;
+            # we are writing the new tag now
+            my ($newVal, $writable, $oldVal, $newFormat, $compress);
+            my $newTag = shift @editTags;
+            my $newInfo = $$editDirs{$newTag};
+            if ($newInfo) {
+                # create the new subdirectory or rewrite existing non-MIE directory
+                my $subTablePtr = GetTagTable($newInfo->{SubDirectory}->{TagTable});
+                unless ($subTablePtr) {
+                    $exifTool->Warn("No tag table for $newTag $$newInfo{Name}");
+                    next;
+                }
+                my %subdirInfo;
+                my $isMieGroup = ($$subTablePtr{WRITE_PROC} and
+                                  $$subTablePtr{WRITE_PROC} eq \&ProcessMIE);
+
+                if ($newTag eq $tag) {
+                    # make sure that either both or neither old and new tags are MIE groups
+                    if ($isMieGroup xor ($format & 0xf3) == 0x10) {
+                        $exifTool->Warn("Tag '$tag' not expected type");
+                        next;   # don't write our new tag
+                    }
+                    # uncompress existing directory into $oldVal since we are editing it
+                    if ($format & 0x04) {
+                        last unless HasZlib($exifTool, 'edit');
+                        $raf->Read($oldVal, $valLen) == $valLen or last MieElement;
+                        my $stat;
+                        my $inflate = Compress::Zlib::inflateInit();
+                        $inflate and ($oldVal, $stat) = $inflate->inflate($oldVal);
+                        unless ($inflate and $stat == Compress::Zlib::Z_STREAM_END()) {
+                            $msg = "Error inflating $tag";
+                            last MieElement;
+                        }
+                        $compress = 1;
+                        $valLen = length $oldVal;    # uncompressed value length
+                    }
+                } else {
+                    # don't create this directory unless necessary
+                    next unless $$addDirs{$newTag};
+                }
+
+                if ($isMieGroup) {
+                    my $hdr;
+                    if ($newTag eq $tag) {
+                        # rewrite existing directory later unless it was compressed
+                        last unless $compress;
+                        # rewrite directory to '$newVal'
+                        $newVal = '';
+                        %subdirInfo = (
+                            OutFile => \$newVal,
+                            RAF => new File::RandomAccess(\$oldVal),
+                        );
+                    } elsif ($optCompress and not $$dirInfo{IsCompressed}) {
+                        # write to memory so we can compress the new MIE group
+                        $compress = 1;
+                        %subdirInfo = (
+                            OutFile => \$newVal,
+                        );
+                    } else {
+                        $hdr = '~' . MIEGroupFormat(1) . chr(length($newTag)) .
+                               "\0" . $newTag;
+                        %subdirInfo = (
+                            OutFile => $outfile,
+                            ToWrite => $toWrite . $hdr,
+                        );
+                    }
+                    $subdirInfo{DirName} = $newInfo->{SubDirectory}->{DirName} || $newTag;
+                    $subdirInfo{Parent} = $dirName;
+                    # don't compress elements of an already compressed group
+                    $subdirInfo{IsCompressed} = 1;
+                    $msg = WriteMIEGroup($exifTool, \%subdirInfo, $subTablePtr);
+                    last MieElement if $msg;
+                    # message is defined but empty if nothing was written
+                    if (defined $msg) {
+                        undef $msg; # not a problem if nothing was written
+                        next;
+                    } elsif (not $compress) {
+                        # group was written already
+                        $toWrite = '';
+                        next;
+                    }
+                    $writable = 'undef';
+                    $newFormat = MIEGroupFormat();
+                } else {
+                    if ($newTag eq $tag) {
+                        unless ($compress) {
+                            # read and edit existing directory
+                            $raf->Read($oldVal, $valLen) == $valLen or last MieElement;
+                        }
+                        %subdirInfo = (
+                            DataPt => \$oldVal,
+                            DataLen => $valLen,
+                            DirName => $$newInfo{Name},
+                            DataPos => $raf->Tell() - $valLen,
+                            DirStart => 0,
+                            DirLen => $valLen,
+                        );
+                    }
+                    $subdirInfo{Parent} = $dirName;
+                    my $writeProc = $newInfo->{SubDirectory}->{WriteProc};
+                    $newVal = $exifTool->WriteDirectory(\%subdirInfo, $subTablePtr, $writeProc);
+                    if (defined $newVal) {
+                        if ($newVal eq '') {
+                            next MieElement if $newTag eq $tag; # deleting the directory
+                            next;       # not creating the new directory
+                        }
+                    } else {
+                        next unless defined $oldVal;
+                        $newVal = $oldVal;  # just copy over the old directory
+                    }
+                    $writable = 'undef';
+                    $newFormat = 0x00;  # all other directories are 'undef' format
+                }
+            } else {
+
+                # get the new tag information
+                $newInfo = $$newTags{$newTag};
+                my $newValueHash = $exifTool->GetNewValueHash($newInfo);
+                my @newVals;
+
+                # write information only to specified group
+                my $writeGroup = $$newValueHash{WriteGroup};
+                last unless $isWriting{$writeGroup};
+
+                # if tag existed, must decide if we want to overwrite the value
+                if ($newTag eq $tag) {
+                    my $isOverwriting;
+                    my $isList = $$newInfo{List};
+                    if ($isList) {
+                        $isOverwriting = -1;    # force processing list elements individually
+                    } else {
+                        $isOverwriting = Image::ExifTool::IsOverwriting($newValueHash);
+                        last unless $isOverwriting;
+                    }
+                    my ($val, $cmpVal);
+                    if ($isOverwriting < 0 or $verbose > 1) {
+                        # check to be sure we can uncompress the value if necessary
+                        HasZlib($exifTool, 'edit') or last if $format & 0x04;
+                        # read the old value
+                        $raf->Read($oldVal, $valLen) == $valLen or last MieElement;
+                        # uncompress if necessary
+                        if ($format & 0x04) {
+                            my $stat;
+                            my $inflate = Compress::Zlib::inflateInit();
+                            # must save original compressed value in case we decide
+                            # not to overwrite it later
+                            $cmpVal = $oldVal;
+                            $inflate and ($oldVal, $stat) = $inflate->inflate($oldVal);
+                            unless ($inflate and $stat == Compress::Zlib::Z_STREAM_END()) {
+                                $msg = "Error inflating $tag";
+                                last MieElement;
+                            }
+                            $valLen = length $oldVal;    # update value length
+                        }
+                        # convert according to specified format
+                        my $formatStr = $mieFormat{$format & 0xfb} || 'undef';
+                        $val = ReadMIEValue(\$oldVal, 0, $formatStr, undef, $valLen);
+                        if ($isOverwriting < 0 and defined $val) {
+                            # handle list values individually
+                            if ($isList) {
+                                my (@vals, $v);
+                                if ($formatStr =~ /_list$/) {
+                                    @vals = split "\0", $val;
+                                } else {
+                                    @vals = $val;
+                                }
+                                # keep any list items that we aren't overwriting
+                                foreach $v (@vals) {
+                                    next if Image::ExifTool::IsOverwriting($newValueHash, $v);
+                                    push @newVals, $v;
+                                }
+                            } else {
+                                # test to see if we really want to overwrite the value
+                                $isOverwriting = Image::ExifTool::IsOverwriting($newValueHash, $val);
+                            }
+                        }
+                    }
+                    if ($isOverwriting) {
+                        # skip the old value if we didn't read it already
+                        unless (defined $oldVal) {
+                            $raf->Seek($valLen, 1) or $msg = 'Seek error';
+                        }
+                        if ($verbose > 1) {
+                            $val .= "($units)" if defined $units;
+                            $val = $exifTool->Printable($val);
+                            print $out "    - $grp1:$$newInfo{Name} = '$val'\n";
+                        }
+                        $deletedTag = $tag;     # remember that we deleted this tag
+                        ++$exifTool->{CHANGED}; # we deleted the old value
+                    } else {
+                        if (defined $oldVal) {
+                            # write original compressed value
+                            $oldVal = $cmpVal if defined $cmpVal;
+                        } else {
+                            $raf->Read($oldVal, $valLen) == $valLen or last MieElement;
+                        }
+                        # write the old value now
+                        Write($outfile, $toWrite, $oldHdr, $oldVal) or $err = 1;
+                        $toWrite = '';
+                    }
+                    unless (@newVals) {
+                        # unshift the new tag info to write it later
+                        unshift @editTags, $newTag;
+                        next MieElement;    # get next element from file
+                    }
+                } else {
+                    # write new value if creating, or if List and list existed, or
+                    # if tag was previously deleted
+                    next unless Image::ExifTool::IsCreating($newValueHash) or
+                        ($newTag eq $lastTag and ($$newInfo{List} or $deletedTag eq $lastTag));
+                }
+                # get the new value to write (undef to delete)
+                push @newVals, Image::ExifTool::GetNewValues($newValueHash);
+                next unless @newVals;
+                $writable = $$newInfo{Writable} || $$tagTablePtr{WRITABLE};
+                if ($writable eq 'string') {
+                    # join multiple values into a single string
+                    $newVal = join "\0", @newVals;
+                    # write string as UTF-8,16 or 32 if value contains valid UTF-8 codes
+                    my $isUTF8 = IsUTF8($newVal);
+                    if ($isUTF8 > 0) {
+                        $writable = 'utf8';
+                        # write UTF-16 or UTF-32 if it is more compact
+                        # (only if Perl version is 5.6.1 or greater)
+                        if ($] >= 5.006001) {
+                            # pack with current byte order
+                            my $pk = (GetByteOrder() eq 'MM') ? 'n' : 'v';
+                            $pk = uc($pk) if $isUTF8 > 1;
+                            # translate to utf16 or utf32
+                            my $tmp = pack("$pk*",unpack('U0U*',$newVal));
+                            if (length $tmp < length $newVal) {
+                                $newVal = $tmp;
+                                $writable = ($isUTF8 > 1) ? 'utf32' : 'utf16';
+                            }
+                        }
+                    }
+                    # write as a list if we have multiple values
+                    $writable .= '_list' if @newVals > 1;
+                } else {
+                    # should only be one element in the list
+                    $newVal = shift @newVals;
+                }
+                $newFormat = $mieCode{$writable};
+                unless (defined $newFormat) {
+                    $msg = "Bad format '$writable' for $$newInfo{Name}";
+                    next MieElement;
+                }
+            }
+
+            # write the new or edited element
+            while (defined $newFormat) {
+                my $valPt = \$newVal;
+                # remove units from value and add to tag name if supported by this tag
+                if ($$newInfo{Units}) {
+                    my $val2;
+                    if ($$valPt =~ /(.*)\((.*)\)$/) {
+                        $val2 = $1;
+                        $newTag .= "($2)";
+                    } else {
+                        $val2 = $$valPt;
+                        # add default units
+                        my $ustr = '(' . $newInfo->{Units}->[0] . ')';
+                        $newTag .= $ustr;
+                        $$valPt .= $ustr;
+                    }
+                    $valPt = \$val2;
+                }
+                # convert value if necessary
+                if ($writable !~ /^(utf|string|undef)/) {
+                    my $val3 = WriteValue($$valPt, $writable, $$newInfo{Count});
+                    defined $val3 or $exifTool->Warn("Error writing $newTag"), last;
+                    $valPt = \$val3;
+                }
+                my $len = length $$valPt;
+                # compress value before writing if required
+                if (($compress or $optCompress) and not $$dirInfo{IsCompressed} and
+                    HasZlib($exifTool, 'write'))
+                {
+                    my $deflate = Compress::Zlib::deflateInit();
+                    my $val4;
+                    if ($deflate) {
+                        $val4 = $deflate->deflate($$valPt);
+                        $val4 .= $deflate->flush() if defined $val4;
+                    }
+                    if (defined $val4) {
+                        my $len4 = length $val4;
+                        my $saved = $len - $len4;
+                        # only use compressed data if it is smaller
+                        if ($saved > 0) {
+                            $verbose and print $out "  [$newTag compression saved $saved bytes]\n";
+                            $newFormat |= 0x04; # set compressed bit
+                            $len = $len4;       # set length
+                            $valPt = \$val4;    # set value pointer
+                        } elsif ($verbose) {
+                            print $out "  [$newTag compression saved $saved bytes -- written uncompressed]\n";
+                        }
+                    } else {
+                        $exifTool->Warn("Error deflating $newTag (written uncompressed)");
+                    }
+                }
+                # calculate the DataLength code
+                my $extLen;
+                if ($len < 253) {
+                    $extLen = '';
+                } elsif ($len < 65536) {
+                    $extLen = Set16u($len);
+                    $len = 255;
+                } elsif ($len <= 0x7fffffff) {
+                    $extLen = Set32u($len);
+                    $len = 254;
+                } else {
+                    $exifTool->Warn("Can't write $newTag (DataLength > 2GB not yet suppported)");
+                    last; # don't write this tag
+                }
+                # write this element (with leading MIE group element if not done already)
+                my $hdr = $toWrite . '~' . chr($newFormat) . chr(length $newTag);
+                Write($outfile, $hdr, chr($len), $newTag, $extLen, $$valPt) or $err = 1;
+                $toWrite = '';
+                if ($verbose > 1 and not $$editDirs{$newTag}) {
+                    $newVal = $exifTool->Printable($newVal);
+                    print $out "    + $grp1:$$newInfo{Name} = '$newVal'\n";
+                }
+                ++$exifTool->{CHANGED};
+                last;   # didn't want to loop anyway
+            }
+            next MieElement if defined $oldVal;
+        }
+#
+# rewrite existing element or descend into uncompressed MIE group
+#
+        # all done this MIE group if we reached the terminator element
+        unless ($tagLen) {
+            # skip over existing terminator data (if any)
+            last if $valLen and not $raf->Seek($valLen, 1);
+            $ok = 1;
+            # write group terminator if necessary
+            unless ($toWrite) {
+                # write end-of-group terminator element
+                my $term = "~\0\0\0";
+                unless ($$dirInfo{Parent}) {
+                    # write extended terminator for file-level group
+                    my $len = ref $outfile eq 'SCALAR' ? length($$outfile) : tell $outfile;
+                    $len += 10; # include length of terminator itself
+                    if ($len and $len <= 0x7fffffff) {
+                        $term = "~\0\0\x06" . Set32u($len) . MIEGroupFormat(1) . "\x04";
+                    }
+                }
+                Write($outfile, $term) or $err = 1;
+            }
+            last;
+        }
+
+        # descend into existing uncompressed MIE group
+        if ($format == 0x10 or $format == 0x18) {
+            my ($subTablePtr, $dirName);
+            my $tagInfo = $exifTool->GetTagInfo($tagTablePtr, $tag);
+            if ($tagInfo and $$tagInfo{SubDirectory}) {
+                $dirName = $tagInfo->{SubDirectory}->{DirName};
+                my $subTable = $tagInfo->{SubDirectory}->{TagTable};
+                $subTablePtr = $subTable ? GetTagTable($subTable) : $tagTablePtr;
+            } else {
+                $subTablePtr = GetTagTable('Image::ExifTool::MIE::Unknown');
+            }
+            my $hdr = '~' . chr($format) . chr(length $tag) . "\0" . $tag;
+            my %subdirInfo = (
+                DirName => $dirName || $tag,
+                RAF     => $raf,
+                ToWrite => $toWrite . $hdr,
+                OutFile => $outfile,
+                Parent  => $dirName,
+                IsCompressed => $$dirInfo{IsCompressed},
+            );
+            my $oldOrder = GetByteOrder();
+            SetByteOrder($format & 0x08 ? 'II' : 'MM');
+            $msg = WriteMIEGroup($exifTool, \%subdirInfo, $subTablePtr);
+            SetByteOrder($oldOrder);
+            last if $msg;
+            if (defined $msg) {
+                undef $msg; # no problem if nothing written
+            } else {
+                $toWrite = '';
+            }
+            next;
+        }
+        # just copy existing element
+        my $oldVal;
+        $raf->Read($oldVal, $valLen) == $valLen or last;
+        if ($toWrite) {
+            Write($outfile, $toWrite) or $err = 1;
+            $toWrite = '';
+        }
+        Write($outfile, $oldHdr, $oldVal) or $err = 1;
+    }
+    # return error message
+    if ($err) {
+        $msg = 'Error writing file';
+    } elsif (not $ok and not $msg) {
+        $msg = 'Unexpected end of file';
+    } elsif (not $msg and $toWrite) {
+        $msg = '';  # flag for nothing written
+        $verbose and print $out "Deleted $grp1 (empty)\n";
+    }
+    return $msg;
+}
+
+#------------------------------------------------------------------------------
+# Process MIE directory
+# Inputs: 0) ExifTool object reference, 1) DirInfo reference, 2) tag table ref
+# Returns: undef on success, or error message if there was a problem
+# Notes: file pointer is positioned at the MIE end on entry
+sub ProcessMIEGroup($$$)
+{
+    my ($exifTool, $dirInfo, $tagTablePtr) = @_;
+    my $raf = $$dirInfo{RAF};
+    my $verbose = $exifTool->Options('Verbose');
+    my $out = $exifTool->Options('TextOut');
+    my ($msg, $buff, $ok, $oldIndent, $mime);
+    my $lastTag = '';
+
+    # get group 1 names: $grp doesn't have numbers (ie. 'MIE-Doc'),
+    # and $grp1 does (ie. 'MIE1-Doc1')
+    my $cnt = $exifTool->{MIE_COUNT};
+    my $grp1 = $tagTablePtr->{GROUPS}->{1};
+    my $n = $$cnt{'MIE-Main'} || 0;
+    if ($grp1 eq 'MIE-Main') {
+        $$cnt{$grp1} = ++$n;
+        $grp1 =~ s/MIE-/MIE$n-/ if $n > 1;
+    } else {
+        $grp1 =~ s/MIE-/MIE$n-/ if $n > 1;
+        $$cnt{$grp1} = ($$cnt{$grp1} || 0) + 1;
+        $grp1 .= $$cnt{$grp1} if $$cnt{$grp1} > 1;
+    }
+    # set group1 name for all tags extracted from this group
+    $exifTool->{SET_GROUP1} = $grp1;
+
+    if ($verbose) {
+        $oldIndent = $exifTool->{INDENT};
+        $exifTool->{INDENT} .= '| ';
+        $exifTool->VerboseDir($grp1);
+    }
+
+    # process all MIE elements
+    for (;;) {
+        $raf->Read($buff, 4) == 4 or last;
+        my ($sync, $format, $tagLen, $valLen) = unpack('aC3', $buff);
+        $sync eq '~' or $msg = 'Invalid sync byte', last;
+
+        # read tag name
+        my ($tag, $units);
+        if ($tagLen) {
+            $raf->Read($tag, $tagLen) == $tagLen or last;
+            $exifTool->Warn("MIE tag '$tag' out of sequence") if $tag lt $lastTag;
+            $lastTag = $tag;
+            # separate units from tag name if they exist
+            $units = $1 if $tag =~ s/\((.*)\)$//;
+        } else {
+            $tag = '';
+        }
+
+        # get multi-byte value length if necessary
+        if ($valLen > 252) {
+            my $n = 1 << (256 - $valLen);
+            $raf->Read($buff, $n) == $n or last;
+            my $fmt = 'int' . ($n * 8) . 'u';
+            $valLen = ReadValue(\$buff, 0, $fmt, 1, $n);
+            if ($valLen > 0x7fffffff) {
+                $msg = "Can't read $tag (DataLength > 2GB not yet supported)";
+                last;
+            }
+        }
+
+        # all done if we reached the group terminator
+        unless ($tagLen) {
+            # skip over terminator data block
+            $ok = 1 unless $valLen and not $raf->Seek($valLen, 1);
+            last;
+        }
+
+        # get tag information hash unless this is free space
+        my ($tagInfo, $value);
+        while ($format != 0x80) {
+            $tagInfo = $exifTool->GetTagInfo($tagTablePtr, $tag);
+            last if $tagInfo;
+            # extract tags with locale code
+            if ($tag =~ /\W/) {
+                if ($tag =~ /^(\w+)-([a-z]{2}_[A-Z]{2})$/) {
+                    my ($baseTag, $langCode) = ($1, $2);
+                    $tagInfo = $exifTool->GetTagInfo($tagTablePtr, $baseTag);
+                    $tagInfo = GetLangInfo($tagInfo, $langCode) if $tagInfo;
+                    last if $tagInfo;
+                } else {
+                    $exifTool->Warn('Invalid MIE tag name');
+                    last;
+                }
+            }
+            # extract unknown tags if specified
+            $tagInfo = {
+                Name => $tag,
+                Writable => 0,
+                PrintConv => 'length($val) > 60 ? substr($val,0,55) . "[...]" : $val',
+            };
+            Image::ExifTool::AddTagToTable($tagTablePtr, $tag, $tagInfo);
+            last;
+        }
+
+        # read value and uncompress if necessary
+        my $formatStr = $mieFormat{$format & 0xfb} || 'undef';
+        if ($tagInfo or ($formatStr eq 'MIE' and $format & 0x04)) {
+            $raf->Read($value, $valLen) == $valLen or last;
+            if ($format & 0x04) {
+                if ($verbose) {
+                    print $out "$$exifTool{INDENT}\[Tag '$tag' $valLen bytes compressed]\n";
+                }
+                next unless HasZlib($exifTool, 'decode');
+                my $stat;
+                my $inflate = Compress::Zlib::inflateInit();
+                $inflate and ($value, $stat) = $inflate->inflate($value);
+                unless ($inflate and $stat == Compress::Zlib::Z_STREAM_END()) {
+                    $exifTool->Warn("Error inflating $tag");
+                    next;
+                }
+                $valLen = length $value;
+            }
+        }
+
+        # process this tag
+        if ($formatStr eq 'MIE') {
+            # process MIE directory
+            my ($subTablePtr, $dirName);
+            if ($tagInfo and $$tagInfo{SubDirectory}) {
+                $dirName = $tagInfo->{SubDirectory}->{DirName};
+                my $subTable = $tagInfo->{SubDirectory}->{TagTable};
+                $subTablePtr = $subTable ? GetTagTable($subTable) : $tagTablePtr;
+            } else {
+                $subTablePtr = GetTagTable('Image::ExifTool::MIE::Unknown');
+            }
+            if ($verbose) {
+                my $order = ', byte order ' . GetByteOrder();
+                $exifTool->VerboseInfo($tag, $tagInfo, Size => $valLen, Extra => $order);
+            }
+            my %subdirInfo = (
+                DirName => $dirName || $tag,
+                RAF => $raf,
+                Parent => $$dirInfo{DirName},
+            );
+            # read from uncompressed data instead if necessary
+            $subdirInfo{RAF} = new File::RandomAccess(\$value) if $format & 0x04;
+
+            my $oldOrder = GetByteOrder();
+            SetByteOrder($format & 0x08 ? 'II' : 'MM');
+            $msg = ProcessMIEGroup($exifTool, \%subdirInfo, $subTablePtr);
+            SetByteOrder($oldOrder);
+            $exifTool->{SET_GROUP1} = $grp1;    # restore this group1 name
+            last if $msg;
+        } else {
+            # process MIE data format types
+            if ($tagInfo) {
+                # extract tag value
+                my $val = ReadMIEValue(\$value, 0, $formatStr, undef, $valLen);
+                unless (defined $val) {
+                    $exifTool->Warn("Error reading $tag value");
+                    $val = '<err>';
+                }
+                # save type or mime type
+                $mime = $val if $tag eq '0Type' or $tag eq '2MIME';
+                if ($verbose) {
+                    my $count;
+                    my $s = Image::ExifTool::FormatSize($formatStr);
+                    if ($s and $formatStr !~ /^(utf|string|undef)/) {
+                        $count = $valLen / $s;
+                    }
+                    $exifTool->VerboseInfo($lastTag, $tagInfo,
+                        DataPt => \$value,
+                        DataPos => $raf->Tell() - $valLen,
+                        Size => $valLen,
+                        Format => $formatStr,
+                        Value => $val,
+                        Count => $count,
+                    );
+                }
+                if ($$tagInfo{SubDirectory}) {
+                    my $subTablePtr = GetTagTable($tagInfo->{SubDirectory}->{TagTable});
+                    my %subdirInfo = (
+                        DirName => $$tagInfo{Name},
+                        DataPt => \$value,
+                        DataPos => $raf->Tell() - $valLen,
+                        DataLen => $valLen,
+                        DirStart => 0,
+                        DirLen => $valLen,
+                    );
+                    my $processProc = $tagInfo->{SubDirectory}->{ProcessProc};
+                    delete $exifTool->{SET_GROUP1};
+                    $exifTool->ProcessDirectory(\%subdirInfo, $subTablePtr, $processProc);
+                    $exifTool->{SET_GROUP1} = $grp1;
+                } elsif ($formatStr =~ /_list$/) {
+                    # split list value into separate strings
+                    my @vals = split "\0", $val;
+                    $exifTool->FoundTag($tagInfo, \@vals);
+                } else {
+                    # add units to value if specified
+                    $val .= "($units)" if defined $units;
+                    $exifTool->FoundTag($tagInfo, $val);
+                }
+            } else {
+                # skip over unknown information or free bytes
+                $raf->Seek($valLen, 1) or $msg = 'Seek error', last;
+                $verbose and $exifTool->VerboseInfo($tag, undef, Size => $valLen);
+            }
+        }
+    }
+    # modify MIME type if necessary
+    $mime and not $$dirInfo{Parent} and $exifTool->ModifyMimeType($mime);
+
+    $ok or $msg or $msg = 'Unexpected end of file';
+    $verbose and $exifTool->{INDENT} = $oldIndent;
+    return $msg;
+}
+
+#------------------------------------------------------------------------------
+# Read/write a MIE file
+# Inputs: 0) ExifTool object reference, 1) DirInfo reference
+# Returns: 1 on success, 0 if this wasn't a valid MIE file, or -1 on write error
+# - process as a trailer if "Trailer" flag set in dirInfo
+sub ProcessMIE($$)
+{
+    my ($exifTool, $dirInfo) = @_;
+    return 1 unless defined $exifTool;
+    my $raf = $$dirInfo{RAF};
+    my $outfile = $$dirInfo{OutFile};
+    my ($buff, $err, $msg, $pos, $end, $isCreating);
+    my $numDocs = 0;
+#
+# process as a trailer (from end of file) if specified
+#
+    if ($$dirInfo{Trailer}) {
+        my $offset = $$dirInfo{Offset} || 0;    # offset from end of file
+        $raf->Seek(-10 - $offset, 2) or return 0;
+        for (;;) {
+            # read and validate last 10 bytes
+            $raf->Read($buff, 10) == 10 or last;
+            last unless $buff =~ /~\0\0\x06.{4}(\x10|\x18)(\x04)$/s or
+                        $buff =~ /(\x10|\x18)(\x08)$/s;
+            SetByteOrder($1 eq "\x10" ? 'MM' : 'II');
+            my $len = ($2 eq "\x04") ? Get32u(\$buff, 4) : Get64u(\$buff);
+            my $curPos = $raf->Tell() or last;
+            last if $len < 12 or $len > $curPos;
+            # validate element header if 8-byte offset was used
+            if ($2 eq "\x08") {
+                last if $len < 14;
+                $raf->Seek($curPos - 14, 0) and $raf->Read($buff, 4) or last;
+                last unless $buff eq "~\0\0\x0a";
+            }
+            # looks like a good group, so remember start position
+            $pos = $curPos - $len;
+            $end = $curPos unless $end;
+            # seek to 10 bytes from end of previous group
+            $raf->Seek($pos - 10, 0) or last;
+        }
+        # seek to start of first MIE group
+        return 0 unless defined $pos and $raf->Seek($pos, 0);
+        # update DataPos and DirLen for ProcessTrailers()
+        $$dirInfo{DataPos} = $pos;
+        $$dirInfo{DirLen} = $end - $pos;
+        if ($outfile and $exifTool->{DEL_GROUP}->{MIE}) {
+            # delete the trailer
+            $exifTool->VPrint(0,"  Deleting MIE trailer\n");
+            ++$exifTool->{CHANGED};
+            return 1;
+        } elsif ($exifTool->Options('Verbose') or $exifTool->{HTML_DUMP}) {
+            $exifTool->DumpTrailer($dirInfo);
+        }
+    }
+#
+# loop through all documents in MIE file
+#
+    for (;;) {
+        # look for "0MIE" group element
+        my $num = $raf->Read($buff, 8);
+        if ($num == 8) {
+            # verify file identifier
+            if ($buff =~ /^~(\x10|\x18)\x04(.)0MIE/) {
+                SetByteOrder($1 eq "\x10" ? 'MM' : 'II');
+                my $len = ord($2);
+                # skip extended DataLength if it exists
+                if ($len > 252 and not $raf->Seek(1 << (256 - $len), 1)) {
+                    $msg = 'Seek error';
+                    last;
+                }
+            } else {
+                return 0 unless $numDocs;   # not a MIE file
+                if ($buff =~ /^~/) {
+                    $msg = 'Non-standard file-level MIE element';
+                } else {
+                    $msg = 'Invalid MIE file-level data';
+                }
+            }
+        } elsif ($numDocs) {
+            last unless $num;   # OK, all done with file
+            $msg = 'Truncated MIE element header';
+        } else {
+            return 0 if $num or not $outfile;
+            # we have the ability to create a MIE file from scratch
+            $buff = ''; # start from nothing
+            SetByteOrder('MM'); # write big-endian
+            $isCreating = 1;
+        }
+        if ($msg) {
+            last if $$dirInfo{Trailer}; # allow other trailers after MIE
+            if ($outfile) {
+                $exifTool->Error($msg);
+            } else {
+                $exifTool->Warn($msg);
+            }
+            last;
+        }
+        # this is a new MIE document -- increment document count
+        unless ($numDocs) {
+            # this is a valid MIE file (unless a trailer on another file)
+            $exifTool->SetFileType() unless $exifTool->{VALUE}->{FileType};
+            $exifTool->{NO_LIST} = 1;   # handle lists ourself
+            $exifTool->{MIE_COUNT} = { };
+            undef $hasZlib;
+        }
+        ++$numDocs;
+
+        # process the MIE groups recursively, beginning with the main MIE group
+        my $tagTablePtr = GetTagTable('Image::ExifTool::MIE::Main');
+
+        my %subdirInfo = (
+            DirName => 'MIE',
+            RAF => $raf,
+            OutFile => $outfile,
+            # don't define Parent so WriteMIEGroup() writes extended terminator
+        );
+        if ($outfile) {
+            # generate lookup for MIE format codes if not done already
+            unless (%mieCode) {
+                foreach (keys %mieFormat) {
+                    $mieCode{$mieFormat{$_}} = $_;
+                }
+            }
+            # update %mieMap with user-defined MIE groups
+            UpdateMieMap() unless $doneMieMap;
+            # initialize write directories, with MIE tags taking priority
+            $exifTool->InitWriteDirs(\%mieMap, 'MIE');
+            $subdirInfo{ToWrite} = '~' . MIEGroupFormat(1) . "\x04\xfe0MIE\0\0\0\0";
+            $msg = WriteMIEGroup($exifTool, \%subdirInfo, $tagTablePtr);
+            if ($msg) {
+                $exifTool->Error($msg);
+                $err = 1;
+                last;
+            } elsif (defined $msg and $isCreating) {
+                $exifTool->Error('Nothing to write');
+                last;
+            }
+        } else {
+            $msg = ProcessMIEGroup($exifTool, \%subdirInfo, $tagTablePtr);
+            if ($msg) {
+                $exifTool->Warn($msg);
+                last;
+            }
+        }
+    }
+    delete $exifTool->{NO_LIST};
+    delete $exifTool->{MIE_COUNT};
+    delete $exifTool->{SET_GROUP1};
+    return $err ? -1 : 1;
+}
+
+1;  # end
+
+__END__
+
+=head1 NAME
+
+Image::ExifTool::MIE - Read/write MIE meta information
+
+=head1 SYNOPSIS
+
+This module is used by Image::ExifTool
+
+=head1 DESCRIPTION
+
+This module contains routines required by Image::ExifTool to read and write
+information in MIE files.
+
+=head1 WHAT IS MIE?
+
+MIE stands for "Meta Information Encapsulation".  The MIE format is an
+extensible, dedicated meta information format which supports storage of
+binary as well as textual meta information.  MIE can be used to encapsulate
+meta information from many sources and bundle it together with any type of
+file.
+
+=head2 Features
+
+Below is very subjective score card comparing the features of a number of
+common file and meta information formats, and comparing them to MIE.  The
+following features are rated for each format with a score of 0 to 10:
+
+  1) Extensible (can incorporate user-defined information).
+  2) Meaningful tag ID's (hint to meaning of unknown information).
+  3) Sequential read/write ability (streamable).
+  4) Hierarchical information structure.
+  5) Easy to implement reader/writer/editor.
+  6) Order of information well defined.
+  7) Large data lengths supported: >64kB (+5) and >4GB (+5).
+  8) Localized text strings.
+  9) Multiple documents in a single file.
+ 10) Compact format doesn't squander disk space or bandwidth.
+ 11) Compressed meta information supported.
+ 12) Relocatable data elements (ie. no fixed offsets).
+ 13) Binary meta information (+7) with variable byte order (+3).
+ 14) Mandatory tags not required (an unecessary complication).
+ 15) Append information to end of file without editing.
+
+                          Feature number                   Total
+     Format  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15   Score
+     ------ ---------------------------------------------  -----
+     MIE    10 10 10 10 10 10 10 10 10 10 10 10 10 10 10    150
+     PDF    10 10  0 10  0  0 10  0 10 10 10  0  7 10 10     97
+     PNG    10 10 10  0  8  0  5 10  0 10 10 10  0 10  0     93
+     XMP    10 10 10 10  2  0 10 10 10  0  0 10  0 10  0     92
+     AIFF    0  5 10 10 10  0  5  0  0 10  0 10  7 10  0     77
+     RIFF    0  5 10 10 10  0  5  0  0 10  0 10  7 10  0     77
+     JPEG   10  0 10  0 10  0  0  0  0 10  0 10  7 10  0     67
+     EPS    10 10 10  0  0  0 10  0 10  0  0  5  0 10  0     65
+     CIFF    0  0  0 10 10  0  5  0  0 10  0 10 10 10  0     65
+     TIFF    0  0  0 10  5 10  5  0 10 10  0  0 10  0  0     60
+     EXIF    0  0  0 10  5 10  0  0  0 10  0  0 10  0  0     45
+     IPTC    0  0 10  0  8  0  0  0  0 10  0 10  7  0  0     45
+
+By design, MIE ranks highest by a significant margin.  Other formats with
+reasonable scores are PDF, PNG and XMP, but each has significant weak
+points.  What may be surprising is that TIFF, EXIF and IPTC rank so low.
+
+As well as scoring high in all these features, the MIE format has the unique
+ability to encapsulate any other type of file, and provides a non-invasive
+method of adding meta information to a file.  The meta information is
+logically separated from the original file data, which is extremely
+important because meta information is routinely lost when files are edited.
+
+Also, the MIE format supports multiple files by simple concatenation,
+enabling all kinds of wonderful features such as linear databases, edit
+histories or non-intrusive file updates.  This ability can also be leveraged
+to allow MIE-format trailers to be added to some other file types.
+
+=head1 MIE 1.1 FORMAT SPECIFICATION (2007-01-21)
+
+=head2 File Structure
+
+A MIE file consists of a series of MIE elements.  A MIE element may contain
+either data or a group of MIE elements, providing a hierarchical format for
+storing data.  Each MIE element is identified by a human-readable tag name,
+and may store data from zero to 2^64-1 bytes in length.
+
+=head2 File Signature
+
+The first element in the MIE file must be an uncompressed MIE group element
+with a tag name of "0MIE".  This restriction allows the first 8 bytes of a
+MIE file to be used to identify a MIE format file.  The following table
+lists the two possible initial byte sequences for a MIE-format file (the
+first for big-endian, and the second for little-endian byte ordering):
+
+    Byte Number:      0    1    2    3    4    5    6    7
+
+    C Characters:     ~ \x10 \x04    ?    0    M    I    E
+        or            ~ \x18 \x04    ?    0    M    I    E
+
+    Hexadecimal:     7e   10   04    ?   30   4d   49   45
+        or           7e   18   04    ?   30   4d   49   45
+
+    Decimal:        126   16    4    ?   48   77   73   69
+        or          126   24    4    ?   48   77   73   69
+
+Note that byte 1 may have one of the two possible values (0x10 or 0x18), and
+byte 3 may have any value (0x00 to 0xff).
+
+=head2 Element Structure
+
+    1 byte  SyncByte = 0x7e (decimal 126, character '~')
+    1 byte  FormatCode (see below)
+    1 byte  TagLength (T)
+    1 byte  DataLength (gives D if DataLength < 253)
+    T bytes TagName (T given by TagLength)
+    2 bytes DataLength2 [exists only if DataLength == 255 (0xff)]
+    4 bytes DataLength4 [exists only if DataLength == 254 (0xfe)]
+    8 bytes DataLength8 [exists only if DataLength == 253 (0xfd)]
+    D bytes DataBlock (D given by DataLength)
+
+The minimum element length is 4 bytes (for a group terminator).  The maximum
+DataBlock size is 2^64-1 bytes.  TagLength and DataLength are unsigned
+integers, and the byte ordering for multi-byte DataLength fields is
+specified by the containing MIE group element.  The SyncByte is byte
+aligned, so no padding is added to align on an N-byte boundary.
+
+=head3 FormatCode
+
+The format code is a bitmask that defines the format of the data:
+
+    7654 3210
+    ++++ ----  FormatType
+    ---- +---  TypeModifier
+    ---- -+--  Compressed
+    ---- --++  FormatSize
+
+=over 4
+
+=item FormatType (bitmask 0xf0):
+
+    0x00 - other (or unknown) data
+    0x10 - MIE group
+    0x20 - text string
+    0x30 - list of null-separated text strings
+    0x40 - integer
+    0x50 - rational
+    0x60 - fixed point
+    0x70 - floating point
+    0x80 - free space
+
+=item TypeModifier (bitmask 0x08):
+
+Modifies the meaning of certain FormatTypes (0x00-0x60):
+
+    0x08 - other data sensitive to MIE group byte order
+    0x18 - MIE group with little-endian byte ordering
+    0x28 - UTF encoded text string
+    0x38 - UTF encoded text string list
+    0x48 - signed integer
+    0x58 - signed rational (denominator is always unsigned)
+    0x68 - signed fixed-point
+
+=item Compressed (bitmask 0x04):
+
+If this bit is set, the data block is compressed using Zlib deflate.  An
+entire MIE group may be compressed, with the exception of file-level groups.
+
+=item FormatSize (bitmask 0x03):
+
+Gives the byte size of each data element:
+
+    0x00 - 8 bits  (1 byte)
+    0x01 - 16 bits (2 bytes)
+    0x02 - 32 bits (4 bytes)
+    0x03 - 64 bits (8 bytes)
+
+The number of bytes in a single value for this format is given by
+2**FormatSize (or 1 << FormatSize).  The number of values is the data length
+divided by this number of bytes.  It is an error if the data length is not
+an even multiple of the format size in bytes.
+
+=back
+
+The following is a list of all currently defined MIE FormatCode values for
+uncompressed data (add 0x04 to each value for compressed data):
+
+    0x00 - other data (insensitive to MIE group byte order) (1)
+    0x01 - other 16-bit data (may be byte swapped)
+    0x02 - other 32-bit data (may be byte swapped)
+    0x03 - other 64-bit data (may be byte swapped)
+    0x08 - other data (sensitive to MIE group byte order) (1)
+    0x10 - MIE group with big-endian values (1)
+    0x18 - MIE group with little-endian values (1)
+    0x20 - ASCII (ISO 8859-1) string (2,3)
+    0x28 - UTF-8 string (2,3,4)
+    0x29 - UTF-16 string (2,3,4)
+    0x2a - UTF-32 string (2,3,4)
+    0x30 - ASCII (ISO 8859-1) string list (3,5)
+    0x38 - UTF-8 string list (3,4,5)
+    0x39 - UTF-16 string list (3,4,5)
+    0x3a - UTF-32 string list (3,4,5)
+    0x40 - unsigned 8-bit integer
+    0x41 - unsigned 16-bit integer
+    0x42 - unsigned 32-bit integer
+    0x43 - unsigned 64-bit integer (6)
+    0x48 - signed 8-bit integer
+    0x49 - signed 16-bit integer
+    0x4a - signed 32-bit integer
+    0x4b - signed 64-bit integer (6)
+    0x52 - unsigned 32-bit rational (16-bit numerator then denominator) (7)
+    0x53 - unsigned 64-bit rational (32-bit numerator then denominator) (7)
+    0x5a - signed 32-bit rational (denominator is unsigned) (7)
+    0x5b - signed 64-bit rational (denominator is unsigned) (7)
+    0x61 - unsigned 16-bit fixed-point (high 8 bits is integer part) (8)
+    0x62 - unsigned 32-bit fixed-point (high 16 bits is integer part) (8)
+    0x69 - signed 16-bit fixed-point (high 8 bits is signed integer) (8)
+    0x6a - signed 32-bit fixed-point (high 16 bits is signed integer) (8)
+    0x72 - 32-bit IEEE float (not recommended for portability reasons)
+    0x73 - 64-bit IEEE double (not recommended for portability reasons) (6)
+    0x80 - free space (value data does not contain useful information)
+
+Notes:
+
+=over 4
+
+=item 1.
+
+The byte ordering specified by the MIE group TypeModifier applies to the MIE
+group element as well as all elements within the group.  Data for all
+FormatCodes except 0x08 (other data, sensitive to byte order) may be
+transferred between MIE groups with different byte order by byte swapping
+the uncompressed data according to the specified data format.  The following
+list illustrates the byte-swapping pattern, based on FormatSize, for all
+format types except rational (FormatType 0x50).
+
+      FormatSize              Change in Byte Sequence
+    --------------      -----------------------------------
+    0x00 (8 bits)       0 1 2 3 4 5 6 7 --> 0 1 2 3 4 5 6 7 (no change)
+    0x01 (16 bits)      0 1 2 3 4 5 6 7 --> 1 0 3 2 5 4 7 6
+    0x02 (32 bits)      0 1 2 3 4 5 6 7 --> 3 2 1 0 7 6 5 4
+    0x03 (64 bits)      0 1 2 3 4 5 6 7 --> 7 6 5 4 3 2 1 0
+
+Rational values consist of two integers, so they are swapped as the next
+lower FormatSize.  For example, a 32-bit rational (FormatSize 0x02, and
+FormatCode 0x52 or 0x5a) is swapped as two 16-bit values (ie. as if it had
+FormatSize 0x01).
+
+=item 2.
+
+The TagName of a string element may have an 6-character suffix to indicate a
+specific locale. (ie. "Title-en_US", or "Keywords-de_DE").
+
+=item 3.
+
+Text strings are not normally null terminated, however they may be padded
+with one or more null characters to the end of the data block to allow
+strings to be edited within fixed-length data blocks.  Newlines in the text
+are indicated by a single LF (0x0a) character.
+
+=item 4.
+
+UTF strings must not begin with a byte order mark (BOM) since the byte order
+and byte size are specified by the MIE format.  If a BOM is found, it should
+be treated as a zero-width non-breaking space.
+
+=item 5.
+
+A list of text strings separated by null characters.  These lists must not
+be null padded or null terminated, since this would be interpreted as
+additional zero-length strings.  For ASCII and UTF-8 strings, the null
+character is a single zero (0x00) byte.  For UTF-16 or UTF-32 strings, the
+null character is 2 or 4 zero bytes respectively.
+
+=item 6.
+
+64-bit integers and doubles are subject to the specified byte ordering for
+both 32-bit words and bytes within these words.  For instance, the high
+order byte is always the first byte if big-endian, and the eighth byte if
+little-endian.  This means that some swapping is always necessary for these
+values on systems where the byte order differs from the word order (ie. some
+ARM systems), regardless of the endian-ness of the stored values.
+
+=item 7.
+
+Rational values are treated as two separate integers.  The numerator always
+comes first regardless of the byte ordering.  In a signed rational value,
+only the numerator is signed.  The denominator of all rational values is
+unsigned (ie. a signed 32-bit rational of 0x80000000/0x80000000 evaluates to
+-1, not +1).
+
+=item 8.
+
+32-bit fixed point values are converted to floating point by treating them
+as an integer and dividing by an appropriate value.  ie)
+
+    16-bit fixed value = 16-bit integer value / 256.0
+    32-bit fixed value = 32-bit integer value / 65536.0
+
+=back
+
+=head3 TagLength
+
+Gives the length of the TagName string.  Any value between 0 and 255 is
+valid, but the TagLength of 0 is valid only for the MIE group terminator.
+
+=head3 DataLength
+
+DataLength is an unsigned byte that gives the number of bytes in the data
+block.  A value between 0 and 252 gives the data length directly, and
+numbers from 253 to 255 are reserved for extended DataLength codes.  Codes
+of 255, 254 and 253 indicate that the element contains an additional 2, 4 or
+8 byte unsigned integer representing the data length.
+
+    0-252      - length of data block
+    255 (0xff) - use DataLength2
+    254 (0xfe) - use DataLength4
+    253 (0xfd) - use DataLength8
+
+A DataLength of zero is valid for any element except a compressed MIE group.
+A zero DataLength for an uncompressed MIE group indicates that the group
+length is unknown.  For other elements, a zero length indicates there is no
+associated data.  A terminator element must have a DataLength of 0, 6 or 10,
+and may not use an extended DataLength.
+
+=head3 TagName
+
+The TagName string is 0 to 255 bytes long, and is composed of the ASCII
+characters A-Z, a-z, 0-9 and underline ('_').  Also, a dash ('-') is used to
+separate the language/country code in the TagName of a localized text
+string, and a units string (possibly containing other ASCII characters) may
+be appear in brackets at the end of the TagName.  The TagName string is NOT
+null terminated.  A MIE element with a tag string of zero length is reserved
+for the group terminator.
+
+MIE elements are sorted alphabetically by TagName within each group.
+Multiple elements with the same TagName are allowed, even within the same
+group.
+
+TagNames should be meaningful.  Case is significant.  Words should be
+lowercase with an uppercase first character, and acronyms should be all
+upper case.  The underline ("_") is provided to allow separation of two
+acronyms or two numbers, but it shouldn't be used unless necessary.  No
+separation is necessary between an acronym and a word (ie. "ISOSetting").
+
+All TagNames should start with an uppercase letter.  An exception to this
+rule allows tags to begin with a digit (0-9) if they must come before other
+tags in the sort order, or a lowercase letter (a-z) if they must come after.
+For instance, the '0Type' element begins with a digit so it comes before,
+and the 'data' element begins with a lowercase letter so that it comes after
+meta information tags in the main "0MIE" group.
+
+Tag names for localized text strings have an 6-character suffix with the
+following format:  The first character is a dash ('-'), followed by a
+2-character lower case ISO 639-1 language code, then an underline ('_'), and
+ending with a 2-character upper case ISO 3166-1 alpha 2 country code.  (ie.
+"-en_US", "-en_GB", "-de_DE" or "-fr_FR".  Note that "GB", and not "UK" is
+the code for Great Britain, although "UK" should be recognized for
+compatibility reasons.)  The suffix is included when sorting the tags
+alphabetically, so the default locale (with no tag-name suffix) always comes
+first.  If the country is unknown or not applicable, a country code of "XX"
+should be used.
+
+Tags with numerical values may allow units of measurement to be specified.
+The units string is stored in brackets at the end of the tag name, and is
+composed of zero or more ASCII characters in the range 0x21 to 0x7d,
+excluding the bracket characters 0x28 and 0x29.  (ie. "Resolution(/cm)" or
+"SpecificHeat(J/kg.K)".)  See L<Image::ExifTool::MIEUnits> for details. Unit
+strings are not localized, and may not be used in combination with localized
+text strings.
+
+Sets of tags which would require a common prefix should be added in a
+separate MIE group instead of adding the prefix to all tag names.  For
+example, instead of these TagName's:
+
+    ExternalFlashType
+    ExternalFlashSerialNumber
+    ExternalFlashFired
+
+one would instead designate a separate "ExternalFlash" MIE group to contain
+the following elements:
+
+    Type
+    SerialNumber
+    Fired
+
+=head3 DataLength2/4/8
+
+These extended DataLength fields exist only if DataLength is 255, 254 or
+253, and are respectively 2, 4 or 8 byte unsigned integers giving the data
+block length.  One of these values must be used if the data block is larger
+than 252 bytes, but they may be used if desired for smaller blocks too
+(although this may add a few unnecessary bytes to the MIE element).
+
+=head3 DataBlock
+
+The data value for the MIE element.  The format of the data is given by the
+FormatCode.  For MIE group elements, the data includes all contained
+elements and the group terminator.
+
+=head2 MIE groups
+
+All MIE data elements must be contained within a group.  A group begins with
+a MIE group element, and ends with a group terminator.  Groups may be nested
+in a hierarchy to arbitrary depth.
+
+A MIE group element is identified by a format code of 0x10 (big endian byte
+ordering) or 0x18 (little endian).  The group terminator is distinguished by
+a zero TagLength (it is the only element allowed to have a zero TagLength),
+and has a FormatCode of 0x00.
+
+The MIE group element is permitted to have a zero DataLength only if the
+data is uncompressed.  This special value indicates that the group length is
+unknown (otherwise the minimum value for DataLength is 4, corresponding the
+the minimum group size which includes a terminator of at least 4 bytes). If
+DataLength is zero, all elements in the group must be parsed until the group
+terminator is found.  If non-zero, DataLength includes the length of all
+elements contained within the group, including the group terminator.  Use of
+a non-zero DataLength is encouraged because it allows readers quickly skip
+over entire MIE groups.  For compressed groups DataLength must be non-zero,
+and is the length of the compressed group data (which includes the
+compressed group terminator).
+
+=head3 Group Terminator
+
+The group terminator has a FormatCode and TagLength of zero.  The terminator
+DataLength must be 0, 6 or 10 bytes, and extended DataLength codes may not
+be used.  With a zero DataLength, the byte sequence for a terminator is "7e
+00 00 00" (hex).  With a DataLength of 6 or 10 bytes, the terminator data
+block contains information about the length and byte ordering of the
+preceding group.  This additional information is recommended for file-level
+groups, and is used in multi-document MIE files and MIE trailers to allow
+the file to be scanned backwards from the end.  (This may also allow some
+documents to be recovered if part of the file is corrupted.)  The structure
+of this optional terminator data block is as follows:
+
+    4 or 8 bytes  GroupLength (unsigned integer)
+    1 byte        ByteOrder (0x10 or 0x18, same as MIE group)
+    1 byte        GroupLengthSize (0x04 or 0x08)
+
+The ByteOrder and GroupLengthSize values give the byte ordering and size of
+the GroupLength integer.  The GroupLength value is the total length of the
+entire MIE group ending with this terminator, including the opening MIE
+group element and the terminator itself.
+
+=head3 File-level MIE groups
+
+File-level MIE groups may NOT be compressed.
+
+All elements in a MIE file are contained within a special group with a
+TagName of "0MIE".  The purpose of the "OMIE" group is to provide a unique
+signature at the start of the file, and to encapsulate information allowing
+files to be easily combined.  The "0MIE" group must be terminated like any
+other group, but it is recommended that the terminator of a file-level group
+include the optional data block (defined above) to provide information about
+the group length and byte order.
+
+It is valid to have more than one "0MIE" group at the file level, allowing
+multiple documents in a single MIE file.  Furthermore, the MIE structure
+enables multi-document files to be generated by simply concatenating two or
+more MIE files.
+
+=head2 Scanning Backwards through a MIE File
+
+The steps below give an algorithm to quickly locate the last document in a
+MIE file:
+
+=over 4
+
+=item 1.
+
+Read the last 10 bytes of the file.  (Note that a valid MIE file may be as
+short as 12 bytes long, but a file this length contains only an an empty MIE
+group.)
+
+=item 2.
+
+If the last byte of the file is zero, then it is not possible to scan
+backward through the file, so the file must be scanned from the beginning.
+Otherwise, proceed to the next step.
+
+=item 3.
+
+If the last byte is 4 or 8, the terminator contains information about the
+byte ordering and length of the group.  Otherwise, stop here because this
+isn't a valid MIE file.
+
+=item 4.
+
+The next-to-last byte must be either 0x10 indicating big-endian byte
+ordering or 0x18 for little-endian ordering, otherwise this isn't a valid
+MIE file.
+
+=item 5.
+
+The value of the preceding 4 or 8 bytes gives the length of the complete
+file-level MIE group (GroupLength).  This length includes both the leading
+MIE group element and the terminator element itself.  The value is an
+unsigned integer with a byte length given in step 3), and a byte order from
+step 4).  From the current file position (at the end of the data read in
+step 1), seek backward by this number of bytes to find the start of the MIE
+group element for this document.
+
+=back
+
+This algorithm may be repeated again beginning at this point in the file to
+locate the next-to-last document, etc.
+
+The table below lists all 5 valid patterns for the last 14 bytes of a
+file-level MIE group, with all numbers in hex.  The comments indicate the
+length and byte ordering of GroupLength (xx) if available:
+
+  ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? 7e 00 00 00  - (no GroupLength)
+  ?? ?? ?? ?? 7e 00 00 06 xx xx xx xx 10 04  - 4 bytes, big endian
+  ?? ?? ?? ?? 7e 00 00 06 xx xx xx xx 18 04  - 4 bytes, little endian
+  7e 00 00 0a xx xx xx xx xx xx xx xx 10 08  - 8 bytes, big endian
+  7e 00 00 0a xx xx xx xx xx xx xx xx 18 08  - 8 bytes, little endian
+
+=head2 Trailer Signature
+
+The MIE format may be used for trailer information appended to other types
+of files.  When this is done, a signature must appear at the end of the main
+MIE group to uniquely identify the MIE format trailer.  To achieve this, a
+"zmie" trailer signature is written as the last element in the main "0MIE"
+group.  This element has a FormatCode of 0, a TagLength of 4, a DataLength
+of 0, and a TagName of "zmie".  With this signature, the hex byte sequence
+"7e 00 04 00 7a 6d 69 65" appears immediately before the final group
+terminator, and the last 22 bytes of the trailer correspond to one of the
+following 4 patterns (where the trailer length is given by "xx", as above):
+
+  ?? ?? ?? ?? 7e 00 04 00 7a 6d 69 65 7e 00 00 06 xx xx xx xx 10 04
+  ?? ?? ?? ?? 7e 00 04 00 7a 6d 69 65 7e 00 00 06 xx xx xx xx 18 04
+  7e 00 04 00 7a 6d 69 65 7e 00 00 0a xx xx xx xx xx xx xx xx 10 08
+  7e 00 04 00 7a 6d 69 65 7e 00 00 0a xx xx xx xx xx xx xx xx 18 08
+
+Note that the zero-DataLength terminator may not be used here because the
+trailer length must be known for seeking backwards from the end of the file.
+
+Multiple trailers may be appended to the same file using this technique.
+
+=head2 MIE Data Values
+
+MIE data values for a given tag are usually not restricted to a specific
+FormatCode.  Any value may be represented in any appropriate format,
+including numbers represented in string (ASCII or UTF) form.
+
+It is preferred that closely related values with the same format are written
+to a single tag instead of using multiple tags.  This improves localization
+of like values and decreases MIE element overhead.  For instance, instead of
+separate ImageWidth and ImageHeight tags, a single ImageSize tag is defined.
+
+Tags which may take on a discrete set of values should have meaningful
+values if possible.  This improves the extensibility of the format and
+allows a more reasonable interpretation of unrecognized values.
+
+=head3 Numerical Representation
+
+Integer and floating point numbers may be represented in binary or string
+form.  In string form, integers are a series of digits with an optional
+leading sign (ie. "[+|-]DDDDDD"), and multiple values are separated by a
+single space character (ie. "23 128 -32").  Floating point numbers are
+similar but may also contain a decimal point and/or a signed exponent with a
+leading 'e' character (ie. "[+|-]DD[.DDDDDD][e(+|-)EEE]").  The string "inf"
+is used to represent infinity.  One advantage of numerical strings is that
+they can have an arbitrarily high precision because the possible number of
+significant digits is virtually unlimited.
+
+Note that numerical values may have associated units of measurement which
+are specified in the L</TagName> string.
+
+=head3 Date/Time Format
+
+All MIE dates are strings in the form "YYYY:mm:dd HH:MM:SS.ss+HH:MM".  The
+fractional seconds (".ss") are optional, and if included may contain any
+number of significant digits (unlike all other fields which are a fixed
+number of digits and must be padded with leading zeros if necessary).  The
+timezone ("+HH:MM" or "-HH:MM") is recommended but not required.  If not
+given, the local system timezone is assumed.
+
+=head2 MIME Type
+
+The basic MIME type for a MIE file is "application/x-mie", however the
+specific MIME type depends on the type of subfile, and is obtained by adding
+"x-mie-" to the MIME type of the subfile.  For example, with a subfile of
+type "image/jpeg", the MIE file MIME type is "image/x-mie-jpeg".  But note
+that the "x-" is not duplicated if the subfile MIME type already starts with
+"x-".  So a subfile with MIME type "image/x-raw" is contained within a MIE
+file of type "image/x-mie-raw", not "image/x-mie-x-raw".  In the case of
+multiple documents in a MIE file, the MIME type is taken from the first
+document.  Regardless of the subfile type, all MIE-format files should have
+a filename extension of ".MIE".
+
+=head2 Levels of Support
+
+Basic MIE reader/writer applications may choose not to provide support for
+some advanced features of the MIE format.  Features which may not be
+supported by all software are:
+
+=over 4
+
+=item Compression
+
+Software not supporting compression must ignore compressed elements and
+groups, but should be able to process the remaining information.
+
+=item Large data lengths
+
+Some software may limit the maximum size of a MIE group or element.
+Historically, a limit of 2GB may be imposed by some systems.  However,
+8-byte data lengths should be supported by all applications provided the
+value doesn't exceed the system limit.  (ie. For systems with a 2GB limit,
+8-byte data lengths should be supported if the upper 17 bits are all zero.)
+If a data length above the system limit is encountered, it may be necessary
+for the application to stop processing if it can not seek to the next
+element in the file.
+
+=back
+
+=head1 EXAMPLES
+
+This section gives examples for working with MIE information using ExifTool.
+
+=head2 Encapsulating Information with Data in a MIE File
+
+The following command encapsulates any file recognized by ExifTool inside a
+MIE file, and initializes MIE tags from information within the file:
+
+    exiftool -o new.mie -tagsfromfile FILE '-mie:all<all' \
+        '-subfilename<filename' '-subfiletype<filetype' \
+        '-subfilemimetype<mimetype' '-subfiledata<=FILE'
+
+where C<FILE> is the name of the file.
+
+For unrecognized files, this command may be used:
+
+    exiftool -o new.mie -subfilename=FILE -subfiletype=TYPE \
+        -subfilemimetype=MIME '-subfiledata<=FILE'
+
+where C<TYPE> and C<MIME> represent the source file type and MIME type
+respectively.
+
+=head2 Adding a MIE Trailer to a File
+
+The MIE format may also be used to store information in a trailer appended
+to another type of file.  Beware that trailers may not be compatible with
+all file formats, but JPEG and TIFF are two formats where additional trailer
+information doesn't create any problems for normal parsing of the file.
+Also note that this technique has the disadvantage that trailer information
+is commonly lost if the file is subsequently edited by other software.
+
+Creating a MIE trailer with ExifTool is a two-step process since ExifTool
+can't currently be used to add a MIE trailer directly.  The example below
+illustrates the steps for adding a MIE trailer with a small preview image
+(C<small.jpg>) to a destination JPEG image (C<dst.jpg>).
+
+Step 1) Create a MIE file with a TrailerSignature containing the desired
+information:
+
+    exiftool -o new.mie -trailersignature=1 -tagsfromfile small.jpg \
+        '-previewimagetype<filetype' '-previewimagesize<imagesize' \
+        '-previewimagename<filename' '-previewimage<=small.jpg'
+
+Step 2) Append the MIE information to another file.  In Unix, this can be
+done with the 'cat' command:
+
+    cat new.mie >> dst.jpg
+
+Once added, ExifTool may be used to edit or delete a MIE trailer in a JPEG
+or TIFF image.
+
+=head2 Multiple MIE Documents in a Single File
+
+The MIE specification allows multiple MIE documents (or trailers) to exist
+in a single file.  A file like this may be created by simply concatenating
+MIE documents.  ExifTool may be used to access information in a specific
+document by adding a copy number to the MIE group name.  For example:
+
+    # write the Author tag in the second MIE document
+    exiftool -mie2:author=phil test.mie
+
+    # delete the first MIE document from a file
+    exiftool -mie1:all= test.mie
+
+=head2 Units of Measurement
+
+Some MIE tags allow values to be specified in different units of
+measurement.  In the MIE file format these units are combined with the tag
+name, but when using ExifTool they are specified in brackets after the
+value:
+
+    exiftool -mie:gpsaltitude='7500(ft)' test.mie
+
+If no units are provided, the default units are written.
+
+=head2 Localized Text
+
+Localized text values are accessed by adding a language/country code to the
+tag name.  For example:
+
+    exiftool -comment-en_us='this is a comment' test.mie
+
+=head1 REVISIONS
+
+  2007-01-21 - Specified LF character (0x0a) for text newline sequence
+  2007-01-19 - Specified ISO 8859-1 character set for extended ASCII codes
+  2007-01-01 - Improved wording of Step 5 for scanning backwards in MIE file
+  2006-12-30 - Added EXAMPLES section and note about UTF BOM
+  2006-12-20 - MIE 1.1:  Changed meaning of TypeModifier bit (0x08) for
+               unknown data (FormatType 0x00), and documented byte swapping
+  2006-12-14 - MIE 1.0:  Added Data Values and Numerical Representations
+               sections, and added ability to specify units in tag names
+  2006-11-09 - Added Levels of Support section
+  2006-11-03 - Added Trailer Signature
+  2005-11-18 - Original specification created
+
+=head1 AUTHOR
+
+Copyright 2003-2007, Phil Harvey (phil at owl.phy.queensu.ca)
+
+This library is free software; you can redistribute it and/or modify it
+under the same terms as Perl itself.  The MIE format itself is also
+copyright Phil Harvey, and is covered by the same free-use license.
+
+=head1 REFERENCES
+
+=over 4
+
+=item L<http://owl.phy.queensu.ca/~phil/exiftool/MIE1.1-20070121.pdf>
+
+=back
+
+=head1 SEE ALSO
+
+L<Image::ExifTool::TagNames/MIE Tags>, L<Image::ExifTool::MIEUnits>,
+L<Image::ExifTool(3pm)|Image::ExifTool>
+
+=cut
+
Index: /gsdl/trunk/perllib/cpan/Image/ExifTool/MIEUnits.pod
===================================================================
--- /gsdl/trunk/perllib/cpan/Image/ExifTool/MIEUnits.pod	(revision 16842)
+++ /gsdl/trunk/perllib/cpan/Image/ExifTool/MIEUnits.pod	(revision 16842)
@@ -0,0 +1,377 @@
+
+=head1 NAME
+
+Image::ExifTool::MIEUnits - MIE units documentation
+
+=head1 DESCRIPTION
+
+The MIE format allows units of measurement to be specified in brackets at
+the end of a MIE tag name (ie. "Volume(m3)"). This document describes the
+standard MIE units abbreviations.
+
+=head1 SYNTAX
+
+The units string may contain any ASCII characters in the range 0x21 ('!') to
+0x7d ('}'), excepting the bracket characters (0x28 and 0x29).  An empty
+string is allowed, and indicates a dimensionless value.  L<Standard
+units|/STANDARD UNITS> should be used where possible.  In the standard
+units, an underline ('_') is used to indicate a subscript, and multiple
+words may be separated with a hyphen ('-').
+
+Exponents should be positive, and require no separator (ie. "m2" for square
+meters).  L<Prefixes|/PREFIXES> may be added to the standard units (ie.
+"cm") except when the resulting name conflicts with another standard unit.
+
+Multiplication is indicated by '.', and division by '/'.  Reciprocal units
+(ie. the multiplicative inverse) are obtained through division rather than
+the use of negative exponents (ie. "/in", not "in-1").  In MIE units,
+multiplication has precedence over division, so everything to the right of a
+'/' is in the denominator.  (See L</EXAMPLES> for a few examples.)
+
+Below is a table summarizing the special characters used in MIE units
+strings.
+
+  .           multiplication
+  /           division (used for negative exponents)
+  -           word separator
+  _           subscript
+  {}          annotation
+  []          used to avoid name conflicts if necessary
+  0-9         exponentiation
+
+=head1 STANDARD UNITS
+
+              dimensionless
+  [G]         Newtonian constant of gravitation (unclassified)
+  [g]         standard acceleration of free fall (acceleration)
+  [h]         Planck constant (action)
+  [k]         Boltzmann constant (unclassified)
+  {cfu}       colony forming units (number)
+  {rbc}       red blood cell count (number)
+  {tbl}       tablets (number)
+  {tot}       particles total count (number)
+  %           percent (fraction)
+  10^N        the number ten for arbitrary powers (number)
+  A           Ampere (electric current)
+  a           year (time)
+  a_g         mean Gregorian year (time)
+  a_j         mean Julian year (time)
+  a_t         tropical year (time)
+  acr         acre, U.S. (area)
+  acr_br      acre, British (area)
+  Ao          Angstrom (length)
+  APL-U       APL unit (biologic activity of anticardiolipin IgA)
+  ar          are (area)
+  arb-U       arbitrary unit (arbitrary)
+  arcmin      minute of arc (plane angle)
+  arcsec      second of arc (plane angle)
+  atm         standard atmosphere (pressure)
+  att         technical atmosphere (pressure)
+  AU          astronomic unit (length)
+  b           barn (action area)
+  B           bel (level)
+  B-kW        bel kilowatt (power level)
+  B-mV        bel millivolt (electric potential level)
+  B-SPL       bel sound pressure (pressure level)
+  B-uV        bel microvolt (electric potential level)
+  B-V         bel volt (electric potential level)
+  B-W         bel watt (power level)
+  bar         bar (pressure)
+  bbl         barrel (fluid volume)
+  Bd          baud (signal transmission rate)
+  bdsk-U      Bodansky unit (biologic activity of phosphatase)
+  beth-U      Bethesda unit (biologic activity of factor VIII inhibitor)
+  bf          board foot (volume)
+  Bi          Biot (electric current)
+  bit         bit (amount of information)
+  Bq          Becquerel (radioactivity)
+  Btu         British thermal unit (energy)
+  Btu_39      British thermal unit at 39 degF (energy)
+  Btu_59      British thermal unit at 59 degF (energy)
+  Btu_60      British thermal unit at 60 degF (energy)
+  Btu_IT      international table British thermal unit (energy)
+  Btu_m       mean British thermal unit (energy)
+  Btu_th      thermochemical British thermal unit (energy)
+  bu          bushel, U.S. (dry volume)
+  bu_br       bushel, British (volume)
+  By          byte (amount of information)
+  C           Coulomb (electric charge)
+  c           velocity of light (velocity)
+  cal         calorie (energy)
+  Cal         nutrition label Calories (energy)
+  cal_15      calorie at 15 degC (energy)
+  cal_20      calorie at 20 degC (energy)
+  cal_IT      international table calorie (energy)
+  cal_m       mean calorie (energy)
+  cal_th      thermochemical calorie (energy)
+  car_Au      carat of gold alloys (mass fraction)
+  car_m       metric carat (mass)
+  cd          candela (luminous intensity)
+  Cel         degree Celsius (temperature)
+  Ch          Charriere (gauge of catheters)
+  ch          Gunter's chain, U.S. (length)
+  ch_br       Gunter's chain, British (length)
+  Ci          Curie (radioactivity)
+  cicero      cicero (length)
+  circ        circle (plane angle)
+  cml         circular mil, international (area)
+  cr          cord, international (volume)
+  crd_us      cord, U.S. (fluid volume)
+  cup_us      cup (volume)
+  d           day (time)
+  deg         degree (plane angle)
+  deg{mag}    degree from magnetic north (plane angle)
+  degF        degree Fahrenheit (temperature)
+  didot       didot (length)
+  diop        diopter (refraction of a lens)
+  dpt         dry pint, U.S. (dry volume)
+  dqt         dry quart, U.S. (dry volume)
+  dr          dram (mass)
+  dr_ap       dram, apothecary (mass)
+  drp         drop (volume)
+  dye-U       Dye unit (biologic activity of amylase)
+  dyn         dyne (force)
+  e           elementary charge (electric charge)
+  eps_0       permittivity of vacuum (electric permittivity)
+  eq          equivalents (amount of substance)
+  erg         erg (energy)
+  eV          electronvolt (energy)
+  F           Farad (electric capacitance)
+  fdr         fluid dram, U.S. (fluid volume)
+  fdr_br      fluid dram, British (volume)
+  foz         fluid ounce, U.S. (fluid volume)
+  foz_br      fluid ounce, British (volume)
+  ft          foot, international (length)
+  ft_br       foot, British (length)
+  ft_us       foot, U.S. (length)
+  fth         fathom, international (length)
+  fth_br      fathom, British (length)
+  fth_us      fathom, U.S. (length)
+  fur         furlong, U.S. (length)
+  G           Gauss (magnetic flux density)
+  g           gram (mass)
+  g.m/{H-B}   gram meter per heartbeat (prop. to ventricular stroke work)
+  g%          gram percent (mass fraction)
+  Gal         Gal (acceleration)
+  gal         gallon, U.S. (fluid volume)
+  gal_br      gallon, British (volume)
+  gal_wi      historical winchester gallon (dry volume)
+  Gb          Gilbert (magnetic tension)
+  gf          gram-force (force)
+  gil         gill, U.S. (fluid volume)
+  gil_br      gill, British (volume)
+  gon         gon (plane angle)
+  GPL-U       GPL unit (biologic activity of anticardiolipin IgG)
+  gr          grain (mass)
+  Gy          Gray (energy dose)
+  H           Henry (inductance)
+  h           hour (time)
+  hd          hand, international (height of horses)
+  hnsf-U      Hounsfield unit (x-ray attenuation)
+  HP          horsepower (power)
+  hp_C        homeopathic potency of centesimal series (homeopathic potency)
+  hp_X        homeopathic potency of decimal series (homeopathic potency)
+  HPF         high power field (view area in microscope)
+  Hz          Herz (frequency)
+  in          inch, international (length)
+  in_br       inch, British (length)
+  in_us       inch, U.S. (length)
+  in-H2O      inch of water column (pressure)
+  in-Hg       inch of mercury column (pressure)
+  iU          international unit (arbitrary)
+  J           Joule (energy)
+  K           Kelvin (temperature)
+  ka-U        King-Armstrong unit (biologic activity of phosphatase)
+  kat         katal (catalytic activity)
+  kg{wet-tis} kilogram of wet tissue (mass)
+  kn          knot, international (velocity)
+  kn_br       knot, British (velocity)
+  knk-U       Kunkel unit (arbitrary biologic activity)
+  Ky          Kayser (lineic number)
+  l           liter (volume)
+  L           liter (volume)
+  lb          pound (mass)
+  lb_ap       pound, apothecary (mass)
+  lb_tr       pound, troy (mass)
+  lbf         pound force (force)
+  lcwt        long hunderdweight (mass)
+  ligne       ligne (length)
+  lk          link for Gunter's chain, U.S. (length)
+  lk_br       link for Gunter's chain, British (length)
+  lm          lumen (luminous flux)
+  Lmb         Lambert (brightness)
+  lne         line (length)
+  LPF         low power field (view area in microscope)
+  lton        long ton (mass)
+  lx          lux (illuminance)
+  ly          light-year (length)
+  m           meter (length)
+  m_e         electron mass (mass)
+  m_p         proton mass (mass)
+  m-H2O       meter of water column (pressure)
+  m-Hg        meter of mercury column (pressure)
+  mclg-U      Mac Lagan unit (arbitrary biologic activity)
+  mesh        mesh, international (lineic number)
+  MET         metabolic equivalent (metabolic cost of physical activity)
+  mg{creat}   milligram of creatinine (mass)
+  mho         mho (electric conductance)
+  mi          mile, international (statute mile) (length)
+  mi_br       mile, British (length)
+  mi_us       mile, U.S. (length)
+  mil         mil, international (length)
+  mil_us      mil, U.S. (length)
+  min         minute (time)
+  min_br      minim, British (volume)
+  min_us      minim, U.S. (fluid volume)
+  mo          month (time)
+  mo_g        mean Gregorian month (time)
+  mo_j        mean Julian month (time)
+  mo_s        synodal month (time)
+  mol         mole (amount of substance)
+  MPL-U       MPL unit (biologic activity of anticardiolipin IgM)
+  mu_0        permeability of vacuum (magnetic permeability)
+  Mx          Maxwell (flux of magnetic induction)
+  N           Newton (force)
+  nmi         nautical mile, international (length)
+  nmi_br      nautical mile, British (length)
+  Np          neper (level)
+  Oe          Oersted (magnetic field intensity)
+  Ohm         Ohm (electric resistance)
+  osm         osmole of dissolved particles (amount of substance)
+  oz          ounce (mass)
+  oz_ap       ounce, apothecary (mass)
+  oz_tr       ounce, troy (mass)
+  P           Poise (dynamic viscosity)
+  Pa          Pascal (pressure)
+  pc          parsec (length)
+  pc_br       pace (length)
+  pca         pica (length)
+  pca_pr      Printer's pica (length)
+  pH          pH (acidity)
+  ph          phot (illuminance)
+  pi          the number pi (number)
+  pied        pied (length)
+  pk          peck, U.S. (dry volume)
+  pk_br       peck, British (volume)
+  pnt         point (length)
+  pnt_pr      Printer's point (length)
+  pouce       pouce (length)
+  ppb         parts per billion (fraction)
+  ppm         parts per million (fraction)
+  ppth        parts per thousand (fraction)
+  pptr        parts per trillion (fraction)
+  PRU         peripheral vascular resistance unit (fluid resistance)
+  psi         pound per square inch (pressure)
+  pt          pint, U.S. (fluid volume)
+  pt_br       pint, British (volume)
+  pwt_tr      pennyweight (mass)
+  qt          quart, U.S. (fluid volume)
+  qt_br       quart, British (volume)
+  R           Roentgen (ion dose)
+  rad         radian (plane angle)
+  RAD         radiation absorbed dose (energy dose)
+  rch         Ramden's chain, U.S. (length)
+  rd          rod, U.S. (length)
+  rd_br       rod, British (length)
+  REM         radiation equivalent man (dose equivalent)
+  rlk_us      link for Ramden's chain (length)
+  s           second (time)
+  S           Siemens (electric conductance)
+  sb          stilb (lum. intensity density)
+  sc_ap       scruple, apothecary (mass)
+  sct         section (area)
+  scwt        short hundredweight (mass)
+  smgy-U      Somogyi unit (biologic activity of amylase)
+  sph         spere (solid angle)
+  sr          streadian (solid angle)
+  st          stere (volume)
+  St          Stokes (kinematic viscosity)
+  ston        short ton (mass)
+  stone       stone (mass)
+  Sv          Sievert (dose equivalent)
+  Sv-U        Svedberg unit (sedimentation coefficient)
+  T           Tesla (magnetic flux density)
+  t           tonne (mass)
+  tb-U        tuberculin unit (biologic activity of tuberculin)
+  tbs         tablespoon, U.S. (volume)
+  todd-U      Todd unit (biologic activity antistreptolysin O)
+  tsp         teaspoon, U.S. (volume)
+  twp         township (area)
+  u           unified atomic mass unit (mass)
+  U           Unit (catalytic activity)
+  USP-U       U.S. Pharmacopeia unit (arbitrary)
+  V           Volt (electric potential)
+  W           Watt (power)
+  Wb          Weber (magnetic flux)
+  wk          week (time)
+  yd          yard, international (length)
+  yd_br       yard, British (length)
+  yd_us       yard, U.S. (length)
+
+=head1 PREFIXES
+
+Standard SI prefixes:
+
+  y           yocto (10^-24)
+  z           zepto (10^-21)
+  a           atto  (10^-18)
+  f           femto (10^-15)
+  p           pico  (10^-12)
+  n           nano  (10^-9)
+  u           micro (10^-6)
+  m           milli (10^-3)
+  c           centi (10^-2)
+  d           deci  (10^-1)
+  da          deka  (10)
+  h           hecto (10^2)
+  k           kilo  (10^3)
+  G           giga  (10^9)
+  M           mega  (10^6)
+  T           tera  (10^12)
+  P           peta  (10^15)
+  E           exa   (10^18)
+  Z           zetta (10^21)
+  Y           yotta (10^24)
+
+Binary power prefixes:
+
+  Ki          kibi  (2^10)
+  Mi          mebi  (2^20)
+  Gi          gibi  (2^30)
+  Ti          tebi  (2^40)
+
+=head1 EXAMPLES
+
+A few examples of combined units strings:
+
+  /cm3        per cubic centimeter
+  in2         square inches
+  kg.m/s2     Newtons (N)
+  m3/kg.s2    units of [G]
+  10^100      googols
+
+=head1 HISTORY
+
+  2006-12-14 - PH Created
+
+=head1 AUTHOR
+
+Copyright 2003-2007, Phil Harvey (phil at owl.phy.queensu.ca)
+
+This library is free software; you can redistribute it and/or modify it
+under the same terms as Perl itself.
+
+=head1 REFERENCES
+
+=over 4
+
+=item L<http://aurora.regenstrief.org/UCUM/>
+
+=back
+
+=head1 SEE ALSO
+
+L<Image::ExifTool::MIE(3pm)|Image::ExifTool::MIE>,
+L<Image::ExifTool(3pm)|Image::ExifTool>
+
+=cut
Index: /gsdl/trunk/perllib/cpan/Image/ExifTool/MIFF.pm
===================================================================
--- /gsdl/trunk/perllib/cpan/Image/ExifTool/MIFF.pm	(revision 16842)
+++ /gsdl/trunk/perllib/cpan/Image/ExifTool/MIFF.pm	(revision 16842)
@@ -0,0 +1,283 @@
+#------------------------------------------------------------------------------
+# File:         MIFF.pm
+#
+# Description:  Read Magick Image File Format meta information
+#
+# Revisions:    06/10/2005 - P. Harvey Created
+#
+# References:   1) http://www.imagemagick.org/script/miff.php
+#               2) http://www.cs.uni.edu/Help/ImageMagick/www/miff.html
+#------------------------------------------------------------------------------
+
+package Image::ExifTool::MIFF;
+
+use strict;
+use vars qw($VERSION);
+use Image::ExifTool qw(:DataAccess :Utils);
+
+$VERSION = '1.03';
+
+# MIFF chunks
+%Image::ExifTool::MIFF::Main = (
+    GROUPS => { 2 => 'Image' },
+    NOTES => q{
+The MIFF format allows aribrary tag names to be used.  Only the standard tag
+names are listed below, however ExifTool will decode any tags found in the
+image.
+    },
+   'background-color' => 'BackgroundColor',
+   'blue-primary' => 'BluePrimary',
+   'border-color' => 'BorderColor',
+   'matt-color' => 'MattColor',
+    class => 'Class',
+    colors => 'Colors',
+    colorspace => 'Colorspace',
+    columns => 'ImageWidth',
+    compression => 'Compression',
+    delay => 'Delay',
+    depth => 'Depth',
+    dispose => 'Dispose',
+    gamma => 'Gamma',
+   'green-primary' => 'GreenPrimary',
+    id => 'ID',
+    iterations => 'Iterations',
+    label => 'Label',
+    matte => 'Matte',
+    montage => 'Montage',
+    packets => 'Packets',
+    page => 'Page',
+    # profile tags.  Note the SubDirectory is not used by ProcessMIFF(),
+    # but is inserted for documentation purposes only
+   'profile-APP1' => [
+        # [this list is just for the sake of the documentation]
+        {
+            Name => 'APP1_Profile',
+            SubDirectory => {
+                TagTable => 'Image::ExifTool::Exif::Main',
+            },
+        },
+        {
+            Name => 'APP1_Profile',
+            SubDirectory => {
+                TagTable => 'Image::ExifTool::XMP::Main',
+            },
+        },
+    ],
+   'profile-exif' => { # haven't seen this, but it would make sense - PH
+        Name => 'EXIF_Profile',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::Exif::Main',
+        },
+    },
+   'profile-icc' => {
+        Name => 'ICC_Profile',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::ICC_Profile::Main',
+        },
+    },
+   'profile-iptc' => {
+        Name => 'IPTC_Profile',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::Photoshop::Main',
+        },
+    },
+   'profile-xmp' => { # haven't seen this, but it would make sense - PH
+        Name => 'XMP_Profile',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::XMP::Main',
+        },
+    },
+   'red-primary' => 'RedPrimary',
+   'rendering-intent' => 'RenderingIntent',
+    resolution => 'Resolution',
+    rows => 'ImageHeight',
+    scene => 'Scene',
+    signature => 'Signature',
+    units => 'Units',
+   'white-point' => 'WhitePoint',
+);
+
+#------------------------------------------------------------------------------
+# Extract meta information from a MIFF image
+# Inputs: 0) ExifTool object reference, 1) dirInfo reference
+# Returns: 1 on success, 0 if this wasn't a valid MIFF image
+sub ProcessMIFF($$)
+{
+    my ($exifTool, $dirInfo) = @_;
+    my $raf = $$dirInfo{RAF};
+    my $verbose = $exifTool->{OPTIONS}->{Verbose};
+    my ($hdr, $buff);
+
+    # validate the MIFF file (note: MIFF files _may_ begin with other
+    # characters, but this starting sequence is strongly suggested.)
+    return 0 unless $raf->Read($hdr, 14) == 14;
+    return 0 unless $hdr eq 'id=ImageMagick';
+    $exifTool->SetFileType();   # set the FileType tag
+
+    # set end-of-line character sequence to read to end of the TEXT
+    # section for new-type MIFF files (text ends with Colon+Ctrl-Z)
+    # Old MIFF files end with Colon+Linefeed, so this will likely
+    # slurp those entire files, which will be slower, but will work
+    # OK except that the profile information won't be decoded
+    my $oldsep = $/;
+    $/ = ":\x1a";
+
+    my $mode = '';
+    my @profiles;
+    if ($raf->ReadLine($buff)) {
+        chomp $buff;    # remove end-of-line chars
+        my $tagTablePtr = GetTagTable('Image::ExifTool::MIFF::Main');
+        my @entries = split ' ', $buff;
+        unshift @entries, $hdr; # put the ID back in
+        my ($tag, $val);
+        foreach (@entries) {
+            if ($mode eq 'com') {
+                $mode = '' if /\}$/;
+                next;
+            } elsif (/^\{/) {
+                $mode = 'com';  # read to the end of the comment
+                next;
+            }
+            if ($mode eq 'val') {
+                $val .= " $_";  # join back together with a space
+                next unless /\}$/;
+                $mode = '';
+                $val =~ s/(^\{|\}$)//g; # remove braces
+            } elsif (/(.+)=(.+)/) {
+                ($tag, $val) = ($1, $2);
+                if ($val =~ /^\{/) {
+                    $mode = 'val';      # read to the end of the value data
+                    next;
+                }
+            } elsif (/^:/) {
+                # this could be the end of an old-style MIFF file
+                last;
+            } else {
+                # something we don't recognize -- stop parsing here
+                $exifTool->Warn('Unrecognized MIFF data');
+                last;
+            }
+            my $tagInfo = $exifTool->GetTagInfo($tagTablePtr, $tag);
+            unless ($tagInfo) {
+                $tagInfo = { Name => $tag };
+                Image::ExifTool::AddTagToTable($tagTablePtr, $tag, $tagInfo);
+            }
+            $verbose and $exifTool->VerboseInfo($tag, $tagInfo,
+                Table  => $tagTablePtr,
+                DataPt => \$val,
+            );
+            # handle profile tags specially
+            if ($tag =~ /^profile-(.*)/) {
+                push @profiles, [$1, $val];
+            } else {
+                $exifTool->FoundTag($tagInfo, $val);
+            }
+        }
+    }
+    $/ = $oldsep;   # restore separator to original value
+
+    # process profile information
+    foreach (@profiles) {
+        my ($type, $len) = @{$_};
+        unless ($len =~ /^\d+$/) {
+            $exifTool->Warn("Invalid length for $type profile");
+            last;   # don't try to read the rest
+        }
+        unless ($raf->Read($buff, $len) == $len) {
+            $exifTool->Warn("Error reading $type profile ($len bytes)");
+            next;
+        }
+        my $processed = 0;
+        my %dirInfo = (
+            Parent   => 'PNG',
+            DataPt   => \$buff,
+            DataPos  => $raf->Tell() - $len,
+            DataLen  => $len,
+            DirStart => 0,
+            DirLen   => $len,
+        );
+        if ($type eq 'icc') {
+            # ICC Profile information
+            my $tagTablePtr = GetTagTable('Image::ExifTool::ICC_Profile::Main');
+            $processed = $exifTool->ProcessDirectory(\%dirInfo, $tagTablePtr);
+        } elsif ($type eq 'iptc') {
+            if ($buff =~ /^8BIM/) {
+                # Photoshop information
+                my $tagTablePtr = GetTagTable('Image::ExifTool::Photoshop::Main');
+                $processed = $exifTool->ProcessDirectory(\%dirInfo, $tagTablePtr);
+            }
+        # I haven't seen 'exif' or 'xmp' profile types yet, but I have seen them
+        # in newer PNG files so presumably they are possible here as well - PH
+        } elsif ($type eq 'APP1' or $type eq 'exif' or $type eq 'xmp') {
+            if ($buff =~ /^$Image::ExifTool::exifAPP1hdr/) {
+                # APP1 EXIF
+                my $hdrLen = length($Image::ExifTool::exifAPP1hdr);
+                $dirInfo{DirStart} += $hdrLen;
+                $dirInfo{DirLen} -= $hdrLen;
+                # use the usual position for EXIF data: 12 bytes from start of file
+                # (this may be wrong, but I can't see where the PNG stores this information)
+                $dirInfo{Base} = 12; # this is the usual value
+                $processed = $exifTool->ProcessTIFF(\%dirInfo);
+            } elsif ($buff =~ /^$Image::ExifTool::xmpAPP1hdr/) {
+                # APP1 XMP
+                my $hdrLen = length($Image::ExifTool::xmpAPP1hdr);
+                my $tagTablePtr = GetTagTable('Image::ExifTool::XMP::Main');
+                $dirInfo{DirStart} += $hdrLen;
+                $dirInfo{DirLen} -= $hdrLen;
+                $processed = $exifTool->ProcessDirectory(\%dirInfo, $tagTablePtr);
+            }
+        }
+        unless ($processed) {
+            $exifTool->Warn("Unknown MIFF $type profile data");
+            if ($verbose) {
+                $exifTool->VerboseDir($type, 0, $len);
+                Image::ExifTool::HexDump(\$buff, undef,
+                    Out => $exifTool->Options('TextOut')
+                ) if $verbose > 2;
+             }
+         }
+    }
+    return 1;
+}
+
+1;  # end
+
+__END__
+
+=head1 NAME
+
+Image::ExifTool::MIFF - Read Magick Image File Format meta information
+
+=head1 SYNOPSIS
+
+This module is used by Image::ExifTool
+
+=head1 DESCRIPTION
+
+This module contains routines required by Image::ExifTool to read MIFF
+(Magick Image File Format) images.
+
+=head1 AUTHOR
+
+Copyright 2003-2007, Phil Harvey (phil at owl.phy.queensu.ca)
+
+This library is free software; you can redistribute it and/or modify it
+under the same terms as Perl itself.
+
+=head1 REFERENCES
+
+=over 4
+
+=item L<http://www.imagemagick.org/script/miff.php>
+
+=item L<http://www.cs.uni.edu/Help/ImageMagick/www/miff.html>
+
+=back
+
+=head1 SEE ALSO
+
+L<Image::ExifTool::TagNames/MIFF Tags>,
+L<Image::ExifTool(3pm)|Image::ExifTool>
+
+=cut
+
Index: /gsdl/trunk/perllib/cpan/Image/ExifTool/MNG.pm
===================================================================
--- /gsdl/trunk/perllib/cpan/Image/ExifTool/MNG.pm	(revision 16842)
+++ /gsdl/trunk/perllib/cpan/Image/ExifTool/MNG.pm	(revision 16842)
@@ -0,0 +1,684 @@
+#------------------------------------------------------------------------------
+# File:         MNG.pm
+#
+# Description:  MNG and JNG meta information tags
+#
+# Revisions:    06/23/2005 - P. Harvey Created
+#
+# References:   1) http://www.libpng.org/pub/mng/
+#------------------------------------------------------------------------------
+
+package Image::ExifTool::MNG;
+
+use strict;
+use vars qw($VERSION);
+use Image::ExifTool qw(:DataAccess :Utils);
+
+$VERSION = '1.00';
+
+# MNG chunks
+%Image::ExifTool::MNG::Main = (
+    GROUPS => { 2 => 'Image' },
+    NOTES => q{
+        This table contains definitions for tags found in MNG and JNG images.  MNG
+        is a superset of PNG and JNG, so a MNG image may contain any of these tags
+        as well as any PNG tags.  Conversely, only some of these tags are valid for
+        JNG images.
+    },
+    BACK => {
+        Name => 'Background',
+        SubDirectory => { TagTable => 'Image::ExifTool::MNG::Background' },
+    },
+    BASI => {
+        Name => 'BasisObject',
+        SubDirectory => { TagTable => 'Image::ExifTool::MNG::BasisObject' },
+    },
+    CLIP => {
+        Name => 'ClipObjects',
+        SubDirectory => { TagTable => 'Image::ExifTool::MNG::ClipObjects' },
+    },
+    CLON => {
+        Name => 'CloneObject',
+        SubDirectory => { TagTable => 'Image::ExifTool::MNG::CloneObject' },
+    },
+    DBYK => {
+        Name => 'DropByKeyword',
+        Binary => 1,
+    },
+    DEFI => {
+        Name => 'DefineObject',
+        SubDirectory => { TagTable => 'Image::ExifTool::MNG::DefineObject' },
+    },
+    DHDR => {
+        Name => 'DeltaPNGHeader',
+        SubDirectory => { TagTable => 'Image::ExifTool::MNG::DeltaPNGHeader' },
+    },
+    DISC => {
+        Name => 'DiscardObjects',
+        ValueConv => 'join(" ",unpack("n*",$val))',
+    },
+    DROP => {
+        Name => 'DropChunks',
+        ValueConv => 'join(" ",$val=~/..../g)',
+    },
+#   ENDL
+    eXPi => {
+        Name => 'ExportImage',
+        SubDirectory => { TagTable => 'Image::ExifTool::MNG::ExportImage' },
+    },
+    fPRI => {
+        Name => 'FramePriority',
+        SubDirectory => { TagTable => 'Image::ExifTool::MNG::FramePriority' },
+    },
+    FRAM => {
+        Name => 'Frame',
+        Binary => 1,
+    },
+#   IJNG
+#   IPNG
+#   JDAA (JNG)
+#   JDAT (JNG)
+    JHDR => { # (JNG)
+        Name => 'JNGHeader',
+        SubDirectory => { TagTable => 'Image::ExifTool::MNG::JNGHeader' },
+    },
+#   JSEP (JNG)
+    LOOP => {
+        Name => 'Loop',
+        SubDirectory => { TagTable => 'Image::ExifTool::MNG::Loop' },
+    },
+    MAGN => {
+        Name => 'MagnifyObject',
+        SubDirectory => { TagTable => 'Image::ExifTool::MNG::MagnifyObject' },
+    },
+#   MEND
+    MHDR => {
+        Name => 'MNGHeader',
+        SubDirectory => { TagTable => 'Image::ExifTool::MNG::MNGHeader' },
+    },
+    MOVE => {
+        Name => 'MoveObjects',
+        SubDirectory => { TagTable => 'Image::ExifTool::MNG::MoveObjects' },
+    },
+    nEED => {
+        Name => 'ResourcesNeeded',
+        Binary => 1,
+    },
+    ORDR => {
+        Name => 'OrderingRestrictions',
+        Binary => 1,
+    },
+    PAST => {
+        Name => 'PasteImage',
+        SubDirectory => { TagTable => 'Image::ExifTool::MNG::PasteImage' },
+    },
+    pHYg => {
+        Name => 'GlobalPixelSize',
+        SubDirectory => { TagTable => 'Image::ExifTool::PNG::PhysicalPixel' },
+    },
+    PPLT => {
+        Name => 'PartialPalette',
+        Binary => 1,
+    },
+    PROM => {
+        Name => 'PromoteParent',
+        SubDirectory => { TagTable => 'Image::ExifTool::MNG::PromoteParent' },
+    },
+    SAVE => {
+        Name => 'SaveObjects',
+        Binary => 1,
+    },
+    SEEK => {
+        Name => 'SeekPoint',
+        ValueConv => '$val=~s/\0.*//s; $val',
+    },
+    SHOW => {
+        Name => 'ShowObjects',
+        SubDirectory => { TagTable => 'Image::ExifTool::MNG::ShowObjects' },
+    },
+    TERM => {
+        Name => 'TerminationAction',
+        SubDirectory => { TagTable => 'Image::ExifTool::MNG::TerminationAction' },
+    },
+);
+
+# MNG MHDR chunk
+%Image::ExifTool::MNG::MNGHeader = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    GROUPS => { 2 => 'Image' },
+    FORMAT => 'int32u',
+    0 => 'ImageWidth',
+    1 => 'ImageHeight',
+    2 => 'TicksPerSecond',
+    3 => 'NominalLayerCount',
+    4 => 'NominalFrameCount',
+    5 => 'NominalPlayTime',
+    6 => {
+        Name => 'SimplicityProfile',
+        PrintConv => 'sprintf("0x%.8x", $val)',
+    },
+);
+
+# MNG BASI chunk
+%Image::ExifTool::MNG::BasisObject = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    GROUPS => { 2 => 'Image' },
+    0 => {
+        Name => 'ImageWidth',
+        Format => 'int32u',
+    },
+    4 => {
+        Name => 'ImageHeight',
+        Format => 'int32u',
+    },
+    8 => 'BitDepth',
+    9 => {
+        Name => 'ColorType',
+        RawConv => '$Image::ExifTool::PNG::colorType = $val',
+        PrintConv => {
+            0 => 'Grayscale',
+            2 => 'RGB',
+            3 => 'Palette',
+            4 => 'Grayscale with Alpha',
+            6 => 'RGB with Alpha',
+        },
+    },
+    10 => {
+        Name => 'Compression',
+        PrintConv => { 0 => 'Deflate/Inflate' },
+    },
+    11 => {
+        Name => 'Filter',
+        PrintConv => { 0 => 'Adaptive' },
+    },
+    12 => {
+        Name => 'Interlace',
+        PrintConv => { 0 => 'Noninterlaced', 1 => 'Adam7 Interlace' },
+    },
+    13 => {
+        Name => 'RedSample',
+        Format => 'int32u',
+    },
+    17 => {
+        Name => 'GreenSample',
+        Format => 'int32u',
+    },
+    21 => {
+        Name => 'BlueSample',
+        Format => 'int32u',
+    },
+    25 => {
+        Name => 'AlphaSample',
+        Format => 'int32u',
+    },
+    26 => 'Viewable',
+);
+
+# MNG LOOP chunk
+%Image::ExifTool::MNG::Loop = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    GROUPS => { 2 => 'Image' },
+    0 => 'NestLevel',
+    1 => {
+        Name => 'IterationCount',
+        Format => 'int32u',
+    },
+    5 => {
+        Name => 'TerminationCondition',
+        PrintConv => {
+            0 => 'Deterministic, not cacheable',
+            1 => 'Decoder discretion, not cacheable',
+            2 => 'User discretion, not cacheable',
+            3 => 'External signal, not cacheable',
+            4 => 'Deterministic, cacheable',
+            5 => 'Decoder discretion, cacheable',
+            6 => 'User discretion, cacheable',
+            7 => 'External signal, cacheable',
+        },
+    },
+    6 => {
+        Name => 'IterationMinMax',
+        Format => 'int32u[2]',
+    },
+    14 => {
+        Name => 'SignalNumber',
+        Format => 'int32u',
+    },
+);
+
+# MNG DEFI chunk
+%Image::ExifTool::MNG::DefineObject = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    GROUPS => { 2 => 'Image' },
+    0 => {
+        Name => 'ObjectID',
+        Format => 'int16u',
+    },
+    2 => 'DoNotShow',
+    3 => 'ConcreteFlag',
+    4 => {
+        Name => 'XYLocation',
+        Format => 'int32u[2]',
+    },
+    12 => {
+        Name => 'ClippingBoundary',
+        Format => 'int32u[4]',
+    },
+);
+
+# MNG CLON chunk
+%Image::ExifTool::MNG::CloneObject = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    GROUPS => { 2 => 'Image' },
+    0 => {
+        Name => 'SourceID',
+        Format => 'int16u',
+    },
+    2 => {
+        Name => 'CloneID',
+        Format => 'int16u',
+    },
+    4 => {
+        Name => 'CloneType',
+        PrintConv => { 0 => 'Full', 1 => 'Parital', 2 => 'Renumber object' },
+    },
+    5 => 'DoNotShow',
+    6 => 'ConcreteFlag',
+    7 => {
+        Name => 'LocalDeltaType',
+        PrintConv => { 0 => 'Absolute', 1 => 'Relative' },
+    },
+    8 => {
+        Name => 'DeltaXY',
+        Format => 'int32u[2]',
+    },
+);
+
+# MNG PAST chunk
+%Image::ExifTool::MNG::PasteImage = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    GROUPS => { 2 => 'Image' },
+    0 => {
+        Name => 'DestinationID',
+        Format => 'int16u',
+    },
+    2 => {
+        Name => 'TargetDeltaType',
+        PrintConv => { 0 => 'Absolute', 1 => 'Relative' },
+    },
+    3 => {
+        Name => 'TargetXY',
+        Format => 'int32u[2]',
+    },
+    11 => {
+        Name => 'SourceID',
+        Format => 'int16u',
+    },
+    13 => {
+        Name => 'CompositionMode',
+        PrintConv => { 0 => 'Over', 1 => 'Replace', 2 => 'Under' },
+    },
+    14 => {
+        Name => 'Orientation',
+        PrintConv => {
+            0 => 'Same as source',
+            2 => 'Flipped left-right, then up-down',
+            4 => 'Flipped left-right',
+            6 => 'Flipped up-down',
+            8 => 'Tiled',
+        },
+    },
+    15 => {
+        Name => 'OffsetOrigin',
+        PrintConv => { 0 => 'Desination Origin', 1 => 'Target Origin' },
+    },
+    16 => {
+        Name => 'OffsetXY',
+        Format => 'int32u[2]',
+    },
+    24 => {
+        Name => 'BoundaryOrigin',
+        PrintConv => { 0 => 'Desination Origin', 1 => 'Target Origin' },
+    },
+    25 => {
+        Name => 'PastClippingBoundary',
+        Format => 'int32u[4]',
+    },
+);
+
+my %magMethod = (
+    0 => 'No Magnification',
+    1 => 'Pixel Replication',
+    2 => 'Linear Interpolation',
+    3 => 'Closest Pixel',
+    4 => 'Color Linear Interpolation and Alpha Closest Pixel',
+    5 => 'Color Closest Pixel and Alpha Linear Interpolation',
+);
+
+# MNG MAGN chunk
+%Image::ExifTool::MNG::MagnifyObject = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    GROUPS => { 2 => 'Image' },
+    0 => {
+        Name => 'FirstObjectID',
+        Format => 'int16u',
+    },
+    2 => {
+        Name => 'LastObjectID',
+        Format => 'int16u',
+    },
+    4 => {
+        Name => 'XMethod',
+        PrintConv => \%magMethod,
+    },
+    5 => {
+        Name => 'XMag',
+        Format => 'int16u',
+    },
+    7 => {
+        Name => 'YMag',
+        Format => 'int16u',
+    },
+    9 => {
+        Name => 'LeftMag',
+        Format => 'int16u',
+    },
+    11 => {
+        Name => 'RightMag',
+        Format => 'int16u',
+    },
+    13 => {
+        Name => 'TopMag',
+        Format => 'int16u',
+    },
+    15 => {
+        Name => 'BottomMag',
+        Format => 'int16u',
+    },
+    17 => {
+        Name => 'YMethod',
+        PrintConv => \%magMethod,
+    },
+);
+
+# MNG TERM chunk
+%Image::ExifTool::MNG::TerminationAction = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    GROUPS => { 2 => 'Image' },
+    0 => {
+        Name => 'TerminationAction',
+        PrintConv => {
+            0 => 'Show Last Frame',
+            1 => 'Display Nothing',
+            2 => 'Show First Frame',
+            3 => 'Repeat Sequence',
+        },
+    },
+    1 => {
+        Name => 'IterationEndAction',
+        PrintConv => {
+            0 => 'Show Last Frame',
+            1 => 'Display Nothing',
+            2 => 'Show First Frame',
+        },
+    },
+    2 => {
+        Name => 'Delay',
+        Format => 'int32u',
+    },
+    6 => {
+        Name => 'IterationMax',
+        Format => 'int32u',
+    },
+);
+
+# MNG BACK chunk
+%Image::ExifTool::MNG::Background = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    GROUPS => { 2 => 'Image' },
+    0 => {
+        Name => 'BackgroundColor',
+        Format => 'int16u[3]',
+    },
+    6 => {
+        Name => 'MandatoryBackground',
+        PrintConv => {
+            0 => 'Color and Image Advisory',
+            1 => 'Color Mandatory, Image Advisory',
+            2 => 'Color Advisory, Image Mandatory',
+            3 => 'Color and Image Mandatory',
+        },
+    },
+    7 => {
+        Name => 'BackgroundImageID',
+        Format => 'int16u',
+    },
+    9 => {
+        Name => 'BackgroundTiling',
+        PrintConv => { 0 => 'No', 1 => 'Yes' },
+    },
+);
+
+# MNG MOVE chunk
+%Image::ExifTool::MNG::MoveObjects = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    GROUPS => { 2 => 'Image' },
+    0 => {
+        Name => 'FirstObject',
+        Format => 'int16u',
+    },
+    2 => {
+        Name => 'LastObject',
+        Format => 'int16u',
+    },
+    4 => {
+        Name => 'DeltaType',
+        PrintConv => { 0 => 'Absolute', 1 => 'Relative' },
+    },
+    5 => {
+        Name => 'DeltaXY',
+        Format => 'int32u[2]',
+    },
+);
+
+# MNG CLIP chunk
+%Image::ExifTool::MNG::ClipObjects = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    GROUPS => { 2 => 'Image' },
+    0 => {
+        Name => 'FirstObject',
+        Format => 'int16u',
+    },
+    2 => {
+        Name => 'LastObject',
+        Format => 'int16u',
+    },
+    4 => {
+        Name => 'DeltaType',
+        PrintConv => { 0 => 'Absolute', 1 => 'Relative' },
+    },
+    5 => {
+        Name => 'ClipBoundary',
+        Format => 'int32u[4]',
+    },
+);
+
+# MNG SHOW chunk
+%Image::ExifTool::MNG::ShowObjects = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    GROUPS => { 2 => 'Image' },
+    0 => {
+        Name => 'FirstObject',
+        Format => 'int16u',
+    },
+    2 => {
+        Name => 'LastObject',
+        Format => 'int16u',
+    },
+    4 => 'ShowMode',
+);
+
+# MNG eXPI chunk
+%Image::ExifTool::MNG::ExportImage = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    GROUPS => { 2 => 'Image' },
+    0 => {
+        Name => 'SnapshotID',
+        Format => 'int16u',
+    },
+    2 => {
+        Name => 'SnapshotName',
+        Format => 'string',
+    },
+);
+
+# MNG fPRI chunk
+%Image::ExifTool::MNG::FramePriority = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    GROUPS => { 2 => 'Image' },
+    0 => {
+        Name => 'DeltaType',
+        PrintConv => { 0 => 'Absolute', 1 => 'Relative' },
+    },
+    2 => 'Priority',
+);
+
+# MNG DHDR chunk
+%Image::ExifTool::MNG::DeltaPNGHeader = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    GROUPS => { 2 => 'Image' },
+    0 => {
+        Name => 'ObjectID',
+        Format => 'int16u',
+    },
+    2 => {
+        Name => 'ImageType',
+        PrintConv => {
+            0 => 'Unspecified',
+            1 => 'PNG',
+            2 => 'JNG',
+        },
+    },
+    3 => {
+        Name => 'DeltaType',
+        PrintConv => {
+            0 => 'Full Replacement',
+            1 => 'Pixel Addition',
+            2 => 'Alpha Addition',
+            3 => 'Color Addition',
+            4 => 'Pixel Replacement',
+            5 => 'Alpha Replacement',
+            6 => 'Color Replacement',
+            7 => 'No Change',
+        },
+    },
+    4 => {
+        Name => 'BlockSize',
+        Format => 'int32u[2]',
+    },
+    12 => {
+        Name => 'BlockLocation',
+        Format => 'int32u[2]',
+    },
+);
+
+# MNG PROM chunk
+%Image::ExifTool::MNG::PromoteParent = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    GROUPS => { 2 => 'Image' },
+    0 => 'NewColorType',
+    1 => 'NewBitDepth',
+    2 => {
+        Name => 'FillMethod',
+        PrintConv => { 0 => 'Bit Replication', 1 => 'Zero Fill' },
+    },
+);
+
+# JNG JHDR chunk
+%Image::ExifTool::MNG::JNGHeader = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    GROUPS => { 2 => 'Image' },
+    0 => {
+        Name => 'ImageWidth',
+        Format => 'int32u',
+    },
+    4 => {
+        Name => 'ImageHeight',
+        Format => 'int32u',
+    },
+    8 => {
+        Name => 'ColorType',
+        PrintConv => {
+            8 => 'Gray',
+            10 => 'Color',
+            12 => 'Gray Alpha',
+            14 => 'Color Alpha',
+        },
+    },
+    9 => 'BitDepth',
+    10 => {
+        Name => 'Compression',
+        PrintConv => { 8 => 'Huffman-coded baseline JPEG' },
+    },
+    11 => {
+        Name => 'Interlace',
+        PrintConv => { 0 => 'Sequential', 8 => 'Progressive' },
+    },
+    12 => 'AlphaBitDepth',
+    13 => {
+        Name => 'AlphaCompression',
+        PrintConv => {
+            0 => 'MNG Grayscale IDAT',
+            8 => 'JNG 8-bit Grayscale JDAA',
+        },
+    },
+    14 => {
+        Name => 'AlphaFilter',
+        PrintConv => { 0 => 'Adaptive MNG (N/A for JPEG)' },
+    },
+    15 => {
+        Name => 'AlphaInterlace',
+        PrintConv => { 0 => 'Noninterlaced' },
+    },
+);
+
+1;  # end
+
+__END__
+
+=head1 NAME
+
+Image::ExifTool::MNG - MNG and JNG meta information tags
+
+=head1 SYNOPSIS
+
+This module is used by Image::ExifTool
+
+=head1 DESCRIPTION
+
+This module contains definitions required by Image::ExifTool to read MNG
+(Multi-image Network Graphics) and JNG (JPEG Network Graphics) images.
+
+=head1 AUTHOR
+
+Copyright 2003-2007, Phil Harvey (phil at owl.phy.queensu.ca)
+
+This library is free software; you can redistribute it and/or modify it
+under the same terms as Perl itself.
+
+=head1 REFERENCES
+
+=over 4
+
+=item L<http://www.libpng.org/pub/mng/>
+
+=back
+
+=head1 SEE ALSO
+
+L<Image::ExifTool::TagNames/MNG Tags>,
+L<Image::ExifTool::TagNames/PNG Tags>,
+L<Image::ExifTool(3pm)|Image::ExifTool>
+
+=cut
+
Index: /gsdl/trunk/perllib/cpan/Image/ExifTool/MPC.pm
===================================================================
--- /gsdl/trunk/perllib/cpan/Image/ExifTool/MPC.pm	(revision 16842)
+++ /gsdl/trunk/perllib/cpan/Image/ExifTool/MPC.pm	(revision 16842)
@@ -0,0 +1,156 @@
+#------------------------------------------------------------------------------
+# File:         MPC.pm
+#
+# Description:  Read Musepack audio meta information
+#
+# Revisions:    11/14/2006 - P. Harvey Created
+#
+# References:   1) http://www.musepack.net/
+#------------------------------------------------------------------------------
+
+package Image::ExifTool::MPC;
+
+use strict;
+use vars qw($VERSION);
+use Image::ExifTool qw(:DataAccess :Utils);
+use Image::ExifTool::FLAC;
+
+$VERSION = '1.00';
+
+# MPC metadata blocks
+%Image::ExifTool::MPC::Main = (
+    PROCESS_PROC => \&Image::ExifTool::FLAC::ProcessBitStream,
+    GROUPS => { 2 => 'Audio' },
+    NOTES => q{
+        Tags used in Musepack (MPC) audio files.  ExifTool also extracts ID3 and APE
+        information from these files.
+    },
+    'Bit032-063' => 'TotalFrames',
+    'Bit080-081' => {
+        Name => 'SampleRate',
+        PrintConv => {
+            0 => 44100,
+            1 => 48000,
+            2 => 37800,
+            3 => 32000,
+        },
+    },
+    'Bit084-087' => {
+        Name => 'Quality',
+        PrintConv => {
+             1 => 'Unstable/Experimental',
+             5 => '0',
+             6 => '1',
+             7 => '2 (Telephone)',
+             8 => '3 (Thumb)',
+             9 => '4 (Radio)',
+            10 => '5 (Standard)',
+            11 => '6 (Xtreme)',
+            12 => '7 (Insane)',
+            13 => '8 (BrainDead)',
+            14 => '9',
+            15 => '10',
+       },
+    },
+    'Bit088-093' => 'MaxBand',
+    'Bit096-111' => 'ReplayGainTrackPeak',
+    'Bit112-127' => 'ReplayGainTrackGain',
+    'Bit128-143' => 'ReplayGainAlbumPeak',
+    'Bit144-159' => 'ReplayGainAlbumGain',
+    'Bit179' => {
+        Name => 'FastSeek',
+        PrintConv => { 0 => 'No', 1 => 'Yes' },
+    },
+    'Bit191' => {
+        Name => 'Gapless',
+        PrintConv => { 0 => 'No', 1 => 'Yes' },
+    },
+    'Bit216-223' => {
+        Name => 'EncoderVersion',
+        PrintConv => '$val =~ s/(\d)(\d)(\d)$/$1.$2.$3/; $val',
+    },
+);
+
+#------------------------------------------------------------------------------
+# Extract information from an MPC file
+# Inputs: 0) ExifTool object reference, 1) dirInfo reference
+# - Just looks for MPC trailer if FileType is already set
+# Returns: 1 on success, 0 if this wasn't a valid MPC file
+sub ProcessMPC($$)
+{
+    my ($exifTool, $dirInfo) = @_;
+
+    # must first check for leading ID3 information
+    unless ($exifTool->{DONE_ID3}) {
+        require Image::ExifTool::ID3;
+        Image::ExifTool::ID3::ProcessID3($exifTool, $dirInfo) and return 1;
+    }
+    my $raf = $$dirInfo{RAF};
+    my $buff;
+
+    # check MPC signature
+    $raf->Read($buff, 32) == 32 and $buff =~ /^MP\+(.)/s or return 0;
+    my $vers = ord($1) & 0x0f;
+    $exifTool->SetFileType();
+
+    # extract audio information (currently only from version 7 MPC files)
+    if ($vers == 0x07) {
+        SetByteOrder('II');
+        my $pos = $raf->Tell() - 32;
+        if ($exifTool->Options('Verbose')) {
+            $exifTool->VPrint(0, "MPC Header (32 bytes):\n");
+            $exifTool->VerboseDump(\$buff, DataPos => $pos);
+        }
+        my $tagTablePtr = GetTagTable('Image::ExifTool::MPC::Main');
+        my %dirInfo = ( DataPt => \$buff, DataPos => $pos );
+        $exifTool->ProcessDirectory(\%dirInfo, $tagTablePtr);
+    } else {
+        $exifTool->Warn('Audio info not currently extracted from this version MPC file');
+    } 
+
+    # process APE trailer if it exists
+    require Image::ExifTool::APE;
+    Image::ExifTool::APE::ProcessAPE($exifTool, $dirInfo);
+
+    return 1;
+}
+
+1;  # end
+
+__END__
+
+=head1 NAME
+
+Image::ExifTool::MPC - Read Musepack audio meta information
+
+=head1 SYNOPSIS
+
+This module is used by Image::ExifTool
+
+=head1 DESCRIPTION
+
+This module contains definitions required by Image::ExifTool to extract meta
+information from Musepack (MPC) audio files.
+
+=head1 AUTHOR
+
+Copyright 2003-2007, Phil Harvey (phil at owl.phy.queensu.ca)
+
+This library is free software; you can redistribute it and/or modify it
+under the same terms as Perl itself.
+
+=head1 REFERENCES
+
+=over 4
+
+=item L<http://www.musepack.net/>
+
+=back
+
+=head1 SEE ALSO
+
+L<Image::ExifTool::TagNames/MPC Tags>,
+L<Image::ExifTool(3pm)|Image::ExifTool>
+
+=cut
+
Index: /gsdl/trunk/perllib/cpan/Image/ExifTool/MPEG.pm
===================================================================
--- /gsdl/trunk/perllib/cpan/Image/ExifTool/MPEG.pm	(revision 16842)
+++ /gsdl/trunk/perllib/cpan/Image/ExifTool/MPEG.pm	(revision 16842)
@@ -0,0 +1,520 @@
+#------------------------------------------------------------------------------
+# File:         MPEG.pm
+#
+# Description:  Read MPEG-1 and MPEG-2 meta information
+#
+# Revisions:    05/11/2006 - P. Harvey Created
+#
+# References:   1) http://www.mp3-tech.org/
+#               2) http://www.getid3.org/
+#------------------------------------------------------------------------------
+
+package Image::ExifTool::MPEG;
+
+use strict;
+use vars qw($VERSION);
+use Image::ExifTool qw(:DataAccess :Utils);
+
+$VERSION = '1.04';
+
+%Image::ExifTool::MPEG::Audio = (
+    GROUPS => { 2 => 'Audio' },
+    'Bit11-12' => {
+        Name => 'MPEGAudioVersion',
+        RawConv => '$self->{MPEG_Vers} = $val',
+        PrintConv => {
+            0 => 2.5,
+            2 => 2,
+            3 => 1,
+        },
+    },
+    'Bit13-14' => {
+        Name => 'AudioLayer',
+        RawConv => '$self->{MPEG_Layer} = $val',
+        PrintConv => {
+            1 => 3,
+            2 => 2,
+            3 => 1,
+        },
+    },
+    # Bit 15 indicates CRC protection
+    'Bit16-19' => [
+        {
+            Name => 'AudioBitrate',
+            Condition => '$self->{MPEG_Vers} == 3 and $self->{MPEG_Layer} == 3',
+            Notes => 'version 1, layer 1',
+            PrintConv => {
+                0 => 'free',
+                1 => 32000,
+                2 => 64000,
+                3 => 96000,
+                4 => 128000,
+                5 => 160000,
+                6 => 192000,
+                7 => 224000,
+                8 => 256000,
+                9 => 288000,
+                10 => 320000,
+                11 => 352000,
+                12 => 384000,
+                13 => 416000,
+                14 => 448000,
+            },
+        },
+        {
+            Name => 'AudioBitrate',
+            Condition => '$self->{MPEG_Vers} == 3 and $self->{MPEG_Layer} == 2',
+            Notes => 'version 1, layer 2',
+            PrintConv => {
+                0 => 'free',
+                1 => 32000,
+                2 => 48000,
+                3 => 56000,
+                4 => 64000,
+                5 => 80000,
+                6 => 96000,
+                7 => 112000,
+                8 => 128000,
+                9 => 160000,
+                10 => 192000,
+                11 => 224000,
+                12 => 256000,
+                13 => 320000,
+                14 => 384000,
+            },
+        },
+        {
+            Name => 'AudioBitrate',
+            Condition => '$self->{MPEG_Vers} == 3 and $self->{MPEG_Layer} == 1',
+            Notes => 'version 1, layer 3',
+            PrintConv => {
+                0 => 'free',
+                1 => 32000,
+                2 => 40000,
+                3 => 48000,
+                4 => 56000,
+                5 => 64000,
+                6 => 80000,
+                7 => 96000,
+                8 => 112000,
+                9 => 128000,
+                10 => 160000,
+                11 => 192000,
+                12 => 224000,
+                13 => 256000,
+                14 => 320000,
+            },
+        },
+        {
+            Name => 'AudioBitrate',
+            Condition => '$self->{MPEG_Vers} != 3 and $self->{MPEG_Layer} == 3',
+            Notes => 'version 2 or 2.5, layer 1',
+            PrintConv => {
+                0 => 'free',
+                1 => 32000,
+                2 => 48000,
+                3 => 56000,
+                4 => 64000,
+                5 => 80000,
+                6 => 96000,
+                7 => 112000,
+                8 => 128000,
+                9 => 144000,
+                10 => 160000,
+                11 => 176000,
+                12 => 192000,
+                13 => 224000,
+                14 => 256000,
+            },
+        },
+        {
+            Name => 'AudioBitrate',
+            Condition => '$self->{MPEG_Vers} != 3 and $self->{MPEG_Layer}',
+            Notes => 'version 2 or 2.5, layer 2 or 3',
+            PrintConv => {
+                0 => 'free',
+                1 => 8000,
+                2 => 16000,
+                3 => 24000,
+                4 => 32000,
+                5 => 40000,
+                6 => 48000,
+                7 => 56000,
+                8 => 64000,
+                9 => 80000,
+                10 => 96000,
+                11 => 112000,
+                12 => 128000,
+                13 => 144000,
+                14 => 160000,
+            },
+        },
+    ],
+    'Bit20-21' => [
+        {
+            Name => 'SampleRate',
+            Condition => '$self->{MPEG_Vers} == 3',
+            Notes => 'version 1',
+            PrintConv => {
+                0 => 44100,
+                1 => 48000,
+                2 => 32000,
+            },
+        },
+        {
+            Name => 'SampleRate',
+            Condition => '$self->{MPEG_Vers} == 2',
+            Notes => 'version 2',
+            PrintConv => {
+                0 => 22050,
+                1 => 24000,
+                2 => 16000,
+            },
+        },
+        {
+            Name => 'SampleRate',
+            Condition => '$self->{MPEG_Vers} == 0',
+            Notes => 'version 2.5',
+            PrintConv => {
+                0 => 11025,
+                1 => 12000,
+                2 => 8000,
+            },
+        },
+    ],
+    # Bit 22 - padding flag
+    # Bit 23 - private bit
+    'Bit24-25' => {
+        Name => 'ChannelMode',
+        PrintConv => {
+            0 => 'Stereo',
+            1 => 'Joint Stereo',
+            2 => 'Dual Channel',
+            3 => 'Single Channel',
+        },
+    },
+    'Bit26' => {
+        Name => 'MSStereo',
+        Condition => '$self->{MPEG_Layer} == 1',
+        Notes => 'layer 3',
+        PrintConv => { 0 => 'Off', 1 => 'On' },
+    },
+    'Bit27' => {
+        Name => 'IntensityStereo',
+        Condition => '$self->{MPEG_Layer} == 1',
+        Notes => 'layer 3',
+        PrintConv => { 0 => 'Off', 1 => 'On' },
+    },
+    'Bit26-27' => {
+        Name => 'ModeExtension',
+        Condition => '$self->{MPEG_Layer} > 1',
+        Notes => 'layer 1 or 2',
+        PrintConv => {
+            0 => 'Bands 4-31',
+            1 => 'Bands 8-31',
+            2 => 'Bands 12-31',
+            3 => 'Bands 16-31',
+        },
+    },
+    'Bit28'    => {
+        Name => 'CopyrightFlag',
+        PrintConv => {
+            0 => 'False',
+            1 => 'True',
+        },
+    },
+    'Bit29'    => {
+        Name => 'OriginalMedia',
+        PrintConv => {
+            0 => 'False',
+            1 => 'True',
+        },
+    },
+    'Bit30-31' => {
+        Name => 'Emphasis',
+        PrintConv => {
+            0 => 'None',
+            1 => '50/15 ms',
+            2 => 'reserved',
+            3 => 'CCIT J.17',
+        },
+    },
+);
+
+%Image::ExifTool::MPEG::Video = (
+    GROUPS => { 2 => 'Video' },
+    'Bit00-11' => 'ImageWidth',
+    'Bit12-23' => 'ImageHeight',
+    'Bit24-27' => {
+        Name => 'AspectRatio',
+        ValueConv => {
+            1 => 1,
+            2 => 0.6735,
+            3 => 0.7031,
+            4 => 0.7615,
+            5 => 0.8055,
+            6 => 0.8437,
+            7 => 0.8935,
+            8 => 0.9157,
+            9 => 0.9815,
+            10 => 1.0255,
+            11 => 1.0695,
+            12 => 1.0950,
+            13 => 1.1575,
+            14 => 1.2015,
+        },
+        PrintConv => {
+            1      => '1:1',
+            0.6735 => '0.6735',
+            0.7031 => '16:9, 625 line, PAL',
+            0.7615 => '0.7615',
+            0.8055 => '0.8055',
+            0.8437 => '16:9, 525 line, NTSC',
+            0.8935 => '0.8935',
+            0.9157 => '4:3, 625 line, PAL, CCIR601',
+            0.9815 => '0.9815',
+            1.0255 => '1.0255',
+            1.0695 => '1.0695',
+            1.0950 => '4:3, 525 line, NTSC, CCIR601',
+            1.1575 => '1.1575',
+            1.2015 => '1.2015',
+        },
+    },
+    'Bit28-31' => {
+        Name => 'FrameRate',
+        ValueConv => {
+            1 => 23.976,
+            2 => 24,
+            3 => 25,
+            4 => 29.97,
+            5 => 30,
+            6 => 50,
+            7 => 59.94,
+            8 => 60,
+        },
+        PrintConv => '"$val fps"',
+    },
+    'Bit32-49' => {
+        Name => 'VideoBitrate',
+        ValueConv => '$val eq 0x3ffff ? "Variable" : $val * 400',
+    },
+    # these tags not very interesting
+    #'Bit50'    => 'MarkerBit',
+    #'Bit51-60' => 'VBVBufferSize',
+    #'Bit61'    => 'ConstrainedParamFlag',
+    #'Bit62'    => 'IntraQuantMatrixFlag',
+);
+
+# composite tags
+%Image::ExifTool::MPEG::Composite = (
+    Duration => {
+        Groups => { 2 => 'Video' },
+        Require => {
+            0 => 'FileSize',
+        },
+        Desire => {
+            1 => 'MPEG:AudioBitrate',
+            2 => 'MPEG:VideoBitrate',
+        },
+        # calculate duration as file size divided by total bitrate
+        # (note: this is only approximate!)
+        ValueConv => q{
+            return undef unless $val[1] or $val[2];
+            return undef if $prt[1] and not $prt[1] =~ /^\d+$/;
+            return undef if $val[2] and not $val[2] =~ /^\d+$/;
+            return (8 * $val[0]) / (($prt[1]||0) + ($val[2]||0));
+        },
+        PrintConv => q{
+            my $h = int($val / 3600);
+            my $m = int($val / 60 - $h * 60);
+            my $s = $val - $h * 3600 - $m * 60;
+            return sprintf("%d:%.2d:%05.2f (approx)",$h,$m,$s);
+        },
+    },
+);
+
+# add our composite tags
+Image::ExifTool::AddCompositeTags('Image::ExifTool::MPEG');
+
+
+#------------------------------------------------------------------------------
+# Process information in an MPEG audio or video frame header
+# Inputs: 0) ExifTool object ref, 1) tag table ref, 2-N) list of 32-bit data words
+sub ProcessFrameHeader($$@)
+{
+    my ($exifTool, $tagTablePtr, @data) = @_;
+    my $tag;
+    foreach $tag (sort keys %$tagTablePtr) {
+        next unless $tag =~ /^Bit(\d{2})-?(\d{2})?/;
+        my ($b1, $b2) = ($1, $2 || $1);
+        my $index = int($b1 / 32);
+        my $word = $data[$index];
+        my $mask = 0;
+        foreach (0 .. ($b2 - $b1)) {
+            $mask += (1 << $_);
+        }
+        my $val = ($word >> (31 + 32*$index - $b2)) & $mask;
+        $exifTool->HandleTag($tagTablePtr, $tag, $val);
+    }
+}
+
+#------------------------------------------------------------------------------
+# Read MPEG audio frame header
+# Inputs: 0) ExifTool object reference, 1) Reference to audio data
+# Returns: 1 on success, 0 if no audio header was found
+sub ProcessMPEGAudio($$)
+{
+    my ($exifTool, $buffPt) = @_;
+    my $word;
+
+    for (;;) {
+        # find frame sync
+        return 0 unless $$buffPt =~ m{(\xff.{3})}sg;
+        $word = unpack('N', $1);    # get audio frame header word
+        unless (($word & 0xffe00000) == 0xffe00000) {
+            pos($$buffPt) = pos($$buffPt) - 2;  # next possible location for frame sync
+            next;
+        }
+        # validate header as much as possible
+        if (($word & 0x180000) == 0x080000 or   # 01 is a reserved version ID
+            ($word & 0x060000) == 0x000000 or   # 00 is a reserved layer description
+            ($word & 0x00f000) == 0x00f000 or   # 1111 is a bad bitrate index
+            ($word & 0x000600) == 0x000600 or   # 11 is a reserved sampling frequency
+            ($word & 0x000003) == 0x000002)     # 10 is a reserved emphasis
+        {
+            return 0;   # invalid frame header
+        }
+        last;
+    }
+    # set file type if not done already
+    $exifTool->SetFileType() unless $exifTool->{VALUE}->{FileType};
+
+    my $tagTablePtr = GetTagTable('Image::ExifTool::MPEG::Audio');
+    ProcessFrameHeader($exifTool, $tagTablePtr, $word);
+    return 1;
+}
+
+#------------------------------------------------------------------------------
+# Read MPEG video frame header
+# Inputs: 0) ExifTool object reference, 1) Reference to video data
+# Returns: 1 on success, 0 if no video header was found
+sub ProcessMPEGVideo($$)
+{
+    my ($exifTool, $buffPt) = @_;
+
+    return 0 unless length $$buffPt >= 4;
+    my ($w1, $w2) = unpack('N2', $$buffPt);
+    # validate as much as possible
+    if (($w1 & 0x000000f0) == 0x00000000 or     # 0000 is a forbidden aspect ratio
+        ($w1 & 0x000000f0) == 0x000000f0 or     # 1111 is a reserved aspect ratio
+        ($w1 & 0x0000000f) == 0 or              # frame rate must be 1-8
+        ($w1 & 0x0000000f) > 8)
+    {
+        return 0;
+    }
+    # set file type if not done already
+    $exifTool->SetFileType('MPEG') unless $exifTool->{VALUE}->{FileType};
+
+    my $tagTablePtr = GetTagTable('Image::ExifTool::MPEG::Video');
+    ProcessFrameHeader($exifTool, $tagTablePtr, $w1, $w2);
+    return 1;
+}
+
+#------------------------------------------------------------------------------
+# Read MPEG audio and video frame headers
+# Inputs: 0) ExifTool object reference, 1) Reference to audio/video data
+# Returns: 1 on success, 0 if no video header was found
+sub ProcessMPEGAudioVideo($$)
+{
+    my ($exifTool, $buffPt) = @_;
+    my %found;
+    my $rtnVal = 0;
+    my %proc = ( audio => \&ProcessMPEGAudio, video => \&ProcessMPEGVideo );
+
+    delete $exifTool->{AudioBitrate};
+    delete $exifTool->{VideoBitrate};
+
+    while ($$buffPt =~ /\0\0\x01(\xb3|\xc0)/g) {
+        my $type = $1 eq "\xb3" ? 'video' : 'audio';
+        next if $found{$type};
+        my $len = length($$buffPt) - pos($$buffPt);
+        last if $len < 4;
+        $len > 256 and $len = 256;
+        my $dat = substr($$buffPt, pos($$buffPt), $len);
+        # process MPEG audio or video
+        if (&{$proc{$type}}($exifTool, \$dat)) {
+            $rtnVal = 1;
+            $found{$type} = 1;
+            # done if we found audio and video
+            last if scalar(keys %found) == 2;
+        }
+    }
+    return $rtnVal;
+}
+
+#------------------------------------------------------------------------------
+# Read information frame an MPEG file
+# Inputs: 0) ExifTool object reference, 1) Directory information reference
+# Returns: 1 on success, 0 if this wasn't a valid MPEG file
+sub ProcessMPEG($$)
+{
+    my ($exifTool, $dirInfo) = @_;
+    my $raf = $$dirInfo{RAF};
+    my $buff;
+
+    $raf->Read($buff, 4) == 4 or return 0;
+    return 0 unless $buff =~ /^\0\0\x01[\xb0-\xbf]/;
+    $exifTool->SetFileType();
+
+    $raf->Seek(0,0);
+    $raf->Read($buff, 65536*4) or return 0;
+
+    return ProcessMPEGAudioVideo($exifTool, \$buff);
+}
+
+1;  # end
+
+__END__
+
+=head1 NAME
+
+Image::ExifTool::MPEG - Read MPEG-1 and MPEG-2 meta information
+
+=head1 SYNOPSIS
+
+This module is used by Image::ExifTool
+
+=head1 DESCRIPTION
+
+This module contains definitions required by Image::ExifTool to read MPEG-1
+and MPEG-2 audio/video files.
+
+=head1 NOTES
+
+Since ISO charges money for the official MPEG specification, this module is
+based on unofficial sources which may be incomplete, inaccurate or outdated.
+
+=head1 AUTHOR
+
+Copyright 2003-2007, Phil Harvey (phil at owl.phy.queensu.ca)
+
+This library is free software; you can redistribute it and/or modify it
+under the same terms as Perl itself.
+
+=head1 REFERENCES
+
+=over 4
+
+=item L<http://www.mp3-tech.org/>
+
+=item L<http://www.getid3.org/>
+
+=back
+
+=head1 SEE ALSO
+
+L<Image::ExifTool::TagNames/MPEG Tags>,
+L<Image::ExifTool(3pm)|Image::ExifTool>
+
+=cut
+
Index: /gsdl/trunk/perllib/cpan/Image/ExifTool/MakerNotes.pm
===================================================================
--- /gsdl/trunk/perllib/cpan/Image/ExifTool/MakerNotes.pm	(revision 16842)
+++ /gsdl/trunk/perllib/cpan/Image/ExifTool/MakerNotes.pm	(revision 16842)
@@ -0,0 +1,1060 @@
+#------------------------------------------------------------------------------
+# File:         MakerNotes.pm
+#
+# Description:  Read and write EXIF maker notes
+#
+# Revisions:    11/11/2004 - P. Harvey Created
+#------------------------------------------------------------------------------
+
+package Image::ExifTool::MakerNotes;
+
+use strict;
+use vars qw($VERSION);
+use Image::ExifTool qw(:DataAccess);
+use Image::ExifTool::Exif;
+
+sub ProcessUnknown($$$);
+
+$VERSION = '1.34';
+
+my $debug;          # set to 1 to enabled debugging code
+
+# conditional list of maker notes
+# Notes:
+# - This is NOT a normal tag table!
+# - All byte orders are now specified because we can now
+#   write maker notes into a file with different byte ordering!
+# - Put these in alphabetical order to make TagNames documentation nicer.
+@Image::ExifTool::MakerNotes::Main = (
+    # decide which MakerNotes to use (based on camera make/model)
+    {
+        Name => 'MakerNoteCanon',
+        # (starts with an IFD)
+        Condition => '$self->{CameraMake} =~ /^Canon/',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::Canon::Main',
+            ByteOrder => 'Unknown',
+        },
+    },
+    {
+        Name => 'MakerNoteCasio',
+        # do negative lookahead assertion just to get tags
+        # in a nice order for documentation
+        # (starts with an IFD)
+        Condition => '$self->{CameraMake}=~/^CASIO(?! COMPUTER CO.,LTD)/',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::Casio::Main',
+            ByteOrder => 'Unknown',
+        },
+    },
+    {
+        Name => 'MakerNoteCasio2',
+        # (starts with "QVC\0")
+        Condition => '$self->{CameraMake}=~/^CASIO COMPUTER CO.,LTD/',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::Casio::Type2',
+            Start => '$valuePtr + 6',
+            ByteOrder => 'Unknown',
+        },
+    },
+    {
+        # The Fuji programmers really botched this one up,
+        # but with a bit of work we can still read this directory
+        Name => 'MakerNoteFujiFilm',
+        # (starts with "FUJIFILM" -- also used by some Leica, Minolta and Sharp models)
+        Condition => '$$valPt =~ /^FUJIFILM/',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::FujiFilm::Main',
+            # there is an 8-byte maker tag (FUJIFILM) we must skip over
+            OffsetPt => '$valuePtr+8',
+            # the pointers are relative to the subdirectory start
+            # (before adding the offsetPt) - PH
+            Base => '$start',
+            ByteOrder => 'LittleEndian',
+        },
+    },
+    {
+        Name => 'MakerNoteHP',  # PhotoSmart 720 (also Vivitar 3705, 3705B and 3715)
+        Condition => '$$valPt =~ /^(Hewlett-Packard|Vivitar)/',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::HP::Main',
+            ProcessProc => \&ProcessUnknown,
+            ByteOrder => 'Unknown',
+        },
+    },
+    {
+        Name => 'MakerNoteHP2', # PhotoSmart E427
+        Condition => q{
+            $self->{CameraMake} =~ /^Hewlett-Packard/ and
+            $$valPt !~ /^.{8}Hewlett-Packard/s and
+            $$valPt !~ /^IIII/ and $$valPt =~ /^\d{3}.\0/s
+        },
+        NotIFD => 1,
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::HP::Type2',
+            Start => '$valuePtr',
+            ByteOrder => 'LittleEndian',
+        },
+    },
+    {
+        Name => 'MakerNoteHP4', # PhotoSmart M627
+        Condition => '$$valPt =~ /^IIII\x04\0/',
+        NotIFD => 1,
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::HP::Type4',
+            Start => '$valuePtr',
+            ByteOrder => 'LittleEndian',
+        },
+    },
+    {
+        Name => 'MakerNoteHP6', # PhotoSmart M425, M525 and M527
+        Condition => '$$valPt =~ /^IIII\x06\0/',
+        NotIFD => 1,
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::HP::Type6',
+            Start => '$valuePtr',
+            ByteOrder => 'LittleEndian',
+        },
+    },
+    {
+        Name => 'MakerNoteJVC',
+        Condition => '$$valPt=~/^JVC /',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::JVC::Main',
+            Start => '$valuePtr + 4',
+            ByteOrder => 'Unknown',
+        },
+    },
+    {
+        Name => 'MakerNoteJVCText',
+        Condition => '$self->{CameraMake}=~/^(JVC|Victor)/ and $$valPt=~/^VER:/',
+        NotIFD => 1,
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::JVC::Text',
+        },
+    },
+    {
+        Name => 'MakerNoteKodak1a',
+        Condition => '$self->{CameraMake}=~/^EASTMAN KODAK/ and $$valPt=~/^KDK INFO/',
+        NotIFD => 1,
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::Kodak::Main',
+            Start => '$valuePtr + 8',
+            ByteOrder => 'BigEndian',
+        },
+    },
+    {
+        Name => 'MakerNoteKodak1b',
+        Condition => '$self->{CameraMake}=~/^EASTMAN KODAK/ and $$valPt=~/^KDK/',
+        NotIFD => 1,
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::Kodak::Main',
+            Start => '$valuePtr + 8',
+            ByteOrder => 'LittleEndian',
+        },
+    },
+    {
+        # used by various Kodak, HP, Pentax and Minolta models
+        Name => 'MakerNoteKodak2',
+        Condition => q{
+            $$valPt =~ /^.{8}Eastman Kodak/s or
+            $$valPt =~ /^\x01\0[\0\x01]\0\0\0\x04\0[a-zA-Z]{4}/
+        },
+        NotIFD => 1,
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::Kodak::Type2',
+            ByteOrder => 'BigEndian',
+        },
+    },
+    {
+        # not much to key on here, but we know the
+        # upper byte of the year should be 0x07:
+        Name => 'MakerNoteKodak3',
+        Condition => '$self->{CameraMake}=~/^EASTMAN KODAK/ and $$valPt=~/^.{12}\x07/s',
+        NotIFD => 1,
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::Kodak::Type3',
+            ByteOrder => 'BigEndian',
+        },
+    },
+    {
+        Name => 'MakerNoteKodak4',
+        Condition => '$self->{CameraMake}=~/^Eastman Kodak/ and $$valPt=~/^.{41}JPG/s',
+        NotIFD => 1,
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::Kodak::Type4',
+            ByteOrder => 'BigEndian',
+        },
+    },
+    {
+        Name => 'MakerNoteKodak5',
+        Condition => q{
+            $self->{CameraMake}=~/^EASTMAN KODAK/ and
+            ($self->{CameraModel}=~/CX(4200|4230|4300|4310|6200|6230)/ or
+            # try to pick up similar models we haven't tested yet
+            $$valPt=~/^\0(\x1a\x18|\x3a\x08|\x59\xf8|\x14\x80)\0/)
+        },
+        NotIFD => 1,
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::Kodak::Type5',
+            ByteOrder => 'BigEndian',
+        },
+    },
+    {
+        Name => 'MakerNoteKodak6a',
+        Condition => q{
+            $self->{CameraMake}=~/^EASTMAN KODAK/ and
+            $self->{CameraModel}=~/DX3215/
+        },
+        NotIFD => 1,
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::Kodak::Type6',
+            ByteOrder => 'BigEndian',
+        },
+    },
+    {
+        Name => 'MakerNoteKodak6b',
+        Condition => q{
+            $self->{CameraMake}=~/^EASTMAN KODAK/ and
+            $self->{CameraModel}=~/DX3700/
+        },
+        NotIFD => 1,
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::Kodak::Type6',
+            ByteOrder => 'LittleEndian',
+        },
+    },
+    {
+        Name => 'MakerNoteKodak7',
+        # look for something that looks like a serial number
+        # (confirmed serial numbers have the format KXXXX########, but we also
+        #  accept other strings from sample images that may be serial numbers)
+        Condition => q{
+            $self->{CameraMake}=~/Kodak/i and
+            $$valPt =~ /^[CK][A-Z\d]{3} ?[A-Z\d]{1,2}\d{2}[A-Z\d]\d{4}[ \0]/
+        },
+        NotIFD => 1,
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::Kodak::Type7',
+            ByteOrder => 'LittleEndian',
+        },
+    },
+    {
+        Name => 'MakerNoteKodakIFD',
+        # look for reasonable number of entries and check
+        # format and count of first IFD entry
+        Condition => q{
+            $self->{CameraMake}=~/Kodak/i and
+            ($$valPt =~ /^\0[\x02-\x7f]..\0[\x01-\x0c]\0\0/s or
+             $$valPt =~ /^[\x02-\x7f]\0..[\x01-\x0c]\0..\0\0/s)
+        },
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::Kodak::IFD',
+            ProcessProc => \&ProcessUnknown,
+            ByteOrder => 'Unknown',
+        },
+    },
+    {
+        Name => 'MakerNoteKodakUnknown',
+        Condition => '$self->{CameraMake}=~/Kodak/i',
+        NotIFD => 1,
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::Kodak::Unknown',
+            ByteOrder => 'BigEndian',
+        },
+    },
+    {
+        Name => 'MakerNoteKyocera',
+        # (starts with "KYOCERA")
+        Condition => '$self->{CameraMake}=~/^KYOCERA/',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::Unknown::Main',
+            Start => '$valuePtr + 22',
+            Base => '$start + 2',
+            EntryBased => 1,
+            ByteOrder => 'Unknown',
+        },
+    },
+    {
+        Name => 'MakerNoteMinolta',
+        Condition => q{
+            $self->{CameraMake}=~/^(Konica Minolta|Minolta)/i and
+            $$valPt !~ /^(MINOL|CAMER|MLY0|KC|\+M\+M|\xd7)/
+        },
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::Minolta::Main',
+            ByteOrder => 'Unknown',
+        },
+    },
+    {
+        # the DiMAGE E323 (MINOL) and E500 (CAMER), and some models
+        # of Mustek, Pentax, Ricoh and Vivitar (CAMER).
+        Name => 'MakerNoteMinolta2',
+        Condition => '$$valPt =~ /^(MINOL|CAMER)\0/ and $$self{OlympusCAMER} = 1',
+        SubDirectory => {
+            # these models use Olympus tags in the range 0x200-0x221 plus 0xf00
+            TagTable => 'Image::ExifTool::Olympus::Main',
+            Start => '$valuePtr + 8',
+            ByteOrder => 'Unknown',
+        },
+    },
+    {
+        # /^MLY0/ - DiMAGE G400, G500, G530, G600
+        # /^KC/   - Revio KD-420Z, DiMAGE E203
+        # /^+M+M/ - DiMAGE E201
+        # /^\xd7/ - DiMAGE RD3000
+        Name => 'MakerNoteMinolta3',
+        Condition => '$self->{CameraMake} =~ /^(Konica Minolta|Minolta)/i',
+        Notes => 'not EXIF-based',
+        Binary => 1,
+        NotIFD => 1,
+    },
+    {
+        # this maker notes starts with a standard TIFF header at offset 0x0a
+        Name => 'MakerNoteNikon',
+        Condition => '$self->{CameraMake}=~/^NIKON/i and $$valPt=~/^Nikon\x00\x02/',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::Nikon::Main',
+            Start => '$valuePtr + 18',
+            Base => '$start - 8',
+            ByteOrder => 'Unknown',
+        },
+    },
+    {
+        # older Nikon maker notes
+        Name => 'MakerNoteNikon2',
+        Condition => '$self->{CameraMake}=~/^NIKON/ and $$valPt=~/^Nikon\x00\x01/',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::Nikon::Type2',
+            Start => '$valuePtr + 8',
+            ByteOrder => 'LittleEndian',
+        },
+    },
+    {
+        # headerless Nikon maker notes
+        Name => 'MakerNoteNikon3',
+        Condition => '$self->{CameraMake}=~/^NIKON/i',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::Nikon::Main',
+            ByteOrder => 'Unknown', # most are little-endian, but D1 is big
+        },
+    },
+    {
+        Name => 'MakerNoteOlympus',
+        # (if Make is 'SEIKO EPSON CORP.', starts with "EPSON\0")
+        # (if Make is 'OLYMPUS OPTICAL CO.,LTD' or 'OLYMPUS CORPORATION',
+        #  starts with "OLYMP\0")
+        Condition => '$$valPt =~ /^(OLYMP|EPSON)\0/',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::Olympus::Main',
+            Start => '$valuePtr + 8',
+            ByteOrder => 'Unknown',
+        },
+    },
+    {
+        Name => 'MakerNoteOlympus2',
+        # new Olympus maker notes start with "OLYMPUS\0"
+        Condition => '$$valPt =~ /^OLYMPUS\0/ and $$self{OlympusType2} = 1',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::Olympus::Main',
+            Start => '$valuePtr + 12',
+            Base => '$start - 12',
+            ByteOrder => 'Unknown',
+        },
+    },
+    {
+        Name => 'MakerNoteLeica',
+        # (starts with "LEICA\0")
+        Condition => '$self->{CameraMake} =~ /^LEICA/',
+        SubDirectory => {
+            # Leica uses the same format as Panasonic
+            TagTable => 'Image::ExifTool::Panasonic::Main',
+            Start => '$valuePtr + 8',
+            ByteOrder => 'Unknown',
+        },
+    },
+    {
+        Name => 'MakerNotePanasonic',
+        # (starts with "Panasonic\0")
+        Condition => '$self->{CameraMake} =~ /^Panasonic/ and $$valPt!~/^MKE/',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::Panasonic::Main',
+            Start => '$valuePtr + 12',
+            ByteOrder => 'Unknown',
+        },
+    },
+    {
+        Name => 'MakerNotePanasonic2',
+        # (starts with "Panasonic\0")
+        Condition => '$self->{CameraMake} =~ /^Panasonic/',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::Panasonic::Type2',
+            ByteOrder => 'LittleEndian',
+        },
+    },
+    {
+        Name => 'MakerNotePentax',
+        # (starts with "AOC\0", but so does MakerNotePentax3)
+        # (also used by some Samsung models)
+        Condition => q{
+            $$valPt=~/^AOC\0/ and
+            $self->{CameraModel} !~ /^PENTAX Optio ?[34]30RS\s*$/
+        },
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::Pentax::Main',
+            # process as Unknown maker notes because the start offset and
+            # byte ordering are so variable
+            ProcessProc => \&ProcessUnknown,
+            # offsets can be totally whacky for Pentax maker notes,
+            # so attempt to fix the offset base if possible
+            FixBase => 1,
+            ByteOrder => 'Unknown',
+        },
+    },
+    {
+        Name => 'MakerNotePentax2',
+        # (starts with an IFD)
+        # Casio-like maker notes used only by the Optio 330 and 430
+        Condition => '$self->{CameraMake}=~/^Asahi/ and $$valPt!~/^AOC\0/',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::Pentax::Type2',
+            ProcessProc => \&ProcessUnknown,
+            FixBase => 1,
+            ByteOrder => 'Unknown',
+        },
+    },
+    {
+        Name => 'MakerNotePentax3',
+        # (starts with "AOC\0", like the more common Pentax maker notes)
+        # Casio maker notes used only by the Optio 330RS and 430RS
+        Condition => '$self->{CameraMake}=~/^Asahi/',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::Casio::Type2',
+            ProcessProc => \&ProcessUnknown,
+            FixBase => 1,
+            ByteOrder => 'Unknown',
+        },
+    },
+    {
+        Name => 'MakerNotePentax4',
+        # (starts with 3 or 4 digits)
+        # HP2-like text-based maker notes used by Optio E20
+        Condition => '$self->{CameraMake}=~/^PENTAX/ and $$valPt=~/^\d{3}/',
+        NotIFD => 1,
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::Pentax::Type4',
+            Start => '$valuePtr',
+            ByteOrder => 'LittleEndian',
+        },
+    },
+    {
+        Name => 'MakerNoteRicoh',
+        Condition => '$self->{CameraMake}=~/^RICOH/ and $$valPt=~/^Ricoh/i',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::Ricoh::Main',
+            Start => '$valuePtr + 8',
+            ByteOrder => 'Unknown',
+        },
+    },
+    {
+        Name => 'MakerNoteRicohText',
+        Condition => '$self->{CameraMake}=~/^RICOH/',
+        NotIFD => 1,
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::Ricoh::Text',
+            ByteOrder => 'Unknown',
+        },
+    },
+    {
+        # PreviewImage (Samsung Digimax plus some other types of cameras)
+        %Image::ExifTool::previewImageTagInfo,
+        Condition => '$$valPt =~ /^\xff\xd8\xff\xdb/',
+        Notes => 'Samsung preview image',
+    },
+    {
+        Name => 'MakerNoteSanyo',
+        # (starts with "SANYO\0")
+        Condition => '$self->{CameraMake}=~/^SANYO/ and $self->{CameraModel} !~ /^(C4|J\d|S\d)\b/',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::Sanyo::Main',
+            Validate => '$val =~ /^SANYO/',
+            Start => '$valuePtr + 8',
+            ByteOrder => 'Unknown',
+        },
+    },
+    {
+        Name => 'MakerNoteSanyoC4',
+        # The C4 offsets are wrong by 12, so they must be fixed
+        Condition => '$self->{CameraMake}=~/^SANYO/ and $self->{CameraModel} =~ /^C4\b/',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::Sanyo::Main',
+            Validate => '$val =~ /^SANYO/',
+            Start => '$valuePtr + 8',
+            FixBase => 1,
+            ByteOrder => 'Unknown',
+        },
+    },
+    {
+        Name => 'MakerNoteSanyoPatch',
+        # The J1, J2, J4, S1, S3 and S4 offsets are completely screwy
+        Condition => '$self->{CameraMake}=~/^SANYO/',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::Sanyo::Main',
+            Validate => '$val =~ /^SANYO/',
+            Start => '$valuePtr + 8',
+            ByteOrder => 'Unknown',
+            FixOffsets => 'Image::ExifTool::Sanyo::FixOffsets($valuePtr, $valEnd, $size, $tagID, $wFlag)',
+        },
+    },
+    {
+        Name => 'MakerNoteSigma',
+        # (starts with "SIGMA\0")
+        Condition => '$self->{CameraMake}=~/^(SIGMA|FOVEON)/',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::Sigma::Main',
+            Validate => '$val =~ /^(SIGMA|FOVEON)/',
+            Start => '$valuePtr + 10',
+            ByteOrder => 'Unknown',
+        },
+    },
+    {
+        Name => 'MakerNoteSony',
+        # (starts with "SONY DSC \0" or "SONY CAM \0")
+        Condition => '$self->{CameraMake}=~/^SONY/ and $self->{TIFF_TYPE}!~/^(SRF|SR2|DNG)$/',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::Sony::Main',
+            # validate the maker note because this is sometimes garbage
+            Validate => 'defined($val) and $val =~ /^SONY (DSC|CAM)/',
+            Start => '$valuePtr + 12',
+            ByteOrder => 'Unknown',
+        },
+    },
+    {
+        Name => 'MakerNoteSonySRF',
+        Condition => '$self->{CameraMake}=~/^SONY/ and $$valPt =~ /^\x01\x00/',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::Sony::SRF',
+            Start => '$valuePtr',
+            ByteOrder => 'Unknown',
+        },
+    },
+    {
+        Name => 'MakerNoteSonySR2',
+        Condition => '$self->{CameraMake}=~/^SONY/',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::Sony::Main',
+            Start => '$valuePtr',
+            ByteOrder => 'Unknown',
+        },
+    },
+    {
+        Name => 'MakerNoteUnknown',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::Unknown::Main',
+            ProcessProc => \&ProcessUnknown,
+            ByteOrder => 'Unknown',
+        },
+    },
+);
+
+# insert writable properties so we can write our maker notes
+my $tagInfo;
+foreach $tagInfo (@Image::ExifTool::MakerNotes::Main) {
+    $$tagInfo{Writable} = 'undef';
+    $$tagInfo{WriteGroup} = 'ExifIFD';
+    $$tagInfo{Groups} = { 1 => 'MakerNotes' };
+    next unless $$tagInfo{SubDirectory};
+    # set up this tag so we can write it
+    $$tagInfo{ValueConv} = '\$val';
+    $$tagInfo{ValueConvInv} = '$val';
+    $$tagInfo{MakerNotes} = 1;
+}
+
+#------------------------------------------------------------------------------
+# Get normal offset of value data from end of maker note IFD
+# Inputs: 0) ExifTool object reference
+# Returns: Array: 0) relative flag (undef for no change)
+#                 1) normal offset from end of IFD to first value (empty if unknown)
+#                 2-N) other possible offsets used by some models
+# Notes: Directory size should be validated before calling this routine
+sub GetMakerNoteOffset($)
+{
+    my $exifTool = shift;
+    # figure out where we expect the value data based on camera type
+    my $make = $exifTool->{CameraMake};
+    my $model = $exifTool->{CameraModel};
+    my ($relative, @offsets);
+
+    # normally value data starts 4 bytes after end of directory, so this is the default
+    if ($make =~ /^Canon/) {
+        push @offsets, ($model =~ /\b(20D|350D|REBEL XT|Kiss Digital N)\b/) ? 6 : 4;
+        # some Canon models (FV-M30, Optura50, Optura60) leave 24 unused bytes
+        # at the end of the IFD (2 spare IFD entries?)
+        push @offsets, 28 if $model =~ /\b(FV\b|OPTURA)/;
+    } elsif ($make =~ /^KYOCERA/) {
+        push @offsets, 12;
+    } elsif ($make =~ /^OLYMPUS/ and $model =~ /^E-(1|300|330)\b/) {
+        push @offsets, 16;
+    } elsif ($make =~ /^OLYMPUS/ and
+        # these Olympus models are just weird
+        $model =~ /^(C2500L|C-1Z?|C-5000Z|X-2|C720UZ|C725UZ|C150|C2Z|E-10|E-20|FerrariMODEL2003|u20D|u10D)\b/)
+    {
+        # no expected offset --> determine offset empirically via FixBase()
+    } elsif ($make =~ /^(Panasonic|SONY|JVC)\b/) {
+        push @offsets, 0;
+    } elsif ($make eq 'FUJIFILM') {
+        # some models have offset of 6, so allow that too (A345,A350,A360,A370)
+        push @offsets, 4, 6;
+    } elsif ($make =~ /^TOSHIBA/) {
+        # similar to Canon, can also have 24 bytes of padding
+        push @offsets, 0, 24;
+    } elsif ($make =~ /^PENTAX/) {
+        push @offsets, 4;
+        # the Pentax addressing mode is determined automatically, but
+        # sometimes the algorithm gets it wrong, but Pentax always uses
+        # absolute addressing, so force it to be absolute
+        $relative = 0;
+    } elsif ($make =~ /^Konica Minolta/i) {
+        # patch for DiMAGE X50, Xg, Z2 and Z10
+        push @offsets, 4, -16;
+    } elsif ($make =~ /^Minolta/) {
+        # patch for DiMAGE 7, X20 and Z1
+        push @offsets, 4, -8, -12;
+    } else {
+        push @offsets, 4;   # the normal offset
+    }
+    return ($relative, @offsets);
+}
+
+#------------------------------------------------------------------------------
+# Get hash of value offsets / block sizes
+# Inputs: 0) Data pointer, 1) offset to start of directory
+# Returns: 0) hash reference: keys are offsets, values are block sizes
+#          1) same thing, but with keys adjusted for value-based offsets
+# Notes: Directory size should be validated before calling this routine
+# - calculates MIN and MAX offsets in entry-based hash
+sub GetValueBlocks($$)
+{
+    my ($dataPt, $dirStart) = @_;
+    my $numEntries = Get16u($dataPt, $dirStart);
+    my ($index, $valPtr, %valBlock, %valBlkAdj, $end);
+    for ($index=0; $index<$numEntries; ++$index) {
+        my $entry = $dirStart + 2 + 12 * $index;
+        my $format = Get16u($dataPt, $entry+2);
+        last if $format < 1 or $format > 13;
+        my $count = Get32u($dataPt, $entry+4);
+        my $size = $count * $Image::ExifTool::Exif::formatSize[$format];
+        next if $size <= 4;
+        $valPtr = Get32u($dataPt, $entry+8);
+        # save location and size of longest block at this offset
+        unless (defined $valBlock{$valPtr} and $valBlock{$valPtr} > $size) {
+            $valBlock{$valPtr} = $size;
+        }
+        # adjust for case of value-based offsets
+        $valPtr += 12 * $index;
+        unless (defined $valBlkAdj{$valPtr} and $valBlkAdj{$valPtr} > $size) {
+            $valBlkAdj{$valPtr} = $size;
+            my $end = $valPtr + $size;
+            if (defined $valBlkAdj{MIN}) {
+                # save minimum only if it has a value of 12 or greater
+                $valBlkAdj{MIN} = $valPtr if $valBlkAdj{MIN} < 12 or $valBlkAdj{MIN} > $valPtr;
+                $valBlkAdj{MAX} = $end if $valBlkAdj{MAX} > $end;
+            } else {
+                $valBlkAdj{MIN} = $valPtr;
+                $valBlkAdj{MAX} = $end;
+            }
+        }
+    }
+    return(\%valBlock, \%valBlkAdj);
+}
+
+#------------------------------------------------------------------------------
+# Fix base for value offsets in maker notes IFD (if necessary)
+# Inputs: 0) ExifTool object ref, 1) DirInfo hash ref
+# Return: amount of base shift (and $dirInfo Base and DataPos are updated,
+#         FixedBy is set if offsets fixed, and Relative or EntryBased may be set)
+sub FixBase($$)
+{
+    local $_;
+    my ($exifTool, $dirInfo) = @_;
+    # don't fix base if fixing offsets individually or if we don't want to fix them
+    return 0 if $$dirInfo{FixOffsets} or $$dirInfo{NoFixBase};
+
+    my $dataPt = $$dirInfo{DataPt};
+    my $dataPos = $$dirInfo{DataPos};
+    my $dirStart = $$dirInfo{DirStart} || 0;
+    my $entryBased = $$dirInfo{EntryBased};
+    my $dirName = $$dirInfo{DirName};
+    my $fixBase = $exifTool->Options('FixBase');
+    my $setBase = (defined $fixBase and $fixBase ne '') ? 1 : 0;
+    my ($fix, $fixedBy);
+#
+# handle special case of Canon maker notes with TIFF trailer containing original offset
+#
+    if ($$exifTool{CameraMake} =~ /^Canon/ and $$dirInfo{DirLen} > 8) {
+        my $trailerPos = $dirStart + $$dirInfo{DirLen} - 8;
+        my $trailer = substr($$dataPt, $trailerPos, 8);
+        if ($trailer =~ /^(II\x2a\0|MM\0\x2a)/ and  # check for TIFF trailer
+            substr($trailer,0,2) eq GetByteOrder()) # validate byte ordering
+        {
+            if ($$exifTool{HTML_DUMP}) {
+                my $filePos = ($$dirInfo{Base} || 0) + $dataPos + $trailerPos;
+                $exifTool->HtmlDump($filePos, 8, '[Canon MakerNote trailer]')
+            }
+            if ($setBase) {
+                $fix = $fixBase;
+            } else {
+                my $oldOffset = Get32u(\$trailer, 4);
+                my $newOffset = $dirStart + $dataPos;
+                $fix = $newOffset - $oldOffset;
+                return 0 unless $fix;
+            }
+            $exifTool->Warn("Adjusted $dirName base by $fix", 1);
+            $$dirInfo{FixedBy} = $fix;
+            $$dirInfo{Base} += $fix;
+            $$dirInfo{DataPos} -= $fix;
+            return $fix;
+        }
+    }
+#
+# analyze value offsets to see if they need fixing.  The first task is to
+# determine the minimum valid offset used (this is tricky, because we have
+# to weed out bad offsets written by some cameras)
+#
+    # get hash of value block positions
+    my ($valBlock, $valBlkAdj) = GetValueBlocks($dataPt, $dirStart);
+    return 0 unless %$valBlock;
+
+    my $ifdLen = 2 + 12 * Get16u($$dirInfo{DataPt}, $dirStart);
+    my $ifdEnd = $dirStart + $ifdLen;
+    my ($relative, @offsets) = GetMakerNoteOffset($exifTool);
+    my $makeDiff = $offsets[0];
+    my $verbose = $exifTool->Options('Verbose');
+    my ($diff, $shift);
+
+    # calculate expected minimum value offset
+    my $expected = $dataPos + $ifdEnd + (defined $makeDiff ? $makeDiff : 4);
+    $debug and print "$expected expected\n";
+
+    # get sorted list of value offsets
+    my @valPtrs = sort { $a <=> $b } keys %$valBlock;
+    my $minPt = $valPtrs[0];    # if life were simple, this would be it
+
+    # zero our counters
+    my ($countNeg12, $countZero, $countOverlap) = (0, 0, 0);
+    my ($valPtr, $last);
+    foreach $valPtr (@valPtrs) {
+        printf("%d - %d (%d)\n", $valPtr, $valPtr + $$valBlock{$valPtr},
+               $valPtr - ($last || 0)) if $debug;
+        if (defined $last) {
+            my $diff = $valPtr - $last;
+            if ($diff == 0 or $diff == 1) {
+                ++$countZero;
+            } elsif ($diff == -12 and not $entryBased) {
+                # you get this when value offsets are relative to the IFD entry
+                ++$countNeg12;
+            } elsif ($diff < 0) {
+                # any other negative difference indicates overlapping values
+                ++$countOverlap if $valPtr; # (but ignore zero value pointers)
+            } elsif ($diff >= $ifdLen) {
+                # ignore previous minimum if we took a jump to near the expected value
+                # (some values can be stored before the IFD)
+                $minPt = $valPtr if abs($valPtr - $expected) <= 4;
+            }
+            # an offset less than 12 is surely garbage, so ignore it
+            $minPt = $valPtr if $minPt < 12;
+        }
+        $last = $valPtr + $$valBlock{$valPtr};
+    }
+    # could this IFD be using entry-based offsets?
+    if ((($countNeg12 > $countZero and $$valBlkAdj{MIN} >= $ifdLen - 2) or
+         ($$valBlkAdj{MIN} == $ifdLen - 2 or $$valBlkAdj{MIN} == $ifdLen + 2)
+        ) and $$valBlkAdj{MAX} <= $$dirInfo{DirLen}-2)
+    {
+        # looks like these offsets are entry-based, so use the offsets
+        # which have been correcting for individual entry position
+        $entryBased = 1;
+        $verbose and $exifTool->Warn("$dirName offsets are entry-based");
+    } else {
+        # calculate difference from normal
+        $diff = ($minPt - $dataPos) - $ifdEnd;
+        $shift = 0;
+        $countOverlap and $exifTool->Warn("Overlapping $dirName values",1);
+        if ($entryBased) {
+            $exifTool->Warn("$dirName offsets do NOT look entry-based");
+            undef $entryBased;
+            undef $relative;
+        }
+    }
+#
+# handle entry-based offsets
+#
+    if ($entryBased) {
+        $debug and print "--> entry-based!\n";
+        # most of my entry-based samples have first value immediately
+        # after last IFD entry (ie. no padding or next IFD pointer)
+        $makeDiff = 0;
+        push @offsets, 4;   # but some do have a next IFD pointer
+        # corrected entry-based offsets are relative to start of first entry
+        my $expected = 12 * Get16u($$dirInfo{DataPt}, $dirStart);
+        $diff = $$valBlkAdj{MIN} - $expected;
+        # set up directory to read values with entry-based offsets
+        # (ignore everything and set base to start of first entry)
+        $shift = $dataPos + $dirStart + 2;
+        $$dirInfo{Base} += $shift;
+        $$dirInfo{DataPos} -= $shift;
+        $$dirInfo{EntryBased} = 1;
+        $$dirInfo{Relative} = 1;    # entry-based offsets are relative
+        delete $$dirInfo{FixBase};  # no automatic base fix
+        undef $fixBase unless $setBase;
+    }
+#
+# return without doing shift if offsets look OK
+#
+    unless ($setBase) {
+        # don't try to fix offsets for whacky cameras
+        return $shift unless defined $makeDiff;
+        # normal value data starts 4 bytes after IFD, but allow 0 or 4...
+        return $shift if $diff == 0 or $diff == 4;
+        # also check for allowed make-specific differences
+        foreach (@offsets) {
+            return $shift if $diff == $_;
+        }
+    }
+#
+# apply the fix, or issue a warning
+#
+    # use default padding of 4 bytes unless already specified
+    $makeDiff = 4 unless defined $makeDiff;
+    $fix = $makeDiff - $diff;   # assume standard diff for this make
+
+    if ($$dirInfo{FixBase}) {
+        # set flag if offsets are relative (base is at or above directory start)
+        if ($dataPos - $fix + $dirStart <= 0) {
+            $$dirInfo{Relative} = (defined $relative) ? $relative : 1;
+        }
+        if ($setBase) {
+            $fixedBy = $fixBase;
+            $fix += $fixBase;
+        }
+    } elsif (defined $fixBase) {
+        $fix = $fixBase if $fixBase ne '';
+        $fixedBy = $fix;
+    } else {
+        # print warning unless difference looks reasonable
+        if ($diff < 0 or $diff > 16 or ($diff & 0x01)) {
+            $exifTool->Warn("Possibly incorrect maker notes offsets (fix by $fix?)",1);
+        }
+        # don't do the fix (but we already adjusted base if entry-based)
+        return $shift;
+    }
+    if (defined $fixedBy) {
+        $exifTool->Warn("Adjusted $dirName base by $fixedBy",1);
+        $$dirInfo{FixedBy} = $fixedBy;
+    }
+    $$dirInfo{Base} += $fix;
+    $$dirInfo{DataPos} -= $fix;
+    return $fix + $shift;
+}
+
+#------------------------------------------------------------------------------
+# Find start of IFD in unknown maker notes
+# Inputs: 0) reference to directory information
+# Returns: offset to IFD on success, undefined otherwise
+# - dirInfo may contain TagInfo reference for tag associated with directory
+# - on success, updates DirStart, DirLen, Base and DataPos in dirInfo
+# - also sets Relative flag in dirInfo if offsets are relative to IFD
+# Note: Changes byte ordering!
+sub LocateIFD($$)
+{
+    my ($exifTool, $dirInfo) = @_;
+    my $dataPt = $$dirInfo{DataPt};
+    my $dirStart = $$dirInfo{DirStart} || 0;
+    my $size = $$dirInfo{DirLen} || ($$dirInfo{DataLen} - $dirStart);
+    my $tagInfo = $$dirInfo{TagInfo};
+    my $ifdOffsetPos;
+    # the IFD should be within the first 32 bytes
+    # (Kyocera sets the current record at 22 bytes)
+    my ($firstTry, $lastTry) = (0, 32);
+
+    # make sure Base and DataPos are defined
+    $$dirInfo{Base} or $$dirInfo{Base} = 0;
+    $$dirInfo{DataPos} or $$dirInfo{DataPos} = 0;
+#
+# use tag information (if provided) to determine directory location
+#
+    if ($tagInfo and $$tagInfo{SubDirectory}) {
+        my $subdir = $$tagInfo{SubDirectory};
+        unless ($$subdir{ProcessProc} and $$subdir{ProcessProc} eq \&ProcessUnknown) {
+            # look for the IFD at the "Start" specified in our SubDirectory information
+            my $valuePtr = $dirStart;
+            my $newStart = $dirStart;
+            if (defined $$subdir{Start}) {
+                #### eval Start ($valuePtr)
+                $newStart = eval($$subdir{Start});
+            }
+            if ($$subdir{Base}) {
+                # calculate subdirectory start relative to $base for eval
+                my $start = $newStart + $$dirInfo{DataPos};
+                #### eval Base ($start)
+                my $baseShift = eval($$subdir{Base});
+                # shift directory base (note: we may do this again below
+                # if an OffsetPt is defined, but that doesn't matter since
+                # the base shift is relative to DataPos, which we set too)
+                $$dirInfo{Base} += $baseShift;
+                $$dirInfo{DataPos} -= $baseShift;
+                # this is a relative directory if Base depends on $start
+                $$dirInfo{Relative} = 1 if $$subdir{Base} =~ /\$start\b/;
+            }
+            # add offset to the start of the directory if necessary
+            if ($$subdir{OffsetPt}) {
+                if ($$subdir{ByteOrder} =~ /^Little/i) {
+                    SetByteOrder('II');
+                } elsif ($$subdir{ByteOrder} =~ /^Big/i) {
+                    SetByteOrder('MM');
+                } else {
+                    warn "Can't have variable byte ordering for SubDirectories using OffsetPt\n";
+                    return undef;
+                }
+                #### eval OffsetPt ($valuePtr)
+                $ifdOffsetPos = eval($$subdir{OffsetPt}) - $dirStart;
+            }
+            # pinpoint position to look for this IFD
+            $firstTry = $lastTry = $newStart - $dirStart;
+        }
+    }
+#
+# scan for something that looks like an IFD
+#
+    if ($size >= 14 + $firstTry) {  # minimum size for an IFD
+        my $offset;
+IFD_TRY: for ($offset=$firstTry; $offset<=$lastTry; $offset+=2) {
+            last if $offset + 14 > $size;    # 14 bytes is minimum size for an IFD
+            my $pos = $dirStart + $offset;
+#
+# look for a standard TIFF header (Nikon uses it, others may as well),
+#
+            if (SetByteOrder(substr($$dataPt, $pos, 2)) and
+                Get16u($dataPt, $pos + 2) == 0x2a)
+            {
+                $ifdOffsetPos = 4;
+            }
+            if (defined $ifdOffsetPos) {
+                # get pointer to IFD
+                my $ptr = Get32u($dataPt, $pos + $ifdOffsetPos);
+                if ($ptr >= $ifdOffsetPos + 4 and $ptr + $offset + 14 <= $size) {
+                    # shift directory start and shorten dirLen accordingly
+                    $$dirInfo{DirStart} += $ptr + $offset;
+                    $$dirInfo{DirLen} -= $ptr + $offset;
+                    # shift pointer base to the start of the TIFF header
+                    my $shift = $$dirInfo{DataPos} + $dirStart + $offset;
+                    $$dirInfo{Base} += $shift;
+                    $$dirInfo{DataPos} -= $shift;
+                    $$dirInfo{Relative} = 1;   # set "relative offsets" flag
+                    return $ptr + $offset;
+                }
+                undef $ifdOffsetPos;
+            }
+#
+# look for a standard IFD (starts with 2-byte entry count)
+#
+            my $num = Get16u($dataPt, $pos);
+            next unless $num;
+            # number of entries in an IFD should be between 1 and 255
+            if (!($num & 0xff)) {
+                # lower byte is zero -- byte order could be wrong
+                ToggleByteOrder();
+                $num >>= 8;
+            } elsif ($num & 0xff00) {
+                # upper byte isn't zero -- not an IFD
+                next;
+            }
+            my $bytesFromEnd = $size - ($offset + 2 + 12 * $num);
+            if ($bytesFromEnd < 4) {
+                next unless $bytesFromEnd == 2 or $bytesFromEnd == 0;
+            }
+            # do a quick validation of all format types
+            my $index;
+            for ($index=0; $index<$num; ++$index) {
+                my $entry = $pos + 2 + 12 * $index;
+                my $format = Get16u($dataPt, $entry+2);
+                my $count = Get32u($dataPt, $entry+4);
+                # allow everything to be zero if not first entry
+                # because some manufacturers pad with null entries
+                next unless $format or $count or $index == 0;
+                # (would like to verify tag ID, but some manufactures don't
+                #  sort entries in order of tag ID so we don't have much of
+                #  a handle to verify this field)
+                # verify format
+                next IFD_TRY if $format < 1 or $format > 13;
+                # count must be reasonable (can't test for zero count because
+                # cameras like the 1DmkIII use this value)
+                next IFD_TRY if $count & 0xff000000;
+            }
+            $$dirInfo{DirStart} += $offset;    # update directory start
+            $$dirInfo{DirLen} -= $offset;
+            return $offset;   # success!!
+        }
+    }
+    return undef;
+}
+
+#------------------------------------------------------------------------------
+# Process unknown maker notes assuming it is in EXIF IFD format
+# Inputs: 0) ExifTool object reference, 1) reference to directory information
+#         2) pointer to tag table
+# Returns: 1 on success, and updates $dirInfo if necessary for new directory
+sub ProcessUnknown($$$)
+{
+    my ($exifTool, $dirInfo, $tagTablePtr) = @_;
+    my $success = 0;
+
+    my $loc = LocateIFD($exifTool, $dirInfo);
+    if (defined $loc) {
+        $exifTool->{UnknownByteOrder} = GetByteOrder();
+        if ($exifTool->Options('Verbose') > 1) {
+            my $out = $exifTool->Options('TextOut');
+            my $indent = $exifTool->{INDENT};
+            $indent =~ s/\| $/  /;
+            printf $out "${indent}Found IFD at offset 0x%.4x in maker notes:\n",
+                    $$dirInfo{DirStart} + $$dirInfo{DataPos} + $$dirInfo{Base};
+        }
+        $success = Image::ExifTool::Exif::ProcessExif($exifTool, $dirInfo, $tagTablePtr);
+    } else {
+        $exifTool->{UnknownByteOrder} = ''; # indicates we tried but didn't set byte order
+        $exifTool->Warn("Unrecognized $$dirInfo{DirName}");
+    }
+    return $success;
+}
+
+
+1;  # end
+
+__END__
+
+=head1 NAME
+
+Image::ExifTool::MakerNotes - Read and write EXIF maker notes
+
+=head1 SYNOPSIS
+
+This module is required by Image::ExifTool.
+
+=head1 DESCRIPTION
+
+This module contains definitions required by Image::ExifTool to interpret
+maker notes in EXIF information.
+
+=head1 AUTHOR
+
+Copyright 2003-2007, Phil Harvey (phil at owl.phy.queensu.ca)
+
+This library is free software; you can redistribute it and/or modify it
+under the same terms as Perl itself.
+
+=head1 SEE ALSO
+
+L<Image::ExifTool::TagNames(3pm)|Image::ExifTool::TagNames>,
+L<Image::ExifTool(3pm)|Image::ExifTool>
+
+=cut
Index: /gsdl/trunk/perllib/cpan/Image/ExifTool/Minolta.pm
===================================================================
--- /gsdl/trunk/perllib/cpan/Image/ExifTool/Minolta.pm	(revision 16842)
+++ /gsdl/trunk/perllib/cpan/Image/ExifTool/Minolta.pm	(revision 16842)
@@ -0,0 +1,1401 @@
+#------------------------------------------------------------------------------
+# File:         Minolta.pm
+#
+# Description:  Minolta EXIF maker notes tags
+#
+# Revisions:    04/06/2004 - P. Harvey Created
+#               09/09/2005 - P. Harvey Added ability to write MRW files
+#
+# References:   1) http://www.dalibor.cz/minolta/makernote.htm
+#               2) Jay Al-Saadi private communication (testing with A2)
+#               3) Shingo Noguchi, PhotoXP (http://www.daifukuya.com/photoxp/)
+#               4) Niels Kristian Bech Jensen private communication
+#               5) http://www.cybercom.net/~dcoffin/dcraw/
+#               6) Pedro Corte-Real private communication
+#               7) ExifTool forum post by bronek (http://www.cpanforum.com/posts/1118)
+#               8) http://www.chauveau-central.net/mrw-format/
+#               9) CPAN Forum post by 'geve' (http://www.cpanforum.com/threads/2168)
+#              10) http://homepage3.nifty.com/kamisaka/makernote/makernote_km.htm
+#              11) http://www.dyxum.com/dforum/forum_posts.asp?TID=6371&PN=1 and
+#                  http://www.dyxum.com/dAdmin/lenses/MakerNoteList_Public.asp?stro=makr
+#              12) http://www.minolta-forum.de/forum/index.php?showtopic=14914
+#              13) http://www.mhohner.de/minolta/lenses.php
+#              14) Jeffery Small private communication (tests with 7D)
+#              15) http://homepage3.nifty.com/kamisaka/makernote/makernote_sony.htm
+#------------------------------------------------------------------------------
+
+package Image::ExifTool::Minolta;
+
+use strict;
+use vars qw($VERSION %minoltaLensIDs %minoltaColorMode);
+use Image::ExifTool qw(:DataAccess :Utils);
+use Image::ExifTool::Exif;
+
+$VERSION = '1.33';
+
+# lens ID numbers (ref 3)
+%minoltaLensIDs = (
+    0 => 'Minolta AF 28-85mm F3.5-4.5',
+    1 => 'Minolta AF 80-200mm F2.8 HS-APO G',
+    2 => 'Minolta AF 28-70mm F2.8 G',
+    3 => 'Minolta AF 28-80mm F4-5.6',
+    5 => 'Minolta AF 35-70mm F3.5-4.5',
+    6 => 'Minolta AF 24-85mm F3.5-4.5 [New]',
+    # 7 => 'AF 100-400mm F4.5-6.7 (D)', ??
+    7 => 'Minolta AF 100-300mm F4.5-5.6 APO [New]',
+    8 => 'Minolta AF 70-210mm F4.5-5.6',
+    9 => 'Minolta AF 50mm F3.5 Macro',
+    10 => 'Minolta AF 28-105mm F3.5-4.5 [New]',
+    11 => 'Minolta AF 300mm F4 HS-APO G',
+    12 => 'Minolta AF 100mm F2.8 Soft Focus',
+    13 => 'Minolta AF 75-300mm F4.5-5.6',
+    14 => 'Minolta AF 100-400mm F4.5-6.7 APO',
+    15 => 'Minolta AF 400mm F4.5 HS-APO G',
+    16 => 'Minolta AF 17-35mm F3.5 G',
+    17 => 'Minolta AF 20-35mm F3.5-4.5',
+    18 => 'Minolta AF 28-80mm F3.5-5.6 II',
+    19 => 'Minolta AF 35mm F1.4',
+    20 => 'Minolta STF 135mm F2.8 [T4.5]',
+    22 => 'Minolta AF 35-80mm F4-5.6',
+    23 => 'Minolta AF 200mm F4 G APO Macro',
+    24 => 'Minolta AF 24-105mm F3.5-4.5 (D)', # or Sigma 18-50mm F2.8 or Sigma 17-70mm F2.8-4.5 (D)',
+    25 => 'Minolta AF 100-300mm F4.5-5.6 (D)',
+    27 => 'Minolta AF 85mm F1.4 G',
+    28 => 'Minolta AF 100mm F2.8 Macro (D)',
+    29 => 'Minolta AF 75-300mm F4.5-5.6 (D)',
+    30 => 'Minolta AF 28-80mm F3.5-5.6 (D)', # or Sigma AF 12-24mm F4.5-5.6 EX DG',
+    31 => 'Minolta AF 50mm F2.8 Macro (D) or AF 50mm F3.5 Macro',
+    # 32 => 'AF 100-400mm F4.5-6.7 (D) x1.5', ??
+    32 => 'Minolta AF 300mm F2.8 G',
+    33 => 'Minolta AF 70-200mm F2.8 G (D) SSM',
+    35 => 'Minolta AF 85mm F1.4 G (D) Limited',
+    36 => 'Minolta AF 28-100mm F3.5-5.6 (D)',
+    38 => 'KonicaMinolta AF 17-35mm F2.8-4 (D)',
+    39 => 'Minolta AF 28-75mm F2.8 (D)',
+    40 => 'KonicaMinolta or Sony AF DT 18-70mm F3.5-5.6 (D)',
+    41 => 'Minolta AF DT 11-18mm F4.5-5.6 (D)',
+    42 => 'Minolta AF DT 18-200mm F3.5-6.3 (D)',
+    43 => 'Minolta AF 35mm F1.4 G',
+    44 => 'Minolta AF 50mm F1.4',
+    45 => 'Carl Zeiss Planar T* 85mm F1.4 ZA',
+    46 => 'Carl Zeiss Vario-Sonnar T* DT 16-80mm F3.5-4.5 ZA',
+    47 => 'Carl Zeiss Sonnar T* 135mm F1.8 ZA',
+    128 => 'Tamron 18-200, 28-300 or 80-300mm F3.5-6.3',
+    129 => 'Tamron 200-400mm F5.6 LD', #12
+    138 => 'Soligor 19-35mm F3.5-4.5', #11
+    25501 => 'Minolta AF 50mm F1.7', #7
+    25511 => 'Minolta AF 35-70mm F4', # or Sigma UC AF 28-70mm F3.5-4.5 or Sigma M-AF 70-200mm F2.8 EX Aspherical', #/12/12
+    25521 => 'Minolta AF 28-85mm F3.5-4.5 [New]', # or Tokina 19-35mm F3.5-4.5 or Tokina 28-70mm F2.8 AT-X', #3/7
+    25531 => 'Minolta AF 28-135mm F4-4.5',
+    25541 => 'Minolta AF 35-105mm F3.5-4.5', #13
+    25551 => 'Minolta AF 70-210mm F4 Macro', # or Sigma 70-210mm F4-5.6 APO or Sigma M-AF 70-200mm F2.8 EX APO', #7/6
+    25561 => 'Minolta AF 135mm F2.8',
+    25571 => 'Minolta AF 28mm F2.8',
+    25581 => 'Minolta AF 24-50mm F4',
+    25601 => 'Minolta AF 100-200mm F4.5',
+    25611 => 'Minolta AF 75-300mm F4.5-5.6', #13 # or Sigma 70-300mm F4-5.6 or Sigma 300mm F4 APO Macro', #12/3/7
+    25621 => 'Minolta AF 50mm F1.4 [New]', #13
+    25631 => 'Minolta AF 300mm F2.8 G',
+    25641 => 'Minolta AF 50mm F2.8 Macro',
+    25651 => 'Minolta AF 600mm F4',
+    25661 => 'Minolta AF 24mm F2.8',
+    25721 => 'Minolta AF 500mm F8 Reflex',
+    25781 => 'Minolta AF 16mm F2.8 Fisheye', # or Sigma 8mm F4 Fisheye or Sigma 14mm F3.5',
+    25791 => 'Minolta AF 20mm F2.8',
+    25811 => 'Minolta AF 100mm F2.8 Macro New', # or Tamron 90mm F2.8 Macro or Sigma 180mm F5.6 Macro',
+    25858 => 'Minolta AF 35-105mm F3.5-4.5 New', # or Tamron 24-135mm F3.5-5.6',
+    25881 => 'Minolta AF 70-210mm F3.5-4.5',
+    25891 => 'Minolta AF 80-200 F2.8 APO', # or Tokina 80-200mm F2.8',
+    25911 => 'Minolta AF 35mm F1.4', #(from Sony list)
+    25921 => 'Minolta AF 85mm F1.4 G (D)',
+    25931 => 'Minolta AF 200mm F2.8 G APO',
+    25941 => 'Minolta AF 3X-1X F1.7-2.8 Macro',
+    25961 => 'Minolta AF 28mm F2',
+    25971 => 'Minolta AF 35mm F2',
+    25981 => 'Minolta AF 100mm F2',
+    26041 => 'Minolta AF 80-200mm F4.5-5.6',
+    26051 => 'Minolta AF 35-80mm F4-5.6', #(from Sony list)
+    26061 => 'Minolta AF 100-300mm F4.5-5.6 (D)',
+    26071 => 'Minolta AF 35-80mm F4-5.6', #13
+    26081 => 'Minolta AF 300mm F2.8 G',
+    26091 => 'Minolta AF 600mm F4 HS-APO G',
+    26121 => 'Minolta AF 200mm F2.8 G HS-APO',
+    26131 => 'Minolta AF 50mm F1.7 New',
+    26151 => 'Minolta AF 28-105mm F3.5-4.5 Power Zoom',
+    26161 => 'Minolta AF 35-200mm F4.5-5.6 Power Zoom',
+    26181 => 'Minolta AF 28-80mm F4-5.6 Power Zoom',
+    26191 => 'Minolta AF 80-200mm F4.5-5.6 Power Zoom',
+    26201 => 'Minolta AF 28-70mm F2.8 G', #11
+    26211 => 'Minolta AF 100-300mm F4.5-5.6 Power Zoom',
+    26241 => 'Minolta AF 35-80mm F4-5.6 Power Zoom',
+    26281 => 'Minolta AF 80-200mm F2.8 G', #11
+    26291 => 'Minolta AF 85mm F1.4 New',
+    26311 => 'Minolta AF 100-300mm F4.5-5.6 APO', #11
+    26321 => 'Minolta AF 24-50mm F4 New',
+    26381 => 'Minolta AF 50mm F2.8 Macro New',
+    26391 => 'Minolta AF 100mm F2.8 Macro',
+    26411 => 'Minolta AF 20mm F2.8 New',
+    26421 => 'Minolta AF 24mm F2.8 New',
+    26441 => 'Minolta AF 100-400mm F4.5-6.7 APO', #11
+    26621 => 'Minolta AF 50mm F1.4 New',
+    26671 => 'Minolta AF 35mm F2 New',
+    26681 => 'Minolta AF 28mm F2 New',
+    26721 => 'Minolta AF 24-105mm F3.5-4.5 (D)', #11
+    45741 => 'Minolta AF 200mm F2.8 G x2', # or Tokina 300mm F2.8 x2',
+);
+
+%minoltaColorMode = (
+    0 => 'Natural color',
+    1 => 'Black & White',
+    2 => 'Vivid color',
+    3 => 'Solarization',
+    4 => 'Adobe RGB',
+    5 => 'Sepia', #10
+    9 => 'Natural', #10
+    12 => 'Portrait', #10
+    13 => 'Natural sRGB',
+    14 => 'Natural+ sRGB',
+    15 => 'Landscape', #10
+    16 => 'Evening', #10
+    17 => 'Night Scene', #10
+    18 => 'Night Portrait', #10
+);
+
+# Minolta tag table
+%Image::ExifTool::Minolta::Main = (
+    WRITE_PROC => \&Image::ExifTool::Exif::WriteExif,
+    CHECK_PROC => \&Image::ExifTool::Exif::CheckExif,
+    WRITABLE => 1,
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
+    0x0000 => {
+        Name => 'MakerNoteVersion',
+        Writable => 'undef',
+        Count => 4,
+    },
+    0x0001 => {
+        Name => 'MinoltaCameraSettingsOld',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::Minolta::CameraSettings',
+            ByteOrder => 'BigEndian',
+        },
+    },
+    0x0003 => {
+        Name => 'MinoltaCameraSettings',
+        # These camera settings are different for the DiMAGE X31
+        Condition => '$self->{CameraModel} ne "DiMAGE X31"',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::Minolta::CameraSettings',
+            ByteOrder => 'BigEndian',
+        },
+    },
+    0x0004 => { #8
+        Name => 'MinoltaCameraSettings7D',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::Minolta::CameraSettings7D',
+            ByteOrder => 'BigEndian',
+        },
+    },
+    # it appears that image stabilization is on if this tag exists (ref 2),
+    # but it is an 8kB binary data block!
+    0x0018 => {
+        Name => 'ImageStabilization',
+        Condition => '$self->{CameraModel} =~ /^DiMAGE (A1|A2|X1)$/',
+        Notes => q{
+            a block of binary data which exists in DiMAGE A2 (and A1/X1?) images only if
+            image stabilization is enabled
+        },
+        ValueConv => '"On"',
+    },
+    0x0040 => {
+        Name => 'CompressedImageSize',
+        Writable => 'int32u',
+    },
+    0x0081 => {
+        # JPEG preview found in DiMAGE 7 images
+        %Image::ExifTool::previewImageTagInfo,
+        Permanent => 1,     # don't add this to a file
+    },
+    0x0088 => {
+        Name => 'PreviewImageStart',
+        Flags => 'IsOffset',
+        OffsetPair => 0x0089, # point to associated byte count
+        DataTag => 'PreviewImage',
+        Writable => 'int32u',
+        Protected => 2,
+    },
+    0x0089 => {
+        Name => 'PreviewImageLength',
+        OffsetPair => 0x0088, # point to associated offset
+        DataTag => 'PreviewImage',
+        Writable => 'int32u',
+        Protected => 2,
+    },
+    0x0100 => { #10 (needs verification - PH)
+        Name => 'SceneMode',
+        PrintConv => {
+            0 => 'Standard',
+            1 => 'Portrait',
+            2 => 'Text',
+            3 => 'Night Scene',
+            4 => 'Evening Scene',
+            5 => 'Sports',
+            6 => 'Landscape',
+            9 => 'Super Macro',
+        },
+    },
+    0x0101 => [
+        {
+            Name => 'ColorMode',
+            Condition => '$self->{CameraMake} !~ /^SONY/',
+            Priority => 0, # Other ColorMode is more reliable for A2
+            Writable => 'int32u',
+            PrintConv => \%minoltaColorMode,
+        },
+        { #15
+            Name => 'ColorMode',
+            Writable => 'int32u',
+            Notes => 'Sony DSLR-A100',
+            PrintConv => {
+                0 => 'Standard',
+                1 => 'Vivid', #PH
+                2 => 'Portrait',
+                3 => 'Landscape',
+                4 => 'Sunset',
+                5 => 'Night Scene',
+                6 => 'B&W',
+                7 => 'Adobe RGB',
+            },
+        },
+    ],
+    0x0102 => {
+        Name => 'MinoltaQuality',
+        Writable => 'int32u',
+        # PrintConv strings conform with Minolta reference manual (ref 4)
+        # (note that Minolta calls an uncompressed TIFF image "Super fine")
+        PrintConv => {
+            0 => 'Raw',
+            1 => 'Super Fine',
+            2 => 'Fine',
+            3 => 'Standard',
+            4 => 'Economy',
+            5 => 'Extra fine',
+        },
+    },
+    # (0x0103 is the same as 0x0102 above) -- this is true for some
+    # cameras (A2/7Hi), but not others - PH
+    0x0103 => [
+        {
+            Name => 'MinoltaQuality',
+            Writable => 'int32u',
+            Condition => '$self->{CameraModel} =~ /^DiMAGE (A2|7Hi)$/',
+            Notes => 'quality for DiMAGE A2/7Hi',
+            Priority => 0, # lower priority because this doesn't work for A200
+            PrintConv => { #4
+                0 => 'Raw',
+                1 => 'Super Fine',
+                2 => 'Fine',
+                3 => 'Standard',
+                4 => 'Economy',
+                5 => 'Extra fine',
+            },
+        },
+        { #PH
+            Name => 'MinoltaImageSize',
+            Writable => 'int32u',
+            Condition => '$self->{CameraModel} !~ /^DiMAGE A200$/',
+            Notes => 'image size for other models except A200',
+            PrintConv => {
+                1 => '1600x1200',
+                2 => '1280x960',
+                3 => '640x480',
+                5 => '2560x1920',
+                6 => '2272x1704',
+                7 => '2048x1536',
+            },
+        },
+    ],
+    0x0104 => { #14
+        Name => 'FlashExposureComp',
+        Description => 'Flash Exposure Compensation',
+        Writable => 'rational64s',
+    },
+# needs verification:
+#    0x0105 => { #10
+#        Name => 'Teleconverter',
+#        PrintConv => {
+#            0 => 'None',
+#            72 => 'Minolta AF 2x APO (D)',
+#            80 => 'Minolta AF 2x APO II',
+#            136 => 'Minolta AF 1.4x APO (D)',
+#            144 => 'Minolta AF 1.4x APO II',
+#        },
+#    },
+    0x0107 => { #8
+        Name => 'ImageStabilization',
+        Writable => 'int32u',
+        PrintConv => {
+            1 => 'Off',
+            5 => 'On',
+        },
+    },
+    0x010a => {
+        Name => 'ZoneMatching',
+        Writable => 'int32u',
+        PrintConv => {
+            0 => 'ISO Setting Used',
+            1 => 'High Key',
+            2 => 'Low Key',
+        },
+    },
+    0x010b => {
+        Name => 'ColorTemperature',
+        Writable => 'int32u',
+    },
+    0x010c => { #3 (Alpha 7)
+        Name => 'LensID',
+        Writable => 'int32u',
+        PrintConv => \%minoltaLensIDs,
+    },
+    # 0x010e - WhiteBalance according to ref #10
+    0x0113 => { #PH
+        Name => 'ImageStabilization',
+        Condition => '$self->{CameraModel} eq "DSLR-A100"',
+        Notes => 'valid for Sony A100 only',
+        Writable => 'int32u',
+        PrintConv => { 0 => 'Off', 1 => 'On' },
+    },
+    0x0114 => [
+        { #8
+            Name => 'MinoltaCameraSettings5D',
+            Condition => '$self->{CameraModel} =~ /^(DYNAX 5D|MAXXUM 5D|ALPHA SWEET)/',
+            SubDirectory => {
+                TagTable => 'Image::ExifTool::Minolta::CameraSettings5D',
+                ByteOrder => 'BigEndian',
+            },
+        },
+        { #PH
+            Name => 'MinoltaCameraSettingsA100',
+            Condition => '$self->{CameraModel} eq "DSLR-A100"',
+            SubDirectory => {
+                TagTable => 'Image::ExifTool::Minolta::CameraSettingsA100',
+                ByteOrder => 'BigEndian', # required because order differs for ARW and JPG images
+            },
+        },
+    ],
+    0x0e00 => {
+        Name => 'PrintIM',
+        Description => 'Print Image Matching',
+        Writable => 0,
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::PrintIM::Main',
+        },
+    },
+    0x0f00 => {
+        Name => 'MinoltaCameraSettings2',
+        Writable => 0,
+    },
+);
+
+%Image::ExifTool::Minolta::CameraSettings = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
+    CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
+    WRITABLE => 1,
+    PRIORITY => 0, # not as reliable as other tags
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
+    FORMAT => 'int32u',
+    FIRST_ENTRY => 0,
+    NOTES => q{
+        There is some variability in CameraSettings information between different
+        models (and sometimes even between different firmware versions), so this
+        information may not be as reliable as it should be.  Because of this, tags
+        in the following tables are set to lower priority to prevent them from
+        superceeding the values of same-named tags in other locations when duplicate
+        tags are disabled.
+    },
+    1 => {
+        Name => 'ExposureMode',
+        PrintConv => {
+            0 => 'Program',
+            1 => 'Aperture Priority',
+            2 => 'Shutter Priority',
+            3 => 'Manual',
+        },
+    },
+    2 => {
+        Name => 'FlashMode',
+        PrintConv => {
+            0 => 'Fill flash',
+            1 => 'Red-eye reduction',
+            2 => 'Rear flash sync',
+            3 => 'Wireless',
+        },
+    },
+    3 => {
+        Name => 'WhiteBalance',
+        PrintConv => 'Image::ExifTool::Minolta::ConvertWhiteBalance($val)',
+    },
+    4 => {
+        Name => 'MinoltaImageSize',
+        PrintConv => {
+            0 => 'Full',
+            1 => '1600x1200',
+            2 => '1280x960',
+            3 => '640x480',
+            6 => '2080x1560', #PH (A2)
+            7 => '2560x1920', #PH (A2)
+            8 => '3264x2176', #PH (A2)
+        },
+    },
+    5 => {
+        Name => 'MinoltaQuality',
+        PrintConv => { #4
+            0 => 'Raw',
+            1 => 'Super Fine',
+            2 => 'Fine',
+            3 => 'Standard',
+            4 => 'Economy',
+            5 => 'Extra Fine',
+        },
+    },
+    6 => {
+        Name => 'DriveMode',
+        PrintConv => {
+            0 => 'Single',
+            1 => 'Continuous',
+            2 => 'Self-timer',
+            4 => 'Bracketing',
+            5 => 'Interval',
+            6 => 'UHS continuous',
+            7 => 'HS continuous',
+        },
+    },
+    7 => {
+        Name => 'MeteringMode',
+        PrintConv => {
+            0 => 'Multi-segment',
+            1 => 'Center weighted',
+            2 => 'Spot',
+        },
+    },
+    8 => {
+        Name => 'ISO',
+        ValueConv => '2 ** (($val-48)/8) * 100',
+        ValueConvInv => '48 + 8*log($val/100)/log(2)',
+        PrintConv => 'int($val + 0.5)',
+        PrintConvInv => '$val',
+    },
+    9 => {
+        Name => 'ExposureTime',
+        ValueConv => '2 ** ((48-$val)/8)',
+        ValueConvInv => '48 - 8*log($val)/log(2)',
+        PrintConv => 'Image::ExifTool::Exif::PrintExposureTime($val)',
+        PrintConvInv => 'eval $val',
+    },
+    10 => {
+        Name => 'FNumber',
+        ValueConv => '2 ** (($val-8)/16)',
+        ValueConvInv => '8 + 16*log($val)/log(2)',
+        PrintConv => 'sprintf("%.1f",$val)',
+        PrintConvInv => '$val',
+    },
+    11 => {
+        Name => 'MacroMode',
+        PrintConv => {
+            0 => 'Off',
+            1 => 'On',
+        },
+    },
+    12 => {
+        Name => 'DigitalZoom',
+        PrintConv => {
+            0 => 'Off',
+            1 => 'Electronic magnification',
+            2 => '2x',
+        },
+    },
+    13 => {
+        Name => 'ExposureCompensation',
+        ValueConv => '$val/3 - 2',
+        ValueConvInv => '($val + 2) * 3',
+        PrintConv => 'Image::ExifTool::Exif::ConvertFraction($val)',
+        PrintConvInv => 'eval $val',
+    },
+    14 => {
+        Name => 'BracketStep',
+        PrintConv => {
+            0 => '1/3 EV',
+            1 => '2/3 EV',
+            2 => '1 EV',
+        },
+    },
+    16 => 'IntervalLength',
+    17 => 'IntervalNumber',
+    18 => {
+        Name => 'FocalLength',
+        ValueConv => '$val / 256',
+        ValueConvInv => '$val * 256',
+        PrintConv => 'sprintf("%.1fmm",$val)',
+        PrintConvInv => '$val=~s/mm$//;$val',
+    },
+    19 => {
+        Name => 'FocusDistance',
+        ValueConv => '$val / 1000',
+        ValueConvInv => '$val * 1000',
+        PrintConv => '$val ? "$val m" : "inf"',
+        PrintConvInv => '$val eq "inf" ? 0 : $val =~ s/\s*m$//, $val',
+    },
+    20 => {
+        Name => 'FlashFired',
+        PrintConv => {
+            0 => 'No',
+            1 => 'Yes',
+        },
+    },
+    21 => {
+        Name => 'MinoltaDate',
+        Groups => { 2 => 'Time' },
+        Shift => 'Time',
+        ValueConv => 'sprintf("%4d:%.2d:%.2d",$val>>16,($val&0xff00)>>8,$val&0xff)',
+        ValueConvInv => 'my @a=($val=~/(\d+):(\d+):(\d+)/); @a ? ($a[0]<<16)+($a[1]<<8)+$a[2] : undef',
+    },
+    22 => {
+        Name => 'MinoltaTime',
+        Groups => { 2 => 'Time' },
+        Shift => 'Time',
+        ValueConv => 'sprintf("%.2d:%.2d:%.2d",$val>>16,($val&0xff00)>>8,$val&0xff)',
+        ValueConvInv => 'my @a=($val=~/(\d+):(\d+):(\d+)/); @a ? ($a[0]<<16)+($a[1]<<8)+$a[2] : undef',
+    },
+    23 => {
+        Name => 'MaxAperture',
+        ValueConv => '2 ** (($val-8)/16)',
+        ValueConvInv => '8 + 16*log($val)/log(2)',
+        PrintConv => 'sprintf("%.1f",$val)',
+        PrintConvInv => '$val',
+    },
+    26 => {
+        Name => 'FileNumberMemory',
+        PrintConv => {
+            0 => 'Off',
+            1 => 'On',
+        },
+    },
+    27 => 'LastFileNumber',
+    28 => {
+        Name => 'ColorBalanceRed',
+        ValueConv => '$val / 256',
+        ValueConvInv => '$val * 256',
+    },
+    29 => {
+        Name => 'ColorBalanceGreen',
+        ValueConv => '$val / 256',
+        ValueConvInv => '$val * 256',
+    },
+    30 => {
+        Name => 'ColorBalanceBlue',
+        ValueConv => '$val / 256',
+        ValueConvInv => '$val * 256',
+    },
+    31 => {
+        Name => 'Saturation',
+        ValueConv => '$val - ($self->{CameraModel}=~/DiMAGE A2/ ? 5 : 3)',
+        ValueConvInv => '$val + ($self->{CameraModel}=~/DiMAGE A2/ ? 5 : 3)',
+        PrintConv => 'Image::ExifTool::Exif::PrintParameter($val)',
+        PrintConvInv => '$val=~/normal/i ? 0 : $val',
+    },
+    32 => {
+        Name => 'Contrast',
+        ValueConv => '$val - ($self->{CameraModel}=~/DiMAGE A2/ ? 5 : 3)',
+        ValueConvInv => '$val + ($self->{CameraModel}=~/DiMAGE A2/ ? 5 : 3)',
+        PrintConv => 'Image::ExifTool::Exif::PrintParameter($val)',
+        PrintConvInv => '$val=~/normal/i ? 0 : $val',
+    },
+    33 => {
+        Name => 'Sharpness',
+        PrintConv => {
+            0 => 'Hard',
+            1 => 'Normal',
+            2 => 'Soft',
+        },
+    },
+    34 => {
+        Name => 'SubjectProgram',
+        PrintConv => {
+            0 => 'None',
+            1 => 'Portrait',
+            2 => 'Text',
+            3 => 'Night portrait',
+            4 => 'Sunset',
+            5 => 'Sports action',
+        },
+    },
+    35 => {
+        Name => 'FlashExposureComp',
+        Description => 'Flash Exposure Compensation',
+        ValueConv => '($val - 6) / 3',
+        ValueConvInv => '$val * 3 + 6',
+        PrintConv => 'Image::ExifTool::Exif::ConvertFraction($val)',
+        PrintConvInv => 'eval $val',
+    },
+    36 => {
+        Name => 'ISOSetting',
+        PrintConv => {
+            0 => 100,
+            1 => 200,
+            2 => 400,
+            3 => 800,
+            4 => 'Auto',
+            5 => 64,
+        },
+    },
+    37 => {
+        Name => 'MinoltaModelID',
+        PrintConv => {
+            0 => 'DiMAGE 7, X1, X21 or X31',
+            1 => 'DiMAGE 5',
+            2 => 'DiMAGE S304',
+            3 => 'DiMAGE S404',
+            4 => 'DiMAGE 7i',
+            5 => 'DiMAGE 7Hi',
+            6 => 'DiMAGE A1',
+            7 => 'DiMAGE A2 or S414',
+        },
+    },
+    38 => {
+        Name => 'IntervalMode',
+        PrintConv => {
+            0 => 'Still Image',
+            1 => 'Time-lapse Movie',
+        },
+    },
+    39 => {
+        Name => 'FolderName',
+        PrintConv => {
+            0 => 'Standard Form',
+            1 => 'Data Form',
+        },
+    },
+    40 => {
+        Name => 'ColorMode',
+        PrintConv => {
+            0 => 'Natural color',
+            1 => 'Black & White',
+            2 => 'Vivid color',
+            3 => 'Solarization',
+            4 => 'Adobe RGB',
+        },
+    },
+    41 => {
+        Name => 'ColorFilter',
+        ValueConv => '$val - ($self->{CameraModel}=~/DiMAGE A2/ ? 5 : 3)',
+        ValueConvInv => '$val + ($self->{CameraModel}=~/DiMAGE A2/ ? 5 : 3)',
+    },
+    42 => 'BWFilter',
+    43 => {
+        Name => 'InternalFlash',
+        PrintConv => {
+            0 => 'No',
+            1 => 'Fired',
+        },
+    },
+    44 => {
+        Name => 'Brightness',
+        ValueConv => '$val/8 - 6',
+        ValueConvInv => '($val + 6) * 8',
+    },
+    45 => 'SpotFocusPointX',
+    46 => 'SpotFocusPointY',
+    47 => {
+        Name => 'WideFocusZone',
+        PrintConv => {
+            0 => 'No zone',
+            1 => 'Center zone (horizontal orientation)',
+            2 => 'Center zone (vertical orientation)',
+            3 => 'Left zone',
+            4 => 'Right zone',
+        },
+    },
+    48 => {
+        Name => 'FocusMode',
+        PrintConv => {
+            0 => 'AF',
+            1 => 'MF',
+        },
+    },
+    49 => {
+        Name => 'FocusArea',
+        PrintConv => {
+            0 => 'Wide Focus (normal)',
+            1 => 'Spot Focus',
+        },
+    },
+    50 => {
+        Name => 'DECPosition',
+        PrintConv => {
+            0 => 'Exposure',
+            1 => 'Contrast',
+            2 => 'Saturation',
+            3 => 'Filter',
+        },
+    },
+    # 7Hi only:
+    51 => {
+        Name => 'ColorProfile',
+        Condition => '$self->{CameraModel} eq "DiMAGE 7Hi"',
+        Notes => 'DiMAGE 7Hi only',
+        PrintConv => {
+            0 => 'Not Embedded',
+            1 => 'Embedded',
+        },
+    },
+    # (the following may be entry 51 for other models?)
+    52 => {
+        Name => 'DataImprint',
+        Condition => '$self->{CameraModel} eq "DiMAGE 7Hi"',
+        Notes => 'DiMAGE 7Hi only',
+        PrintConv => {
+            0 => 'None',
+            1 => 'YYYY/MM/DD',
+            2 => 'MM/DD/HH:MM',
+            3 => 'Text',
+            4 => 'Text + ID#',
+        },
+    },
+    63 => { #9
+        Name => 'FlashMetering',
+        PrintConv => {
+            0 => 'ADI (Advanced Distance Integration)',
+            1 => 'Pre-flash TTL',
+            2 => 'Manual flash control',
+        },
+    },
+);
+
+# Camera settings used by the 7D (ref 8)
+%Image::ExifTool::Minolta::CameraSettings7D = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
+    CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
+    WRITABLE => 1,
+    PRIORITY => 0,
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
+    FORMAT => 'int16u',
+    FIRST_ENTRY => 0,
+    0x00 => {
+        Name => 'ExposureMode',
+        PrintConv => {
+            0 => 'Program',
+            1 => 'Aperture Priority',
+            2 => 'Shutter Priority',
+            3 => 'Manual',
+            4 => 'Auto?',
+            5 => 'Program-shift A',
+            6 => 'Program-shift S',
+        },
+    },
+    0x02 => { #PH
+        Name => 'MinoltaImageSize',
+        PrintConv => {
+            0 => 'Large',
+            1 => 'Medium',
+            2 => 'Small',
+        },
+    },
+    0x03 => {
+        Name => 'MinoltaQuality',
+        PrintConv => {
+            0 => 'RAW',
+            16 => 'Fine', #PH
+            32 => 'Normal', #PH
+            34 => 'RAW+JPEG',
+            48 => 'Economy', #PH
+        },
+    },
+    0x04 => {
+        Name => 'WhiteBalance',
+        PrintConv => {
+            0 => 'Auto',
+            1 => 'Daylight',
+            2 => 'Shade',
+            3 => 'Cloudy',
+            4 => 'Tungsten',
+            5 => 'Fluorescent',
+            0x100 => 'Kelvin',
+            0x200 => 'Manual',
+        },
+    },
+    0x0e => {
+        Name => 'FocusMode',
+        PrintConv => {
+            0 => 'Single-shot AF',
+            1 => 'Continuous AF',
+            3 => 'Automatic AF',
+            4 => 'Manual',
+        },
+    },
+    0x10 => {
+        Name => 'AFPoints',
+        PrintConv => { BITMASK => {
+            0 => 'Center',
+            1 => 'Top',
+            2 => 'Top-Right',
+            3 => 'Right',
+            4 => 'Bottom-Right',
+            5 => 'Bottom',
+            6 => 'Bottom-Left',
+            7 => 'Left',
+            8 => 'Top-Left',
+        } },
+    },
+    0x15 => {
+        Name => 'Flash',
+        PrintConv => { 0 => 'Off', 1 => 'On' },
+    },
+# needs verification:
+#    0x16 => { #10
+#        Name => 'FlashMode',
+#        PrintConv => {
+#            0 => 'Normal',
+#            1 => 'Red-eye reduction',
+#            2 => 'Rear flash sync',
+#        },
+#    },
+    0x1c => {
+        Name => 'ISOSetting',
+        PrintConv => {
+            0 => 'Auto', #10
+            1 => 100,
+            3 => 200,
+            4 => 400,
+            5 => 800,
+            6 => 1600,
+            7 => 3200,
+        },
+    },
+    0x1e => {
+        Name => 'ExposureCompensation',
+        Format => 'int16s',
+        ValueConv => '$val / 24',
+        ValueConvInv => '$val * 24',
+        PrintConv => 'Image::ExifTool::Exif::ConvertFraction($val)',
+        PrintConvInv => 'eval $val',
+    },
+    0x25 => {
+        Name => 'ColorSpace',
+        PrintConv => {
+            0 => 'Natural sRGB',
+            1 => 'Natural+ sRGB',
+            4 => 'Adobe RGB',
+        },
+    },
+    0x26 => {
+        Name => 'Sharpness',
+        ValueConv => '$val - 10',
+        ValueConvInv => '$val + 10',
+    },
+    0x27 => {
+        Name => 'Contrast',
+        ValueConv => '$val - 10',
+        ValueConvInv => '$val + 10',
+    },
+    0x28 => {
+        Name => 'Saturation',
+        ValueConv => '$val - 10',
+        ValueConvInv => '$val + 10',
+    },
+    0x2d => 'FreeMemoryCardImages',
+    0x3f => {
+        Format => 'int16s',
+        Name => 'ColorTemperature',
+        ValueConv => '$val * 100',
+        ValueConvInv => '$val / 100',
+    },
+# needs verification:
+#    0x40 => { #10
+#        Name => 'HueAdjustment',
+#        Unknown => 1,
+#        ValueConv => '$val - 10',
+#        ValueConvInv => '$val + 10',
+#    },
+    0x46 => {
+        Name => 'Rotation',
+        PrintConv => {
+            72 => 'Horizontal (normal)',
+            76 => 'Rotate 90 CW',
+            82 => 'Rotate 270 CW',
+        },
+    },
+    0x47 => {
+        Name => 'FNumber',
+        ValueConv => '2 ** (($val-8)/16)',
+        ValueConvInv => '8 + 16*log($val)/log(2)',
+        PrintConv => 'sprintf("%.1f",$val)',
+        PrintConvInv => '$val',
+    },
+    0x48 => {
+        Name => 'ExposureTime',
+        ValueConv => '2 ** ((48-$val)/8)',
+        ValueConvInv => '48 - 8*log($val)/log(2)',
+        PrintConv => 'Image::ExifTool::Exif::PrintExposureTime($val)',
+        PrintConvInv => 'eval $val',
+    },
+    0x4a => 'FreeMemoryCardImages',
+    0x5e => {
+        Name => 'ImageNumber',
+        Notes => q{
+            this information may appear at index 98 (0x62), depending on firmware
+            version
+        },
+        ValueConv => '$val + 1',
+        ValueConvInv => '$val - 1',
+    },
+    0x60 => {
+        Name => 'NoiseReduction',
+        PrintConv => { 0 => 'Off', 1 => 'On' },
+    },
+    0x62 => {
+        Name => 'ImageNumber2',
+        ValueConv => '$val + 1',
+        ValueConvInv => '$val - 1',
+    },
+    0x71 => {
+        Name => 'ImageStabilization',
+        PrintConv => { 0 => 'Off', 1 => 'On' },
+    },
+    0x75 => {
+        Name => 'ZoneMatchingOn',
+        PrintConv => { 0 => 'Off', 1 => 'On' },
+    },
+);
+
+# Camera settings used by the 5D (ref 8)
+%Image::ExifTool::Minolta::CameraSettings5D = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
+    CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
+    WRITABLE => 1,
+    PRIORITY => 0,
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
+    FORMAT => 'int16u',
+    FIRST_ENTRY => 0,
+    0x0a => {
+        Name => 'ExposureMode',
+        PrintConv => {
+            0 => 'Program',
+            1 => 'Aperture Priority',
+            2 => 'Shutter Priority',
+            3 => 'Manual',
+            4 => 'Auto?',
+            4131 => 'Connected Copying?',
+        },
+    },
+    0x0c => { #PH
+        Name => 'MinoltaImageSize',
+        PrintConv => {
+            0 => 'Large',
+            1 => 'Medium',
+            2 => 'Small',
+        },
+    },
+    0x0d => {
+        Name => 'MinoltaQuality',
+        PrintConv => {
+            0 => 'RAW',
+            16 => 'Fine', #PH
+            32 => 'Normal', #PH
+            34 => 'RAW+JPEG',
+            48 => 'Economy', #PH
+        },
+    },
+    0x0e => {
+        Name => 'WhiteBalance',
+        PrintConv => {
+            0 => 'Auto',
+            1 => 'Daylight',
+            2 => 'Cloudy',
+            3 => 'Shade',
+            4 => 'Tungsten',
+            5 => 'Fluorescent',
+            6 => 'Flash',
+            0x100 => 'Kelvin',
+            0x200 => 'Manual',
+        },
+    },
+    # 0x0f-0x11 something to do with WB RGB levels as shot? (PH)
+    # 0x12-0x17 RGB levels for other WB modes (with G missing)? (PH)
+    0x1f => { #PH
+        Name => 'Flash',
+        PrintConv => {
+            0 => 'Did not fire',
+            1 => 'Fired',
+        },
+    },
+# needs verification:
+#    0x20 => { #10
+#        Name => 'FlashMode',
+#        PrintConv => {
+#            0 => 'Normal',
+#            1 => 'Red-eye reduction',
+#            2 => 'Rear flash sync',
+#        },
+#    },
+    0x25 => {
+        Name => 'MeteringMode',
+        PrintConv => {
+            0 => 'Multi-segment',
+            1 => 'Center weighted',
+            2 => 'Spot',
+        },
+    },
+    0x26 => {
+        Name => 'ISOSetting',
+        PrintConv => {
+            0 => 'Auto',
+            1 => 100,
+            3 => 200,
+            4 => 400,
+            5 => 800,
+            6 => 1600,
+            7 => 3200,
+            8 => '200 (Zone Matching High)',
+            10 => '80 (Zone Matching Low)',
+        },
+    },
+# looks wrong:
+#    0x28 => { #10
+#        Name => 'ExposureCompensation',
+#        ValueConv => '$val / 24',
+#        ValueConvInv => '$val * 24',
+#    },
+# needs verification:
+#    0x2f => { #10
+#        Name => 'ColorSpace',
+#        PrintConv => {
+#            0 => 'Natural sRGB',
+#            1 => 'Natural+ sRGB',
+#            2 => 'Monochrome',
+#            4 => 'Adobe RGB (ICC)',
+#            5 => 'Adobe RGB',
+#        },
+#    },
+    0x30 => {
+        Name => 'Sharpness',
+        ValueConv => '$val - 10',
+        ValueConvInv => '$val + 10',
+    },
+    0x31 => {
+        Name => 'Contrast',
+        ValueConv => '$val - 10',
+        ValueConvInv => '$val + 10',
+    },
+    0x32 => {
+        Name => 'Saturation',
+        ValueConv => '$val - 10',
+        ValueConvInv => '$val + 10',
+    },
+    0x35 => { #PH
+        Name => 'ExposureTime',
+        ValueConv => '2 ** ((48-$val)/8)',
+        ValueConvInv => '48 - 8*log($val)/log(2)',
+        PrintConv => 'Image::ExifTool::Exif::PrintExposureTime($val)',
+        PrintConvInv => 'eval $val',
+    },
+    0x36 => { #PH
+        Name => 'FNumber',
+        ValueConv => '2 ** (($val-8)/16)',
+        ValueConvInv => '8 + 16*log($val)/log(2)',
+        PrintConv => 'sprintf("%.1f",$val)',
+        PrintConvInv => '$val',
+    },
+    0x37 => 'FreeMemoryCardImages',
+    # 0x38 definitely not related to exposure comp as in ref 8 (PH)
+    0x49 => { #PH
+        Name => 'ColorTemperature',
+        Format => 'int16s',
+        ValueConv => '$val * 100',
+        ValueConvInv => '$val / 100',
+    },
+# needs verification:
+#    0x4a => { #10
+#        Name => 'HueAdjustment',
+#        ValueConv => '$val - 10',
+#        ValueConvInv => '$val + 10',
+#    },
+    0x50 => {
+        Name => 'Rotation',
+        PrintConv => {
+            72 => 'Horizontal (normal)',
+            76 => 'Rotate 90 CW',
+            82 => 'Rotate 270 CW',
+        },
+    },
+    0x53 => {
+        Name => 'ExposureCompensation',
+        ValueConv => '$val / 100 - 3',
+        ValueConvInv => '($val + 3) * 100',
+        PrintConv => 'Image::ExifTool::Exif::ConvertFraction($val)',
+        PrintConvInv => 'eval $val',
+    },
+    0x54 => 'FreeMemoryCardImages',
+# needs verification:
+#    0x65 => { #10
+#        Name => 'Rotation',
+#        PrintConv => {
+#            0 => 'Horizontal (normal)',
+#            1=> 'Rotate 90 CW',
+#            2 => 'Rotate 270 CW',
+#        },
+#    },
+    # 0x66 maybe program mode or some setting like this? (PH)
+    0x6e => { #10
+        Name => 'ColorTemperature',
+        Format => 'int16s',
+        ValueConv => '$val * 100',
+        ValueConvInv => '$val / 100',
+    },
+# needs verification:
+#    0x71 => { #10
+#        Name => 'PictureFinish',
+#        PrintConv => {
+#            0 => 'Natural',
+#            1 => 'Natural+',
+#            2 => 'Portrait',
+#            3 => 'Wind Scene',
+#            4 => 'Evening Scene',
+#            5 => 'Night Scene',
+#            6 => 'Night Portrait',
+#            7 => 'Monochrome',
+#            8 => 'Adobe RGB',
+#            9 => 'Adobe RGB (ICC)',
+#        },
+#    },
+    # 0x95 FlashStrength? (PH)
+    # 0xa4 similar information to 0x27, except with different values
+    0xae => {
+        Name => 'ImageNumber',
+        ValueConv => '$val + 1',
+        ValueConvInv => '$val - 1',
+    },
+    0xb0 => {
+        Name => 'NoiseReduction',
+        PrintConv => { 0 => 'Off', 1 => 'On' },
+    },
+    0xbd => {
+        Name => 'ImageStabilization',
+        PrintConv => { 0 => 'Off', 1 => 'On' },
+    },
+);
+
+# Camera settings used by the Sony DSLR-A100 (ref PH)
+%Image::ExifTool::Minolta::CameraSettingsA100 = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
+    CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
+    NOTES => 'Camera settings information for the Sony DSLR-A100.',
+    WRITABLE => 1,
+    PRIORITY => 0,
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
+    FORMAT => 'int16u',
+    FIRST_ENTRY => 0,
+    0x00 => { #15
+        Name => 'ExposureMode',
+        PrintHex => 1,
+        PrintConv => {
+            0 => 'Program',
+            1 => 'Aperture Priority',
+            2 => 'Shutter Priority',
+            3 => 'Manual',
+            4 => 'Auto',
+            0x1013 => 'Portrait',
+            0x1053 => 'Landscape',
+        },
+    },
+    0x01 => { #15
+        Name => 'ExposureCompensation',
+        ValueConv => '$val / 100 - 3',
+        ValueConvInv => 'int(($val + 3) * 100 + 0.5)',
+    },
+    # 0x09 some sort of APEX value (ref 15)
+    0x0b => { #15
+        Name => 'WhiteBalance',
+        PrintHex => 1,
+        PrintConv => {
+            0 => 'Auto',
+            1 => 'Daylight',
+            2 => 'Cloudy',
+            3 => 'Shade',
+            4 => 'Tungsten',
+            5 => 'Fluorescent',
+            6 => 'Flash',
+            0x100 => 'Kelvin',
+            0x200 => 'Manual',
+        },
+    },
+    # 0x12 => 'MeteringMode' (ref 15)
+    # 0x13 => 'ISO' (ref 15)
+    # 0x14 => 'ZoneMatching' (ref 15)
+    # 0x15 => 'DynamicRangeOptimizer' (ref 15)
+    0x16 => { #15
+        Name => 'ColorMode',
+        PrintConv => {
+            0 => 'Standard',
+            1 => 'Vivid',
+            2 => 'Portrait',
+            3 => 'Landscape',
+            4 => 'Sunset',
+            5 => 'Night Scene',
+            7 => 'B&W',
+            8 => 'Adobe RGB',
+        },
+    },
+    # 0x17 => 'ColorSpace' (ref 15)
+    0x18 => { #15
+        Name => 'Sharpness',
+        ValueConv => '$val - 10',
+        ValueConvInv => '$val + 10',
+        PrintConv => 'Image::ExifTool::Exif::PrintParameter($val)',
+        PrintConvInv => '$val=~/normal/i ? 0 : $val',
+    },
+    0x19 => { #15
+        Name => 'Contrast',
+        ValueConv => '$val - 10',
+        ValueConvInv => '$val + 10',
+        PrintConv => 'Image::ExifTool::Exif::PrintParameter($val)',
+        PrintConvInv => '$val=~/normal/i ? 0 : $val',
+    },
+    0x1a => { #15
+        Name => 'Saturation',
+        ValueConv => '$val - 10',
+        ValueConvInv => '$val + 10',
+        PrintConv => 'Image::ExifTool::Exif::PrintParameter($val)',
+        PrintConvInv => '$val=~/normal/i ? 0 : $val',
+    },
+    0x1e => { #PH
+        Name => 'DriveMode',
+        PrintConv => {
+            0 => 'Single Frame',
+            1 => 'Continuous',
+            2 => 'Self-Timer',
+            3 => 'Continuous Bracketing',
+            4 => 'Single-Frame Bracketing',
+            5 => 'White Balance Bracketing',
+        },
+    },
+    0x3f => { #PH
+        Name => 'NoiseReduction',
+        PrintConv => { 0 => 'Off', 1 => 'On' },
+    },
+    0x5a => { #15
+        Name => 'Rotation',
+        PrintConv => {
+            0 => 'Horizontal (Normal)',
+            1 => 'Rotate 270 CW',
+            2 => 'Rotate 90 CW',
+        },
+    },
+    0x5e => { #15
+        Name => 'ColorTemperature',
+        ValueConv => '$val * 100',
+        ValueConvInv => '$val / 100',
+    },
+    0x87 => { #15
+        Name => 'ImageStabilization',
+        Writable => 'int32u',
+        PrintConv => { 0 => 'Off', 1 => 'On' },
+    },
+);
+
+# basic Minolta white balance lookup
+my %minoltaWhiteBalance = (
+    0 => 'Auto',
+    1 => 'Daylight',
+    2 => 'Cloudy',
+    3 => 'Tungsten',
+    5 => 'Custom',
+    7 => 'Fluorescent',
+    8 => 'Fluorescent 2',
+    11 => 'Custom 2',
+    12 => 'Custom 3',
+    # the following come from tests with the A2 (ref 2)
+    0x0800000 => 'Auto',
+    0x1800000 => 'Daylight',
+    0x2800000 => 'Cloudy',
+    0x3800000 => 'Tungsten',
+    0x4800000 => 'Flash',
+    0x5800000 => 'Fluorescent',
+    0x6800000 => 'Shade',
+    0x7800000 => 'Custom1',
+    0x8800000 => 'Custom2',
+    0x9800000 => 'Custom3',
+);
+
+#------------------------------------------------------------------------------
+# PrintConv for Minolta white balance
+sub ConvertWhiteBalance($)
+{
+    my $val = shift;
+    my $printConv = $minoltaWhiteBalance{$val};
+    unless (defined $printConv) {
+        if ($val & 0xffff0000) {
+            # the A2 values can be shifted by += 3 settings, where
+            # each setting adds or subtracts 0x0010000 (ref 2)
+            my $type = ($val & 0xff000000) + 0x800000;
+            if ($minoltaWhiteBalance{$type}) {
+                $printConv = $minoltaWhiteBalance{$type} .
+                             sprintf("%+.8g", ($val - $type) / 0x10000);
+            } else {
+                $printConv = sprintf("Unknown (0x%x)", $val);
+            }
+        } else {
+            $printConv = sprintf("Unknown ($val)");
+        }
+    }
+    return $printConv;
+}
+
+1;  # end
+
+__END__
+
+=head1 NAME
+
+Image::ExifTool::Minolta - Minolta EXIF maker notes tags
+
+=head1 SYNOPSIS
+
+This module is loaded automatically by Image::ExifTool when required.
+
+=head1 DESCRIPTION
+
+This module contains definitions required by Image::ExifTool to interpret
+Minolta and Konica-Minolta maker notes in EXIF information, and to read
+and write Minolta RAW (MRW) images.
+
+=head1 AUTHOR
+
+Copyright 2003-2007, Phil Harvey (phil at owl.phy.queensu.ca)
+
+This library is free software; you can redistribute it and/or modify it
+under the same terms as Perl itself.
+
+=head1 REFERENCES
+
+=over 4
+
+=item L<http://www.dalibor.cz/minolta/makernote.htm>
+
+=item L<http://www.cybercom.net/~dcoffin/dcraw/>
+
+=back
+
+=head1 ACKNOWLEDGEMENTS
+
+Thanks to Jay Al-Saadi, Niels Kristian Bech Jensen, Shingo Noguchi, Pedro
+Corte-Real and Jeffery Small for the information they provided.
+
+=head1 SEE ALSO
+
+L<Image::ExifTool::TagNames/Minolta Tags>,
+L<Image::ExifTool(3pm)|Image::ExifTool>
+
+=cut
Index: /gsdl/trunk/perllib/cpan/Image/ExifTool/MinoltaRaw.pm
===================================================================
--- /gsdl/trunk/perllib/cpan/Image/ExifTool/MinoltaRaw.pm	(revision 16842)
+++ /gsdl/trunk/perllib/cpan/Image/ExifTool/MinoltaRaw.pm	(revision 16842)
@@ -0,0 +1,359 @@
+#------------------------------------------------------------------------------
+# File:         MinoltaRaw.pm
+#
+# Description:  Read/write Konica-Minolta RAW (MRW) meta information
+#
+# Revisions:    03/11/2006 - P. Harvey Split out from Minolta.pm
+#
+# References:   1) http://www.cybercom.net/~dcoffin/dcraw/
+#               2) http://www.chauveau-central.net/mrw-format/
+#------------------------------------------------------------------------------
+
+package Image::ExifTool::MinoltaRaw;
+
+use strict;
+use vars qw($VERSION);
+use Image::ExifTool qw(:DataAccess :Utils);
+
+$VERSION = '1.02';
+
+# Minolta MRW tags
+%Image::ExifTool::MinoltaRaw::Main = (
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
+    NOTES => 'These tags are used in Minolta RAW format (MRW) images.',
+    "\0TTW" => {
+        Name => 'MinoltaTTW',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::Exif::Main',
+            # this EXIF information starts with a TIFF header
+            ProcessProc => \&Image::ExifTool::ProcessTIFF,
+            WriteProc => \&Image::ExifTool::WriteTIFF,
+        },
+    },
+    "\0PRD" => {
+        Name => 'MinoltaPRD',
+        SubDirectory => { TagTable => 'Image::ExifTool::MinoltaRaw::PRD' },
+    },
+    "\0WBG" => {
+        Name => 'MinoltaWBG',
+        SubDirectory => { TagTable => 'Image::ExifTool::MinoltaRaw::WBG' },
+    },
+    "\0RIF" => {
+        Name => 'MinoltaRIF',
+        SubDirectory => { TagTable => 'Image::ExifTool::MinoltaRaw::RIF' },
+    },
+);
+
+# Minolta MRW PRD information (ref 2)
+%Image::ExifTool::MinoltaRaw::PRD = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
+    CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
+    WRITABLE => 1,
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
+    FIRST_ENTRY => 0,
+    0 => {
+        Name => 'FirmwareID',
+        Format => 'string[8]',
+    },
+    8 => {
+        Name => 'SensorHeight',
+        Format => 'int16u',
+    },
+    10 => {
+        Name => 'SensorWidth',
+        Format => 'int16u',
+    },
+    12 => {
+        Name => 'ImageHeight',
+        Format => 'int16u',
+    },
+    14 => {
+        Name => 'ImageWidth',
+        Format => 'int16u',
+    },
+    16 => {
+        Name => 'RawDepth',
+        Format => 'int8u',
+    },
+    17 => {
+        Name => 'BitDepth',
+        Format => 'int8u',
+    },
+    18 => {
+        Name => 'StorageMethod',
+        Format => 'int8u',
+        PrintConv => {
+            82 => 'Padded',
+            89 => 'Linear',
+        },
+    },
+    23 => {
+        Name => 'BayerPattern',
+        Format => 'int8u',
+        PrintConv => {
+            1 => 'RGGB',
+            4 => 'GBRG',
+        },
+    },
+);
+
+# Minolta MRW WBG information (ref 2)
+%Image::ExifTool::MinoltaRaw::WBG = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
+    CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
+    WRITABLE => 1,
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
+    FIRST_ENTRY => 0,
+    0 => {
+        Name => 'WBScale',
+        Format => 'int8u[4]',
+    },
+    4 => {
+        Name => 'WBLevels',
+        Format => 'int16u[4]',
+    },
+);
+
+# Minolta MRW RIF information (ref 2)
+%Image::ExifTool::MinoltaRaw::RIF = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
+    CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
+    WRITABLE => 1,
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Image' },
+    FIRST_ENTRY => 0,
+    1 => {
+        Name => 'Saturation',
+        Format => 'int8s',
+    },
+    2 => {
+        Name => 'Contrast',
+        Format => 'int8s',
+    },
+    3 => {
+        Name => 'Sharpness',
+        Format => 'int8s',
+    },
+    4 => {
+        Name => 'WBMode',
+        PrintConv => 'Image::ExifTool::MinoltaRaw::ConvertWBMode($val)',
+    },
+    5 => {
+        Name => 'ProgramMode',
+        PrintConv => {
+            0 => 'None',
+            1 => 'Portrait',
+            2 => 'Text',
+            3 => 'Night Portrait',
+            4 => 'Sunset',
+            5 => 'Sports',
+        },
+    },
+    6 => {
+        Name => 'ISOSetting',
+        ValueConv => '2 ** (($val-48)/8) * 100',
+        ValueConvInv => '48 + 8*log($val/100)/log(2)',
+        PrintConv => 'int($val + 0.5)',
+        PrintConvInv => '$val',
+    },
+    7 => {
+        Name => 'ColorMode',
+        PrintHex => 1,
+        PrintConv => {
+            0 => 'Normal',
+            1 => 'Black & White',
+            2 => 'Vivid color',
+            3 => 'Solarization',
+            4 => 'Adobe RGB',
+            13 => 'Natural sRGB',
+            14 => 'Natural+ sRGB',
+            0x84 => 'Adobe RGB', # what does the high bit mean?
+        },
+    },
+    56 => {
+        Name => 'ColorFilter',
+        Format => 'int8s',
+    },
+    57 => 'BWFilter',
+    58 => {
+        Name => 'ZoneMatching',
+        PrintConv => {
+            0 => 'ISO Setting Used',
+            1 => 'High Key',
+            2 => 'Low Key',
+        },
+    },
+    59 => {
+        Name => 'Hue',
+        Format => 'int8s',
+    },
+    60 => {
+        Name => 'ColorTemperature',
+        ValueConv => '$val * 100',
+        ValueConvInv => '$val / 100',
+    },
+);
+
+#------------------------------------------------------------------------------
+# PrintConv for WBMode
+sub ConvertWBMode($)
+{
+    my $val = shift;
+    my %mrwWB = (
+        0 => 'Auto',
+        1 => 'Daylight',
+        2 => 'Cloudy',
+        3 => 'Tungsten',
+        4 => 'Flash/Fluorescent',
+        5 => 'Fluorescent',
+        6 => 'Shade',
+        7 => 'User 1',
+        8 => 'User 2',
+        9 => 'User 3',
+        10 => 'Temperature',
+    );
+    my $lo = $val & 0x0f;
+    my $wbstr = $mrwWB{$lo} || "Unknown ($lo)";
+    my $hi = $val >> 4;
+    $wbstr .= ' (' . ($hi - 8) . ')' if $hi >= 6 and $hi <=12;
+    return $wbstr;
+}
+
+#------------------------------------------------------------------------------
+# Read or write Minolta MRW file
+# Inputs: 0) ExifTool object reference, 1) dirInfo reference
+# Returns: 1 on success, 0 if this wasn't a valid MRW file, or -1 on write error
+sub ProcessMRW($$)
+{
+    my ($exifTool, $dirInfo) = @_;
+    my $raf = $$dirInfo{RAF};
+    my $outfile = $$dirInfo{OutFile};
+    my $verbose = $exifTool->Options('Verbose');
+    my $out = $exifTool->Options('TextOut');
+    my ($data, $err, $outBuff);
+
+    $raf->Read($data,8) == 8 or return 0;
+    $data =~ /^\0MRM/ or return 0;
+    $exifTool->SetFileType() unless $exifTool->{VALUE}->{FileType};
+    SetByteOrder('MM');
+    my $tagTablePtr = GetTagTable('Image::ExifTool::MinoltaRaw::Main');
+    if ($outfile) {
+        $exifTool->InitWriteDirs('TIFF'); # use same write dirs as TIFF
+        $outBuff = '';
+    }
+    my $offset = Get32u(\$data, 4) + 8;
+    my $pos = 8;
+    my $rtnVal = 1;
+    $verbose and printf $out "  [MRW Data Offset: 0x%x]\n", $offset;
+    # loop through MRW segments (ref 1)
+    while ($pos < $offset) {
+        $raf->Read($data,8) == 8 or $err = 1, last;
+        $pos += 8;
+        my $tag = substr($data, 0, 4);
+        my $len = Get32u(\$data, 4);
+        if ($verbose) {
+            print $out "MRW ",$exifTool->Printable($tag)," segment ($len bytes):\n";
+            if ($verbose > 2) {
+                $raf->Read($data,$len) == $len and $raf->Seek($pos,0) or $err = 1, last;
+                my %parms = (Addr => $pos, Out => $out);
+                $parms{MaxLen} = 96 unless $verbose > 3;
+                Image::ExifTool::HexDump(\$data,undef,%parms);
+            }
+        }
+        my $tagInfo = $exifTool->GetTagInfo($tagTablePtr, $tag);
+        if ($tagInfo and $$tagInfo{SubDirectory}) {
+            my $subTable = GetTagTable($tagInfo->{SubDirectory}->{TagTable});
+            my $buff;
+            $raf->Read($buff, $len) == $len or $err = 1, last;
+            my %subdirInfo = (
+                DataPt => \$buff,
+                DataLen => $len,
+                DataPos => $pos,
+                DirStart => 0,
+                DirLen => $len,
+                DirName => $$tagInfo{Name},
+                Parent => 'MRW',
+                NoTiffEnd => 1, # no end-of-TIFF check
+            );
+            if ($outfile) {
+                my $writeProc = $tagInfo->{SubDirectory}->{WriteProc};
+                my $val = $exifTool->WriteDirectory(\%subdirInfo, $subTable, $writeProc);
+                if (defined $val and length $val) {
+                    # pad to an even 4 bytes (can't hurt, and it seems to be the standard)
+                    $val .= "\0" x (4 - (length($val) & 0x03)) if length($val) & 0x03;
+                    $outBuff .= $tag . Set32u(length $val) . $val;
+                } elsif (not defined $val) {
+                    $outBuff .= $data . $buff;  # copy over original information
+                }
+            } else {
+                my $processProc = $tagInfo->{SubDirectory}->{ProcessProc};
+                $exifTool->ProcessDirectory(\%subdirInfo, $subTable, $processProc);
+            }
+        } elsif ($outfile) {
+            # add this segment to the output buffer
+            my $buff;
+            $raf->Read($buff, $len) == $len or $err = 1, last;
+            $outBuff .= $data . $buff;
+        } else {
+            # skip this segment
+            $raf->Seek($pos+$len, 0) or $err = 1, last;
+        }
+        $pos += $len;
+    }
+    $pos == $offset or $err = 1;    # meta information length check
+
+    if ($outfile) {
+        # write the file header then the buffered meta information
+        Write($outfile, "\0MRM", Set32u(length $outBuff), $outBuff) or $rtnVal = -1;
+        # copy over image data
+        while ($raf->Read($outBuff, 65536)) {
+            Write($outfile, $outBuff) or $rtnVal = -1;
+        }
+    }
+    $err and $exifTool->Error("MRW format error");
+    return $rtnVal;
+}
+
+1;  # end
+
+__END__
+
+=head1 NAME
+
+Image::ExifTool::MinoltaRaw - Read/write Konica-Minolta RAW (MRW) information
+
+=head1 SYNOPSIS
+
+This module is loaded automatically by Image::ExifTool when required.
+
+=head1 DESCRIPTION
+
+This module contains definitions required by Image::ExifTool to read and
+write Konica-Minolta RAW (MRW) images.
+
+=head1 AUTHOR
+
+Copyright 2003-2007, Phil Harvey (phil at owl.phy.queensu.ca)
+
+This library is free software; you can redistribute it and/or modify it
+under the same terms as Perl itself.
+
+=head1 REFERENCES
+
+=over 4
+
+=item L<http://www.cybercom.net/~dcoffin/dcraw/>
+
+=item L<http://www.chauveau-central.net/mrw-format/>
+
+=back
+
+=head1 SEE ALSO
+
+L<Image::ExifTool::TagNames/MinoltaRaw Tags>,
+L<Image::ExifTool::Minolta(3pm)|Image::ExifTool::Minolta>,
+L<Image::ExifTool(3pm)|Image::ExifTool>
+
+=cut
Index: /gsdl/trunk/perllib/cpan/Image/ExifTool/Nikon.pm
===================================================================
--- /gsdl/trunk/perllib/cpan/Image/ExifTool/Nikon.pm	(revision 16842)
+++ /gsdl/trunk/perllib/cpan/Image/ExifTool/Nikon.pm	(revision 16842)
@@ -0,0 +1,1714 @@
+#------------------------------------------------------------------------------
+# File:         Nikon.pm
+#
+# Description:  Nikon EXIF maker notes tags
+#
+# Revisions:    12/09/2003 - P. Harvey Created
+#               05/17/2004 - P. Harvey Added information from Joseph Heled
+#               09/21/2004 - P. Harvey Changed tag 2 to ISOUsed & added PrintConv
+#               12/01/2004 - P. Harvey Added default PRINT_CONV
+#               12/06/2004 - P. Harvey Added SceneMode
+#               01/01/2005 - P. Harvey Decode preview image and preview IFD
+#               03/35/2005 - T. Christiansen additions
+#               05/10/2005 - P. Harvey Decode encrypted lens data
+#
+# References:   1) http://park2.wakwak.com/~tsuruzoh/Computer/Digicams/exif-e.html
+#               2) Joseph Heled private communication (tests with D70)
+#               3) Thomas Walter private communication (tests with Coolpix 5400)
+#               4) http://www.cybercom.net/~dcoffin/dcraw/
+#               5) Brian Ristuccia private communication (tests with D70)
+#               6) Danek Duvall private communication (tests with D70)
+#               7) Tom Christiansen private communication (tests with D70)
+#               8) Robert Rottmerhusen private communication
+#               9) http://members.aol.com/khancock/pilot/nbuddy/
+#              10) Werner Kober private communication (D2H, D2X, D100, D70, D200)
+#              11) http://www.rottmerhusen.com/objektives/lensid/thirdparty.html
+#              12) http://libexif.sourceforge.net/internals/mnote-olympus-tag_8h-source.html
+#              13) Roger Larsson private communication (tests with D200)
+#              14) http://homepage3.nifty.com/kamisaka/makernote/makernote_nikon.htm
+#              15) http://tomtia.plala.jp/DigitalCamera/MakerNote/index.asp
+#              16) Jeffrey Friedl private communication (D200 with firmware update)
+#              17) http://www.wohlberg.net/public/software/photo/nstiffexif/
+#              18) Anonymous user private communication (D70, D200, D2x)
+#              19) Jens Duttke private communication
+#              20) Bruce Stevens private communication
+#              21) Vladimir Sauta private communication (D80)
+#------------------------------------------------------------------------------
+
+package Image::ExifTool::Nikon;
+
+use strict;
+use vars qw($VERSION);
+use Image::ExifTool qw(:DataAccess :Utils);
+use Image::ExifTool::Exif;
+
+$VERSION = '1.76';
+
+# nikon lens ID numbers (ref 8/11)
+my %nikonLensIDs = (
+    Notes => q{
+        The Nikon LensID is constructed as a Composite tag from the raw hex values
+        of 8 other tags: LensIDNumber, LensFStops, MinFocalLength, MaxFocalLength,
+        MaxApertureAtMinFocal, MaxApertureAtMaxFocal, MCUVersion and LensType, in
+        that order.  (source:
+        L<http://www.rottmerhusen.com/objektives/lensid/thirdparty.html>)
+    },
+    # (hex digits must be uppercase in keys below)
+    '01 58 50 50 14 14 02 00' => 'AF Nikkor 50mm f/1.8',
+    '01 00 00 00 00 00 02 00' => 'AF Teleconverter TC-16A 1.6x',
+    '01 00 00 00 00 00 08 00' => 'AF Teleconverter TC-16A 1.6x',
+    '02 42 44 5C 2A 34 02 00' => 'AF Zoom-Nikkor 35-70mm f/3.3-4.5',
+    '02 42 44 5C 2A 34 08 00' => 'AF Zoom-Nikkor 35-70mm f/3.3-4.5',
+    '03 48 5C 81 30 30 02 00' => 'AF Zoom-Nikkor 70-210mm f/4',
+    '04 48 3C 3C 24 24 03 00' => 'AF Nikkor 28mm f/2.8',
+    '05 54 50 50 0C 0C 04 00' => 'AF Nikkor 50mm f/1.4',
+    '06 54 53 53 24 24 06 00' => 'AF Micro-Nikkor 55mm f/2.8',
+    '07 40 3C 62 2C 34 03 00' => 'AF Zoom-Nikkor 28-85mm f/3.5-4.5',
+    '08 40 44 6A 2C 34 04 00' => 'AF Zoom-Nikkor 35-105mm f/3.5-4.5',
+    '09 48 37 37 24 24 04 00' => 'AF Nikkor 24mm f/2.8',
+    '0A 48 8E 8E 24 24 03 00' => 'AF Nikkor 300mm f/2.8 IF-ED',
+    '0B 48 7C 7C 24 24 05 00' => 'AF Nikkor 180mm f/2.8 IF-ED',
+    '0D 40 44 72 2C 34 07 00' => 'AF Zoom-Nikkor 35-135mm f/3.5-4.5',
+    '0E 48 5C 81 30 30 05 00' => 'AF Zoom-Nikkor 70-210mm f/4',
+    '0F 58 50 50 14 14 05 00' => 'AF Nikkor 50mm f/1.8 N',
+    '10 48 8E 8E 30 30 08 00' => 'AF Nikkor 300mm f/4 IF-ED',
+    '11 48 44 5C 24 24 08 00' => 'AF Zoom-Nikkor 35-70mm f/2.8',
+    '12 48 5C 81 30 3C 09 00' => 'AF Nikkor 70-210mm f/4-5.6',
+    '13 42 37 50 2A 34 0B 00' => 'AF Zoom-Nikkor 24-50mm f/3.3-4.5',
+    '14 48 60 80 24 24 0B 00' => 'AF Zoom-Nikkor 80-200mm f/2.8 ED',
+    '15 4C 62 62 14 14 0C 00' => 'AF Nikkor 85mm f/1.8',
+    '17 3C A0 A0 30 30 11 00' => 'Nikkor 500mm f/4 P',
+    '18 40 44 72 2C 34 0E 00' => 'AF Zoom-Nikkor 35-135mm f/3.5-4.5 N',
+    '1A 54 44 44 18 18 11 00' => 'AF Nikkor 35mm f/2',
+    '1B 44 5E 8E 34 3C 10 00' => 'AF Zoom-Nikkor 75-300mm f/4.5-5.6',
+    '1C 48 30 30 24 24 12 00' => 'AF Nikkor 20mm f/2.8',
+    '1D 42 44 5C 2A 34 12 00' => 'AF Zoom-Nikkor 35-70mm f/3.3-4.5 N',
+    '1E 54 56 56 24 24 13 00' => 'AF Micro-Nikkor 60mm f/2.8',
+    '1F 54 6A 6A 24 24 14 00' => 'AF Micro-Nikkor 105mm f/2.8',
+    '20 48 60 80 24 24 15 00' => 'AF Zoom-Nikkor ED 80-200mm f/2.8',
+    '21 40 3C 5C 2C 34 16 00' => 'AF Zoom-Nikkor 28-70mm f/3.5-4.5',
+    '22 48 72 72 18 18 16 00' => 'AF DC-Nikkor 135mm f/2',
+    '24 48 60 80 24 24 1A 02' => 'AF Zoom-Nikkor ED 80-200mm f/2.8D',
+    '25 48 44 5C 24 24 1B 02' => 'AF Zoom-Nikkor 35-70mm f/2.8D',
+    '25 48 44 5C 24 24 52 02' => 'AF Zoom-Nikkor 35-70mm f/2.8D N',
+    '27 48 8E 8E 24 24 F2 02' => 'AF-I Nikkor 300mm f/2.8D IF-ED',
+    '27 48 8E 8E 24 24 1D 02' => 'AF-I Nikkor 300mm f/2.8D IF-ED',
+    '28 3C A6 A6 30 30 1D 02' => 'AF-I Nikkor 600mm f/4D IF-ED',
+    '2A 54 3C 3C 0C 0C 26 02' => 'AF Nikkor 28mm f/1.4D',
+    '2C 48 6A 6A 18 18 27 02' => 'AF DC-Nikkor 105mm f/2D',
+    '2D 48 80 80 30 30 21 02' => 'AF Micro-Nikkor 200mm f/4D IF-ED',
+    '2E 48 5C 82 30 3C 28 02' => 'AF Nikkor 70-210mm f/4-5.6D',
+    '2F 48 30 44 24 24 29 02' => 'AF Zoom-Nikkor 20-35mm f/2.8(IF)',
+    '31 54 56 56 24 24 25 02' => 'AF Micro-Nikkor 60mm f/2.8D',
+    '32 54 6A 6A 24 24 35 02' => 'AF Micro-Nikkor 105mm f/2.8D',
+    '33 48 2D 2D 24 24 31 02' => 'AF Nikkor 18mm f/2.8D',
+    '34 48 29 29 24 24 32 02' => 'AF Fisheye Nikkor 16mm f/2.8D',
+    '36 48 37 37 24 24 34 02' => 'AF Nikkor 24mm f/2.8D',
+    '37 48 30 30 24 24 36 02' => 'AF Nikkor 20mm f/2.8D',
+    '38 4C 62 62 14 14 37 02' => 'AF Nikkor 85mm f/1.8D',
+    '3B 48 44 5C 24 24 3A 02' => 'AF Zoom-Nikkor 35-70mm f/2.8D N',
+    '3D 3C 44 60 30 3C 3E 02' => 'AF Zoom-Nikkor 35-80mm f/4-5.6D',
+    '3E 48 3C 3C 24 24 3D 02' => 'AF Nikkor 28mm f/2.8D',
+    '41 48 7C 7C 24 24 43 02' => 'AF Nikkor 180mm f/2.8D IF-ED',
+    '42 54 44 44 18 18 44 02' => 'AF Nikkor 35mm f/2D',
+    '43 54 50 50 0C 0C 46 02' => 'AF Nikkor 50mm f/1.4D',
+    '46 3C 44 60 30 3C 49 02' => 'AF Zoom-Nikkor 35-80mm f/4-5.6D N',
+    '47 42 37 50 2A 34 4A 02' => 'AF Zoom-Nikkor 24-50mm f/3.3-4.5D',
+    '48 48 8E 8E 24 24 4B 02' => 'AF-S Nikkor 300mm f/2.8D IF-ED',
+    '4A 54 62 62 0C 0C 4D 02' => 'AF Nikkor 85mm f/1.4D IF',
+    '4C 40 37 6E 2C 3C 4F 02' => 'AF Zoom-Nikkor 24-120mm f/3.5-5.6D IF',
+    '4D 40 3C 80 2C 3C 62 02' => 'AF Zoom-Nikkor 28-200mm f/3.5-5.6D IF',
+    '4E 48 72 72 18 18 51 02' => 'AF DC-Nikkor 135mm f/2D',
+    '4F 40 37 5C 2C 3C 53 06' => 'IX-Nikkor 24-70mm f/3.5-5.6',
+    '53 48 60 80 24 24 60 02' => 'AF Zoom-Nikkor 80-200mm f/2.8D ED',
+    '54 44 5C 7C 34 3C 58 02' => 'AF Zoom-Micro Nikkor 70-180mm f/4.5-5.6D ED',
+    '56 48 5C 8E 30 3C 5A 02' => 'AF Zoom-Nikkor 70-300mm f/4-5.6D ED',
+    '59 48 98 98 24 24 5D 02' => 'AF-S Nikkor 400mm f/2.8D IF-ED',
+    '5A 3C 3E 56 30 3C 5E 06' => 'IX-Nikkor 30-60mm f/4-5.6',
+    '5D 48 3C 5C 24 24 63 02' => 'AF-S Zoom-Nikkor 28-70mm f/2.8D IF-ED',
+    '5E 48 60 80 24 24 64 02' => 'AF-S Zoom-Nikkor 80-200mm f/2.8D IF-ED',
+    '5F 40 3C 6A 2C 34 65 02' => 'AF Zoom-Nikkor 28-105mm f/3.5-4.5D IF',
+    '60 40 3C 60 2C 3C 66 02' => 'AF Zoom-Nikkor 28-80mm f/3.5-5.6D', #(http://www.exif.org/forum/topic.asp?TOPIC_ID=16)
+    '61 44 5E 86 34 3C 67 02' => 'AF Zoom-Nikkor 75-240mm f/4.5-5.6D',
+    '63 48 2B 44 24 24 68 02' => 'AF-S Nikkor 17-35mm f/2.8D IF-ED',
+    '64 00 62 62 24 24 6A 02' => 'PC Micro-Nikkor 85mm f/2.8D',
+    '65 44 60 98 34 3C 6B 0A' => 'AF VR Zoom-Nikkor 80-400mm f/4.5-5.6D ED',
+    '66 40 2D 44 2C 34 6C 02' => 'AF Zoom-Nikkor 18-35mm f/3.5-4.5D IF-ED',
+    '67 48 37 62 24 30 6D 02' => 'AF Zoom-Nikkor 24-85mm f/2.8-4D IF',
+    '68 42 3C 60 2A 3C 6E 06' => 'AF Zoom-Nikkor 28-80mm f/3.3-5.6G',
+    '69 48 5C 8E 30 3C 6F 06' => 'AF Zoom-Nikkor 70-300mm f/4-5.6G',
+    '6A 48 8E 8E 30 30 70 02' => 'AF-S Nikkor 300mm f/4D IF-ED',
+    '6B 48 24 24 24 24 71 02' => 'AF Nikkor ED 14mm f/2.8D',
+    '6D 48 8E 8E 24 24 73 02' => 'AF-S Nikkor 300mm f/2.8D IF-ED II',
+    '6E 48 98 98 24 24 74 02' => 'AF-S Nikkor 400mm f/2.8D IF-ED II',
+    '6F 3C A0 A0 30 30 75 02' => 'AF-S Nikkor 500mm f/4D IF-ED',
+    '70 3C A6 A6 30 30 76 02' => 'AF-S Nikkor 600mm f/4D IF-ED',
+    '72 48 4C 4C 24 24 77 00' => 'Nikkor 45mm f/2.8 P',
+    '74 40 37 62 2C 34 78 06' => 'AF-S Zoom-Nikkor 24-85mm f/3.5-4.5G IF-ED',
+    '75 40 3C 68 2C 3C 79 06' => 'AF Zoom-Nikkor 28-100mm f/3.5-5.6G',
+    '76 58 50 50 14 14 7A 02' => 'AF Nikkor 50mm f/1.8D',
+    '77 48 5C 80 24 24 7B 0E' => 'AF-S VR Zoom-Nikkor 70-200mm f/2.8G IF-ED',
+    '78 40 37 6E 2C 3C 7C 0E' => 'AF-S VR Zoom-Nikkor 24-120mm f/3.5-5.6G IF-ED',
+    '79 40 3C 80 2C 3C 7F 06' => 'AF Zoom-Nikkor 28-200mm f/3.5-5.6G IF-ED',
+    '7A 3C 1F 37 30 30 7E 06' => 'AF-S DX Zoom-Nikkor 12-24mm f/4G IF-ED',
+    '7B 48 80 98 30 30 80 0E' => 'AF-S VR Zoom-Nikkor 200-400mm f/4G IF-ED',
+    '7D 48 2B 53 24 24 82 06' => 'AF-S DX Zoom-Nikkor 17-55mm f/2.8G IF-ED',
+    '7F 40 2D 5C 2C 34 84 06' => 'AF-S DX Zoom-Nikkor 18-70mm f/3.5-4.5G IF-ED',
+    '80 48 1A 1A 24 24 85 06' => 'AF DX Fisheye-Nikkor 10.5mm f/2.8G ED',
+    '81 54 80 80 18 18 86 0E' => 'AF-S VR Nikkor 200mm f/2G IF-ED',
+    '82 48 8E 8E 24 24 87 0E' => 'AF-S VR Nikkor 300mm f/2.8G IF-ED',
+    '89 3C 53 80 30 3C 8B 06' => 'AF-S DX Zoom-Nikkor 55-200mm f/4-5.6G ED',
+    '8A 54 6A 6A 24 24 8C 0E' => 'AF-S VR Micro-Nikkor 105mm f/2.8G IF-ED', #10
+    '8B 40 2D 80 2C 3C FD 0E' => 'AF-S DX VR Zoom-Nikkor 18-200mm f/3.5-5.6G IF-ED', #21
+    '8B 40 2D 80 2C 3C 8D 0E' => 'AF-S DX VR Zoom-Nikkor 18-200mm f/3.5-5.6G IF-ED',
+    '8C 40 2D 53 2C 3C 8E 06' => 'AF-S DX Zoom-Nikkor 18-55mm f/3.5-5.6G ED',
+    '8D 44 5C 8E 34 3C 8F 0E' => 'AF-S VR Zoom-Nikkor 70-300mm f/4.5-5.6G IF-ED', #10
+    '8F 40 2D 72 2C 3C 91 06' => 'AF-S DX Zoom-Nikkor 18-135mm f/3.5-5.6G IF-ED',
+    '90 3B 53 80 30 3C 92 0E' => 'AF-S DX VR Zoom-Nikkor 55-200mm f/4-5.6G IF-ED',
+    '93 48 37 5C 24 24 95 06' => 'AF-S Nikkor 24-70 f/2.8G ED',
+    '94 40 2D 53 2C 3C 96 06' => 'AF-S DX Zoom-Nikkor 18-55mm f/3.5-5.6G ED II', #10 (D40)
+    '96 48 98 98 24 24 98 0E' => 'AF-S Nikkor 400mm f/2.8G ED VR',
+    '98 3C A6 A6 30 30 9A 0E' => 'AF-S Nikkor 600mm f/4G ED VR',
+#
+    '06 3F 68 68 2C 2C 06 00' => 'Cosina 100mm f/3.5 Macro',
+#
+    '26 48 11 11 30 30 1C 02' => 'Sigma 8mm F4 EX Circular Fisheye',
+    '79 40 11 11 2C 2C 1C 06' => 'Sigma 8mm F3.5 EX', #19
+    '48 3C 19 31 30 3C 4B 06' => 'Sigma 10-20mm F4-5.6 EX DC HSM',
+    '48 38 1F 37 34 3C 4B 06' => 'Sigma 12-24mm F4.5-5.6 EX Aspherical DG HSM',
+    '02 3F 24 24 2C 2C 02 00' => 'Sigma 14mm F3.5',
+    '48 48 24 24 24 24 4B 02' => 'Sigma 14mm F2.8 EX ASPHERICAL HSM',
+    '26 48 27 27 24 24 1C 02' => 'Sigma 15mm F2.8 EX Diagonal Fish-Eye',
+    '26 40 27 3F 2C 34 1C 02' => 'Sigma 15-30mm F3.5-4.5 EX Aspherical DG DF',
+    '48 48 2B 44 24 30 4B 06' => 'Sigma 17-35mm F2.8-4 EX DG  Aspherical HSM',
+    '26 54 2B 44 24 30 1C 02' => 'Sigma 17-35mm F2.8-4 EX ASPHERICAL',
+    '7F 48 2B 5C 24 34 1C 06' => 'Sigma 17-70mm F2.8-4.5 DC MACRO Asp. IF',
+    '26 48 2D 50 24 24 1C 06' => 'Sigma 18-50mm F2.8 EX DC',
+    '26 40 2D 50 2C 3C 1C 06' => 'Sigma 18-50mm F3.5-5.6 DC',
+    '7A 40 2D 50 2C 3C 4B 06' => 'Sigma 18-50mm F3.5-5.6 DC HSM',
+    '26 40 2D 70 2B 3C 1C 06' => 'Sigma 18-125mm F3.5-5.6 DC',
+    '26 40 2D 80 2C 40 1C 06' => 'Sigma 18-200mm F3.5-6.3 DC',
+    '26 58 31 31 14 14 1C 02' => 'Sigma 20mm F1.8 EX Aspherical DG DF RF',
+    '26 58 37 37 14 14 1C 02' => 'Sigma 24mm F1.8 EX Aspherical DG DF MACRO',
+    '02 46 37 37 25 25 02 00' => 'Sigma 24mm F2.8 Macro',
+    '26 48 37 56 24 24 1C 02' => 'Sigma 24-60mm F2.8 EX DG',
+    '26 54 37 5C 24 24 1C 02' => 'Sigma 24-70mm F2.8 EX DG Macro',
+    '67 54 37 5C 24 24 1C 02' => 'Sigma 24-70mm F2.8 EX DG Macro',
+    '26 40 37 5C 2C 3C 1C 02' => 'Sigma 24-70mm F3.5-5.6 ASPHERICAL HF',
+    '26 54 37 73 24 34 1C 02' => 'Sigma 24-135mm F2.8-4.5',
+    '26 58 3C 3C 14 14 1C 02' => 'Sigma 28mm F1.8 EX DG DF',
+    '26 48 3C 5C 24 24 1C 06' => 'Sigma 28-70mm F2.8 EX DG',
+    '26 48 3C 5C 24 30 1C 02' => 'Sigma 28-70mm F2.8-4 HIGH SPEED ZOOM',
+    '02 46 3C 5C 25 25 02 00' => 'Sigma 28-70mm F2.8',
+    '02 3F 3C 5C 2D 35 02 00' => 'Sigma 28-70mm F3.5-4.5 UC',
+    '26 40 3C 60 2C 3C 1C 02' => 'Sigma 28-80mm F3.5-5.6 Mini Zoom Macro II Aspherical',
+    '26 3E 3C 6A 2E 3C 1C 02' => 'Sigma 28-105mm F3.8-5.6 UC-III ASPHERICAL IF',
+    '26 40 3C 80 2C 3C 1C 02' => 'Sigma 28-200mm F3.5-5.6 Compact Aspherical Hyperzoom Macro',
+    '26 40 3C 80 2B 3C 1C 02' => 'Sigma 28-200mm F3.5-5.6 Compact Aspherical Hyperzoom Macro',
+    '26 41 3C 8E 2C 40 1C 02' => 'Sigma 28-300mm F3.5-6.3 DG MACRO',
+    '26 40 3C 8E 2C 40 1C 02' => 'Sigma 28-300mm F3.5-6.3 Macro',
+    '48 54 3E 3E 0C 0C 4B 06' => 'Sigma 30mm F1.4 EX DC HSM',
+    '02 40 44 73 2B 36 02 00' => 'Sigma 35-135mm F3.5-4.5 a',
+    '32 54 50 50 24 24 35 02' => 'Sigma 50mm F2.8 EX DG Macro',
+    '7A 47 50 76 24 24 4B 06' => 'Sigma APO 50-150mm F2.8 EX DC HSM',
+    '48 3C 50 A0 30 40 4B 02' => 'Sigma 50-500mm F4-6.3 EX APO RF HSM',
+    '26 3C 54 80 30 3C 1C 06' => 'Sigma 55-200mm F4-5.6 DC',
+    '7A 3B 53 80 30 3C 4B 06' => 'Sigma 55-200mm F4-5.6 DC HSM',
+    '79 48 5C 5C 24 24 1C 06' => 'Sigma 70mm F2.8 EX DG Macro', #19
+    '48 54 5C 80 24 24 4B 02' => 'Sigma 70-200mm F2.8 EX APO IF HSM',
+    '02 46 5C 82 25 25 02 00' => 'Sigma 70-210mm F2.8 APO', #19
+    '26 3C 5C 82 30 3C 1C 02' => 'Sigma 70-210mm F4-5.6 UC-II',
+    '26 3C 5C 8E 30 3C 1C 02' => 'Sigma 70-300mm F4-5.6 DG MACRO',
+    '56 3C 5C 8E 30 3C 1C 02' => 'Sigma 70-300mm F4-5.6 APO Macro Super II',
+    '02 37 5E 8E 35 3D 02 00' => 'Sigma 75-300mm F4.5-5.6 APO',
+    '02 48 65 65 24 24 02 00' => 'Sigma 90mm F2.8 Macro',
+    '77 44 61 98 34 3C 7B 0E' => 'Sigma 80-400mm f4.5-5.6 EX OS',
+    '48 48 68 8E 30 30 4B 02' => 'Sigma 100-300mm F4 EX IF HSM',
+    '48 54 6F 8E 24 24 4B 02' => 'Sigma APO 120-300mm F2.8 EX DG HSM',
+    '26 44 73 98 34 3C 1C 02' => 'Sigma 135-400mm F4.5-5.6 APO Aspherical',
+    '48 48 76 76 24 24 4B 06' => 'Sigma 150mm F2.8 EX DG APO Macro HSM',
+    '48 4C 7C 7C 2C 2C 4B 02' => 'Sigma 180mm F3.5 EX DG Macro', #19
+    '26 40 7B A0 34 40 1C 02' => 'Sigma APO 170-500mm F5-6.3 ASPHERICAL RF',
+    '48 4C 7D 7D 2C 2C 4B 02' => 'Sigma APO MACRO 180mm F3.5 EX DG HSM',
+    '48 54 8E 8E 24 24 4B 02' => 'Sigma APO 300mm F2.8 EX DG HSM',
+    '26 48 8E 8E 30 30 1C 02' => 'Sigma APO TELE MACRO 300mm F4',
+    '48 3C 8E B0 3C 3C 4B 02' => 'Sigma APO 300-800 F5.6 EX DG HSM',
+    '02 2F 98 98 3D 3D 02 00' => 'Sigma 400mm F5.6 APO',
+    '02 37 A0 A0 34 34 02 00' => 'Sigma APO 500mm F4.5', #20
+#
+    '03 43 5C 81 35 35 02 00' => 'Soligor AF C/D ZOOM UMCS 70-210mm 1:4.5',
+#
+    '00 40 18 2B 2C 34 00 06' => 'Tokina AT-X 107 DX Fish-Eye - AF 10-17mm F3.5-4.5',
+    '00 3C 1F 37 30 30 00 06' => 'Tokina AT-X 124 AF PRO DX - AF 12-24mm F4',
+    '00 48 29 50 24 24 00 06' => 'Tokina AT-X 165 PRO DX - AF 16-50mm F2.8',
+    '00 40 2B 2B 2C 2C 00 02' => 'Tokina AT-X 17 AF PRO - AF 17mm F3.5',
+    '25 48 3C 5C 24 24 1B 02' => 'Tokina AT-X 287 AF PRO SV 28-70mm F2.8',
+    '00 48 3C 60 24 24 00 02' => 'Tokina AT-X 280 AF PRO 28-80mm F2.8 ASPHERICAL',
+    '00 48 50 72 24 24 00 06' => 'Tokina AT-X 535 PRO DX - AF 50-135mm F2.8',
+    '14 54 60 80 24 24 0B 00' => 'Tokina AT-X 828 AF 80-200mm F2.8',
+    '24 44 60 98 34 3C 1A 02' => 'Tokina AT-X 840 AF II 80-400mm F4.5-5.6',
+    '00 44 60 98 34 3C 00 02' => 'Tokina AT-X 840D 80-400mm F4.5-5.6', #PH
+    '00 54 68 68 24 24 00 02' => 'Tokina AT-X M100 PRO D - 100mm F2.8',
+    '14 48 68 8E 30 30 0B 00' => 'Tokina AT-X 340 AF II 100-300mm F4',
+    '00 54 8E 8E 24 24 00 02' => 'Tokina AT-X 300 AF PRO 300mm F2.8',
+#
+    '00 36 1C 2D 34 3C 00 06' => 'Tamron SP AF11-18mm f/4.5-5.6 Di II LD Aspherical (IF)',
+    '07 46 2B 44 24 30 03 02' => 'Tamron SP AF17-35mm f/2.8-4 Di LD Aspherical (IF)',
+    '00 53 2B 50 24 24 00 06' => 'Tamron SP AF17-50mm f/2.8 (A16)', #PH
+    '00 3F 2D 80 2B 40 00 06' => 'Tamron AF18-200mm f/3.5-6.3 XR Di II LD Aspherical (IF)',
+    '00 3F 2D 80 2C 40 00 06' => 'Tamron AF18-200mm f/3.5-6.3 XR Di II LD Aspherical (IF) Macro',
+    '07 40 2F 44 2C 34 03 02' => 'Tamron AF19-35mm f/3.5-4.5 N',
+    '07 40 30 45 2D 35 03 02' => 'Tamron AF19-35mm f/3.5-4.5',
+    '00 49 30 48 22 2B 00 02' => 'Tamron SP AF20-40mm f/2.7-3.5',
+    '0E 4A 31 48 23 2D 0E 02' => 'Tamron SP AF20-40mm f/2.7-3.5',
+    '45 41 37 72 2C 3C 48 02' => 'Tamron SP AF24-135mm f/3.5-5.6 AD Aspherical (IF) Macro',
+    '33 54 3C 5E 24 24 62 02' => 'Tamron SP AF28-75mm f/2.8 XR Di LD Aspherical (IF) Macro',
+    '10 3D 3C 60 2C 3C D2 02' => 'Tamron AF28-80mm f/3.5-5.6 Aspherical',
+    '45 3D 3C 60 2C 3C 48 02' => 'Tamron AF28-80mm f/3.5-5.6 Aspherical',
+    '00 48 3C 6A 24 24 00 02' => 'Tamron SP AF28-105mm f/2.8',
+    '0B 3E 3D 7F 2F 3D 0E 02' => 'Tamron AF28-200mm f/3.8-5.6D',
+    '0B 3E 3D 7F 2F 3D 0E 00' => 'Tamron AF28-200mm f/3.8-5.6',
+    '4D 41 3C 8E 2B 40 62 02' => 'Tamron AF28-300mm f/3.5-6.3 XR Di LD Aspherical (IF)',
+    '4D 41 3C 8E 2C 40 62 02' => 'Tamron AF28-300mm f/3.5-6.3D',
+    '69 48 5C 8E 30 3C 6F 02' => 'Tamron AF70-300mm f/4-5.6 LD Macro 1:2',
+    '32 53 64 64 24 24 35 02' => 'Tamron SP AF90mm f/2.8 Di 1:1 Macro',
+    '00 4C 7C 7C 2C 2C 00 02' => 'Tamron SP AF180mm f/3.5 Di Model B01',
+    '20 3C 80 98 3D 3D 1E 02' => 'Tamron AF200-400mm f/5.6 LD IF',
+    '00 3E 80 A0 38 3F 00 02' => 'Tamron SP AF200-500mm f/5-6.3 Di LD (IF)',
+    '00 3F 80 A0 38 3F 00 02' => 'Tamron SP AF200-500mm f/5-6.3 Di',
+#
+    '00 00 00 00 00 00 00 01' => 'Manual Lens No CPU',
+    '1E 5D 64 64 20 20 13 00' => 'Unknown 90mm f/2.5',
+    '2F 40 30 44 2C 34 29 02' => 'Unknown 20-35mm f/3.5-4.5D',
+    '12 3B 68 8D 3D 43 09 02' => 'Unknown 100-290mm f/5.6-6.7',
+);
+
+# Nikon maker note tags
+%Image::ExifTool::Nikon::Main = (
+    PROCESS_PROC => \&Image::ExifTool::Nikon::ProcessNikon,
+    WRITE_PROC => \&Image::ExifTool::Exif::WriteExif,
+    CHECK_PROC => \&Image::ExifTool::Exif::CheckExif,
+    WRITABLE => 1,
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
+    PRINT_CONV => 'Image::ExifTool::Nikon::FormatString($val)',
+    0x0001 => { #2
+        # the format differs for different models.  for D70, this is a string '0210',
+        # but for the E775 it is binary: "\x00\x01\x00\x00"
+        Name => 'MakerNoteVersion',
+        Writable => 'undef',
+        Count => 4,
+        # convert to string if binary
+        ValueConv => '$_=$val; /^[\x00-\x09]/ and $_=join("",unpack("CCCC",$_)); $_',
+        ValueConvInv => '$val',
+        PrintConv => '$_=$val;s/^(\d{2})/$1\./;s/^0//;$_',
+        PrintConvInv => '$_=$val;s/\.//;"0$_"',
+    },
+    0x0002 => {
+        # this is the ISO actually used by the camera
+        # (may be different than ISO setting if auto)
+        Name => 'ISO',
+        Writable => 'int16u',
+        Count => 2,
+        Priority => 0,  # the EXIF ISO is more reliable
+        Groups => { 2 => 'Image' },
+        # first number is 1 for "Hi ISO" modes (H0.3, H0.7 and H1.0 on D80) - PH
+        PrintConv => '$_=$val;s/^0 //;s/^1 (\d+)/Hi $1/;$_',
+        PrintConvInv => '$_=$val;/^\d+/ ? "0 $_" : (s/Hi ?//i ? "1 $_" : $_)',
+    },
+    0x0003 => { Name => 'ColorMode',    Writable => 'string' },
+    0x0004 => { Name => 'Quality',      Writable => 'string' },
+    0x0005 => { Name => 'WhiteBalance', Writable => 'string' },
+    0x0006 => { Name => 'Sharpness',    Writable => 'string' },
+    0x0007 => { Name => 'FocusMode',    Writable => 'string' },
+    0x0008 => { Name => 'FlashSetting', Writable => 'string' },
+    # FlashType shows 'Built-in,TTL' when builtin flash fires,
+    # and 'Optional,TTL' when external flash is used (ref 2)
+    0x0009 => { #2
+        Name => 'FlashType',
+        Writable => 'string',
+        Count => 13,
+    },
+    0x000b => { Name => 'WhiteBalanceFineTune', Writable => 'int16s' }, #2
+    0x000c => {
+        Name => 'ColorBalance1',
+        Writable => 'rational64u',
+        Count => 4,
+    },
+    0x000d => { #15
+        Name => 'ProgramShift',
+        Writable => 'undef',
+        Count => 4,
+        ValueConv => 'my ($a,$b,$c)=unpack("c3",$val); $c ? $a*($b/$c) : 0',
+        ValueConvInv => q{
+            my $a = int($val*6 + ($val>0 ? 0.5 : -0.5));
+            $a<-128 or $a>127 ? undef : pack("c4",$a,1,6,0);
+        },
+        PrintConv => 'Image::ExifTool::Exif::ConvertFraction($val)',
+        PrintConvInv => 'eval $val',
+    },
+    0x000e => {
+        Name => 'ExposureDifference',
+        Writable => 'undef',
+        Count => 4,
+        ValueConv => 'my ($a,$b,$c)=unpack("c3",$val); $c ? $a*($b/$c) : 0',
+        ValueConvInv => q{
+            my $a = int($val*12 + ($val>0 ? 0.5 : -0.5));
+            $a<-128 or $a>127 ? undef : pack("c4",$a,1,12,0);
+        },
+        PrintConv => '$val ? sprintf("%+.1f",$val) : 0',
+        PrintConvInv => '$val',
+    },
+    0x000f => { Name => 'ISOSelection', Writable => 'string' }, #2
+    0x0010 => {
+        Name => 'DataDump',
+        Writable => 0,
+        Binary => 1,
+    },
+    0x0011 => {
+        Name => 'NikonPreview',
+        Groups => { 1 => 'NikonPreview', 2 => 'Image' },
+        Flags => 'SubIFD',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::Nikon::PreviewImage',
+            Start => '$val',
+        },
+    },
+    0x0012 => { #2
+        Name => 'FlashExposureComp',
+        Description => 'Flash Exposure Compensation',
+        Writable => 'undef',
+        Count => 4,
+        ValueConv => 'my ($a,$b,$c)=unpack("c3",$val); $c ? $a*($b/$c) : 0',
+        ValueConvInv => q{
+            my $a = int($val*6 + ($val>0 ? 0.5 : -0.5));
+            $a<-128 or $a>127 ? undef : pack("c4",$a,1,6,0);
+        },
+        PrintConv => 'Image::ExifTool::Exif::ConvertFraction($val)',
+        PrintConvInv => 'eval $val',
+    },
+    # D70 - another ISO tag
+    0x0013 => { #2
+        Name => 'ISOSetting',
+        Writable => 'int16u',
+        Count => 2,
+        PrintConv => '$_=$val;s/^0 //;$_',
+        PrintConvInv => '"0 $val"',
+    },
+    # D70 Image boundary?? top x,y bot-right x,y
+    0x0016 => { #2
+        Name => 'ImageBoundary',
+        Writable => 'int16u',
+        Count => 4,
+    },
+    0x0018 => { #5
+        Name => 'FlashExposureBracketValue',
+        Writable => 'undef',
+        Count => 4,
+        ValueConv => 'my ($a,$b,$c)=unpack("c3",$val); $c ? $a*($b/$c) : 0',
+        ValueConvInv => q{
+            my $a = int($val*6 + ($val>0 ? 0.5 : -0.5));
+            $a<-128 or $a>127 ? undef : pack("c4",$a,1,6,0);
+        },
+        PrintConv => 'sprintf("%.1f",$val)',
+        PrintConvInv => '$val',
+    },
+    0x0019 => { #5
+        Name => 'ExposureBracketValue',
+        Writable => 'rational64s',
+        PrintConv => 'Image::ExifTool::Exif::ConvertFraction($val)',
+        PrintConvInv => 'eval $val',
+    },
+    0x001a => { #PH
+        Name => 'ImageProcessing',
+        Writable => 'string',
+    },
+    0x001b => { #15
+        Name => 'CropHiSpeed',
+        Writable => 'int16u',
+        Count => 7,
+        PrintConv => q{
+            my @a = split ' ', $val;
+            return "Unknown ($val)" unless @a == 7;
+            $a[0] = $a[0] ? "On" : "Off";
+            return "$a[0] ($a[1]x$a[2] cropped to $a[3]x$a[4] at pixel $a[5],$a[6])";
+        }
+    },
+    0x001d => { #4
+        Name => 'SerialNumber',
+        Writable => 0,
+        Notes => 'Not writable because this value is used as a key to decrypt other information',
+        RawConv => '$self->{NikonInfo}->{SerialNumber} = $val',
+    },
+    0x001e => { #14
+        Name => 'ColorSpace',
+        Writable => 'int16u',
+        PrintConv => {
+            1 => 'sRGB',
+            2 => 'Adobe RGB',
+        },
+    },
+    0x0020 => { #16
+        Name => 'ImageAuthentication',
+        Writable => 'int8u',
+        PrintConv => { 0 => 'Off', 1 => 'On' },
+    },
+    0x0080 => { Name => 'ImageAdjustment',  Writable => 'string' },
+    0x0081 => { Name => 'ToneComp',         Writable => 'string' }, #2
+    0x0082 => { Name => 'AuxiliaryLens',    Writable => 'string' },
+    0x0083 => {
+        Name => 'LensType',
+        Writable => 'int8u',
+        # credit to Tom Christiansen (ref 7) for figuring this out...
+        PrintConv => q[$_ = $val ? Image::ExifTool::DecodeBits($val,
+            {
+                0 => 'MF',
+                1 => 'D',
+                2 => 'G',
+                3 => 'VR',
+            }) : 'AF';
+            # remove commas and change "D G" to just "G"
+            s/,//g; s/\bD G\b/G/; $_
+        ],
+        PrintConvInv => q[
+            my $bits = 0;
+            $bits |= 0x01 if $val =~ /\bMF\b/i;
+            $bits |= 0x02 if $val =~ /\bD\b/i;
+            $bits |= 0x06 if $val =~ /\bG\b/i;
+            $bits |= 0x08 if $val =~ /\bVR\b/i;
+            return $bits;
+        ],
+    },
+    0x0084 => { #2
+        Name => "Lens",
+        Writable => 'rational64u',
+        Count => 4,
+        # short focal, long focal, aperture at short focal, aperture at long focal
+        PrintConv => q{
+            $val =~ tr/,/./;    # in case locale is whacky
+            my ($a,$b,$c,$d) = split ' ', $val;
+            ($a==$b ? $a : "$a-$b") . "mm f/" . ($c==$d ? $c : "$c-$d")
+        },
+        PrintConvInv => '$_=$val; tr/a-z\///d; s/(^|\s)([0-9.]+)(?=\s|$)/$1$2-$2/g; s/-/ /g; $_',
+    },
+    0x0085 => {
+        Name => 'ManualFocusDistance',
+        Writable => 'rational64u',
+    },
+    0x0086 => {
+        Name => 'DigitalZoom',
+        Writable => 'rational64u',
+    },
+    0x0087 => { #5
+        Name => 'FlashMode',
+        Writable => 'int8u',
+        PrintConv => {
+            0 => 'Did Not Fire',
+            1 => 'Fired, Manual', #14
+            7 => 'Fired, External', #14
+            8 => 'Fired, Commander Mode',
+            9 => 'Fired, TTL Mode',
+        },
+    },
+    0x0088 => {
+        Name => 'AFInfo',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::Nikon::AFInfo',
+        },
+    },
+    0x0089 => { #5
+        Name => 'ShootingMode',
+        Writable => 'int16u',
+        # credit to Tom Christiansen (ref 7) for figuring this out...
+        # The (new?) bit 5 seriously complicates our life here: after firmwareB's
+        # 1.03, bit 5 turns on when you ask for BUT DO NOT USE the long-range
+        # noise reduction feature, probably because even not using it, it still
+        # slows down your drive operation to 50% (1.5fps max not 3fps).  But no
+        # longer does !$val alone indicate single-frame operation. - TC
+        PrintConv => q[
+            $_ = '';
+            unless ($val & 0x87) {
+                return 'Single-Frame' unless $val;
+                $_ = 'Single-Frame, ';
+            }
+            return $_ . Image::ExifTool::DecodeBits($val,
+            {
+                0 => 'Continuous',
+                1 => 'Delay',
+                2 => 'PC Control',
+                4 => 'Exposure Bracketing',
+                5 => 'Unused LE-NR Slowdown',
+                6 => 'White-Balance Bracketing',
+                7 => 'IR Control',
+            });
+        ],
+    },
+    0x008a => { #15
+        Name => 'AutoBracketRelease',
+        Writable => 'int16u',
+        PrintConv => {
+            0 => 'None',
+            1 => 'Auto Release',
+            2 => 'Manual Release',
+        },
+    },
+    0x008b => { #8
+        Name => 'LensFStops',
+        ValueConv => 'my ($a,$b,$c)=unpack("C3",$val); $c ? $a*($b/$c) : 0',
+        ValueConvInv => 'my $a=int($val*12+0.5);$a<256 ? pack("C4",$a,1,12,0) : undef',
+        PrintConv => 'sprintf("%.2f", $val)',
+        PrintConvInv => '$val',
+        Writable => 'undef',
+        Count => 4,
+    },
+    0x008c => {
+        Name => 'NEFCurve1',
+        Writable => 0,
+        Binary => 1,
+    },
+    0x008d => { Name => 'ColorHue' ,        Writable => 'string' }, #2
+    # SceneMode takes on the following values: PORTRAIT, PARTY/INDOOR, NIGHT PORTRAIT,
+    # BEACH/SNOW, LANDSCAPE, SUNSET, NIGHT SCENE, MUSEUM, FIREWORKS, CLOSE UP, COPY,
+    # BACK LIGHT, PANORAMA ASSIST, SPORT, DAWN/DUSK
+    0x008f => { Name => 'SceneMode',        Writable => 'string' }, #2
+    # LightSource shows 3 values COLORED SPEEDLIGHT NATURAL.
+    # (SPEEDLIGHT when flash goes. Have no idea about difference between other two.)
+    0x0090 => { Name => 'LightSource',      Writable => 'string' }, #2
+    0x0091 => [ #18
+        {
+            Condition => '$$valPt =~ /^02/',
+            Name => 'ShotInfo',
+            Writable => 0, # can't yet write encrypted data
+            SubDirectory => {
+                TagTable => 'Image::ExifTool::Nikon::ShotInfo',
+                ProcessProc => \&Image::ExifTool::Nikon::ProcessNikonEncrypted,
+                DecryptStart => 4,
+                DecryptLen => 0x24e,
+            },
+        },
+        {
+            Name => 'ShotInfo',
+            Writable => 0,
+            Unknown => 1, # no tags known so don't process unless necessary
+            SubDirectory => {
+                TagTable => 'Image::ExifTool::Nikon::ShotInfo',
+                DirOffset => 4,
+            },
+        },
+    ],
+    0x0092 => { #2
+        Name => 'HueAdjustment',
+        Writable => 'int16s',
+    },
+# this doesn't look right:
+#    0x0093 => { #15
+#        Name => 'SaturationAdjustment',
+#        Writable => 'int16s',
+#    },
+    0x0094 => { Name => 'Saturation',       Writable => 'int16s' },
+    0x0095 => { Name => 'NoiseReduction',   Writable => 'string' },
+    0x0096 => {
+        Name => 'NEFCurve2',
+        Writable => 0,
+        Binary => 1,
+    },
+    0x0097 => [ #4
+        {
+            Condition => '$$valPt =~ /^0100/', # (D100)
+            Name => 'ColorBalance0100',
+            SubDirectory => {
+                Start => '$valuePtr + 72',
+                TagTable => 'Image::ExifTool::Nikon::ColorBalance1',
+            },
+        },
+        {
+            Condition => '$$valPt =~ /^0102/', # (D2H)
+            Name => 'ColorBalance0102',
+            SubDirectory => {
+                Start => '$valuePtr + 10',
+                TagTable => 'Image::ExifTool::Nikon::ColorBalance2',
+            },
+        },
+        {
+            Condition => '$$valPt =~ /^0103/', # (D70)
+            Name => 'ColorBalance0103',
+            # D70:  at file offset 'tag-value + base + 20', 4 16 bits numbers,
+            # v[0]/v[1] , v[2]/v[3] are the red/blue multipliers.
+            SubDirectory => {
+                Start => '$valuePtr + 20',
+                TagTable => 'Image::ExifTool::Nikon::ColorBalance3',
+            },
+        },
+        {
+            Condition => '$$valPt =~ /^0205/', # (D50)
+            Name => 'ColorBalance0205',
+            Writable => 0, # can't yet write encrypted data
+            SubDirectory => {
+                TagTable => 'Image::ExifTool::Nikon::ColorBalance2',
+                ProcessProc => \&Image::ExifTool::Nikon::ProcessNikonEncrypted,
+                DecryptStart => 4,
+                DecryptLen => 22, # 284 bytes encrypted, but don't need to decrypt it all
+                DirOffset => 14,
+            },
+        },
+        {
+            Condition => '$$valPt =~ /^02/', # (D2X=0204,D2Hs=0206,D200=0207,D40=0208)
+            Name => 'ColorBalance02',
+            Writable => 0, # can't yet write encrypted data
+            SubDirectory => {
+                TagTable => 'Image::ExifTool::Nikon::ColorBalance2',
+                ProcessProc => \&Image::ExifTool::Nikon::ProcessNikonEncrypted,
+                DecryptStart => 284,
+                DecryptLen => 14, # 324 bytes encrypted, but don't need to decrypt it all
+                DirOffset => 6,
+            },
+        },
+        {
+            Name => 'ColorBalanceUnknown',
+            Writable => 0,
+        },
+    ],
+    0x0098 => [
+        { #8
+            Condition => '$$valPt =~ /^0100/', # D100, D1X - PH
+            Name => 'LensData0100',
+            SubDirectory => {
+                TagTable => 'Image::ExifTool::Nikon::LensData00',
+            },
+        },
+        { #8
+            Condition => '$$valPt =~ /^0101/', # D70, D70s - PH
+            Name => 'LensData0101',
+            SubDirectory => {
+                TagTable => 'Image::ExifTool::Nikon::LensData01',
+            },
+        },
+        # note: this information is encrypted if the version is 02xx
+        { #8
+            # 0201 - D200, D2Hs, D2X and D2Xs
+            # 0202 - D40, D40X and D80
+            # 0203 - D300
+            Condition => '$$valPt =~ /^020[1-3]/',
+            Name => 'LensData0201',
+            Writable => 0, # can't yet write encrypted data
+            SubDirectory => {
+                TagTable => 'Image::ExifTool::Nikon::LensData01',
+                ProcessProc => \&Image::ExifTool::Nikon::ProcessNikonEncrypted,
+                DecryptStart => 4,
+            },
+        },
+        {
+            Name => 'LensDataUnknown',
+            Writable => 0,
+        },
+    ],
+    0x0099 => { #2/15
+        Name => 'RawImageCenter',
+        Writable => 'int16u',
+        Count => 2,
+    },
+    0x009a => { #10
+        Name => 'SensorPixelSize',
+        Writable => 'rational64u',
+        Count => 2,
+        PrintConv => '$val=~s/ / x /;"$val um"',
+        PrintConvInv => '$val=~tr/a-zA-Z/ /;$val',
+    },
+    # 0x009c - observed values: "", "TWO-SHOT" - PH
+    0x00a0 => { Name => 'SerialNumber',     Writable => 'string' }, #2
+    0x00a2 => { # size of compressed image data plus EOI segment (ref 10)
+        Name => 'ImageDataSize',
+        Writable => 'int32u',
+    },
+    0x00a5 => { #15
+        Name => 'ImageCount',
+        Writable => 'int32u',
+    },
+    0x00a6 => { #15
+        Name => 'DeletedImageCount',
+        Writable => 'int32u',
+    },
+    # the sum of 0xa5 and 0xa6 is equal to 0xa7 ShutterCount (D2X,D2Hs,D2H,D200, ref 10)
+    0x00a7 => { # Number of shots taken by camera so far (ref 2)
+        Name => 'ShutterCount',
+        Writable => 0,
+        Notes => 'Not writable because this value is used as a key to decrypt other information',
+        RawConv => '$self->{NikonInfo}->{ShutterCount} = $val',
+    },
+    0x00a9 => { #2
+        Name => 'ImageOptimization',
+        Writable => 'string',
+        Count => 16,
+    },
+    0x00aa => { Name => 'Saturation',       Writable => 'string' }, #2
+    0x00ab => { Name => 'VariProgram',      Writable => 'string' }, #2
+    0x00ac => { Name => 'ImageStabilization',Writable=> 'string' }, #14
+    0x00ad => { Name => 'AFResponse',       Writable => 'string' }, #14
+    0x00b0 => { #PH
+        Name => 'MultiExposure',
+        Condition => '$$valPt =~ /^0100/',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::Nikon::MultiExposure',
+        },
+    },
+    0x00b1 => { #14/PH/19 (D80)
+        Name => 'HighISONoiseReduction',
+        Writable => 'int16u',
+        PrintConv => {
+            0 => 'Off',
+            1 => 'Minimal', # for high ISO (>800) when setting is "Off"
+            2 => 'Low',     # Low,Normal,High take effect for ISO > 400
+            4 => 'Normal',
+            6 => 'High',
+        },
+    },
+    # 0x00b2 (string: 'Normal', 0xc3's, 0xff's or 0x20's)
+    0x0e00 => {
+        Name => 'PrintIM',
+        Description => 'Print Image Matching',
+        Writable => 0,
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::PrintIM::Main',
+        },
+    },
+    # 0x0e01 I don't know what this is, but in D70 NEF files produced by Nikon
+    # Capture, the data for this tag extends 4 bytes past the end of the maker notes.
+    # Very odd.  I hope these 4 bytes aren't useful because they will get lost by any
+    # utility that blindly copies the maker notes (not ExifTool) - PH
+    0x0e01 => {
+        Name => 'NikonCaptureData',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::NikonCapture::Main',
+        },
+    },
+    0x0e09 => { #12
+        Name => 'NikonCaptureVersion',
+        Writable => 'string',
+    },
+    # 0x0e0e is in D70 Nikon Capture files (not out-of-the-camera D70 files) - PH
+    0x0e0e => { #PH
+        Name => 'NikonCaptureOffsets',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::Nikon::CaptureOffsets',
+            Validate => '$val =~ /^0100/',
+            Start => '$valuePtr + 4',
+        },
+    },
+    0x0e10 => { #17
+        Name => 'NikonScanIFD',
+        Groups => { 1 => 'NikonScan', 2 => 'Image' },
+        Flags => 'SubIFD',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::Nikon::Scan',
+            Start => '$val',
+        },
+    },
+    # 0x0e13 - some sort of edit history written by Nikon Capture
+);
+
+# NikonScan IFD entries (ref 17)
+%Image::ExifTool::Nikon::Scan = (
+    WRITE_PROC => \&Image::ExifTool::Exif::WriteExif,
+    CHECK_PROC => \&Image::ExifTool::Exif::CheckExif,
+    WRITE_GROUP => 'NikonScan',
+    WRITABLE => 1,
+    GROUPS => { 0 => 'MakerNotes', 1 => 'NikonScan', 2 => 'Image' },
+    NOTES => 'This information is written by the Nikon Scan software.',
+    0x02 => { Name => 'FilmType',    Writable => 'string', },
+    0x40 => { Name => 'MultiSample', Writable => 'string' },
+    0x41 => { Name => 'BitDepth',    Writable => 'int16u' },
+    0x50 => {
+        Name => 'MasterGain',
+        Writable => 'rational64s',
+        PrintConv => 'sprintf("%.2f",$val)',
+        PrintConvInv => '$val',
+    },
+    0x51 => {
+        Name => 'ColorGain',
+        Writable => 'rational64s',
+        Count => 3,
+        PrintConv => 'sprintf("%.2f %.2f %.2f",split(" ",$val))',
+        PrintConvInv => '$val',
+    },
+    0x100 => { Name => 'DigitalICE', Writable => 'string' },
+    0x110 => {
+        Name => 'ROCInfo',
+        SubDirectory => { TagTable => 'Image::ExifTool::Nikon::ROC' },
+    },
+    0x120 => {
+        Name => 'GEMInfo',
+        SubDirectory => { TagTable => 'Image::ExifTool::Nikon::GEM' },
+    },
+);
+
+# ref 17
+%Image::ExifTool::Nikon::ROC = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
+    CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
+    FORMAT => 'int32u',
+    WRITABLE => 1,
+    FIRST_ENTRY => 0,
+    GROUPS => { 0 => 'MakerNotes', 1 => 'NikonScan', 2 => 'Image' },
+    0 => {
+        Name => 'DigitalROC',
+        ValueConv => '$val / 10',
+        ValueConvInv => 'int($val * 10)',
+    },
+);
+
+# ref 17
+%Image::ExifTool::Nikon::GEM = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
+    CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
+    FORMAT => 'int32u',
+    WRITABLE => 1,
+    FIRST_ENTRY => 0,
+    GROUPS => { 0 => 'MakerNotes', 1 => 'NikonScan', 2 => 'Image' },
+    0 => {
+        Name => 'DigitalGEM',
+        ValueConv => '$val<95 ? $val/20-1 : 4',
+        ValueConvInv => '$val == 4 ? 95 : int(($val + 1) * 20)',
+    },
+);
+
+# Nikon AF information (ref 13)
+%Image::ExifTool::Nikon::AFInfo = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
+    CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
+    WRITABLE => 1,
+    FIRST_ENTRY => 0,
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
+    0 => {
+        Name => 'AFAreaMode',
+        PrintConv => {
+            0 => 'Single Area',
+            1 => 'Dynamic Area',
+            2 => 'Dynamic Area, Closest Subject',
+            3 => 'Group Dynamic',
+            4 => 'Single Area (wide)',
+            5 => 'Dynamic Area (wide)',
+        },
+    },
+    1 => {
+        Name => 'AFPoint',
+        Notes => 'in some focus modes this value is not meaningful',
+        PrintConv => {
+            0 => 'Center',
+            1 => 'Top',
+            2 => 'Bottom',
+            3 => 'Left',
+            4 => 'Right',
+            5 => 'Upper-left',
+            6 => 'Upper-right',
+            7 => 'Lower-left',
+            8 => 'Lower-right',
+            9 => 'Far Left',
+            10 => 'Far Right',
+        },
+    },
+    2 => {
+        Name => 'AFPointsInFocus',
+        Format => 'int16u',
+        PrintConv => {
+            BITMASK => {
+                0 => 'Center',
+                1 => 'Top',
+                2 => 'Bottom',
+                3 => 'Left',
+                4 => 'Right',
+                5 => 'Upper-left',
+                6 => 'Upper-right',
+                7 => 'Lower-left',
+                8 => 'Lower-right',
+                9 => 'Far Left',
+                10 => 'Far Right',
+            },
+        },
+    },
+);
+
+# ref PH
+%Image::ExifTool::Nikon::CaptureOffsets = (
+    PROCESS_PROC => \&ProcessNikonCaptureOffsets,
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
+    1 => 'IFD0_Offset',
+    2 => 'PreviewIFD_Offset',
+    3 => 'SubIFD_Offset',
+);
+
+# ref 4
+%Image::ExifTool::Nikon::ColorBalance1 = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
+    CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
+    WRITABLE => 1,
+    FORMAT => 'int16u',
+    FIRST_ENTRY => 0,
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
+    0 => {
+        Name => 'WB_RBGGLevels',
+        Format => 'int16u[4]',
+        Protected => 1,
+    },
+);
+
+# ref 4
+%Image::ExifTool::Nikon::ColorBalance2 = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
+    CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
+    WRITABLE => 1,
+    FORMAT => 'int16u',
+    FIRST_ENTRY => 0,
+    NOTES => q{
+        This information is encrypted for most camera models, and if encrypted is
+        not currently writable by exiftool.
+    },
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
+    0 => {
+        Name => 'WB_RGGBLevels',
+        Format => 'int16u[4]',
+        Protected => 1,
+    },
+);
+
+# ref 4
+%Image::ExifTool::Nikon::ColorBalance3 = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
+    CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
+    WRITABLE => 1,
+    FORMAT => 'int16u',
+    FIRST_ENTRY => 0,
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
+    0 => {
+        Name => 'WB_RGBGLevels',
+        Format => 'int16u[4]',
+        Protected => 1,
+    },
+);
+
+%Image::ExifTool::Nikon::Type2 = (
+    WRITE_PROC => \&Image::ExifTool::Exif::WriteExif,
+    CHECK_PROC => \&Image::ExifTool::Exif::CheckExif,
+    WRITABLE => 1,
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
+    0x0003 => 'Quality',
+    0x0004 => 'ColorMode',
+    0x0005 => 'ImageAdjustment',
+    0x0006 => 'CCDSensitivity',
+    0x0007 => 'WhiteBalance',
+    0x0008 => 'Focus',
+    0x000A => 'DigitalZoom',
+    0x000B => 'Converter',
+);
+
+# these are standard EXIF tags, but they are duplicated here so we
+# can change some names to extract the Nikon preview separately
+%Image::ExifTool::Nikon::PreviewImage = (
+    WRITE_PROC => \&Image::ExifTool::Exif::WriteExif,
+    CHECK_PROC => \&Image::ExifTool::Exif::CheckExif,
+    GROUPS => { 0 => 'MakerNotes', 1 => 'NikonPreview', 2 => 'Image'},
+    0x103 => {
+        Name => 'Compression',
+        PrintConv => \%Image::ExifTool::Exif::compression,
+        Priority => 0,
+    },
+    0x11a => {
+        Name => 'XResolution',
+        Priority => 0,
+    },
+    0x11b => {
+        Name => 'YResolution',
+        Priority => 0,
+    },
+    0x128 => {
+        Name => 'ResolutionUnit',
+        PrintConv => {
+            1 => 'None',
+            2 => 'inches',
+            3 => 'cm',
+        },
+        Priority => 0,
+    },
+    0x201 => {
+        Name => 'PreviewImageStart',
+        Flags => [ 'IsOffset', 'Permanent' ],
+        OffsetPair => 0x202, # point to associated byte count
+        DataTag => 'PreviewImage',
+        Writable => 'int32u',
+        WriteGroup => 'NikonPreview',
+        Protected => 2,
+    },
+    0x202 => {
+        Name => 'PreviewImageLength',
+        Flags => 'Permanent' ,
+        OffsetPair => 0x201, # point to associated offset
+        DataTag => 'PreviewImage',
+        Writable => 'int32u',
+        WriteGroup => 'NikonPreview',
+        Protected => 2,
+    },
+    0x213 => {
+        Name => 'YCbCrPositioning',
+        PrintConv => {
+            1 => 'Centered',
+            2 => 'Co-sited',
+        },
+        Priority => 0,
+    },
+);
+
+# these are duplicated enough times to make it worthwhile to define them centrally
+my %nikonApertureConversions = (
+    ValueConv => '2**($val/24)',
+    ValueConvInv => '$val>0 ? 24*log($val)/log(2) : 0',
+    PrintConv => 'sprintf("%.1f",$val)',
+    PrintConvInv => '$val',
+);
+
+my %nikonFocalConversions = (
+    ValueConv => '5 * 2**($val/24)',
+    ValueConvInv => '$val>0 ? 24*log($val/5)/log(2) : 0',
+    PrintConv => 'sprintf("%.1fmm",$val)',
+    PrintConvInv => '$val=~s/\s*mm$//;$val',
+);
+
+# Version 100 Nikon lens data
+%Image::ExifTool::Nikon::LensData00 = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
+    CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
+    WRITABLE => 1,
+    FIRST_ENTRY => 0,
+    NOTES => 'This structure is used by the D100, and D1X with firmware version 1.1.',
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
+    0x00 => {
+        Name => 'LensDataVersion',
+        Format => 'undef[4]',
+    },
+    0x06 => { #8
+        Name => 'LensIDNumber',
+        Notes => 'see LensID values below',
+    },
+    0x07 => { #8
+        Name => 'LensFStops',
+        ValueConv => '$val / 12',
+        ValueConvInv => '$val * 12',
+        PrintConv => 'sprintf("%.2f", $val)',
+        PrintConvInv => '$val',
+    },
+    0x08 => { #8/9
+        Name => 'MinFocalLength',
+        %nikonFocalConversions,
+    },
+    0x09 => { #8/9
+        Name => 'MaxFocalLength',
+        %nikonFocalConversions,
+    },
+    0x0a => { #8
+        Name => 'MaxApertureAtMinFocal',
+        %nikonApertureConversions,
+    },
+    0x0b => { #8
+        Name => 'MaxApertureAtMaxFocal',
+        %nikonApertureConversions,
+    },
+    0x0c => 'MCUVersion', #8
+);
+
+# Nikon lens data (note: needs decrypting if LensDataVersion is 020x)
+%Image::ExifTool::Nikon::LensData01 = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    FIRST_ENTRY => 0,
+    NOTES => q{
+        Nikon encrypts the LensData information below if LensDataVersion is 0201 or
+        higher, but  the decryption algorithm is known so the information can be
+        extracted.  It isn't yet writable, however, because the encryption adds
+        complications which make writing more difficult.
+    },
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
+    0x00 => {
+        Name => 'LensDataVersion',
+        Format => 'string[4]',
+    },
+    0x04 => { #8
+        Name => 'ExitPupilPosition',
+        ValueConv => '$val ? 2048 / $val : $val',
+        ValueConvInv => '$val ? 2048 / $val : $val',
+        PrintConv => 'sprintf("%.1fmm",$val)',
+        PrintConvInv => '$val=~s/ ?mm$//; $val',
+    },
+    0x05 => { #8
+        Name => 'AFAperture',
+        %nikonApertureConversions,
+    },
+    0x08 => { #8
+        Name => 'FocusPosition',
+        PrintConv => 'sprintf("0x%02x", $val)',
+        PrintConvInv => '$val',
+    },
+    0x09 => { #8/9
+        # With older AF lenses this does not work... (ref 13)
+        # ie) AF Nikkor 50mm f/1.4 => 48 (0x30)
+        # AF Zoom-Nikkor 35-105mm f/3.5-4.5 => @35mm => 15 (0x0f), @105mm => 141 (0x8d)
+        Notes => 'this focus distance is approximate, and not very accurate for some lenses',
+        Name => 'FocusDistance',
+        ValueConv => '0.01 * 10**($val/40)', # in m
+        ValueConvInv => '$val>0 ? 40*log($val*100)/log(10) : 0',
+        PrintConv => '$val ? sprintf("%.2f m",$val) : "inf"',
+        PrintConvInv => '$val eq "inf" ? 0 : $val =~ s/\s*m$//, $val',
+    },
+    0x0a => { #8/9
+        Name => 'FocalLength',
+        Priority => 0,
+        %nikonFocalConversions,
+    },
+    0x0b => { #8
+        Name => 'LensIDNumber',
+        Notes => 'see LensID values below',
+    },
+    0x0c => { #8
+        Name => 'LensFStops',
+        ValueConv => '$val / 12',
+        ValueConvInv => '$val * 12',
+        PrintConv => 'sprintf("%.2f", $val)',
+        PrintConvInv => '$val',
+    },
+    0x0d => { #8/9
+        Name => 'MinFocalLength',
+        %nikonFocalConversions,
+    },
+    0x0e => { #8/9
+        Name => 'MaxFocalLength',
+        %nikonFocalConversions,
+    },
+    0x0f => { #8
+        Name => 'MaxApertureAtMinFocal',
+        %nikonApertureConversions,
+    },
+    0x10 => { #8
+        Name => 'MaxApertureAtMaxFocal',
+        %nikonApertureConversions,
+    },
+    0x11 => 'MCUVersion', #8
+    0x12 => { #8
+        Name => 'EffectiveMaxAperture',
+        %nikonApertureConversions,
+    },
+);
+
+# information encrypted in some cameras - ref 18
+%Image::ExifTool::Nikon::ShotInfo = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    FIRST_ENTRY => 0,
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
+    DATAMEMBER => [ 0 ],
+    NOTES => q{
+        This information is encrypted for ShotInfoVersion 02xx, and some tags are
+        only valid for specific versions.
+    },
+    0x00 => {
+        Name => 'ShotInfoVersion',
+        RawConv => '$$self{ShotInfoVersion} = $val',
+        Format => 'string[4]',
+    },
+    0x66 => {
+        Name => 'VR_0x66',
+        Condition => '$$self{ShotInfoVersion} =~ /^(0204)$/',
+        Format => 'int8u',
+        Unknown => 1,
+        Notes => 'D2X, D2Xs (unverified)',
+        PrintConv => {
+            0 => 'Off',
+            1 => 'On (normal)',
+            2 => 'On (active)',
+        },
+    },
+    # 6a, 6e not correct for 0103 (D70), 0207 (D200)
+    0x6a => {
+        Name => 'ShutterCount',
+        Condition => '$$self{ShotInfoVersion} =~ /^(0204)$/',
+        Format => 'int32u',
+        Priority => 0,
+        Notes => 'D2X, D2Xs',
+    },
+    0x6e => {
+        Name => 'DeletedImageCount',
+        Condition => '$$self{ShotInfoVersion} =~ /^(0204)$/',
+        Format => 'int32u',
+        Priority => 0,
+        Notes => 'D2X, D2Xs',
+    },
+    0x75 => { #19
+        Name => 'VibrationReduction',
+        Condition => '$$self{ShotInfoVersion} =~ /^(0207)$/',
+        Format => 'int8u',
+        Notes => 'D200',
+        PrintConv => {
+            0 => 'Off',
+            1 => 'On (1)',
+            2 => 'On (2)',
+        },
+    },
+    0x82 => { # educated guess, needs verification
+        Name => 'VibrationReduction',
+        Condition => '$$self{ShotInfoVersion} =~ /^(0204)$/',
+        Format => 'int8u',
+        Notes => 'D2X, D2Xs',
+        PrintConv => {
+            0 => 'Off',
+            1 => 'On',
+        },
+    },
+    0x157 => { #19
+        Name => 'ShutterCount',
+        Condition => '$$self{ShotInfoVersion} =~ /^(0205)$/',
+        Format => 'undef[2]',
+        Priority => 0,
+        Notes => 'D50',
+        # treat as a 2-byte big-endian integer
+        ValueConv => 'unpack("n", $val)',
+        ValueConvInv => 'pack("n",$val)',
+    },
+    0x1ae => { #19
+        Name => 'VibrationReduction',
+        Condition => '$$self{ShotInfoVersion} =~ /^(0205)$/',
+        Format => 'int8u',
+        Notes => 'D50',
+        PrintHex => 1,
+        PrintConv => {
+            0x00 => 'n/a',
+            0x0c => 'Off',
+            0x0f => 'On',
+        },
+    },
+    0x24a => { #19
+        Name => 'ShutterCount',
+        Condition => '$$self{ShotInfoVersion} =~ /^(0208)$/',
+        Format => 'int32u',
+        Priority => 0,
+        Notes => 'D80',
+    },
+    0x24e => { #19
+        Name => 'VibrationReduction',
+        Condition => '$$self{ShotInfoVersion} =~ /^(0208)$/',
+        Notes => 'D80',
+        PrintHex => 1,
+        PrintConv => {
+            0x00 => 'Off, Horizontal',
+            0x01 => 'Off, Rotated 270 CW',
+            0x02 => 'Off, Rotated 90 CW',
+            0x03 => 'Off, Rotated 180',
+            0x18 => 'On, Horizontal',
+            0x19 => 'On, Rotated 270 CW',
+            0x1a => 'On, Rotated 90 CW',
+            0x1b => 'On, Rotated 180',
+        },
+    },
+    # note: DecryptLen currently set to 0x24e
+);
+
+# Multi exposure / image overlay information (ref PH)
+%Image::ExifTool::Nikon::MultiExposure = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
+    CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
+    WRITABLE => 1,
+    FIRST_ENTRY => 0,
+    FORMAT => 'int32u',
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
+    0 => {
+        Name => 'MultiExposureVersion',
+        Format => 'string[4]',
+    },
+    1 => {
+        Name => 'MultiExposureMode',
+        PrintConv => {
+            0 => 'Off',
+            1 => 'Multiple Exposure',
+            2 => 'Image Overlay',
+        },
+    },
+    2 => 'MultiExposureShots',
+    3 => {
+        Name => 'MultiExposureAutoGain',
+        PrintConv => { 0 => 'Off', 1 => 'On' },
+    },
+);
+
+# tags in Nikon QuickTime videos (PH - observations with Coolpix S3)
+# (note: very similar to information in Pentax videos)
+%Image::ExifTool::Nikon::MOV = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
+    NOTES => q{
+        This information is found in Nikon QT video images, and is very similar to
+        information found in Pentax MOV videos.
+    },
+    0x00 => {
+        Name => 'Make',
+        Format => 'string[5]',
+        PrintConv => 'ucfirst(lc($val))',
+    },
+    0x18 => {
+        Name => 'Model',
+        Description => 'Camera Model Name',
+        Format => 'string[8]',
+    },
+    0x26 => {
+        Name => 'ExposureTime',
+        Format => 'int32u',
+        ValueConv => '$val ? 10 / $val : 0',
+        PrintConv => 'Image::ExifTool::Exif::PrintExposureTime($val)',
+    },
+    0x2a => {
+        Name => 'FNumber',
+        Format => 'int32u',
+        ValueConv => '$val * 0.1',
+        PrintConv => 'sprintf("%.1f",$val)',
+    },
+    0x32 => {
+        Name => 'ExposureCompensation',
+        Format => 'int32s',
+        ValueConv => '$val * 0.1',
+        PrintConv => 'Image::ExifTool::Exif::ConvertFraction($val)',
+    },
+    0x44 => {
+        Name => 'WhiteBalance',
+        Format => 'int16u',
+        PrintConv => {
+            0 => 'Auto',
+            1 => 'Daylight',
+            2 => 'Shade',
+            3 => 'Fluorescent', #2
+            4 => 'Tungsten',
+            5 => 'Manual',
+        },
+    },
+    0x48 => {
+        Name => 'FocalLength',
+        Writable => 'int32u',
+        ValueConv => '$val * 0.1',
+        PrintConv => 'sprintf("%.1fmm",$val)',
+    },
+    0xaf => {
+        Name => 'Software',
+        Format => 'string[16]',
+    },
+    0xdf => { # (this is a guess ... could also be offset 0xdb)
+        Name => 'ISO',
+        Format => 'int16u',
+    },
+);
+
+
+# Nikon composite tags
+%Image::ExifTool::Nikon::Composite = (
+    GROUPS => { 2 => 'Camera' },
+    LensSpec => {
+        Description => 'Lens',
+        Require => {
+            0 => 'Nikon:Lens',
+            1 => 'Nikon:LensType',
+        },
+        ValueConv => '"$val[0] $val[1]"',
+        PrintConv => '"$prt[0] $prt[1]"',
+    },
+    LensID => {
+        SeparateTable => 'Nikon LensID',    # print values in a separate table
+        Require => {
+            0 => 'Nikon:LensIDNumber',
+            1 => 'LensFStops',
+            2 => 'MinFocalLength',
+            3 => 'MaxFocalLength',
+            4 => 'MaxApertureAtMinFocal',
+            5 => 'MaxApertureAtMaxFocal',
+            6 => 'MCUVersion',
+            7 => 'Nikon:LensType',
+        },
+        # construct lens ID string as per ref 11
+        ValueConv => 'uc(join(" ",(unpack("H*",pack("C*",@raw)) =~ /../g)))',
+        PrintConv => \%nikonLensIDs,
+    },
+);
+
+# add our composite tags
+Image::ExifTool::AddCompositeTags('Image::ExifTool::Nikon');
+
+
+#------------------------------------------------------------------------------
+# Clean up formatting of string values
+# Inputs: 0) string value
+# Returns: formatted string value
+# - removes trailing spaces and changes case to something more sensible
+sub FormatString($)
+{
+    my $str = shift;
+    # limit string length (can be very long for some unknown tags)
+    if (length($str) > 60) {
+        $str = substr($str,0,55) . "[...]";
+    } else {
+        $str =~ s/\s+$//;   # remove trailing white space and null terminator
+        # Don't change case of hyphenated strings (like AF-S) or non-words (no vowels)
+        unless ($str =~ /-/ or $str !~ /[AEIOUY]/) {
+            # change all letters but the first to lower case
+            $str =~ s/([A-Z]{1})([A-Z]+)/$1\L$2/g;
+        }
+    }
+    return $str;
+}
+
+#------------------------------------------------------------------------------
+# decoding tables from ref 4
+my @xlat = (
+  [ 0xc1,0xbf,0x6d,0x0d,0x59,0xc5,0x13,0x9d,0x83,0x61,0x6b,0x4f,0xc7,0x7f,0x3d,0x3d,
+    0x53,0x59,0xe3,0xc7,0xe9,0x2f,0x95,0xa7,0x95,0x1f,0xdf,0x7f,0x2b,0x29,0xc7,0x0d,
+    0xdf,0x07,0xef,0x71,0x89,0x3d,0x13,0x3d,0x3b,0x13,0xfb,0x0d,0x89,0xc1,0x65,0x1f,
+    0xb3,0x0d,0x6b,0x29,0xe3,0xfb,0xef,0xa3,0x6b,0x47,0x7f,0x95,0x35,0xa7,0x47,0x4f,
+    0xc7,0xf1,0x59,0x95,0x35,0x11,0x29,0x61,0xf1,0x3d,0xb3,0x2b,0x0d,0x43,0x89,0xc1,
+    0x9d,0x9d,0x89,0x65,0xf1,0xe9,0xdf,0xbf,0x3d,0x7f,0x53,0x97,0xe5,0xe9,0x95,0x17,
+    0x1d,0x3d,0x8b,0xfb,0xc7,0xe3,0x67,0xa7,0x07,0xf1,0x71,0xa7,0x53,0xb5,0x29,0x89,
+    0xe5,0x2b,0xa7,0x17,0x29,0xe9,0x4f,0xc5,0x65,0x6d,0x6b,0xef,0x0d,0x89,0x49,0x2f,
+    0xb3,0x43,0x53,0x65,0x1d,0x49,0xa3,0x13,0x89,0x59,0xef,0x6b,0xef,0x65,0x1d,0x0b,
+    0x59,0x13,0xe3,0x4f,0x9d,0xb3,0x29,0x43,0x2b,0x07,0x1d,0x95,0x59,0x59,0x47,0xfb,
+    0xe5,0xe9,0x61,0x47,0x2f,0x35,0x7f,0x17,0x7f,0xef,0x7f,0x95,0x95,0x71,0xd3,0xa3,
+    0x0b,0x71,0xa3,0xad,0x0b,0x3b,0xb5,0xfb,0xa3,0xbf,0x4f,0x83,0x1d,0xad,0xe9,0x2f,
+    0x71,0x65,0xa3,0xe5,0x07,0x35,0x3d,0x0d,0xb5,0xe9,0xe5,0x47,0x3b,0x9d,0xef,0x35,
+    0xa3,0xbf,0xb3,0xdf,0x53,0xd3,0x97,0x53,0x49,0x71,0x07,0x35,0x61,0x71,0x2f,0x43,
+    0x2f,0x11,0xdf,0x17,0x97,0xfb,0x95,0x3b,0x7f,0x6b,0xd3,0x25,0xbf,0xad,0xc7,0xc5,
+    0xc5,0xb5,0x8b,0xef,0x2f,0xd3,0x07,0x6b,0x25,0x49,0x95,0x25,0x49,0x6d,0x71,0xc7 ],
+  [ 0xa7,0xbc,0xc9,0xad,0x91,0xdf,0x85,0xe5,0xd4,0x78,0xd5,0x17,0x46,0x7c,0x29,0x4c,
+    0x4d,0x03,0xe9,0x25,0x68,0x11,0x86,0xb3,0xbd,0xf7,0x6f,0x61,0x22,0xa2,0x26,0x34,
+    0x2a,0xbe,0x1e,0x46,0x14,0x68,0x9d,0x44,0x18,0xc2,0x40,0xf4,0x7e,0x5f,0x1b,0xad,
+    0x0b,0x94,0xb6,0x67,0xb4,0x0b,0xe1,0xea,0x95,0x9c,0x66,0xdc,0xe7,0x5d,0x6c,0x05,
+    0xda,0xd5,0xdf,0x7a,0xef,0xf6,0xdb,0x1f,0x82,0x4c,0xc0,0x68,0x47,0xa1,0xbd,0xee,
+    0x39,0x50,0x56,0x4a,0xdd,0xdf,0xa5,0xf8,0xc6,0xda,0xca,0x90,0xca,0x01,0x42,0x9d,
+    0x8b,0x0c,0x73,0x43,0x75,0x05,0x94,0xde,0x24,0xb3,0x80,0x34,0xe5,0x2c,0xdc,0x9b,
+    0x3f,0xca,0x33,0x45,0xd0,0xdb,0x5f,0xf5,0x52,0xc3,0x21,0xda,0xe2,0x22,0x72,0x6b,
+    0x3e,0xd0,0x5b,0xa8,0x87,0x8c,0x06,0x5d,0x0f,0xdd,0x09,0x19,0x93,0xd0,0xb9,0xfc,
+    0x8b,0x0f,0x84,0x60,0x33,0x1c,0x9b,0x45,0xf1,0xf0,0xa3,0x94,0x3a,0x12,0x77,0x33,
+    0x4d,0x44,0x78,0x28,0x3c,0x9e,0xfd,0x65,0x57,0x16,0x94,0x6b,0xfb,0x59,0xd0,0xc8,
+    0x22,0x36,0xdb,0xd2,0x63,0x98,0x43,0xa1,0x04,0x87,0x86,0xf7,0xa6,0x26,0xbb,0xd6,
+    0x59,0x4d,0xbf,0x6a,0x2e,0xaa,0x2b,0xef,0xe6,0x78,0xb6,0x4e,0xe0,0x2f,0xdc,0x7c,
+    0xbe,0x57,0x19,0x32,0x7e,0x2a,0xd0,0xb8,0xba,0x29,0x00,0x3c,0x52,0x7d,0xa8,0x49,
+    0x3b,0x2d,0xeb,0x25,0x49,0xfa,0xa3,0xaa,0x39,0xa7,0xc5,0xa7,0x50,0x11,0x36,0xfb,
+    0xc6,0x67,0x4a,0xf5,0xa5,0x12,0x65,0x7e,0xb0,0xdf,0xaf,0x4e,0xb3,0x61,0x7f,0x2f ]
+);
+
+# decrypt Nikon data block (ref 4)
+# Inputs: 0) reference to data block, 1) serial number key, 2) shutter count key
+#         4) optional start offset (default 0)
+#         5) optional number of bytes to decode (default to the end of the data)
+# Returns: Decrypted data block
+sub Decrypt($$$;$$)
+{
+    my ($dataPt, $serial, $count, $start, $len) = @_;
+    $start or $start = 0;
+    my $end = $len ? $start + $len : length($$dataPt);
+    my $i;
+    my $key = 0;
+    for ($i=0; $i<4; ++$i) {
+        $key ^= ($count >> ($i*8)) & 0xff;
+    }
+    my $ci = $xlat[0][$serial & 0xff];
+    my $cj = $xlat[1][$key];
+    my $ck = 0x60;
+    my @data = unpack('C*',$$dataPt);
+    for ($i=$start; $i<$end; ++$i) {
+        $cj = ($cj + $ci * $ck) & 0xff;
+        $ck = ($ck + 1) & 0xff;
+        $data[$i] ^= $cj;
+    }
+    return pack('C*',@data);
+}
+
+#------------------------------------------------------------------------------
+# process Nikon Encrypted data block
+# Inputs: 0) ExifTool object reference, 1) reference to directory information
+#         2) pointer to tag table
+# Returns: 1 on success
+sub ProcessNikonEncrypted($$$)
+{
+    my ($exifTool, $dirInfo, $tagTablePtr) = @_;
+    # get the encrypted directory data
+    my $buff = substr(${$$dirInfo{DataPt}}, $$dirInfo{DirStart}, $$dirInfo{DirLen});
+    # save it until we have enough information to decrypt it later
+    push @{$exifTool->{NikonInfo}->{Encrypted}}, [ $tagTablePtr, $buff, $$dirInfo{TagInfo}];
+    if ($exifTool->Options('Verbose')) {
+        my $indent = substr($exifTool->{INDENT}, 0, -2);
+        $exifTool->VPrint(0, $indent, "[$dirInfo->{TagInfo}->{Name} directory to be decrypted later]\n");
+    }
+    return 1;
+}
+
+#------------------------------------------------------------------------------
+# process Nikon Capture Offsets IFD (ref PH)
+# Inputs: 0) ExifTool object reference, 1) reference to directory information
+#         2) pointer to tag table
+# Returns: 1 on success
+# Notes: This isn't a normal IFD, but is close...
+sub ProcessNikonCaptureOffsets($$$)
+{
+    my ($exifTool, $dirInfo, $tagTablePtr) = @_;
+    my $dataPt = $$dirInfo{DataPt};
+    my $dirStart = $$dirInfo{DirStart};
+    my $dirLen = $$dirInfo{DirLen};
+    my $success = 0;
+    return 0 unless $dirLen > 2;
+    my $count = Get16u($dataPt, $dirStart);
+    return 0 unless $count and $count * 12 + 2 <= $dirLen;
+    if ($exifTool->Options('Verbose')) {
+        $exifTool->VerboseDir('NikonCaptureOffsets', $count);
+    }
+    my $index;
+    for ($index=0; $index<$count; ++$index) {
+        my $pos = $dirStart + 12 * $index + 2;
+        my $tagID = Get32u($dataPt, $pos);
+        my $value = Get32u($dataPt, $pos + 4);
+        $exifTool->HandleTag($tagTablePtr, $tagID, $value,
+            Index  => $index,
+            DataPt => $dataPt,
+            Start  => $pos,
+            Size   => 12,
+        ) and $success = 1;
+    }
+    return $success;
+}
+
+#------------------------------------------------------------------------------
+# Process Nikon Makernotes directory
+# Inputs: 0) ExifTool object reference
+#         1) Reference to directory information hash
+#         2) Pointer to tag table for this directory
+# Returns: 1 on success, otherwise returns 0 and sets a Warning
+sub ProcessNikon($$$)
+{
+    my ($exifTool, $dirInfo, $tagTablePtr) = @_;
+    my $verbose = $exifTool->Options('Verbose');
+    my $nikonInfo = $exifTool->{NikonInfo} = { };
+    my @encrypted;  # list to save encrypted data
+    $$nikonInfo{Encrypted} = \@encrypted;
+    my $rtnVal = Image::ExifTool::Exif::ProcessExif($exifTool, $dirInfo, $tagTablePtr);
+    # process any encrypted information we found
+    my $encryptedDir;
+    if (@encrypted) {
+        my $serial = $exifTool->{NikonInfo}->{SerialNumber} || 0;
+        my $count = $exifTool->{NikonInfo}->{ShutterCount};
+        unless (defined $count) {
+            $exifTool->Warn("Can't decrypt Nikon information (no ShutterCount key)");
+            undef @encrypted;
+        }
+        foreach $encryptedDir (@encrypted) {
+            my ($subTablePtr, $data, $tagInfo) = @$encryptedDir;
+            my ($start, $len, $offset);
+            if ($tagInfo and $$tagInfo{SubDirectory}) {
+                $start = $tagInfo->{SubDirectory}->{DecryptStart};
+                # may decrypt only part of the information to save time
+                if ($verbose < 3 and $exifTool->Options('Unknown') < 2) {
+                    $len = $tagInfo->{SubDirectory}->{DecryptLen};
+                }
+                $offset = $tagInfo->{SubDirectory}->{DirOffset};
+            }
+            $start or $start = 0;
+            if (defined $offset) {
+                # offset, if specified, is releative to start of encrypted data
+                $offset += $start;
+            } else {
+                $offset = 0;
+            }
+            my $maxLen = length($data) - $start;
+            if ($len) {
+                $len = $maxLen if $len > $maxLen;
+            } else {
+                $len = $maxLen;
+            }
+            # use fixed serial numbers if no good serial number found
+            unless ($serial =~ /^\d+$/) {
+                if ($exifTool->{CameraModel} =~ /\bD50$/) {
+                    $serial = 0x22; # D50 (ref 8)
+                } else {
+                    $serial = 0x60; # D200 (ref 10), D40X (ref PH)
+                }
+            }
+            $data = Decrypt(\$data, $serial, $count, $start, $len);
+            my %subdirInfo = (
+                DataPt   => \$data,
+                DirStart => $offset,
+                DirLen   => length($data) - $offset,
+            );
+            if ($verbose > 2) {
+                $exifTool->VerboseDir("Decrypted $$tagInfo{Name}");
+                my %parms = (
+                    Prefix => $exifTool->{INDENT},
+                    Out => $exifTool->Options('TextOut'),
+                );
+                $parms{MaxLen} = 96 unless $verbose > 3;
+                Image::ExifTool::HexDump(\$data, undef, %parms);
+            }
+            # process the decrypted information
+            $exifTool->ProcessBinaryData(\%subdirInfo, $subTablePtr);
+        }
+    }
+    delete $exifTool->{NikonInfo};
+    return $rtnVal;
+}
+
+1;  # end
+
+__END__
+
+=head1 NAME
+
+Image::ExifTool::Nikon - Nikon EXIF maker notes tags
+
+=head1 SYNOPSIS
+
+This module is loaded automatically by Image::ExifTool when required.
+
+=head1 DESCRIPTION
+
+This module contains definitions required by Image::ExifTool to interpret
+Nikon maker notes in EXIF information.
+
+=head1 AUTHOR
+
+Copyright 2003-2007, Phil Harvey (phil at owl.phy.queensu.ca)
+
+This library is free software; you can redistribute it and/or modify it
+under the same terms as Perl itself.
+
+=head1 REFERENCES
+
+=over 4
+
+=item L<http://park2.wakwak.com/~tsuruzoh/Computer/Digicams/exif-e.html>
+
+=item L<http://www.cybercom.net/~dcoffin/dcraw/>
+
+=item L<http://members.aol.com/khancock/pilot/nbuddy/>
+
+=item L<http://www.rottmerhusen.com/objektives/lensid/thirdparty.html>
+
+=item L<http://homepage3.nifty.com/kamisaka/makernote/makernote_nikon.htm>
+
+=item L<http://www.wohlberg.net/public/software/photo/nstiffexif/>
+
+=back
+
+=head1 ACKNOWLEDGEMENTS
+
+Thanks to Joseph Heled, Thomas Walter, Brian Ristuccia, Danek Duvall, Tom
+Christiansen, Robert Rottmerhusen, Werner Kober, Roger Larsson and Jens
+Duttke for their help figuring out some Nikon tags, and Bruce Stevens for
+his additions to the LensID list.
+
+=head1 SEE ALSO
+
+L<Image::ExifTool::TagNames/Nikon Tags>,
+L<Image::ExifTool::TagNames/NikonCapture Tags>,
+L<Image::ExifTool(3pm)|Image::ExifTool>
+
+=cut
Index: /gsdl/trunk/perllib/cpan/Image/ExifTool/NikonCapture.pm
===================================================================
--- /gsdl/trunk/perllib/cpan/Image/ExifTool/NikonCapture.pm	(revision 16842)
+++ /gsdl/trunk/perllib/cpan/Image/ExifTool/NikonCapture.pm	(revision 16842)
@@ -0,0 +1,677 @@
+#------------------------------------------------------------------------------
+# File:         NikonCapture.pm
+#
+# Description:  Read/write Nikon Capture information
+#
+# Revisions:    11/08/2005 - P. Harvey Created
+#
+# References:   1) http://www.cybercom.net/~dcoffin/dcraw/
+#------------------------------------------------------------------------------
+
+package Image::ExifTool::NikonCapture;
+
+use strict;
+use vars qw($VERSION);
+use Image::ExifTool qw(:DataAccess :Utils);
+use Image::ExifTool::Exif;
+
+$VERSION = '1.02';
+
+# common print conversions
+my %offOn = ( 0 => 'Off', 1 => 'On' );
+my %noYes = ( 0 => 'No', 1 => 'Yes' );
+my %unsharpColor = (
+    0 => 'RGB',
+    1 => 'Red',
+    2 => 'Green',
+    3 => 'Blue',
+    4 => 'Yellow',
+    5 => 'Magenta',
+    6 => 'Cyan',
+);
+
+# Nikon Capture data (ref PH)
+%Image::ExifTool::NikonCapture::Main = (
+    PROCESS_PROC => \&ProcessNikonCapture,
+    WRITE_PROC => \&WriteNikonCapture,
+    CHECK_PROC => \&Image::ExifTool::Exif::CheckExif,
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Image' },
+    NOTES => q{
+        This information is written by the Nikon Capture software in tag 0x0e01 of
+        the maker notes of NEF images.
+    },
+    0x008ae85e => {
+        Name => 'LCHEditor',
+        Writable => 'int8u',
+        PrintConv => \%offOn,
+    },
+    0x0c89224b => {
+        Name => 'ColorAberrationControl',
+        Writable => 'int8u',
+        PrintConv => \%offOn,
+    },
+    0x2175eb78 => {
+        Name => 'D-LightingHQ',
+        Writable => 'int8u',
+        PrintConv => \%offOn,
+    },
+    0x2fc08431 => {
+        Name => 'StraightenAngle',
+        Writable => 'double',
+    },
+    0x374233e0 => {
+        Name => 'CropData',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::NikonCapture::CropData',
+        },
+    },
+    0x3cfc73c6 => {
+        Name => 'RedEyeData',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::NikonCapture::RedEyeData',
+        },
+    },
+    0x5f0e7d23 => {
+        Name => 'ColorBooster',
+        Writable => 'int8u',
+        PrintConv => \%offOn,
+    },
+    0x6a6e36b6 => {
+        Name => 'D-LightingHQSelected',
+        Writable => 'int8u',
+        PrintConv => \%noYes,
+    },
+    0x753dcbc0 => {
+        Name => 'NoiseReduction',
+        Writable => 'int8u',
+        PrintConv => \%offOn,
+    },
+    0x76a43200 => {
+        Name => 'UnsharpMask',
+        Writable => 'int8u',
+        PrintConv => \%offOn,
+    },
+    0x76a43201 => {
+        Name => 'Curves',
+        Writable => 'int8u',
+        PrintConv => \%offOn,
+    },
+    0x76a43202 => {
+        Name => 'ColorBalanceAdj',
+        Writable => 'int8u',
+        PrintConv => \%offOn,
+    },
+    0x76a43203 => {
+        Name => 'AdvancedRaw',
+        Writable => 'int8u',
+        PrintConv => \%offOn,
+    },
+    0x76a43204 => {
+        Name => 'WhiteBalanceAdj',
+        Writable => 'int8u',
+        PrintConv => \%offOn,
+    },
+    0x76a43205 => {
+        Name => 'VignetteControl',
+        Writable => 'int8u',
+        PrintConv => \%offOn,
+    },
+    0x76a43206 => {
+        Name => 'FlipHorizontal',
+        Writable => 'int8u',
+        PrintConv => \%noYes,
+    },
+    0x76a43207 => { # rotation angle in degrees
+        Name => 'Rotation',
+        Writable => 'int16u',
+    },
+    0x84589434 => {
+        Name => 'BrightnessData',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::NikonCapture::Brightness',
+        },
+    },
+    0x890ff591 => {
+        Name => 'D-LightingHQData',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::NikonCapture::DLightingHQ',
+        },
+    },
+    0x926f13e0 => {
+        Name => 'NoiseReductionData',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::NikonCapture::NoiseReduction',
+        },
+    },
+    0x9ef5f6e0 => {
+        Name => 'IPTCData',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::IPTC::Main',
+        },
+    },
+    0xab5eca5e => {
+        Name => 'PhotoEffects',
+        Writable => 'int8u',
+        PrintConv => \%offOn,
+    },
+    0xac6bd5c0 => {
+        Name => 'VignetteControlIntensity',
+        Writable => 'int16s',
+    },
+    0xb0384e1e => {
+        Name => 'PhotoEffectsData',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::NikonCapture::PhotoEffects',
+        },
+    },
+    0xb999a36f => {
+        Name => 'ColorBoostData',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::NikonCapture::ColorBoost',
+        },
+    },
+    0xbf3c6c20 => {
+        Name => 'WBAdjData',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::NikonCapture::WBAdjData',
+        },
+    },
+    0xce5554aa => {
+        Name => 'D-LightingHS',
+        Writable => 'int8u',
+        PrintConv => \%offOn,
+    },
+    0xe37b4337 => {
+        Name => 'D-LightingHSData',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::NikonCapture::DLightingHS',
+        },
+    },
+    0xe42b5161 => {
+        Name => 'UnsharpData',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::NikonCapture::UnsharpData',
+        },
+    },
+    0xfe443a45 => {
+        Name => 'ImageDustOff',
+        Writable => 'int8u',
+        PrintConv => \%offOn,
+    },
+);
+
+%Image::ExifTool::NikonCapture::UnsharpData = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
+    CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
+    WRITABLE => 1,
+    FORMAT => 'int8u',
+    FIRST_ENTRY => 0,
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Image' },
+    0 => 'UnsharpCount',
+    19 => { Name => 'Unsharp1Color', Format => 'int16u', PrintConv => \%unsharpColor },
+    23 => { Name => 'Unsharp1Intensity', Format => 'int16u' },
+    25 => { Name => 'Unsharp1HaloWidth', Format => 'int16u' },
+    27 => 'Unsharp1Threshold',
+    46 => { Name => 'Unsharp2Color', Format => 'int16u', PrintConv => \%unsharpColor },
+    50 => { Name => 'Unsharp2Intensity', Format => 'int16u' },
+    52 => { Name => 'Unsharp2HaloWidth', Format => 'int16u' },
+    54 => 'Unsharp2Threshold',
+    73 => { Name => 'Unsharp3Color', Format => 'int16u', PrintConv => \%unsharpColor },
+    77 => { Name => 'Unsharp3Intensity', Format => 'int16u' },
+    79 => { Name => 'Unsharp3HaloWidth', Format => 'int16u' },
+    81 => 'Unsharp3Threshold',
+    100 => { Name => 'Unsharp4Color', Format => 'int16u', PrintConv => \%unsharpColor },
+    104 => { Name => 'Unsharp4Intensity', Format => 'int16u' },
+    106 => { Name => 'Unsharp4HaloWidth', Format => 'int16u' },
+    108 => 'Unsharp4Threshold',
+    # there could be more, but I grow bored of this... :P
+);
+
+%Image::ExifTool::NikonCapture::DLightingHS = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
+    CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
+    WRITABLE => 1,
+    FORMAT => 'int32u',
+    FIRST_ENTRY => 0,
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Image' },
+    0 => 'D-LightingHSAdjustment',
+    1 => 'D-LightingHSColorBoost',
+);
+
+%Image::ExifTool::NikonCapture::DLightingHQ = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
+    CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
+    WRITABLE => 1,
+    FORMAT => 'int32u',
+    FIRST_ENTRY => 0,
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Image' },
+    0 => 'D-LightingHSShadow',
+    1 => 'D-LightingHSHighlight',
+    2 => 'D-LightingHSColorBoost',
+);
+
+%Image::ExifTool::NikonCapture::ColorBoost = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
+    CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
+    WRITABLE => 1,
+    FORMAT => 'int8u',
+    FIRST_ENTRY => 0,
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Image' },
+    0 => {
+        Name => 'ColorBoostType',
+        PrintConv => {
+            0 => 'Nature',
+            1 => 'People',
+        },
+    },
+    1 => {
+        Name => 'ColorBoostLevel',
+        Format => 'int32u',
+    },
+);
+
+%Image::ExifTool::NikonCapture::WBAdjData = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
+    CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
+    WRITABLE => 1,
+    FORMAT => 'int8u',
+    FIRST_ENTRY => 0,
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Image' },
+    0x00 => {
+        Name => 'WBAdjRedBalance',
+        Format => 'double',
+    },
+    0x08 => {
+        Name => 'WBAdjBlueBalance',
+        Format => 'double',
+    },
+    0x10 => {
+        Name => 'WBAdjMode',
+        PrintConv => {
+            1 => 'Use Gray Point',
+            2 => 'Recorded Value',
+            3 => 'Use Temperature',
+            4 => 'Calculate Automatically'
+        },
+    },
+    0x15 => {
+        Name => 'WBAdjLighting',
+        PrintConv => {
+            0 => 'None',
+            1 => 'Incandescent',
+            2 => 'Daylight',
+            3 => 'Standard Fluorescent',
+            4 => 'High Color Rendering Fluorescent',
+            5 => 'Flash',
+        },
+    },
+    0x18 => {
+        Name => 'WBAdjTemperature',
+        Format => 'int16u',
+    },
+);
+
+%Image::ExifTool::NikonCapture::PhotoEffects = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
+    CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
+    WRITABLE => 1,
+    FORMAT => 'int8u',
+    FIRST_ENTRY => 0,
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Image' },
+    0 => {
+        Name => 'PhotoEffectsType',
+        PrintConv => {
+            0 => 'None',
+            1 => 'B&W',
+            2 => 'Sepia',
+            3 => 'Tinted',
+        },
+    },
+    4 => {
+        Name => 'PhotoEffectsRed',
+        Format => 'int16s',
+    },
+    6 => {
+        Name => 'PhotoEffectsGreen',
+        Format => 'int16s',
+    },
+    8 => {
+        Name => 'PhotoEffectsBlue',
+        Format => 'int16s',
+    },
+);
+
+%Image::ExifTool::NikonCapture::Brightness = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
+    CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
+    WRITABLE => 1,
+    FORMAT => 'int8u',
+    FIRST_ENTRY => 0,
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Image' },
+    0 => {
+        Name => 'BrightnessAdj',
+        Format => 'double',
+        ValueConv => '$val * 50',
+        ValueConvInv => '$val / 50',
+    },
+    8 => {
+        Name => 'EnhanceDarkTones',
+        PrintConv => \%offOn,
+    },
+);
+
+%Image::ExifTool::NikonCapture::NoiseReduction = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
+    CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
+    WRITABLE => 1,
+    FORMAT => 'int8u',
+    FIRST_ENTRY => 0,
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Image' },
+    4 => {
+        Name => 'EdgeNoiseReduction',
+        PrintConv => \%offOn,
+    },
+    5 => {
+        Name => 'ColorMoireReduction',
+        PrintConv => {
+            0 => 'Off',
+            1 => 'Low',
+            2 => 'Medium',
+            3 => 'High',
+        },
+    },
+    9 => {
+        Name => 'NoiseReductionIntensity',
+        Format => 'int32u',
+    },
+    13 => {
+        Name => 'NoiseReductionSharpness',
+        Format => 'int32u',
+    },
+    17 => {
+        Name => 'NoiseReductionMethod',
+        Format => 'int16u',
+        PrintConv => {
+            0 => 'Faster',
+            1 => 'Better Quality',
+        },
+    },
+);
+
+%Image::ExifTool::NikonCapture::CropData = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
+    CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
+    WRITABLE => 1,
+    FORMAT => 'int8u',
+    FIRST_ENTRY => 0,
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Image' },
+    0x1e => {
+        Name => 'CropLeft',
+        Format => 'double',
+        ValueConv => '$val / 2',
+        ValueConvInv => '$val * 2',
+    },
+    0x26 => {
+        Name => 'CropTop',
+        Format => 'double',
+        ValueConv => '$val / 2',
+        ValueConvInv => '$val * 2',
+    },
+    0x2e => {
+        Name => 'CropRight',
+        Format => 'double',
+        ValueConv => '$val / 2',
+        ValueConvInv => '$val * 2',
+    },
+    0x36 => {
+        Name => 'CropBottom',
+        Format => 'double',
+        ValueConv => '$val / 2',
+        ValueConvInv => '$val * 2',
+    },
+    0x8e => {
+        Name => 'OutputWidthInches',
+        Format => 'double',
+    },
+    0x96 => {
+        Name => 'OutputHeightInches',
+        Format => 'double',
+    },
+    0x9e => {
+        Name => 'ScaledResolution',
+        Format => 'double',
+    },
+    0xae => {
+        Name => 'SourceResolution',
+        Format => 'double',
+        ValueConv => '$val / 2',
+        ValueConvInv => '$val * 2',
+    },
+    0xb6 => {
+        Name => 'OutputResolution',
+        Format => 'double',
+    },
+    0xbe => {
+        Name => 'CropOutputScale',
+        Format => 'double',
+    },
+    0xc6 => {
+        Name => 'OutputWidth',
+        Format => 'double',
+    },
+    0xce => {
+        Name => 'OutputHeight',
+        Format => 'double',
+    },
+    0xd6 => {
+        Name => 'OutputPixels',
+        Format => 'double',
+    },
+);
+
+%Image::ExifTool::NikonCapture::RedEyeData = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
+    CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
+    WRITABLE => 1,
+    FORMAT => 'int8u',
+    FIRST_ENTRY => 0,
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Image' },
+    0 => {
+        Name => 'RedEyeCorrection',
+        PrintConv => {
+            0 => 'Off',
+            1 => 'Automatic',
+            2 => 'Click on Eyes',
+        },
+    },
+);
+
+#------------------------------------------------------------------------------
+# write Nikon Capture data (ref 1)
+# Inputs: 0) ExifTool object reference, 1) reference to directory information
+#         2) pointer to tag table
+# Returns: 1 on success
+sub WriteNikonCapture($$$)
+{
+    my ($exifTool, $dirInfo, $tagTablePtr) = @_;
+    $exifTool or return 1;    # allow dummy access to autoload this package
+
+    # no need to edit this information unless necessary
+    unless ($exifTool->{EDIT_DIRS}->{MakerNotes} or $exifTool->{EDIT_DIRS}->{IPTC}) {
+        return undef;
+    }
+    my $dataPt = $$dirInfo{DataPt};
+    my $dirStart = $$dirInfo{DirStart};
+    my $dirLen = $$dirInfo{DirLen};
+    my $dirEnd = $dirStart + $dirLen;
+    if ($dirLen < 22) {
+        $exifTool->Warn('Short Nikon Capture Data',1);
+        return undef;
+    }
+    # make sure the capture data is properly contained
+    SetByteOrder('II');
+    my $tagID = Get32u($dataPt, $dirStart);
+    my $size = Get32u($dataPt, $dirStart + 18);
+    unless ($tagID == 0x7a86a940 and $size + 18 == $dirLen) {
+        $exifTool->Warn('Unrecognized Nikon Capture Data header');
+        return undef;
+    }
+    my $outBuff = '';
+    my $pos;
+    my $newTags = $exifTool->GetNewTagInfoHash($tagTablePtr);
+
+    # loop through all entries in the Nikon Capture data
+    for ($pos=$dirStart+22; $pos+22<$dirEnd; $pos+=22+$size) {
+        $tagID = Get32u($dataPt, $pos);
+        $size = Get32u($dataPt, $pos + 18) - 4;
+        last if $size < 0 or $pos + 22 + $size > $dirEnd;
+        my $tagInfo = $exifTool->GetTagInfo($tagTablePtr, $tagID);
+        if ($tagInfo) {
+            my $newVal;
+            if ($$tagInfo{SubDirectory}) {
+                # rewrite the subdirectory
+                my %subdirInfo = (
+                    DataPt => $dataPt,
+                    DirStart => $pos + 22,
+                    DirLen => $size,
+                );
+                my $subTable = GetTagTable($tagInfo->{SubDirectory}->{TagTable});
+                # ignore minor errors in IPTC since there is typically trailing garbage
+                my $oldSetting = $exifTool->Options('IgnoreMinorErrors');
+                $$tagInfo{Name} =~ /IPTC/ and $exifTool->Options(IgnoreMinorErrors => 1);
+                # rewrite the directory
+                $newVal = $exifTool->WriteDirectory(\%subdirInfo, $subTable);
+                # restore our original options
+                $exifTool->Options(IgnoreMinorErrors => $oldSetting);
+            } elsif ($$newTags{$tagID}) {
+                # get new value for this tag if we are writing it
+                my $format = $$tagInfo{Format} || $$tagInfo{Writable};
+                my $oldVal = ReadValue($dataPt,$pos+22,$format,1,$size);
+                my $newValueHash = $exifTool->GetNewValueHash($tagInfo);
+                if (Image::ExifTool::IsOverwriting($newValueHash, $oldVal)) {
+                    my $val = $exifTool->GetNewValues($tagInfo);
+                    $newVal = WriteValue($val, $$tagInfo{Writable}) if defined $val;
+                    if (defined $newVal and length $newVal) {
+                        ++$exifTool->{CHANGED};
+                    } else {
+                        undef $newVal;
+                        $exifTool->Warn("Can't delete $$tagInfo{Name}");
+                    }
+                }
+            }
+            if (defined $newVal) {
+                next unless length $newVal; # don't write zero length information
+                # write the new value
+                $outBuff .= substr($$dataPt, $pos, 18);
+                $outBuff .= Set32u(length($newVal) + 4);
+                $outBuff .= $newVal;
+                next;
+            }
+        }
+        # rewrite the existing information
+        $outBuff .= substr($$dataPt, $pos, 22 + $size);
+    }
+    unless ($pos == $dirEnd) {
+        $exifTool->Warn('Nikon Capture Data improperly terminated',1);
+        return undef;
+    }
+    # add the header and return the new directory
+    return substr($$dataPt, $dirStart, 18) .
+           Set32u(length($outBuff) + 4) .
+           $outBuff;
+}
+
+#------------------------------------------------------------------------------
+# process Nikon Capture data (ref 1)
+# Inputs: 0) ExifTool object reference, 1) reference to directory information
+#         2) pointer to tag table
+# Returns: 1 on success
+sub ProcessNikonCapture($$$)
+{
+    my ($exifTool, $dirInfo, $tagTablePtr) = @_;
+    my $dataPt = $$dirInfo{DataPt};
+    my $dirStart = $$dirInfo{DirStart};
+    my $dirLen = $$dirInfo{DirLen};
+    my $dirEnd = $dirStart + $dirLen;
+    my $verbose = $exifTool->Options('Verbose');
+    my $success = 0;
+    SetByteOrder('II');
+    $verbose and $exifTool->VerboseDir('NikonCapture', 0, $dirLen);
+    my $pos;
+    for ($pos=$dirStart+22; $pos+22<$dirEnd; ) {
+        my $tagID = Get32u($dataPt, $pos);
+        my $size = Get32u($dataPt, $pos + 18) - 4;
+        $pos += 22;
+        last if $size < 0 or $pos + $size > $dirEnd;
+        my $tagInfo = $exifTool->GetTagInfo($tagTablePtr, $tagID);
+        if ($tagInfo or $verbose) {
+            my ($format, $value);
+            # (note that Writable will be 0 for Unknown tags)
+            $tagInfo and $format = ($$tagInfo{Format} || $$tagInfo{Writable});
+            # generate a reasonable default format type for short values
+            if (not $format and ($size == 1 or $size == 2 or $size == 4)) {
+                $format = 'int' . ($size * 8) . 'u';
+            }
+            if ($format) {
+                $value = ReadValue($dataPt,$pos,$format,1,$size);
+            } elsif ($size == 1) {
+                $value = substr($$dataPt, $pos, $size);
+            }
+            $exifTool->HandleTag($tagTablePtr, $tagID, $value,
+                DataPt => $dataPt,
+                Start  => $pos,
+                Size   => $size,
+            ) and $success = 1;
+        }
+        $pos += $size;
+    }
+    return $success;
+}
+
+1;  # end
+
+=head1 NAME
+
+Image::ExifTool::NikonCapture - Read/write Nikon Capture information
+
+=head1 SYNOPSIS
+
+This module is loaded automatically by Image::ExifTool when required.
+
+=head1 DESCRIPTION
+
+This module contains routines to read and write Nikon Capture information in
+the maker notes of NEF images.
+
+=head1 AUTHOR
+
+Copyright 2003-2007, Phil Harvey (phil at owl.phy.queensu.ca)
+
+This library is free software; you can redistribute it and/or modify it
+under the same terms as Perl itself.
+
+=head1 REFERENCES
+
+=over 4
+
+=item L<http://www.cybercom.net/~dcoffin/dcraw/>
+
+=back
+
+=head1 SEE ALSO
+
+L<Image::ExifTool::TagNames/NikonCapture Tags>,
+L<Image::ExifTool::TagNames/Nikon Tags>,
+L<Image::ExifTool(3pm)|Image::ExifTool>
+
+=cut
Index: /gsdl/trunk/perllib/cpan/Image/ExifTool/Olympus.pm
===================================================================
--- /gsdl/trunk/perllib/cpan/Image/ExifTool/Olympus.pm	(revision 16842)
+++ /gsdl/trunk/perllib/cpan/Image/ExifTool/Olympus.pm	(revision 16842)
@@ -0,0 +1,2404 @@
+#------------------------------------------------------------------------------
+# File:         Olympus.pm
+#
+# Description:  Olympus/Epson EXIF maker notes tags
+#
+# Revisions:    12/09/2003 - P. Harvey Created
+#               11/11/2004 - P. Harvey Added Epson support
+#
+# References:   1) http://park2.wakwak.com/~tsuruzoh/Computer/Digicams/exif-e.html
+#               2) http://www.cybercom.net/~dcoffin/dcraw/
+#               3) http://www.ozhiker.com/electronics/pjmt/jpeg_info/olympus_mn.html
+#               4) Markku Hnninen private communication (tests with E-1)
+#               5) Rmi Guyomarch from http://forums.dpreview.com/forums/read.asp?forum=1022&message=12790396
+#               6) Frank Ledwon private communication (tests with E/C-series cameras)
+#               7) Michael Meissner private communication
+#               8) Shingo Noguchi, PhotoXP (http://www.daifukuya.com/photoxp/)
+#               9) Mark Dapoz private communication
+#              10) Lilo Huang private communication (E-330)
+#              11) http://olypedia.de/Olympus_Makernotes
+#              12) Ioannis Panagiotopoulos private communication (E-510)
+#------------------------------------------------------------------------------
+
+package Image::ExifTool::Olympus;
+
+use strict;
+use vars qw($VERSION);
+use Image::ExifTool::Exif;
+use Image::ExifTool::APP12;
+
+$VERSION = '1.40';
+
+my %offOn = ( 0 => 'Off', 1 => 'On' );
+
+%Image::ExifTool::Olympus::Main = (
+    WRITE_PROC => \&Image::ExifTool::Exif::WriteExif,
+    CHECK_PROC => \&Image::ExifTool::Exif::CheckExif,
+    WRITABLE => 1,
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
+#
+# Tags 0x0000 through 0x0103 are the same as Konica/Minolta cameras (ref 3)
+#
+    0x0000 => {
+        Name => 'MakerNoteVersion',
+        Writable => 'undef',
+    },
+    0x0001 => {
+        Name => 'MinoltaCameraSettingsOld',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::Minolta::CameraSettings',
+            ByteOrder => 'BigEndian',
+        },
+    },
+    0x0003 => {
+        Name => 'MinoltaCameraSettings',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::Minolta::CameraSettings',
+            ByteOrder => 'BigEndian',
+        },
+    },
+    0x0040 => {
+        Name => 'CompressedImageSize',
+        Writable => 'int32u',
+    },
+    0x0081 => {
+        Name => 'PreviewImageData',
+        Binary => 1,
+        Writable => 0,
+    },
+    0x0088 => {
+        Name => 'PreviewImageStart',
+        Flags => 'IsOffset',
+        OffsetPair => 0x0089, # point to associated byte count
+        DataTag => 'PreviewImage',
+        Writable => 0,
+        Protected => 2,
+    },
+    0x0089 => {
+        Name => 'PreviewImageLength',
+        OffsetPair => 0x0088, # point to associated offset
+        DataTag => 'PreviewImage',
+        Writable => 0,
+        Protected => 2,
+    },
+    0x0100 => {
+        Name => 'ThumbnailImage',
+        Writable => 'undef',
+        WriteCheck => '$self->CheckImage(\$val)',
+        Binary => 1,
+    },
+    0x0101 => {
+        Name => 'ColorMode',
+        Writable => 'int16u',
+        PrintConv => {
+            0 => 'Natural color',
+            1 => 'Black&white',
+            2 => 'Vivid color',
+            3 => 'Solarization',
+            4 => 'Adobe RGB',
+        },
+    },
+    0x0102 => {
+        Name => 'MinoltaQuality',
+        Writable => 'int16u',
+        PrintConv => {
+            0 => 'Raw',
+            1 => 'Superfine',
+            2 => 'Fine',
+            3 => 'Normal',
+            4 => 'Economy',
+            5 => 'Extra fine',
+        },
+    },
+    # (0x0103 is the same as 0x0102 above)
+    0x0103 => {
+        Name => 'MinoltaQuality',
+        Writable => 'int16u',
+        PrintConv => {
+            0 => 'Raw',
+            1 => 'Superfine',
+            2 => 'Fine',
+            3 => 'Normal',
+            4 => 'Economy',
+            5 => 'Extra fine',
+        },
+    },
+    0x0104 => { #11
+        Name => 'BodyFirmwareVersion',
+        Writable => 'string',
+    },
+#
+# end Konica/Minolta tags
+#
+    0x0200 => {
+        Name => 'SpecialMode',
+        Notes => q{
+            3 numbers: 1. Shooting mode: 0=Normal, 2=Fast, 3=Panorama;
+            2. Sequence Number; 3. Panorama Direction: 1=Left-Right,
+            2=Right-Left, 3=Bottom-Top, 4=Top-Bottom
+        },
+        Writable => 'int32u',
+        Count => 3,
+        PrintConv => sub { #3
+            my $val = shift;
+            my @v = split ' ', $val;
+            return $val unless @v >= 3;
+            my @v0 = ('Normal','Unknown (1)','Fast','Panorama');
+            my @v2 = ('(none)','Left to Right','Right to Left','Bottom to Top','Top to Bottom');
+            $val = $v0[$v[0]] || "Unknown ($v[0])";
+            $val .= ", Sequence: $v[1]";
+            $val .= ', Panorama: ' . ($v2[$v[2]] || "Unknown ($v[2])");
+            return $val;
+        },
+    },
+    0x0201 => {
+        Name => 'Quality',
+        Writable => 'int16u',
+        Notes => q{
+            Quality values are decoded based on the CameraType tag. All types
+            represent SQ, HQ and SHQ as sequential integers, but in general
+            SX-type cameras start with a value of 0 for SQ while others start
+            with 1
+        },
+        # These values are different for different camera types
+        # (can't have Condition based on CameraType because it isn't known
+        #  when this tag is extracted)
+        PrintConv => sub {
+            my ($val, $self) = @_;
+            my %t1 = ( # all SX camera types except SX151
+                0 => 'SQ (Low)',
+                1 => 'HQ (Normal)',
+                2 => 'SHQ (Fine)',
+                6 => 'RAW', #PH - C5050WZ
+            );
+            my %t2 = ( # all other types
+                1 => 'SQ (Low)',
+                2 => 'HQ (Normal)',
+                3 => 'SHQ (Fine)',
+                4 => 'RAW',
+                5 => 'Medium-Fine', #PH
+                6 => 'Small-Fine', #PH
+                33 => 'Uncompressed', #PH - C2100Z
+            );
+            my $conv = $self->{CameraType} =~ /^SX(?!151\b)/ ? \%t1 : \%t2;
+            return $$conv{$val} ? $$conv{$val} : "Unknown ($val)";
+        },
+        # (no PrintConvInv because we don't know CameraType at write time)
+    },
+    0x0202 => {
+        Name => 'Macro',
+        Writable => 'int16u',
+        PrintConv => {
+            0 => 'Off',
+            1 => 'On',
+            2 => 'Super Macro', #6
+        },
+    },
+    0x0203 => { #6
+        Name => 'BWMode',
+        Description => 'Black & White Mode',
+        Writable => 'int16u',
+        PrintConv => \%offOn,
+    },
+    0x0204 => {
+        Name => 'DigitalZoom',
+        Writable => 'rational64u',
+        PrintConv => '$val=~/\./ or $val.=".0"; $val',
+        PrintConvInv => '$val',
+    },
+    0x0205 => { #6
+        Name => 'FocalPlaneDiagonal',
+        Writable => 'rational64u',
+        PrintConv => '"$val mm"',
+        PrintConvInv => '$val=~s/\s*mm$//;$val',
+    },
+    0x0206 => { #6
+        Name => 'LensDistortionParams',
+        Writable => 'int16s',
+        Count => 6,
+    },
+    0x0207 => { #PH (was incorrectly FirmwareVersion, ref 1,3)
+        Name => 'CameraType',
+        Writable => 'string',
+        DataMember => 'CameraType',
+        RawConv => '$self->{CameraType} = $val',
+    },
+    0x0208 => {
+        Name => 'TextInfo',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::Olympus::TextInfo',
+        },
+    },
+    0x0209 => {
+        Name => 'CameraID',
+        Format => 'string', # this really should have been a string
+    },
+    0x020b => { #PH
+        Name => 'EpsonImageWidth',
+        Writable => 'int16u',
+    },
+    0x020c => { #PH
+        Name => 'EpsonImageHeight',
+        Writable => 'int16u',
+    },
+    0x020d => { #PH
+        Name => 'EpsonSoftware',
+        Writable => 'string',
+    },
+    0x0280 => { #PH
+        %Image::ExifTool::previewImageTagInfo,
+        Notes => 'found in ERF and JPG images from some Epson models',
+        Format => 'undef',
+        Writable => 'int8u',
+    },
+    0x0300 => { #6
+        Name => 'PreCaptureFrames',
+        Writable => 'int16u',
+    },
+    0x0301 => { #11
+        Name => 'WhiteBoard',
+        Writable => 'int16u',
+    },
+    0x0302 => { #6
+        Name => 'OneTouchWB',
+        Writable => 'int16u',
+        PrintConv => {
+            0 => 'Off',
+            1 => 'On',
+            2 => 'On (Preset)',
+        },
+    },
+    0x0303 => { #11
+        Name => 'WhiteBalanceBracket',
+        Writable => 'int16u',
+    },
+    0x0304 => { #11
+        Name => 'WhiteBalanceBias',
+        Writable => 'int16u',
+    },
+   # 0x0305 => 'PrintMaching', ? #11
+    0x0403 => { #11
+        Name => 'SceneMode',
+        Writable => 'int16u',
+        PrintConv => {
+            0 => 'Normal',
+            1 => 'Standard',
+            2 => 'Auto',
+            4 => 'Portrait',
+            5 => 'Landscape+Portrait',
+            6 => 'Landscape',
+            7 => 'Night Scene',
+            8 => 'Night+Portrait',
+            9 => 'Sport',
+            10 => 'Self Portrait',
+            11 => 'Indoor',
+            12 => 'Beach&Snow',
+            13 => 'Beach',
+            14 => 'Snow',
+            15 => 'Self Portrait+Self Timer',
+            16 => 'Sunset',
+            17 => 'Cuisine',
+            18 => 'Documents',
+            19 => 'Candle',
+            20 => 'Fireworks',
+            21 => 'Available Light',
+            22 => 'Vivid',
+            23 => 'Underwater Wide1',
+            24 => 'Underwater Macro',
+            25 => 'Museum',
+            26 => 'Behind Glass',
+            27 => 'Auction',
+            28 => 'Shoot&Select1',
+            29 => 'Shoot&Select2',
+            30 => 'Underwater Wide2',
+            31 => 'Digital Image Stabilization',
+            32 => 'Face Portrait',
+            33 => 'Pet',
+        },
+    },
+    0x0404 => { #PH (D595Z, C7070WZ)
+        Name => 'SerialNumber',
+        Writable => 'string',
+    },
+    0x0405 => { #11
+        Name => 'Firmware',
+        Writable => 'string',
+    },
+    0x0e00 => {
+        Name => 'PrintIM',
+        Description => 'Print Image Matching',
+        Writable => 0,
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::PrintIM::Main',
+        },
+    },
+    0x0f00 => {
+        Name => 'DataDump',
+        Writable => 0,
+        Binary => 1,
+    },
+    0x0f01 => { #6
+        Name => 'DataDump2',
+        Writable => 0,
+        Binary => 1,
+    },
+    0x1000 => { #6
+        Name => 'ShutterSpeedValue',
+        Writable => 'rational64s',
+        Priority => 0,
+        ValueConv => 'abs($val)<100 ? 1/(2**$val) : 0',
+        ValueConvInv => '$val>0 ? -log($val)/log(2) : -100',
+        PrintConv => 'Image::ExifTool::Exif::PrintExposureTime($val)',
+        PrintConvInv => 'eval $val',
+    },
+    0x1001 => { #6
+        Name => 'ISOValue',
+        Writable => 'rational64s',
+        Priority => 0,
+        ValueConv => '100 * 2 ** ($val - 5)',
+        ValueConvInv => '$val>0 ? log($val/100)/log(2)+5 : 0',
+        PrintConv => 'int($val * 100 + 0.5) / 100',
+        PrintConvInv => '$val',
+    },
+    0x1002 => { #6
+        Name => 'ApertureValue',
+        Writable => 'rational64s',
+        Priority => 0,
+        ValueConv => '2 ** ($val / 2)',
+        ValueConvInv => '$val>0 ? 2*log($val)/log(2) : 0',
+        PrintConv => 'sprintf("%.1f",$val)',
+        PrintConvInv => '$val',
+    },
+    0x1003 => { #6
+        Name => 'BrightnessValue',
+        Writable => 'rational64s',
+        Priority => 0,
+    },
+    0x1004 => { #3
+        Name => 'FlashMode',
+        Writable => 'int16u',
+        PrintConv => {
+            2 => 'On', #PH
+            3 => 'Off', #PH
+        },
+    },
+    0x1005 => { #6
+        Name => 'FlashDevice',
+        Writable => 'int16u',
+        PrintConv => {
+            0 => 'None',
+            1 => 'Internal',
+            4 => 'External',
+            5 => 'Internal + External',
+        },
+    },
+    0x1006 => { #6
+        Name =>'ExposureCompensation',
+        Writable => 'rational64s',
+    },
+    0x1007 => { #6
+        Name => 'SensorTemperature',
+        Writable => 'int16s',
+    },
+    0x1008 => { #6
+        Name => 'LensTemperature',
+        Writable => 'int16s',
+    },
+    0x1009 => { #11
+        Name => 'LightCondition',
+        Writable => 'int16u',
+    },
+    0x100a => { #11
+        Name => 'FocusRange',
+        Writable => 'int16u',
+        PrintConv => {
+            0 => 'Normal',
+            1 => 'Macro',
+        },
+    },
+    0x100b => { #6
+        Name => 'FocusMode',
+        Writable => 'int16u',
+        PrintConv => {
+            0 => 'Auto',
+            1 => 'Manual',
+        },
+    },
+    0x100c => { #6
+        Name => 'ManualFocusDistance',
+        Writable => 'rational64u',
+        PrintConv => '"$val mm"', #11
+        PrintConvInv => '$val=~s/\s*mm$//; $val',
+    },
+    0x100d => { #6
+        Name => 'ZoomStepCount',
+        Writable => 'int16u',
+    },
+    0x100e => { #6
+        Name => 'FocusStepCount',
+        Writable => 'int16u',
+    },
+    0x100f => { #6
+        Name => 'Sharpness',
+        Writable => 'int16u',
+        Priority => 0,
+        PrintConv => {
+            0 => 'Normal',
+            1 => 'Hard',
+            2 => 'Soft',
+        },
+    },
+    0x1010 => { #6
+        Name => 'FlashChargeLevel',
+        Writable => 'int16u',
+    },
+    0x1011 => { #3
+        Name => 'ColorMatrix',
+        Writable => 'int16u',
+        Format => 'int16s',
+        Count => 9,
+    },
+    0x1012 => { #3
+        Name => 'BlackLevel',
+        Writable => 'int16u',
+        Count => 4,
+    },
+    0x1013 => { #11
+        Name => 'ColorTemperatureBG',
+        Writable => 'int16u',
+        Unknown => 1, # (doesn't look like a temperature)
+    },
+    0x1014 => { #11
+        Name => 'ColorTemperatureRG',
+        Writable => 'int16u',
+        Unknown => 1, # (doesn't look like a temperature)
+    },
+    0x1015 => { #6
+        Name => 'WBMode',
+        Writable => 'int16u',
+        Count => 2,
+        PrintConv => {
+            '1'   => 'Auto',
+            '1 0' => 'Auto',
+            '1 2' => 'Auto (2)',
+            '1 4' => 'Auto (4)',
+            '2 2' => '3000 Kelvin',
+            '2 3' => '3700 Kelvin',
+            '2 4' => '4000 Kelvin',
+            '2 5' => '4500 Kelvin',
+            '2 6' => '5500 Kelvin',
+            '2 7' => '6500 Kelvin',
+            '2 8' => '7500 Kelvin',
+            '3 0' => 'One-touch',
+        },
+    },
+    0x1017 => { #2
+        Name => 'RedBalance',
+        Writable => 'int16u',
+        Count => 2,
+        ValueConv => '$val=~s/ .*//; $val / 256',
+        ValueConvInv => '$val*=256;"$val 64"',
+    },
+    0x1018 => { #2
+        Name => 'BlueBalance',
+        Writable => 'int16u',
+        Count => 2,
+        ValueConv => '$val=~s/ .*//; $val / 256',
+        ValueConvInv => '$val*=256;"$val 64"',
+    },
+    0x1019 => { #11
+        Name => 'ColorMatrixNumber',
+        Writable => 'int16u',
+    },
+    # 0x101a is same as CameraID ("OLYMPUS DIGITAL CAMERA") for C2500L - PH
+    0x101a => { #3
+        Name => 'SerialNumber',
+        Writable => 'string',
+    },
+    0x101b => { #11
+        Name => 'ExternalFlashAE1_0',
+        Writable => 'int32u',
+        Unknown => 1, # (what are these?)
+    },
+    0x101c => { #11
+        Name => 'ExternalFlashAE2_0',
+        Writable => 'int32u',
+        Unknown => 1,
+    },
+    0x101d => { #11
+        Name => 'InternalFlashAE1_0',
+        Writable => 'int32u',
+        Unknown => 1,
+    },
+    0x101e => { #11
+        Name => 'InternalFlashAE2_0',
+        Writable => 'int32u',
+        Unknown => 1,
+    },
+    0x101f => { #11
+        Name => 'ExternalFlashAE1',
+        Writable => 'int32u',
+        Unknown => 1,
+    },
+    0x1020 => { #11
+        Name => 'ExternalFlashAE2',
+        Writable => 'int32u',
+        Unknown => 1,
+    },
+    0x1021 => { #11
+        Name => 'InternalFlashAE1',
+        Writable => 'int32u',
+        Unknown => 1,
+    },
+    0x1022 => { #11
+        Name => 'InternalFlashAE2',
+        Writable => 'int32u',
+        Unknown => 1,
+    },
+    0x1023 => { #6
+        Name => 'FlashExposureComp',
+        Writable => 'rational64s',
+    },
+    0x1024 => { #11
+        Name => 'InternalFlashTable',
+        Writable => 'int16u',
+    },
+    0x1025 => { #11
+        Name => 'ExternalFlashGValue',
+        Writable => 'rational64s',
+    },
+    0x1026 => { #6
+        Name => 'ExternalFlashBounce',
+        Writable => 'int16u',
+        PrintConv => {
+            0 => 'No',
+            1 => 'Yes',
+        },
+    },
+    0x1027 => { #6
+        Name => 'ExternalFlashZoom',
+        Writable => 'int16u',
+    },
+    0x1028 => { #6
+        Name => 'ExternalFlashMode',
+        Writable => 'int16u',
+    },
+    0x1029 => { #3
+        Name => 'Contrast',
+        Writable => 'int16u',
+        PrintConv => { #PH (works with E1)
+            0 => 'High',
+            1 => 'Normal',
+            2 => 'Low',
+        },
+    },
+    0x102a => { #3
+        Name => 'SharpnessFactor',
+        Writable => 'int16u',
+    },
+    0x102b => { #3
+        Name => 'ColorControl',
+        Writable => 'int16u',
+        Count => 6,
+    },
+    0x102c => { #3
+        Name => 'ValidBits',
+        Writable => 'int16u',
+        Count => 2,
+    },
+    0x102d => { #3
+        Name => 'CoringFilter',
+        Writable => 'int16u',
+    },
+    0x102e => { #PH
+        Name => 'OlympusImageWidth',
+        Writable => 'int32u',
+    },
+    0x102f => { #PH
+        Name => 'OlympusImageHeight',
+        Writable => 'int32u',
+    },
+    0x1030 => { #11
+        Name => 'SceneDetect',
+        Writable => 'int16u',
+    },
+    0x1031 => { #11
+        Name => 'SceneArea',
+        Writable => 'int32u',
+        Count => 8,
+        Unknown => 1, # (numbers don't make much sense?)
+    },
+    # 0x1032 HAFFINAL? #11
+    0x1033 => { #11
+        Name => 'SceneDetectArea',
+        Writable => 'int32u',
+        Count => 720,
+        Binary => 1,
+        Unknown => 1, # (but what does it mean?)
+    },
+    0x1034 => { #3
+        Name => 'CompressionRatio',
+        Writable => 'rational64u',
+    },
+    0x1035 => { #6
+        Name => 'PreviewImageValid',
+        Writable => 'int32u',
+        PrintConv => { 0 => 'No', 1 => 'Yes' },
+    },
+    0x1036 => { #6
+        Name => 'PreviewImageStart',
+        Flags => 'IsOffset',
+        OffsetPair => 0x1037, # point to associated byte count
+        DataTag => 'PreviewImage',
+        Writable => 'int32u',
+        Protected => 2,
+    },
+    0x1037 => { #6
+        Name => 'PreviewImageLength',
+        OffsetPair => 0x1036, # point to associated offset
+        DataTag => 'PreviewImage',
+        Writable => 'int32u',
+        Protected => 2,
+    },
+    0x1038 => { #11
+        Name => 'AFResult',
+        Writable => 'int16u',
+    },
+    0x1039 => { #6
+        Name => 'CCDScanMode',
+        Writable => 'int16u',
+        PrintConv => {
+            0 => 'Interlaced',
+            1 => 'Progressive',
+        },
+    },
+    0x103a => { #6
+        Name => 'NoiseReduction',
+        Writable => 'int16u',
+        PrintConv => {
+            0 => 'Off',
+            1 => 'On',
+        },
+    },
+    0x103b => { #6
+        Name => 'InfinityLensStep',
+        Writable => 'int16u',
+    },
+    0x103c => { #6
+        Name => 'NearLensStep',
+        Writable => 'int16u',
+    },
+    0x103d => { #11
+        Name => 'LightValueCenter',
+        Writable => 'rational64s',
+    },
+    0x103e => { #11
+        Name => 'LightValuePeriphery',
+        Writable => 'rational64s',
+    },
+    0x103f => { #11
+        Name => 'FieldCount',
+        Writable => 'int16u',
+        Unknown => 1, # (but what does it mean?)
+    },
+#
+# Olympus really screwed up the format of the following subdirectories (for the
+# E-1 and E-300 anyway). Not only is the subdirectory value data not included in
+# the size, but also the count is 2 bytes short for the subdirectory itself
+# (presumably the Olympus programmers forgot about the 2-byte entry count at the
+# start of the subdirectory).  This mess is straightened out and these subdirs
+# are written properly when ExifTool rewrites the file.  (This problem has been
+# fixed in the new-style MakerNoteOlympus2 maker notes since a standard SubIFD
+# offset value is used.) - PH
+#
+    0x2010 => [ #PH
+        {
+            Name => 'Equipment',
+            Condition => 'not $$self{OlympusType2}',
+            SubDirectory => {
+                TagTable => 'Image::ExifTool::Olympus::Equipment',
+                ByteOrder => 'Unknown',
+            },
+        },
+        {
+            Name => 'Equipment2',
+            Groups => { 1 => 'MakerNotes' },    # SubIFD needs group 1 set
+            Flags => 'SubIFD',
+            FixFormat => 'ifd',
+            SubDirectory => {
+                TagTable => 'Image::ExifTool::Olympus::Equipment',
+                Start => '$val',
+            },
+        },
+    ],
+    0x2020 => [ #PH
+        {
+            Name => 'CameraSettings',
+            Condition => 'not $$self{OlympusType2}',
+            SubDirectory => {
+                TagTable => 'Image::ExifTool::Olympus::CameraSettings',
+                ByteOrder => 'Unknown',
+            },
+        },
+        {
+            Name => 'CameraSettings2',
+            Groups => { 1 => 'MakerNotes' },
+            Flags => 'SubIFD',
+            FixFormat => 'ifd',
+            SubDirectory => {
+                TagTable => 'Image::ExifTool::Olympus::CameraSettings',
+                Start => '$val',
+            },
+        },
+    ],
+    0x2030 => [ #PH
+        {
+            Name => 'RawDevelopment',
+            Condition => 'not $$self{OlympusType2}',
+            SubDirectory => {
+                TagTable => 'Image::ExifTool::Olympus::RawDevelopment',
+                ByteOrder => 'Unknown',
+            },
+        },
+        {
+            Name => 'RawDevelopment2',
+            Groups => { 1 => 'MakerNotes' },
+            Flags => 'SubIFD',
+            FixFormat => 'ifd',
+            SubDirectory => {
+                TagTable => 'Image::ExifTool::Olympus::RawDevelopment',
+                Start => '$val',
+            },
+        },
+    ],
+    0x2031 => [ #11
+        {
+            Name => 'RawDev2',
+            Condition => 'not $$self{OlympusType2}',
+            SubDirectory => {
+                TagTable => 'Image::ExifTool::Olympus::RawDevelopment2',
+                ByteOrder => 'Unknown',
+            },
+        },
+        {
+            Name => 'RawDev2_2',
+            Groups => { 1 => 'MakerNotes' },
+            Flags => 'SubIFD',
+            FixFormat => 'ifd',
+            SubDirectory => {
+                TagTable => 'Image::ExifTool::Olympus::RawDevelopment2',
+                Start => '$val',
+            },
+        },
+    ],
+    0x2040 => [ #PH
+        {
+            Name => 'ImageProcessing',
+            Condition => 'not $$self{OlympusType2}',
+            SubDirectory => {
+                TagTable => 'Image::ExifTool::Olympus::ImageProcessing',
+                ByteOrder => 'Unknown',
+            },
+        },
+        {
+            Name => 'ImageProcessing2',
+            Groups => { 1 => 'MakerNotes' },
+            Flags => 'SubIFD',
+            FixFormat => 'ifd',
+            SubDirectory => {
+                TagTable => 'Image::ExifTool::Olympus::ImageProcessing',
+                Start => '$val',
+            },
+        },
+    ],
+    0x2050 => [ #PH
+        {
+            Name => 'FocusInfo',
+            Condition => 'not ($$self{OlympusType2} or $$self{OlympusCAMER})',
+            SubDirectory => {
+                TagTable => 'Image::ExifTool::Olympus::FocusInfo',
+                ByteOrder => 'Unknown',
+            },
+        },
+        {
+            Name => 'FocusInfo2',
+            Condition => 'not $$self{OlympusCAMER}',
+            Groups => { 1 => 'MakerNotes' },
+            Flags => 'SubIFD',
+            FixFormat => 'ifd',
+            SubDirectory => {
+                TagTable => 'Image::ExifTool::Olympus::FocusInfo',
+                Start => '$val',
+            },
+        },
+        {
+            # ASCII-based camera parameters if makernotes starts with "CAMER\0"
+            Name => 'CameraParameters',
+            Writable => 'undef',
+            Binary => 1,
+        },
+    ],
+    0x2100 => { #11
+        Name => 'Olympus2100',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::Olympus::FETags',
+            ByteOrder => 'Unknown',
+        },
+    },
+    0x2200 => { #11
+        Name => 'Olympus2200',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::Olympus::FETags',
+            ByteOrder => 'Unknown',
+        },
+    },
+    0x2300 => { #11
+        Name => 'Olympus2300',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::Olympus::FETags',
+            ByteOrder => 'Unknown',
+        },
+    },
+    0x2400 => { #11
+        Name => 'Olympus2400',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::Olympus::FETags',
+            ByteOrder => 'Unknown',
+        },
+    },
+    0x2500 => { #11
+        Name => 'Olympus2500',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::Olympus::FETags',
+            ByteOrder => 'Unknown',
+        },
+    },
+    0x2600 => { #11
+        Name => 'Olympus2600',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::Olympus::FETags',
+            ByteOrder => 'Unknown',
+        },
+    },
+    0x2700 => { #11
+        Name => 'Olympus2700',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::Olympus::FETags',
+            ByteOrder => 'Unknown',
+        },
+    },
+    0x2800 => { #11
+        Name => 'Olympus2800',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::Olympus::FETags',
+            ByteOrder => 'Unknown',
+        },
+    },
+    0x2900 => { #11
+        Name => 'Olympus2900',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::Olympus::FETags',
+            ByteOrder => 'Unknown',
+        },
+    },
+    0x3000 => [
+        { #6
+            Name => 'RawInfo',
+            Condition => 'not $$self{OlympusType2}',
+            SubDirectory => {
+                TagTable => 'Image::ExifTool::Olympus::RawInfo',
+                ByteOrder => 'Unknown',
+            },
+        },
+        { #PH
+            Name => 'RawInfo2',
+            Groups => { 1 => 'MakerNotes' },
+            Flags => 'SubIFD',
+            FixFormat => 'ifd',
+            SubDirectory => {
+                TagTable => 'Image::ExifTool::Olympus::RawInfo',
+                Start => '$val',
+            },
+        },
+    ],
+);
+
+# TextInfo tags
+%Image::ExifTool::Olympus::TextInfo = (
+    PROCESS_PROC => \&Image::ExifTool::APP12::ProcessAPP12,
+    NOTES => q{
+        This information is in text format (similar to APP12 information, but with
+        spaces instead of linefeeds).  Below are tags which have been observed, but
+        any information found here will be extracted, even if the tag is not listed.
+    },
+    GROUPS => { 0 => 'MakerNotes', 1 => 'Olympus', 2 => 'Image' },
+    Resolution => { },
+    Type => {
+        Name => 'CameraType',
+        Groups => { 2 => 'Camera' },
+        DataMember => 'CameraType',
+        RawConv => '$self->{CameraType} = $val',
+    },
+);
+
+# Olympus Equipment IFD
+%Image::ExifTool::Olympus::Equipment = (
+    WRITE_PROC => \&Image::ExifTool::Exif::WriteExif,
+    CHECK_PROC => \&Image::ExifTool::Exif::CheckExif,
+    WRITABLE => 1,
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
+    0x000 => { #PH
+        Name => 'EquipmentVersion',
+        Writable => 'undef',
+        Count => 4,
+    },
+    0x100 => { #6
+        Name => 'CameraType2',
+        Writable => 'string',
+        Count => 6,
+    },
+    0x101 => { #PH
+        Name => 'SerialNumber',
+        Writable => 'string',
+        Count => 32,
+        PrintConv => '$val=~s/\s+$//;$val',
+        PrintConvInv => 'pack("A31",$val)', # pad with spaces to 31 chars
+    },
+    0x102 => { #6
+        Name => 'InternalSerialNumber',
+        Notes => '16 digits: 0-3=model, 4=year, 5-6=month, 8-12=unit number',
+        Writable => 'string',
+        Count => 32,
+    },
+    0x103 => { #6
+        Name => 'FocalPlaneDiagonal',
+        Writable => 'rational64u',
+        PrintConv => '"$val mm"',
+        PrintConvInv => '$val=~s/\s*mm$//;$val',
+    },
+    0x104 => { #6
+        Name => 'BodyFirmwareVersion',
+        Writable => 'int32u',
+        PrintConv => '$val=sprintf("%x",$val);$val=~s/(.{3})$/\.$1/;$val',
+        PrintConvInv => '$val=sprintf("%.3f",$val);$val=~s/\.//;hex($val)',
+    },
+    0x201 => { #6
+        Name => 'LensType',
+        Writable => 'int8u',
+        Count => 6,
+        Notes => '6 numbers: 1. Make, 2. Unknown, 3. Model, 4. Release, 5-6. Unknown',
+        PrintConv => 'Image::ExifTool::Olympus::PrintLensInfo($val,"Lens")',
+    },
+    # apparently the first 3 digits of the lens s/n give the type (ref 4):
+    # 010 = 50macro
+    # 040 = EC-14
+    # 050 = 14-54
+    # 060 = 50-200
+    # 080 = EX-25
+    # 101 = FL-50
+    0x202 => { #PH
+        Name => 'LensSerialNumber',
+        Writable => 'string',
+        Count => 32,
+        PrintConv => '$val=~s/\s+$//;$val',
+        PrintConvInv => 'pack("A31",$val)', # pad with spaces to 31 chars
+    },
+    0x204 => { #6
+        Name => 'LensFirmwareVersion',
+        Writable => 'int32u',
+        PrintConv => '$val=sprintf("%x",$val);$val=~s/(.{3})$/\.$1/;$val',
+        PrintConvInv => '$val=sprintf("%.3f",$val);$val=~s/\.//;hex($val)',
+    },
+    0x205 => { #11
+        Name => 'MaxApertureAtMinFocal',
+        Writable => 'int16u',
+        ValueConv => '$val ? sqrt(2)**($val/256) : 0',
+        ValueConvInv => '$val>0 ? int(512*log($val)/log(2)+0.5) : 0',
+        PrintConv => 'sprintf("%.1f",$val)',
+        PrintConvInv => '$val',
+    },
+    0x206 => { #5
+        Name => 'MaxApertureAtMaxFocal',
+        Writable => 'int16u',
+        ValueConv => '$val ? sqrt(2)**($val/256) : 0',
+        ValueConvInv => '$val>0 ? int(512*log($val)/log(2)+0.5) : 0',
+        PrintConv => 'sprintf("%.1f",$val)',
+        PrintConvInv => '$val',
+    },
+    0x207 => { #PH
+        Name => 'MinFocalLength',
+        Writable => 'int16u',
+    },
+    0x208 => { #PH
+        Name => 'MaxFocalLength',
+        Writable => 'int16u',
+    },
+    0x20a => { #9
+        Name => 'MaxApertureAtCurrentFocal',
+        Writable => 'int16u',
+        ValueConv => '$val ? sqrt(2)**($val/256) : 0',
+        ValueConvInv => '$val>0 ? int(512*log($val)/log(2)+0.5) : 0',
+        PrintConv => 'sprintf("%.1f",$val)',
+        PrintConvInv => '$val',
+    },
+    0x20b => { #11
+        Name => 'LensProperties',
+        Writable => 'int16u',
+        PrintConv => 'sprintf("0x%x",$val)',
+        PrintConvInv => '$val',
+    },
+    0x301 => { #6
+        Name => 'Extender',
+        Writable => 'int8u',
+        Count => 6,
+        Notes => '6 numbers: 1. Make, 2. Unknown, 3. Model, 4. Release, 5-6. Unknown.',
+        PrintConv => 'Image::ExifTool::Olympus::PrintLensInfo($val,"Extender")',
+    },
+    0x302 => { #4
+        Name => 'ExtenderSerialNumber',
+        Writable => 'string',
+        Count => 32,
+    },
+    0x303 => { #9
+        Name => 'ExtenderModel',
+        Writable => 'string',
+    },
+    0x304 => { #6
+        Name => 'ExtenderFirmwareVersion',
+        Writable => 'int32u',
+        PrintConv => '$val=sprintf("%x",$val);$val=~s/(.{3})$/\.$1/;$val',
+        PrintConvInv => '$val=sprintf("%.3f",$val);$val=~s/\.//;hex($val)',
+    },
+    0x1000 => { #6
+        Name => 'FlashType',
+        Writable => 'int16u',
+        PrintConv => {
+            0 => 'None',
+            2 => 'Simple E-System',
+            3 => 'E-System',
+        },
+    },
+    0x1001 => { #6
+        Name => 'FlashModel',
+        Writable => 'int16u',
+        PrintConv => {
+            0 => 'None',
+            1 => 'FL-20',
+            2 => 'FL-50',
+            3 => 'RF-11',
+            4 => 'TF-22',
+            5 => 'FL-36',
+        },
+    },
+    0x1002 => { #6
+        Name => 'FlashFirmwareVersion',
+        Writable => 'int32u',
+        PrintConv => '$val=sprintf("%x",$val);$val=~s/(.{3})$/\.$1/;$val',
+        PrintConvInv => '$val=sprintf("%.3f",$val);$val=~s/\.//;hex($val)',
+    },
+    0x1003 => { #4
+        Name => 'FlashSerialNumber',
+        Writable => 'string',
+        Count => 32,
+    },
+);
+
+# Olympus camera settings IFD
+%Image::ExifTool::Olympus::CameraSettings = (
+    WRITE_PROC => \&Image::ExifTool::Exif::WriteExif,
+    CHECK_PROC => \&Image::ExifTool::Exif::CheckExif,
+    WRITABLE => 1,
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
+    0x000 => { #PH
+        Name => 'CameraSettingsVersion',
+        Writable => 'undef',
+        Count => 4,
+    },
+    0x100 => { #6
+        Name => 'PreviewImageValid',
+        Writable => 'int32u',
+        PrintConv => { 0 => 'No', 1 => 'Yes' },
+    },
+    0x101 => { #PH
+        Name => 'PreviewImageStart',
+        Flags => 'IsOffset',
+        OffsetPair => 0x102,
+        DataTag => 'PreviewImage',
+        Writable => 'int32u',
+        Protected => 2,
+    },
+    0x102 => { #PH
+        Name => 'PreviewImageLength',
+        OffsetPair => 0x101,
+        DataTag => 'PreviewImage',
+        Writable => 'int32u',
+        Protected => 2,
+    },
+    0x200 => { #4
+        Name => 'ExposureMode',
+        Writable => 'int16u',
+        PrintConv => {
+            1 => 'Manual',
+            2 => 'Program', #6
+            3 => 'Aperture-priority AE',
+            4 => 'Shutter speed priority AE',
+            5 => 'Program-shift', #6
+        }
+    },
+    0x201 => { #6
+        Name => 'AELock',
+        Writable => 'int16u',
+        PrintConv => {
+            0 => 'Off',
+            1 => 'On',
+        },
+    },
+    0x202 => { #PH/4
+        Name => 'MeteringMode',
+        Writable => 'int16u',
+        PrintConv => {
+            2 => 'Center Weighted',
+            3 => 'Spot',
+            5 => 'ESP',
+            261 => 'Pattern+AF', #6
+            515 => 'Spot+Highlight control', #6
+            1027 => 'Spot+Shadow control', #6
+        },
+    },
+    0x300 => { #6
+        Name => 'MacroMode',
+        Writable => 'int16u',
+        PrintConv => {
+            0 => 'Off',
+            1 => 'On',
+            2 => 'Super Macro', #11
+        },
+    },
+    0x301 => { #6
+        Name => 'FocusMode',
+        Writable => 'int16u',
+        PrintConv => {
+            0 => 'Single AF',
+            1 => 'Sequential shooting AF',
+            2 => 'Continuous AF',
+            3 => 'Multi AF',
+            10 => 'MF',
+        },
+    },
+    0x302 => { #6
+        Name => 'FocusProcess',
+        Writable => 'int16u',
+        PrintConv => {
+            0 => 'AF Not Used',
+            1 => 'AF Used',
+        },
+    },
+    0x303 => { #6
+        Name => 'AFSearch',
+        Writable => 'int16u',
+        PrintConv => {
+            0 => 'Not Ready',
+            1 => 'Ready',
+        },
+    },
+    0x304 => { #PH/4
+        Name => 'AFAreas',
+        Format => 'int32u',
+        Count => 64,
+        PrintConv => 'Image::ExifTool::Olympus::PrintAFAreas($val)',
+    },
+    0x400 => { #6
+        Name => 'FlashMode',
+        Writable => 'int16u',
+        PrintConv => {
+            0 => 'Off',
+            BITMASK => {
+                0 => 'On',
+                1 => 'Fill-in',
+                2 => 'Red-eye',
+                3 => 'Slow-sync',
+                4 => 'Forced On',
+                5 => '2nd Curtain',
+            },
+        },
+    },
+    0x401 => { #6
+        Name => 'FlashExposureCompensation',
+        Writable => 'rational64s',
+    },
+    0x500 => { #6
+        Name => 'WhiteBalance2',
+        Writable => 'int16u',
+        PrintConv => {
+            0 => 'Auto',
+            16 => '7500K (Fine Weather with Shade)',
+            17 => '6000K (Cloudy)',
+            18 => '5300K (Fine Weather)',
+            20 => '3000K (Tungsten light)',
+            21 => '3600K (Tungsten light-like)',
+            33 => '6600K (Daylight fluorescent)',
+            34 => '4500K (Neutral white fluorescent)',
+            35 => '4000K (Cool white fluorescent)',
+            48 => '3600K (Tungsten light-like)',
+            256 => 'Custom WB 1',
+            257 => 'Custom WB 2',
+            258 => 'Custom WB 3',
+            259 => 'Custom WB 4',
+            512 => 'Custom WB 5400K',
+            513 => 'Custom WB 2900K',
+            514 => 'Custom WB 8000K',
+        },
+    },
+    0x501 => { #PH/4
+        Name => 'WhiteBalanceTemperature',
+        Writable => 'int16u',
+        PrintConv => '$val ? $val : "Auto"',
+        PrintConvInv => '$val=~/^\d+$/ ? $val : 0',
+    },
+    0x502 => {  #PH/4
+        Name => 'WhiteBalanceBracket',
+        Writable => 'int16s',
+    },
+    0x503 => { #PH/4/6
+        Name => 'CustomSaturation',
+        Writable => 'int16s',
+        Count => 3,
+        Notes => '3 numbers: 1. CS Value, 2. Min, 3. Max',
+        PrintConv => q{
+            my ($a,$b,$c)=split ' ',$val;
+            if ($self->{CameraModel} =~ /^E-1\b/) {
+                $a-=$b; $c-=$b;
+                return "CS$a (min CS0, max CS$c)";
+            } else {
+                return "$a (min $b, max $c)";
+            }
+        },
+    },
+    0x504 => { #PH/4
+        Name => 'ModifiedSaturation',
+        Writable => 'int16u',
+        PrintConv => {
+            0 => 'Off',
+            1 => 'CM1 (Red Enhance)',
+            2 => 'CM2 (Green Enhance)',
+            3 => 'CM3 (Blue Enhance)',
+            4 => 'CM4 (Skin Tones)',
+        },
+    },
+    0x505 => { #PH/4
+        Name => 'ContrastSetting',
+        Writable => 'int16s',
+        Count => 3,
+        Notes => 'value, min, max',
+        PrintConv => 'my @v=split " ",$val; "$v[0] (min $v[1], max $v[2])"',
+        PrintConvInv => '$val=~tr/-0-9 //dc;$val',
+    },
+    0x506 => { #PH/4
+        Name => 'SharpnessSetting',
+        Writable => 'int16s',
+        Count => 3,
+        Notes => 'value, min, max',
+        PrintConv => 'my @v=split " ",$val; "$v[0] (min $v[1], max $v[2])"',
+        PrintConvInv => '$val=~tr/-0-9 //dc;$val',
+    },
+    0x507 => { #PH/4
+        Name => 'ColorSpace',
+        Writable => 'int16u',
+        PrintConv => { #6
+            0 => 'sRGB',
+            1 => 'Adobe RGB',
+            2 => 'Pro Photo RGB',
+        },
+    },
+    0x509 => { #6
+        Name => 'SceneMode',
+        Writable => 'int16u',
+        PrintConv => {
+            0 => 'Standard',
+            6 => 'Auto', #6
+            7 => 'Sport',
+            8 => 'Portrait',
+            9 => 'Landscape+Portrait',
+            10 => 'Landscape',
+            11 => 'Night Scene',
+            13 => 'Panorama', #6
+            16 => 'Landscape+Portrait', #6
+            17 => 'Night+Portrait',
+            19 => 'Fireworks',
+            20 => 'Sunset',
+            22 => 'Macro',
+            25 => 'Documents',
+            26 => 'Museum',
+            28 => 'Beach&Snow',
+            30 => 'Candle',
+            35 => 'Underwater Wide1', #6
+            36 => 'Underwater Macro', #6
+            39 => 'High Key',
+            40 => 'Digital Image Stabilization', #6
+            44 => 'Underwater Wide2', #6
+            45 => 'Low Key', #6
+            46 => 'Children', #6
+            48 => 'Nature Macro', #6
+        },
+    },
+    0x50a => { #PH/4/6
+        Name => 'NoiseReduction',
+        Writable => 'int16u',
+        PrintConv => {
+            0 => 'Off',
+            1 => 'Noise Reduction',
+            2 => 'Noise Filter',
+            3 => 'Noise Reduction + Noise Filter',
+            4 => 'Noise Filter (ISO Boost)', #6
+            5 => 'Noise Reduction + Noise Filter (ISO Boost)', #6
+        },
+    },
+    0x50b => { #6
+        Name => 'DistortionCorrection',
+        Writable => 'int16u',
+        PrintConv => \%offOn,
+    },
+    0x50c => { #PH/4
+        Name => 'ShadingCompensation',
+        Writable => 'int16u',
+        PrintConv => \%offOn,
+    },
+    0x50d => { #PH/4
+        Name => 'CompressionFactor',
+        Writable => 'rational64u',
+    },
+    0x50f => { #6
+        Name => 'Gradation',
+        Writable => 'int16s',
+        Count => 3,
+        PrintConv => {
+           '-1 -1 1' => 'Low Key',
+            '0 -1 1' => 'Normal',
+            '1 -1 1' => 'High Key',
+        },
+    },
+    0x520 => { #6
+        Name => 'PictureMode',
+        Writable => 'int16u',
+        PrintConv => {
+            1 => 'Vivid',
+            2 => 'Natural',
+            3 => 'Muted',
+            256 => 'Monotone',
+            512 => 'Sepia',
+        },
+    },
+    0x521 => { #6
+        Name => 'PictureModeSaturation',
+        Writable => 'int16s',
+        Count => 3,
+        Notes => 'value, min, max',
+        PrintConv => 'my @v=split " ",$val; "$v[0] (min $v[1], max $v[2])"',
+        PrintConvInv => '$val=~tr/-0-9 //dc;$val',
+    },
+    0x522 => { #6
+        Name => 'PictureModeHue',
+        Writable => 'int16s',
+        Unknown => 1, # (needs verification)
+    },
+    0x523 => { #6
+        Name => 'PictureModeContrast',
+        Writable => 'int16s',
+        Count => 3,
+        Notes => 'value, min, max',
+        PrintConv => 'my @v=split " ",$val; "$v[0] (min $v[1], max $v[2])"',
+        PrintConvInv => '$val=~tr/-0-9 //dc;$val',
+    },
+    0x524 => { #6
+        Name => 'PictureModeSharpness',
+        # verified as the Sharpness setting in the Picture Mode menu for the E-410
+        Writable => 'int16s',
+        Count => 3,
+        Notes => 'value, min, max',
+        PrintConv => 'my @v=split " ",$val; "$v[0] (min $v[1], max $v[2])"',
+        PrintConvInv => '$val=~tr/-0-9 //dc;$val',
+    },
+    0x525 => { #6
+        Name => 'PictureModeBWFilter',
+        Writable => 'int16s',
+        PrintConv => {
+            0 => 'n/a',
+            1 => 'Neutral',
+            2 => 'Yellow',
+            3 => 'Orange',
+            4 => 'Red',
+            5 => 'Green',
+        },
+    },
+    0x526 => { #6
+        Name => 'PictureModeTone',
+        Writable => 'int16s',
+        PrintConv => {
+            0 => 'n/a',
+            1 => 'Neutral',
+            2 => 'Sepia',
+            3 => 'Blue',
+            4 => 'Purple',
+            5 => 'Green',
+        },
+    },
+    0x527 => { #12
+        Name => 'NoiseFilter',
+        Writable => 'int16s',
+        Count => 3,
+        PrintConv => {
+           '-2 -2 1' => 'Off',
+           '-1 -2 1' => 'Low',
+           '0 -2 1' => 'Standard',
+           '1 -2 1' => 'High',
+        },
+    },
+    0x600 => { #PH/4
+        Name => 'DriveMode',
+        Writable => 'int16u',
+        Count => -1,
+        Notes => '2 or 3 numbers: 1. Mode, 2. Shot number, 3. Mode bits',
+        PrintConv => q{
+            my ($a,$b,$c) = split ' ',$val;
+            return 'Single Shot' unless $a;
+            if ($a == 5 and defined $c) {
+                $a = DecodeBits($c, { #6
+                    0 => 'AE',
+                    1 => 'WB',
+                    2 => 'FL',
+                    3 => 'MF',
+                }) . ' Bracketing';
+                $a =~ s/, /+/g;
+            } else {
+                my %a = (
+                    1 => 'Continuous Shooting',
+                    2 => 'Exposure Bracketing',
+                    3 => 'White Balance Bracketing',
+                    4 => 'Exposure+WB Bracketing', #6
+                );
+                $a = $a{$a} || "Unknown ($a)";
+            }
+            return "$a, Shot $b";
+        },
+    },
+    0x601 => { #6
+        Name => 'PanoramaMode',
+        Writable => 'int16u',
+        Notes => '2 numbers: 1. Mode, 2. Shot number',
+        PrintConv => q{
+            my ($a,$b) = split ' ',$val;
+            return 'Off' unless $a;
+            my %a = (
+                1 => 'Left to right',
+                2 => 'Right to left',
+                3 => 'Bottom to top',
+                4 => 'Top to bottom',
+            );
+            return ($a{$a} || "Unknown ($a)") . ', Shot ' . $b;
+        },
+    },
+    0x603 => { #PH/4
+        Name => 'ImageQuality2',
+        Writable => 'int16u',
+        PrintConv => {
+            1 => 'SQ',
+            2 => 'HQ',
+            3 => 'SHQ',
+            4 => 'RAW',
+        },
+    },
+    0x901 => { #PH (u770SW)
+        # 2 numbers: 1st looks like meters above sea level, 2nd is usually 3x the 1st (feet?)
+        Name => 'ManometerReading',
+        Writable => 'int32s',
+        Count => 2,
+    },
+);
+
+# Olympus RAW processing IFD (ref 6)
+%Image::ExifTool::Olympus::RawDevelopment = (
+    WRITE_PROC => \&Image::ExifTool::Exif::WriteExif,
+    CHECK_PROC => \&Image::ExifTool::Exif::CheckExif,
+    WRITABLE => 1,
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
+    0x000 => { #PH
+        Name => 'RawDevVersion',
+        Writable => 'undef',
+        Count => 4,
+    },
+    0x100 => {
+        Name => 'RawDevExposureBiasValue',
+        Writable => 'rational64s',
+    },
+    0x101 => {
+        Name => 'RawDevWhiteBalanceValue',
+        Writable => 'int16u',
+    },
+    0x102 => {
+        Name => 'RawDevWBFineAdjustment',
+        Writable => 'int16s',
+    },
+    0x103 => {
+        Name => 'RawDevGrayPoint',
+        Writable => 'int16u',
+        Count => 3,
+    },
+    0x104 => {
+        Name => 'RawDevSaturationEmphasis',
+        Writable => 'int16s',
+        Count => 3,
+    },
+    0x105 => {
+        Name => 'RawDevMemoryColorEmphasis',
+        Writable => 'int16u',
+    },
+    0x106 => {
+        Name => 'RawDevContrastValue',
+        Writable => 'int16s',
+        Count => 3,
+    },
+    0x107 => {
+        Name => 'RawDevSharpnessValue',
+        Writable => 'int16s',
+        Count => 3,
+    },
+    0x108 => {
+        Name => 'RawDevColorSpace',
+        Writable => 'int16u',
+        PrintConv => { #11
+            0 => 'sRGB',
+            1 => 'Adobe RGB',
+            2 => 'Pro Photo RGB',
+        },
+    },
+    0x109 => {
+        Name => 'RawDevEngine',
+        Writable => 'int16u',
+        PrintConv => { #11
+            0 => 'High Speed',
+            1 => 'High Function',
+            2 => 'Advanced High Speed',
+            3 => 'Advanced High Function',
+        },
+    },
+    0x10a => {
+        Name => 'RawDevNoiseReduction',
+        Writable => 'int16u',
+        PrintConv => { #11
+            BITMASK => {
+                0 => 'Noise Reduction',
+                1 => 'Noise Filter',
+                2 => 'ISO Boost',
+            },
+        },
+    },
+    0x10b => {
+        Name => 'RawDevEditStatus',
+        Writable => 'int16u',
+        PrintConv => { #11
+            0 => 'Original',
+            1 => 'Edited (Landscape)',
+            6 => 'Edited (Portrait)',
+            8 => 'Edited (Portrait)',
+        },
+    },
+    0x10c => {
+        Name => 'RawDevSettings',
+        Writable => 'int16u',
+        PrintConv => { #11
+            BITMASK => {
+                0 => 'WB Color Temp',
+                2 => 'WB Gray Point',
+                3 => 'Saturation',
+                4 => 'Contrast',
+                5 => 'Sharpness',
+                6 => 'Color Space',
+                7 => 'High Function',
+                8 => 'Noise Reduction',
+            },
+        },
+    },
+);
+
+# Olympus RAW processing B IFD (ref 11)
+%Image::ExifTool::Olympus::RawDevelopment2 = (
+    WRITE_PROC => \&Image::ExifTool::Exif::WriteExif,
+    CHECK_PROC => \&Image::ExifTool::Exif::CheckExif,
+    WRITABLE => 1,
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
+    0x000 => {
+        Name => 'RawDevVersion',
+        Writable => 'undef',
+        Count => 4,
+    },
+    0x100 => {
+        Name => 'RawDevExposureBiasValue',
+        Writable => 'rational64s',
+    },
+    0x101 => {
+        Name => 'RawDevWhiteBalance',
+        Writable => 'int16u',
+        PrintConv => {
+            1 => 'Color Temperature',
+            2 => 'Gray Point',
+        },
+    },
+    0x102 => {
+        Name => 'RawDevWhiteBalanceValue',
+        Writable => 'int16u',
+    },
+    0x103 => {
+        Name => 'RawDevWBFineAdjustment',
+        Writable => 'int16s',
+    },
+    0x104 => {
+        Name => 'RawDevGrayPoint',
+        Writable => 'int16u',
+        Count => 3,
+    },
+    0x105 => {
+        Name => 'RawDevContrastValue',
+        Writable => 'int16s',
+        Count => 3,
+    },
+    0x106 => {
+        Name => 'RawDevSharpnessValue',
+        Writable => 'int16s',
+        Count => 3,
+    },
+    0x107 => {
+        Name => 'RawDevSaturationEmphasis',
+        Writable => 'int16s',
+        Count => 3,
+    },
+    0x108 => {
+        Name => 'RawDevMemoryColorEmphasis',
+        Writable => 'int16u',
+    },
+    0x109 => {
+        Name => 'RawDevColorSpace',
+        Writable => 'int16u',
+        PrintConv => {
+            0 => 'sRGB',
+            1 => 'Adobe RGB',
+            2 => 'Pro Photo RGB',
+        },
+    },
+    0x10a => {
+        Name => 'RawDevNoiseReduction',
+        Writable => 'int16u',
+        PrintConv => {
+            BITMASK => {
+                0 => 'Noise Reduction',
+                1 => 'Noise Filter',
+                2 => 'ISO Boost',
+            },
+        },
+    },
+    0x10b => {
+        Name => 'RawDevEngine',
+        Writable => 'int16u',
+        PrintConv => {
+            0 => 'High Speed',
+            1 => 'High Function',
+        },
+    },
+    0x10c => {
+        Name => 'RawDevPictureMode',
+        Writable => 'int16u',
+        PrintConv => {
+            1 => 'Vivid',
+            2 => 'Natural',
+            3 => 'Muted',
+            256 => 'Monotone',
+            512 => 'Sepia',
+        },
+    },
+    0x10d => {
+        Name => 'RawDevPMSaturation',
+        Writable => 'int16s',
+        Count => 3,
+    },
+    0x10e => {
+        Name => 'RawDevPMContrast',
+        Writable => 'int16s',
+        Count => 3,
+    },
+    0x10f => {
+        Name => 'RawDevPMSharpness',
+        Writable => 'int16s',
+        Count => 3,
+    },
+    0x110 => {
+        Name => 'RawDevPM_BWFilter',
+        Writable => 'int16u',
+        PrintConv => {
+            1 => 'Neutral',
+            2 => 'Yellow',
+            3 => 'Orange',
+            4 => 'Red',
+            5 => 'Green',
+        },
+    },
+    0x111 => {
+        Name => 'RawDevPMPictureTone',
+        Writable => 'int16u',
+        PrintConv => {
+            1 => 'Neutral',
+            2 => 'Sepia',
+            3 => 'Blue',
+            4 => 'Purple',
+            5 => 'Green',
+        },
+    },
+    0x112 => {
+        Name => 'RawDevGradation',
+        Writable => 'int16s',
+        Count => 3,
+    },
+);
+
+# Olympus Image processing IFD
+%Image::ExifTool::Olympus::ImageProcessing = (
+    WRITE_PROC => \&Image::ExifTool::Exif::WriteExif,
+    CHECK_PROC => \&Image::ExifTool::Exif::CheckExif,
+    WRITABLE => 1,
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
+    0x000 => { #PH
+        Name => 'ImageProcessingVersion',
+        Writable => 'undef',
+        Count => 4,
+    },
+    0x100 => { #6
+        Name => 'WB_RBLevels',
+        Writable => 'int16u',
+        Count => 2,
+    },
+    0x200 => { #6
+        Name => 'ColorMatrix',
+        Writable => 'int16u',
+        Format => 'int16s',
+        Count => 9,
+    },
+    0x300 => { #PH/4
+        Name => 'SmoothingParameter1',
+        Writable => 'int16u',
+    },
+    0x310 => { #PH/4
+        Name => 'SmoothingParameter2',
+        Writable => 'int16u',
+    },
+    0x600 => { #PH/4
+        Name => 'SmoothingThresholds',
+        Writable => 'int16u',
+        Count => 4,
+    },
+    0x610 => { #PH/4
+        Name => 'SmoothingThreshold2',
+        Writable => 'int16u',
+    },
+    0x611 => { #4/6
+        Name => 'ValidBits',
+        Writable => 'int16u',
+        Count => 2,
+    },
+    0x614 => { #PH
+        Name => 'OlympusImageWidth2',
+        Writable => 'int32u',
+    },
+    0x615 => { #PH
+        Name => 'OlympusImageHeight2',
+        Writable => 'int32u',
+    },
+    # 0x1010-0x1012 are the processing options used in camera or in
+    # Olympus software, which 0x050a-0x050c are in-camera only (ref 6)
+    0x1010 => { #PH/4
+        Name => 'NoiseReduction2',
+        Writable => 'int16u',
+        PrintConv => {
+            0 => 'Off',
+            1 => 'Noise Filter',
+            2 => 'Noise Reduction',
+            3 => 'Noise Reduction + Noise Filter', #6
+            4 => 'Noise Filter (ISO Boost)', #6
+            5 => 'Noise Reduction + Noise Filter (ISO Boost)', #6
+        },
+    },
+    0x1011 => { #6
+        Name => 'DistortionCorrection2',
+        Writable => 'int16u',
+        PrintConv => {
+            0 => 'Off',
+            1 => 'On',
+        },
+    },
+    0x1012 => { #PH/4
+        Name => 'ShadingCompensation2',
+        Writable => 'int16u',
+        PrintConv => \%offOn,
+    },
+    0x1103 => { #PH
+        Name => 'UnknownBlock',
+        Writable => 'undef',
+        Notes => 'unknown 142kB block in ORF images, not copied to JPEG images',
+        # 'Drop' because too large for APP1 in JPEG images
+        Flags => [ 'Unknown', 'Binary', 'Drop' ],
+    },
+);
+
+# Olympus Focus Info IFD
+%Image::ExifTool::Olympus::FocusInfo = (
+    WRITE_PROC => \&Image::ExifTool::Exif::WriteExif,
+    CHECK_PROC => \&Image::ExifTool::Exif::CheckExif,
+    WRITABLE => 1,
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
+    0x000 => { #PH
+        Name => 'FocusInfoVersion',
+        Writable => 'undef',
+        Count => 4,
+    },
+    0x209 => { #PH/4
+        Name => 'AutoFocus',
+        Writable => 'int16u',
+        PrintConv => \%offOn,
+        Unknown => 1, #6
+    },
+    0x300 => { #6
+        Name => 'ZoomStepCount',
+        Writable => 'int16u',
+    },
+    0x301 => { #11
+        Name => 'FocusStepCount',
+        Writable => 'int16u',
+    },
+    0x305 => { #4
+        Name => 'FocusDistance',
+        Writable => 'rational64u',
+        # this rational value looks like it is in mm when the denominator is
+        # 1 (E-1), and cm when denominator is 10 (E-300), so if we ignore the
+        # denominator we are consistently in mm - PH
+        Format => 'int32u',
+        Count => 2,
+        ValueConv => q{
+            my ($a,$b) = split ' ',$val;
+            return 0 if $a == 0xffffffff;
+            return $a / 1000;
+        },
+        ValueConvInv => q{
+            return '4294967295 1' unless $val;
+            $val = int($val * 1000 + 0.5);
+            return "$val 1";
+        },
+        PrintConv => '$val ? "$val m" : "inf"',
+        PrintConvInv => '$val eq "inf" ? 0 : $val=~s/\s*m$//, $val',
+    },
+    0x308 => { #11
+        Name => 'AFPoint',
+        Writable => 'int16u',
+        PrintConv => {
+            0 => 'Left',
+            1 => 'Center (1)',
+            2 => 'Right',
+            3 => 'Center (3)',
+            255 => 'None',
+        },
+    },
+    # 0x31a Continuous AF parameters?
+    # 0x1200-0x1209 Flash information:
+    0x1201 => { #6
+        Name => 'ExternalFlash',
+        Writable => 'int16u',
+        Count => 2,
+        PrintConv => {
+            '0 0' => 'Off',
+            '1 0' => 'On',
+        },
+    },
+    0x1203 => { #11
+        Name => 'ExternalFlashGuideNumber',
+        Writable => 'rational64s',
+        Unknown => 1, # (needs verification)
+    },
+    0x1204 => { #11(reversed)/7
+        Name => 'ExternalFlashBounce',
+        Writable => 'int16u',
+        PrintConv => {
+            0 => 'Bounce or Off',
+            1 => 'Direct',
+        },
+    },
+    0x1205 => { #11 (ref converts to mm using table)
+        Name => 'ExternalFlashZoom',
+        Writable => 'rational64u',
+    },
+    0x1208 => { #6
+        Name => 'InternalFlash',
+        Writable => 'int16u',
+        Count => -1,
+        PrintConv => {
+            '0'   => 'Off',
+            '1'   => 'On',
+            '0 0' => 'Off',
+            '1 0' => 'On',
+        },
+    },
+    0x1209 => { #6
+        Name => 'ManualFlash',
+        Writable => 'int16u',
+        Count => 2,
+        Notes => '2 numbers: 1. 0=Off, 1=On, 2. Flash strength',
+        PrintConv => q{
+            my ($a,$b) = split ' ',$val;
+            return 'Off' unless $a;
+            $b = ($b == 1) ? 'Full' : "1/$b";
+            return "On ($b strength)";
+        },
+    },
+    0x1500 => { #6
+        Name => 'SensorTemperature',
+        Writable => 'int16s',
+    },
+    0x1600 => { # ref http://fourthirdsphoto.com/vbb/showpost.php?p=107607&postcount=15
+        Name => 'ImageStabilization',
+        Writable => 'undef',
+        # if the first 4 bytes are non-zero, then bit 0x01 of byte 44
+        # gives the stabilization mode
+        PrintConv => q{
+            $val =~ /^\0{4}/ ? 'Off' : 'On, ' .
+            (unpack('x44C',$val) & 0x01 ? 'Mode 1' : 'Mode 2')
+        },
+    },
+    # 0x102a same as Subdir4-0x300
+);
+
+# Olympus raw information tags (ref 6)
+%Image::ExifTool::Olympus::RawInfo = (
+    WRITE_PROC => \&Image::ExifTool::Exif::WriteExif,
+    CHECK_PROC => \&Image::ExifTool::Exif::CheckExif,
+    NOTES => 'These tags are found only in ORF images of some models (ie. C8080WZ).',
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
+    0x000 => {
+        Name => 'RawInfoVersion',
+        Writable => 'undef',
+        Count => 4,
+    },
+    0x100 => {
+        Name => 'WB_RBLevelsUsed',
+        Writable => 'int16',
+        Count => 2,
+    },
+    0x110 => {
+        Name => 'WB_RBLevelsAuto',
+        Writable => 'int16',
+        Count => 2,
+    },
+    0x120 => {
+        Name => 'WB_RBLevelsShade',
+        Writable => 'int16',
+        Count => 2,
+    },
+    0x121 => {
+        Name => 'WB_RBLevelsCloudy',
+        Writable => 'int16',
+        Count => 2,
+    },
+    0x122 => {
+        Name => 'WB_RBLevelsFineWeather',
+        Writable => 'int16',
+        Count => 2,
+    },
+    0x123 => {
+        Name => 'WB_RBLevelsTungsten',
+        Writable => 'int16',
+        Count => 2,
+    },
+    0x124 => {
+        Name => 'WB_RBLevelsEveningSunlight',
+        Writable => 'int16',
+        Count => 2,
+    },
+    0x130 => {
+        Name => 'WB_RBLevelsDaylightFluor',
+        Writable => 'int16',
+        Count => 2,
+    },
+    0x131 => {
+        Name => 'WB_RBLevelsDayWhiteFluor',
+        Writable => 'int16',
+        Count => 2,
+    },
+    0x132 => {
+        Name => 'WB_RBLevelsCoolWhiteFluor',
+        Writable => 'int16',
+        Count => 2,
+    },
+    0x133 => {
+        Name => 'WB_RBLevelsWhiteFluorescent',
+        Writable => 'int16',
+        Count => 2,
+    },
+    0x200 => {
+        Name => 'ColorMatrix2',
+        Format => 'int16s',
+        Writable => 'int16u',
+        Count => 9,
+    },
+    # 0x240 => 'ColorMatrixDefault', ?
+    # 0x250 => 'ColorMatrixSaturation', ?
+    # 0x251 => 'ColorMatrixHue', ?
+    # 0x252 => 'ColorMatrixContrast', ?
+    # 0x300 => sharpness-related
+    # 0x301 => list of sharpness-related values
+    0x310 => {
+        Name => 'CoringFilter',
+        Writable => 'int16u',
+    },
+    0x311 => {
+        Name => 'CoringValues',
+        Writable => 'int16u',
+        Count => 11,
+    },
+    0x600 => {
+        Name => 'BlackLevel2',
+        Writable => 'int16u',
+        Count => 4,
+    },
+    0x601 => {
+        Name => 'YCbCrCoefficients',
+        Notes => 'stored as int16u[6], but extracted as rational32u[3]',
+        Format => 'rational32u',
+    },
+    0x611 => {
+        Name => 'ValidPixelDepth',
+        Writable => 'int16u',
+        Count => 2,
+    },
+    0x612 => { #11
+        Name => 'StartOffsetX',
+        Writable => 'int16u',
+    },
+    0x613 => { #11
+        Name => 'StartOffsetY',
+        Writable => 'int16u',
+    },
+    0x614 => {
+        Name => 'FinalImageWidth',
+        Writable => 'int32u',
+    },
+    0x615 => {
+        Name => 'FinalImageHeight',
+        Writable => 'int32u',
+    },
+    0x1000 => {
+        Name => 'LightSource',
+        Writable => 'int16u',
+        PrintConv => {
+            0 => 'Unknown',
+            16 => 'Shade',
+            17 => 'Cloudy',
+            18 => 'Fine Weather',
+            20 => 'Tungsten (incandescent)',
+            22 => 'Evening Sunlight',
+            33 => 'Daylight Fluorescent (D 5700 - 7100K)',
+            34 => 'Day White Fluorescent (N 4600 - 5400K)',
+            35 => 'Cool White Fluorescent (W 3900 - 4500K)',
+            36 => 'White Fluorescent (WW 3200 - 3700K)',
+            256 => 'One Touch White Balance',
+            512 => 'Custom 1-4',
+        },
+    },
+    # the following 5 tags all have 3 values: val, min, max
+    0x1001 => {
+        Name => 'WhiteBalanceComp',
+        Writable => 'int16s',
+        Count => 3,
+    },
+    0x1010 => {
+        Name => 'SaturationSetting',
+        Writable => 'int16s',
+        Count => 3,
+    },
+    0x1011 => {
+        Name => 'HueSetting',
+        Writable => 'int16s',
+        Count => 3,
+    },
+    0x1012 => {
+        Name => 'ContrastSetting',
+        Writable => 'int16s',
+        Count => 3,
+    },
+    0x1013 => {
+        Name => 'SharpnessSetting',
+        Writable => 'int16s',
+        Count => 3,
+    },
+    # settings written by Camedia Master 4.x
+    0x2000 => {
+        Name => 'CMExposureCompensation',
+        Writable => 'rational64s',
+    },
+    0x2001 => {
+        Name => 'CMWhiteBalance',
+        Writable => 'int16u',
+    },
+    0x2002 => {
+        Name => 'CMWhiteBalanceComp',
+        Writable => 'int16s',
+    },
+    0x2010 => {
+        Name => 'CMWhiteBalanceGrayPoint',
+        Writable => 'int16u',
+        Count => 3,
+    },
+    0x2020 => {
+        Name => 'CMSaturation',
+        Writable => 'int16s',
+        Count => 3,
+    },
+    0x2021 => {
+        Name => 'CMHue',
+        Writable => 'int16s',
+        Count => 3,
+    },
+    0x2022 => {
+        Name => 'CMContrast',
+        Writable => 'int16s',
+        Count => 3,
+    },
+    0x2023 => {
+        Name => 'CMSharpness',
+        Writable => 'int16s',
+        Count => 3,
+    },
+);
+
+# Tags found only in some FE models
+%Image::ExifTool::Olympus::FETags = (
+    WRITE_PROC => \&Image::ExifTool::Exif::WriteExif,
+    CHECK_PROC => \&Image::ExifTool::Exif::CheckExif,
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
+    NOTES => q{
+        Some FE models write a large number of tags here, but most of this
+        information remains unknown.
+    },
+    0x0100 => {
+        Name => 'BodyFirmwareVersion',
+        Writable => 'string',
+    },
+);
+
+# Olympus composite tags
+%Image::ExifTool::Olympus::Composite = (
+    GROUPS => { 2 => 'Camera' },
+    ExtenderStatus => {
+        Notes => q{
+            Olympus cameras have the quirk that they may retain the extender settings
+            after the extender is removed until the camera is powered off.  This tag is
+            an attempt to represent the actual status of the extender.
+        },
+        Require => {
+            0 => 'Olympus:Extender',
+            1 => 'Olympus:LensType',
+            2 => 'MaxApertureValue',
+        },
+        ValueConv => 'Image::ExifTool::Olympus::ExtenderStatus($val[0],$prt[1],$val[2])',
+        PrintConv => {
+            0 => 'Not attached',
+            1 => 'Attached',
+            2 => 'Removed',
+        },
+    },
+);
+
+# add our composite tags
+Image::ExifTool::AddCompositeTags('Image::ExifTool::Olympus');
+
+
+#------------------------------------------------------------------------------
+# Determine if the extender (EX-25/EC-14) was really attached (ref 9)
+# Inputs: 0) Extender, 1) LensType string, 2) MaxApertureAtMaxFocal
+# Returns: 0=not attached, 1=attached, 2=could have been removed
+# Notes: Olympus has a bug in the in-camera firmware which results in the
+# extender information being cached and written into the EXIF data even after
+# the extender has been removed.  You must power cycle the camera to prevent it
+# from writing the cached extender information into the EXIF data.
+sub ExtenderStatus($$$)
+{
+    my ($extender, $lensType, $maxAperture) = @_;
+    my @info = split ' ', $extender;
+    # validate that extender identifier is reasonable
+    return 0 unless @info >= 4 and $info[2];
+    # if it's not an EC-14 (id 0 4) then assume it was really attached
+    # (other extenders don't seem to affect the reported max aperture)
+    return 1 if "$info[0] $info[2]" ne "0 4";
+    # get the maximum aperture for this lens (in $1)
+    $lensType =~ / F(\d+(.\d+)?)/ or return 1;
+    # If the maximum aperture at the maximum focal length is greater than the
+    # known max/max aperture of the lens, then the extender must be attached
+    return ($maxAperture - $1 > 0.2) ? 1 : 2;
+}
+
+#------------------------------------------------------------------------------
+# Print lens information (ref 6)
+# Inputs: 0) Lens info (string of integers: Make, Unknown, Model, Release, ...)
+#         1) 'Lens' or 'Extender'
+sub PrintLensInfo($$)
+{
+    my ($val, $type) = @_;
+    my @info = split ' ', $val;
+    return "Unknown ($val)" unless @info >= 4;
+    return 'None' unless $info[2];
+    my %make = (
+        0 => 'Olympus',
+        1 => 'Sigma',
+        2 => 'Leica',
+        3 => 'Leica',
+    );
+    my %model = (
+        Lens => {
+            # Olympus lenses (key is "make model" with optional "release")
+            '0 1 0' => 'Zuiko Digital ED 50mm F2.0 Macro',
+            '0 1 1' => 'Zuiko Digital 40-150mm F3.5-4.5', #8
+            '0 2'   => 'Zuiko Digital ED 150mm F2.0',
+            '0 3'   => 'Zuiko Digital ED 300mm F2.8',
+            '0 5 0' => 'Zuiko Digital 14-54mm F2.8-3.5',
+            '0 5 1' => 'Zuiko Digital Pro ED 90-250mm F2.8', #9
+            '0 6 0' => 'Zuiko Digital ED 50-200mm F2.8-3.5',
+            '0 6 1' => 'Zuiko Digital ED 8mm F3.5 Fisheye', #9
+            '0 7 0' => 'Zuiko Digital 11-22mm F2.8-3.5',
+            '0 7 1' => 'Zuiko Digital 18-180mm F3.5-6.3', #6
+            '0 21'  => 'Zuiko Digital ED 7-14mm F4.0',
+            '0 23'  => 'Zuiko Digital Pro ED 35-100mm F2.0', #7
+            '0 24'  => 'Zuiko Digital 14-45mm F3.5-5.6',
+            '0 32'  => 'Zuiko Digital 35mm F3.5 Macro', #9
+            '0 34'  => 'Zuiko Digital 17.5-45mm F3.5-5.6', #9
+            '0 35'  => 'Zuiko Digital ED 14-42mm F3.5-5.6', #PH
+            '0 36'  => 'Zuiko Digital ED 40-150mm F4.0-5.6', #PH
+            # Sigma lenses
+            '1 1'   => '18-50mm F3.5-5.6', #8
+            '1 2'   => '55-200mm F4.0-5.6 DC',
+            '1 3'   => '18-125mm F3.5-5.6 DC',
+            '1 4'   => '18-125mm F3.5-5.6', #7
+            '1 5'   => '30mm F1.4', #10
+            '1 7'   => '105mm F2.8 DG', #PH
+            '1 8'   => '150mm F2.8 DG HSM', #PH
+            '1 17'  => '135-400mm F4.5-5.6 DG ASP APO RF', #11
+            '1 18'  => '300-800mm F5.6 EX DG APO', #11
+            # Leica lenses (ref 11)
+            '2 1'   => 'D Vario Elmarit 14-50mm, F2.8-3.5 Asph.',
+            '2 2'   => 'D Summilux 25mm, F1.4 Asph.',
+            '3 1'   => 'D Vario Elmarit 14-50mm, F2.8-3.5 Asph.',
+            '3 2'   => 'D Summilux 25mm, F1.4 Asph.',
+        },
+        Extender => {
+            # Olympus extenders
+            '0 4'   => 'Zuiko Digital EC-14 1.4x Teleconverter',
+            '0 8'   => 'EX-25 Extension Tube',
+        },
+    );
+    my %release = (
+        0 => '', # production
+        1 => ' (pre-release)',
+    );
+    my $make = $make{$info[0]} || "Unknown Make ($info[0])";
+    my $str = "$info[0] $info[2]";
+    my $rel = $release{$info[3]};
+    my $model = $model{$type}->{"$str $info[3]"};
+    if ($model) {
+        $rel = '';  # don't print release if it is used to differentiate models
+    } else {
+        $rel = " Unknown Release ($info[3])" unless defined $rel;
+        $model = $model{$type}->{$str} || "Unknown Model ($str)";
+    }
+    return "$make $model$rel";
+}
+
+#------------------------------------------------------------------------------
+# Print AF points
+# Inputs: 0) AF point data (string of integers)
+# Notes: I'm just guessing that the 2nd and 4th bytes are the Y coordinates,
+# and that more AF points will show up in the future (derived from E-1 images,
+# and the E-1 uses just one of 3 possible AF points, all centered in Y) - PH
+sub PrintAFAreas($)
+{
+    my $val = shift;
+    my @points = split ' ', $val;
+    my %afPointNames = (
+        0x36794285 => 'Left',
+        0x79798585 => 'Center',
+        0xBD79C985 => 'Right',
+    );
+    $val = '';
+    my $pt;
+    foreach $pt (@points) {
+        next unless $pt;
+        $val and $val .= ', ';
+        $afPointNames{$pt} and $val .= $afPointNames{$pt} . ' ';
+        my @coords = unpack('C4',pack('N',$pt));
+        $val .= "($coords[0],$coords[1])-($coords[2],$coords[3])";
+    }
+    $val or $val = 'none';
+    return $val;
+}
+
+#------------------------------------------------------------------------------
+# Process ORF file
+# Inputs: 0) ExifTool object reference, 1) directory information reference
+# Returns: 1 if this looked like a valid ORF file, 0 otherwise
+sub ProcessORF($$)
+{
+    my ($exifTool, $dirInfo) = @_;
+    return $exifTool->ProcessTIFF($dirInfo);
+}
+
+1;  # end
+
+__END__
+
+=head1 NAME
+
+Image::ExifTool::Olympus - Olympus/Epson maker notes tags
+
+=head1 SYNOPSIS
+
+This module is loaded automatically by Image::ExifTool when required.
+
+=head1 DESCRIPTION
+
+This module contains definitions required by Image::ExifTool to interpret
+Olympus or Epson maker notes in EXIF information.
+
+=head1 AUTHOR
+
+Copyright 2003-2007, Phil Harvey (phil at owl.phy.queensu.ca)
+
+This library is free software; you can redistribute it and/or modify it
+under the same terms as Perl itself.
+
+=head1 REFERENCES
+
+=over 4
+
+=item L<http://park2.wakwak.com/~tsuruzoh/Computer/Digicams/exif-e.html>
+
+=item L<http://www.cybercom.net/~dcoffin/dcraw/>
+
+=item L<http://www.ozhiker.com/electronics/pjmt/jpeg_info/olympus_mn.html>
+
+=item L<http://olypedia.de/Olympus_Makernotes>
+
+=back
+
+=head1 ACKNOWLEDGEMENTS
+
+Thanks to Markku Hanninen, Remi Guyomarch, Frank Ledwon, Michael Meissner,
+Mark Dapoz and Ioannis Panagiotopoulos for their help figuring out some
+Olympus tags, and Lilo Huang for adding to the LensType list.
+
+=head1 SEE ALSO
+
+L<Image::ExifTool::TagNames/Olympus Tags>,
+L<Image::ExifTool(3pm)|Image::ExifTool>
+
+=cut
Index: /gsdl/trunk/perllib/cpan/Image/ExifTool/PDF.pm
===================================================================
--- /gsdl/trunk/perllib/cpan/Image/ExifTool/PDF.pm	(revision 16842)
+++ /gsdl/trunk/perllib/cpan/Image/ExifTool/PDF.pm	(revision 16842)
@@ -0,0 +1,1294 @@
+#------------------------------------------------------------------------------
+# File:         PDF.pm
+#
+# Description:  Read PDF meta information
+#
+# Revisions:    07/11/05 - P. Harvey Created
+#               07/25/05 - P. Harvey Add support for encrypted documents
+#
+# References:   1) http://partners.adobe.com/public/developer/pdf/index_reference.html
+#               2) http://www.cr0.net:8040/code/crypto/rc4/
+#------------------------------------------------------------------------------
+
+package Image::ExifTool::PDF;
+
+use strict;
+use vars qw($VERSION);
+use Image::ExifTool qw(:DataAccess :Utils);
+require Exporter;
+
+$VERSION = '1.10';
+
+sub LocateObject($$);
+sub FetchObject($$$$);
+sub ExtractObject($$;$$);
+sub ReadToNested($;$);
+sub ProcessDict($$$$;$);
+
+my %warnedOnce;     # hash of warnings we issued
+my %streamObjs;     # hash of stream objects
+my %fetched;        # dicts fetched in verbose mode (to avoid cyclical recursion)
+my $lastFetched;    # last fetched object reference (used for decryption)
+my $cryptInfo;      # encryption object reference (plus additional information)
+
+# tags in main PDF directories
+%Image::ExifTool::PDF::Main = (
+    Info => {
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::PDF::Info',
+        },
+    },
+    Root => {
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::PDF::Root',
+        },
+    },
+);
+
+# tags in PDF Info directory
+%Image::ExifTool::PDF::Info = (
+    GROUPS => { 2 => 'Image' },
+    EXTRACT_UNKNOWN => 1, # extract all unknown tags in this directory
+    NOTES => q{
+        As well as the tags listed below, the PDF specification allows for
+        user-defined tags to exist in the Info dictionary.  These tags, which should
+        have corresponding XMP-pdfx entries in the PDF Metadata, are also extracted
+        by ExifTool.
+    },
+    Title       => { },
+    Author      => { Groups => { 2 => 'Author' } },
+    Subject     => { },
+    Keywords    => { List => 1 },  # this is a list of tokens
+    Creator     => { },
+    Producer    => { },
+    CreationDate => {
+        Name => 'CreateDate',
+        Groups => { 2 => 'Time' },
+        ValueConv => 'Image::ExifTool::PDF::ConvertPDFDate($self, $val)',
+    },
+    ModDate => {
+        Name => 'ModifyDate',
+        Groups => { 2 => 'Time' },
+        ValueConv => 'Image::ExifTool::PDF::ConvertPDFDate($self, $val)',
+    },
+    Trapped => {
+        # remove leading '/' from '/True' or '/False'
+        ValueConv => '$val=~s{^/}{}; $val',
+    },
+);
+
+# tags in the PDF Root document catalog
+%Image::ExifTool::PDF::Root = (
+    NOTES => 'This is the PDF document catalog.',
+    Metadata => {
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::PDF::Metadata',
+        },
+    },
+    Pages => {
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::PDF::Pages',
+        },
+    },
+);
+
+# tags in PDF Pages directory
+%Image::ExifTool::PDF::Pages = (
+    GROUPS => { 2 => 'Image' },
+    Count => 'PageCount',
+    Kids => {
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::PDF::Kids',
+        },
+    },
+);
+
+# tags in PDF Kids directory
+%Image::ExifTool::PDF::Kids = (
+    Metadata => {
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::PDF::Metadata',
+        },
+    },
+    PieceInfo => {
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::PDF::PieceInfo',
+        },
+    },
+    Resources => {
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::PDF::Resources',
+        },
+    },
+);
+
+# tags in PDF Resources directory
+%Image::ExifTool::PDF::Resources = (
+    ColorSpace => {
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::PDF::ColorSpace',
+        },
+    },
+);
+
+# tags in PDF ColorSpace directory
+%Image::ExifTool::PDF::ColorSpace = (
+    DefaultRGB => {
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::PDF::DefaultRGB',
+        },
+    },
+);
+
+# tags in PDF DefaultRGB directory
+%Image::ExifTool::PDF::DefaultRGB = (
+    ICCBased => {
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::PDF::ICCBased',
+        },
+    },
+);
+
+# tags in PDF ICCBased directory
+%Image::ExifTool::PDF::ICCBased = (
+    stream => {
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::ICC_Profile::Main',
+        },
+    },
+);
+
+# tags in PDF PieceInfo directory
+%Image::ExifTool::PDF::PieceInfo = (
+    AdobePhotoshop => {
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::PDF::AdobePhotoshop',
+        },
+    },
+);
+
+# tags in PDF AdobePhotoshop directory
+%Image::ExifTool::PDF::AdobePhotoshop = (
+    Private => {
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::PDF::Private',
+        },
+    },
+);
+
+# tags in PDF Private directory
+%Image::ExifTool::PDF::Private = (
+    ImageResources => {
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::PDF::ImageResources',
+        },
+    },
+);
+
+# tags in PDF ImageResources directory
+%Image::ExifTool::PDF::ImageResources = (
+    stream => {
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::Photoshop::Main',
+        },
+    },
+);
+
+# tags in PDF Metadata directory
+%Image::ExifTool::PDF::Metadata = (
+    GROUPS => { 2 => 'Image' },
+    XML_stream => { # this is the stream for a Subtype /XML dictionary (not a real tag)
+        Name => 'XMP',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::XMP::Main',
+        },
+    },
+);
+
+# unknown tags for use in verbose option
+%Image::ExifTool::PDF::Unknown = (
+    GROUPS => { 2 => 'Unknown' },
+);
+
+#------------------------------------------------------------------------------
+# Issue one warning of each type
+# Inputs: 0) ExifTool object reference, 1) warning
+sub WarnOnce($$)
+{
+    my ($exifTool, $warn) = @_;
+    unless ($warnedOnce{$warn}) {
+        $warnedOnce{$warn} = 1;
+        $exifTool->Warn($warn);
+    }
+}
+
+#------------------------------------------------------------------------------
+# Set PDF format error warning
+# Inputs: 0) ExifTool object reference, 1) error string
+# Returns: 1
+sub PDFErr($$)
+{
+    my ($exifTool, $str) = @_;
+    $exifTool->Warn("PDF format error ($str)");
+    return 1;
+}
+
+#------------------------------------------------------------------------------
+# Convert from PDF to EXIF-style date/time
+# Inputs: 0) ExifTool object reference,
+#         1) PDF date/time string (D:yyyymmddhhmmss+hh'mm')
+# Returns: EXIF date string (yyyy:mm:dd hh:mm:ss+hh:mm)
+sub ConvertPDFDate($$)
+{
+    my ($exifTool, $date) = @_;
+    # remove optional 'D:' prefix
+    $date =~ s/^D://;
+    # fill in default values if necessary
+    #              yyyymmddhhmmss
+    my $default = '00000101000000';
+    if (length $date < length $default) {
+        $date .= substr($default, length $date);
+    }
+    $date =~ /^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})(.*)/ or return $date;
+    $date = "$1:$2:$3 $4:$5:$6";
+    if ($7) {
+        my @t = split /'/, $7;
+        $date .= $t[0];
+        $date .= ':' . ($t[1] || 0) if $t[0] ne 'Z';
+    }
+    return $exifTool->ConvertDateTime($date);
+}
+
+#------------------------------------------------------------------------------
+# Locate an object in the XRref tables
+# Inputs: 0) XRef reference, 1) object reference string
+# Returns: offset to object in file, or undefined on error
+sub LocateObject($$)
+{
+    my ($xref, $ref) = @_;
+    return undef unless $xref;
+    return $$xref{$ref} if $$xref{$ref};
+    # get the object number
+    return undef unless $ref =~ /^(\d+)/;
+    my $objNum = $1;
+#
+# scan our XRef stream dictionaries for this object
+#
+    return undef unless $$xref{dicts};
+    my $dict;
+    foreach $dict (@{$$xref{dicts}}) {
+        # quick check to see if the object is in the range for this xref stream
+        next if $objNum >= $$dict{Size};
+        my $index = $$dict{Index};
+        next if $objNum < $$index[0];
+        # scan the tables for the specified object
+        my $size = $$dict{entry_size};
+        my $num = scalar(@$index) / 2;
+        my $tot = 0;
+        my $i;
+        for ($i=0; $i<$num; ++$i) {
+            my $start = $$index[$i*2];
+            my $count = $$index[$i*2+1];
+            # table is in ascending order, so quit if we have passed the object
+            last if $objNum >= $start + $count;
+            if ($objNum >= $start) {
+                my $offset = $size * ($objNum - $start + $tot);
+                last if $offset + $size > length $$dict{stream};
+                my @c = unpack("x$offset C$size", $$dict{stream});
+                # extract values from this table entry
+                # (can be 1, 2, 3, 4, etc.. bytes per value)
+                my (@t, $j, $k, $ref2);
+                my $w = $$dict{W};
+                for ($j=0; $j<3; ++$j) {
+                    # use default value if W entry is 0 (as per spec)
+                    $$w[$j] or $t[$j] = ($j ? 1 : 0), next;
+                    $t[$j] = shift(@c);
+                    for ($k=1; $k < $$w[$j]; ++$k) {
+                        $t[$j] = 256 * $t[$j] + shift(@c);
+                    }
+                }
+                if ($t[0] == 1) {
+                    # normal object reference: use "o g R" as hash ref
+                    # (o = object number, g = generation number)
+                    $ref2 = "$objNum $t[2] R";
+                    # xref is offset of object from start
+                    $$xref{$ref2} = $t[1];
+                } elsif ($t[0] == 2) {
+                    # compressed object reference:
+                    $ref2 = "$objNum 0 R";
+                    # xref is object index and stream object reference
+                    $$xref{$ref2} = "I$t[2] $t[1] 0 R";
+                } else {
+                    last;
+                }
+                return $$xref{$ref} if $ref eq $ref2;
+            }
+            $tot += $count;
+        }
+    }
+    return undef;
+}
+
+#------------------------------------------------------------------------------
+# Fetch indirect object from file (from inside a stream if required)
+# Inputs: 0) ExifTool object reference, 1) object reference string, 2) xref lookup,
+# Returns: object data or undefined on error
+sub FetchObject($$$$)
+{
+    my ($exifTool, $ref, $xref, $tag) = @_;
+    $lastFetched = $ref;    # save this for decoding if necessary
+    my $offset = LocateObject($xref, $ref);
+    unless ($offset) {
+        $exifTool->Warn("Bad $tag reference");
+        return undef;
+    }
+    my ($data, $obj);
+    if ($offset =~ s/^I(\d+) //) {
+        my $index = $1; # object index in stream
+        my ($objNum) = split ' ', $ref; # save original object number
+        $ref = $offset; # now a reference to the containing stream object
+        my $obj = $streamObjs{$ref};
+        unless ($obj) {
+            # don't try to load the same object stream twice
+            return undef if defined $obj;
+            $streamObjs{$ref} = '';
+            # load the parent object stream
+            $obj = FetchObject($exifTool, $ref, $xref, $tag);
+            # make sure it contains everything we need
+            return undef unless defined $obj and ref($obj) eq 'HASH';
+            return undef unless $$obj{First} and $$obj{N};
+            return undef unless DecodeStream($exifTool, $obj);
+            # add a special 'table' entry to this dictionary which contains
+            # the list of object number/offset pairs from the stream header
+            my $num = $$obj{N} * 2;
+            my @table = split ' ', $$obj{stream}, $num;
+            return undef unless @table == $num;
+            # remove everything before first object in stream
+            $$obj{stream} = substr($$obj{stream}, $$obj{First});
+            $table[$num-1] =~ s/^(\d+).*/$1/;  # trim excess from last number
+            $$obj{table} = \@table;
+            # save the object stream so we don't have to re-load it later
+            $streamObjs{$ref} = $obj;
+        }
+        # verify that we have the specified object
+        my $i = 2 * $index;
+        my $table = $$obj{table};
+        unless ($index < $$obj{N} and $$table[$i] == $objNum) {
+            $exifTool->Warn("Bad index for stream object $tag");
+            return undef;
+        }
+        # extract the object at the specified index in the stream
+        # (offsets in table are in sequential order, so we can subract from
+        #  the next offset to get the object length)
+        $offset = $$table[$i + 1];
+        my $len = ($$table[$i + 3] || length($$obj{stream})) - $offset;
+        $data = substr($$obj{stream}, $offset, $len);
+        return ExtractObject($exifTool, \$data);
+    }
+    my $raf = $exifTool->{RAF};
+    $raf->Seek($offset, 0) or $exifTool->Warn("Bad $tag offset"), return undef;
+    # verify that we are reading the expected object
+    $raf->ReadLine($data) or $exifTool->Warn("Error reading $tag data"), return undef;
+    ($obj = $ref) =~ s/R/obj/;
+    unless ($data =~ s/^$obj//) {
+        $exifTool->Warn("$tag object ($obj) not found at $offset");
+        return undef;
+    }
+    return ExtractObject($exifTool, \$data, $raf, $xref);
+}
+
+#------------------------------------------------------------------------------
+# Extract PDF object from combination of buffered data and file
+# Inputs: 0) ExifTool object reference, 1) data reference,
+#         2) optional raf reference, 3) optional xref table
+# Returns: converted PDF object or undef on error
+#          a) dictionary object --> hash reference
+#          b) array object --> array reference
+#          c) indirect reference --> scalar reference
+#          d) string, name, integer, boolean, null --> scalar value
+# - updates $$dataPt on return to contain unused data
+# - creates two bogus entries ('stream' and 'tags') in dictionaries to represent
+#   the stream data and a list of the tags (not including 'stream' and 'tags')
+#   in their original order
+sub ExtractObject($$;$$)
+{
+    my ($exifTool, $dataPt, $raf, $xref) = @_;
+    my (@tags, $data, $objData);
+    my $dict = { };
+    my $delim;
+
+    for (;;) {
+        if ($$dataPt =~ /^\s*(<{1,2}|\[|\()/) {
+            $delim = $1;
+            $objData = ReadToNested($dataPt, $raf);
+            return undef unless defined $objData;
+            last;
+        } elsif ($$dataPt =~ s{^\s*(\S[^[(/<>\s]*)\s*}{}) {
+#
+# extract boolean, numerical, string, name, null object or indirect reference
+#
+            $objData = $1;
+            # look for an indirect reference
+            if ($objData =~ /^\d+$/ and $$dataPt =~ s/^(\d+)\s+R//) {
+                $objData .= "$1 R";
+                $objData = \$objData;   # return scalar reference
+            }
+            return $objData;    # return simple scalar or scalar reference
+        }
+        $raf and $raf->ReadLine($data) or return undef;
+        $$dataPt .= $data;
+    }
+#
+# extract literal string
+#
+    if ($delim eq '(') {
+        $objData = $1 if $objData =~ /.*?\((.*)\)/s;    # remove brackets
+        # decode escape sequences in literal strings
+        while ($objData =~ /\\(.)/sg) {
+            my $n = pos($objData) - 2;
+            my $c = $1;
+            my $r;
+            if ($c =~ /[0-7]/) {
+                # get up to 2 more octal digits
+                $c .= $1 if $objData =~ /\G([0-7]{1,2})/g;
+                # convert octal escape code
+                $r = chr(oct($c) & 0xff);
+            } elsif ($c eq "\x0d") {
+                # the string is continued if the line ends with '\'
+                # (also remove "\x0d\x0a")
+                $c .= $1 if $objData =~ /\G(\x0a)/g;
+                $r = '';
+            } elsif ($c eq "\x0a") {
+                # (also remove "\x0a\x0d")
+                $c .= $1 if $objData =~ /\G(\x0d)/g;
+                $r = '';
+            } else {
+                # convert escaped characters
+                ($r = $c) =~ tr/nrtbf/\n\r\t\b\f/;
+            }
+            substr($objData, $n, length($c)+1) = $r;
+            # contine search after this character
+            pos($objData) = $n + length($r);
+        }
+        Decrypt(\$objData) if $cryptInfo;
+        # convert from UTF-16 (big endian) to UTF-8 or Latin if necessary
+        if ($objData =~ s/^\xfe\xff//) {
+            $objData = $exifTool->Unicode2Charset($objData, 'MM');
+        }
+        return $objData;
+#
+# extract hex string
+#
+    } elsif ($delim eq '<') {
+        # decode hex data
+        $objData =~ tr/0-9A-Fa-f//dc;
+        $objData .= '0' if length($objData) & 0x01; # (by the spec)
+        $objData = pack('H*', $objData);
+        Decrypt(\$objData) if $cryptInfo;
+        return $objData;
+#
+# extract array
+#
+    } elsif ($delim eq '[') {
+        $objData =~ /.*?\[(.*)\]/s or return;    # remove brackets
+        my $data = $1;
+        my @list;
+        for (;;) {
+            last unless $data =~ m{\s*(\S[^[(/<>\s]*)}sg;
+            my $val = $1;
+            if ($val =~ /^(<{1,2}|\[|\()/) {
+                my $pos = pos($data) - length($val);
+                # nested dict, array, literal string or hex string
+                my $buff = substr($data, $pos);
+                $val = ReadToNested(\$buff);
+                last unless defined $val;
+                pos($data) = $pos + length($val);
+                $val = ExtractObject($exifTool, \$val);
+            } elsif ($val =~ /^\d/) {
+                my $pos = pos($data);
+                if ($data =~ /\G\s+(\d+)\s+R/g) {
+                    $val = \ "$val $1 R";   # make a reference
+                } else {
+                    pos($data) = $pos;
+                }
+            }
+            push @list, $val;
+        }
+        return \@list;
+    }
+#
+# extract dictionary
+#
+    # Note: entries are not necessarily separated by whitespace (doh!)
+    # ie) "/Tag/Name", "/Tag(string)", "/Tag[array]", etc are legal!
+    # Also, they may be separated by a comment (ie. "/Tag%comment\nValue"),
+    # but comments have already been removed
+    while ($objData =~ m{(\s*)/([^/[\]()<>{}\s]+)\s*(\S[^[(/<>\s]*)}sg) {
+        my $tag = $2;
+        my $val = $3;
+        if ($val =~ /^(<{1,2}|\[|\()/) {
+            # nested dict, array, literal string or hex string
+            $objData = substr($objData, pos($objData)-length($val));
+            $val = ReadToNested(\$objData, $raf);
+            last unless defined $val;
+            $val = ExtractObject($exifTool, \$val);
+            pos($objData) = 0;
+        } elsif ($val =~ /^\d/) {
+            my $pos = pos($objData);
+            if ($objData =~ /\G\s+(\d+)\s+R/g) {
+                $val = \ "$val $1 R";   # make a reference
+            } else {
+                pos($objData) = $pos;
+            }
+        }
+        if ($$dict{$tag}) {
+            # duplicate dictionary entries are not allowed
+            $exifTool->Warn("Duplicate $tag entry in dictionary (ignored)");
+        } else {
+            # save the entry
+            push @tags, $tag;
+            $$dict{$tag} = $val;
+        }
+    }
+    return undef unless @tags;
+    $$dict{tags} = \@tags;
+    return $dict unless $raf;   # direct objects can not have streams
+#
+# extract the stream object
+#
+    # dictionary must specify stream Length
+    my $length = $$dict{Length} or return $dict;
+    if (ref $length) {
+        $length = $$length;
+        my $oldpos = $raf->Tell();
+        # get the location of the object specifying the length
+        my $offset = LocateObject($xref, $length) or return $dict;
+        $raf->Seek($offset, 0) or $exifTool->Warn("Bad Length offset"), return $dict;
+        # verify that we are reading the expected object
+        $raf->ReadLine($data) or $exifTool->Warn("Error reading Length data"), return $dict;
+        $length =~ s/R/obj/;
+        unless ($data =~ /^$length/) {
+            $exifTool->Warn("Length object ($length) not found at $offset");
+            return $dict;
+        }
+        $raf->ReadLine($data) or $exifTool->Warn("Error reading stream Length"), return $dict;
+        $data =~ /(\d+)/ or $exifTool->Warn("Stream length not found"), return $dict;
+        $length = $1;
+        $raf->Seek($oldpos, 0); # restore position to start of stream
+    }
+    # extract the trailing stream data
+    for (;;) {
+        # find the stream token
+        if ($$dataPt =~ /(\S+)/) {
+            last unless $1 eq 'stream';
+            # read an extra line because it may contain our \x0a
+            $$dataPt .= $data if $raf->ReadLine($data);
+            # remove our stream header
+            $$dataPt =~ s/^.*stream(\x0a|\x0d\x0a)//s;
+            my $more = $length - length($$dataPt);
+            if ($more > 0) {
+                unless ($raf->Read($data, $more) == $more) {
+                    $exifTool->Warn("Error reading stream data");
+                    $$dataPt = '';
+                    return $dict;
+                }
+                $$dict{stream} = $$dataPt . $data;
+                $$dataPt = '';
+            } elsif ($more < 0) {
+                $$dict{stream} = substr($$dataPt, 0, $length);
+                $$dataPt = substr($$dataPt, $length);
+            } else {
+                $$dict{stream} = $$dataPt;
+                $$dataPt = '';
+            }
+            last;
+        }
+        $raf->ReadLine($data) or last;
+        $$dataPt .= $data;
+    }
+    return $dict;
+}
+
+#------------------------------------------------------------------------------
+# Read to nested delimiter
+# Inputs: 0) data reference, 1) optional raf reference
+# Returns: data up to and including matching delimiter (or undef on error)
+# - updates data reference with trailing data
+# - unescapes characters in literal strings
+sub ReadToNested($;$)
+{
+    my ($dataPt, $raf) = @_;
+    # matching closing delimiters
+    my %closingDelim = (
+        '<<' => '>>',
+        '('  => ')',
+        '['  => ']',
+        '<'  => '>',
+    );
+    my @delim = ('');   # closing delimiter list, most deeply nested first
+    pos($$dataPt) = 0;  # begin at start of data
+    for (;;) {
+        unless ($$dataPt =~ /(\\*)(\(|\)|<{1,2}|>{1,2}|\[|\]|%)/g) {
+            # must read some more data
+            my $buff;
+            last unless $raf and $raf->ReadLine($buff);
+            $$dataPt .= $buff;
+            pos($$dataPt) = length($$dataPt) - length($buff);
+            next;
+        }
+        # are we in a literal string?
+        if ($delim[0] eq ')') {
+            # ignore escaped delimiters (preceeded by odd number of \'s)
+            next if length($1) & 0x01;
+            # ignore all delimiters but unescaped braces
+            next unless $2 eq '(' or $2 eq ')';
+        } elsif ($2 eq '%') {
+            # ignore the comment
+            my $pos = pos($$dataPt) - 1;
+            # remove everything from '%' up to but not including newline
+            $$dataPt =~ /.*/g;
+            my $end = pos($$dataPt);
+            $$dataPt = substr($$dataPt, 0, $pos) . substr($$dataPt, $end);
+            pos($$dataPt) = $pos;
+            next;
+        }
+        if ($closingDelim{$2}) {
+            # push the corresponding closing delimiter
+            unshift @delim, $closingDelim{$2};
+            next;
+        }
+        unless ($2 eq $delim[0]) {
+            # handle the case where we find a ">>>" and interpret it
+            # as ">> >" instead of "> >>"
+            next unless $2 eq '>>' and $delim[0] eq '>';
+            pos($$dataPt) = pos($$dataPt) - 1;
+        }
+        my $delim = shift @delim;   # remove from nesting list
+        next if $delim[0];          # keep going if we have more nested delimiters
+        my $pos = pos($$dataPt);
+        my $buff = substr($$dataPt, 0, $pos);
+        $$dataPt = substr($$dataPt, $pos);
+        return $buff;   # success!
+    }
+    return undef;   # didn't find matching delimiter
+}
+
+#------------------------------------------------------------------------------
+# Decode filtered stream
+# Inputs: 0) ExifTool object reference, 1) dictionary reference
+# Returns: true if stream has been decoded OK
+sub DecodeStream($$)
+{
+    my ($exifTool, $dict) = @_;
+
+    return 0 unless $$dict{stream}; # no stream to decode
+    # apply decryption first if required
+    if ($cryptInfo and not $$dict{decrypted}) {
+        $$dict{decrypted} = 1;
+        if ($$cryptInfo{meta} or ($$dict{Type} and $$dict{Type} ne '/Metadata')) {
+            Decrypt(\$$dict{stream});
+        }
+    }
+    return 1 unless $$dict{Filter};
+    if ($$dict{Filter} eq '/FlateDecode') {
+        if (eval 'require Compress::Zlib') {
+            my $inflate = Compress::Zlib::inflateInit();
+            my ($buff, $stat);
+            $inflate and ($buff, $stat) = $inflate->inflate($$dict{stream});
+            if ($inflate and $stat == Compress::Zlib::Z_STREAM_END()) {
+                $$dict{stream} = $buff;
+                # move Filter to prevent double decoding
+                $$dict{oldFilter} = $$dict{Filter};
+                $$dict{Filter} = '';
+            } else {
+                $exifTool->Warn('Error inflating stream');
+                return 0;
+            }
+        } else {
+            WarnOnce($exifTool,'Install Compress::Zlib to decode filtered streams');
+            return 0;
+        }
+#
+# apply anti-predictor if necessary
+#
+        return 1 unless $$dict{DecodeParms};
+        my $pre = $dict->{DecodeParms}->{Predictor};
+        return 1 unless $pre and $pre != 1;
+        if ($pre != 12) {
+            # currently only support 'up' prediction
+            WarnOnce($exifTool,"FlateDecode Predictor $pre not currently supported");
+            return 0;
+        }
+        my $cols = $dict->{DecodeParms}->{Columns};
+        unless ($cols) {
+            # currently only support 'up' prediction
+            WarnOnce($exifTool,'No Columns for decoding stream');
+            return 0;
+        }
+        my @bytes = unpack('C*', $$dict{stream});
+        my @pre = (0) x $cols;  # initialize predictor array
+        my $buff = '';
+        while (@bytes > $cols) {
+            unless (($_ = shift @bytes) == 2) {
+                WarnOnce($exifTool, "Unsupported PNG filter $_");
+                return 0;
+            }
+            foreach (@pre) {
+                $_ = ($_ + shift(@bytes)) & 0xff;
+            }
+            $buff .= pack('C*', @pre);
+        }
+        $$dict{stream} = $buff;
+    } else {
+        WarnOnce($exifTool, "Unsupported Filter $$dict{Filter}");
+        return 0;
+    }
+    return 1;
+}
+
+#------------------------------------------------------------------------------
+# Initialize state for RC4 en/decryption (ref 2)
+# Inputs: 0) RC4 key string
+# Returns: RC4 key hash reference
+sub RC4Init($)
+{
+    my @key = unpack('C*', shift);
+    my @state = (0 .. 255);
+    my ($i, $j) = (0, 0);
+    while ($i < 256) {
+        my $st = $state[$i];
+        $j = ($j + $st + $key[$i % scalar(@key)]) & 0xff;
+        $state[$i++] = $state[$j];
+        $state[$j] = $st;
+    }
+    return { State => \@state, XY => [ 0, 0 ] };
+}
+
+#------------------------------------------------------------------------------
+# Apply RC4 en/decryption (ref 2)
+# Inputs: 0) data reference, 1) RC4 key hash reference or RC4 key string
+# - can call this method directly with a key string, or with with the key
+#   reference returned by RC4Init
+# - RC4 is a symmetric algorithm, so encryption is the same as decryption
+sub RC4Crypt($$)
+{
+    my ($dataPt, $key) = @_;
+    $key = RC4Init($key) unless ref $key eq 'HASH';
+    my $state = $$key{State};
+    my ($x, $y) = @{$$key{XY}};
+
+    my @data = unpack('C*', $$dataPt);
+    foreach (@data) {
+         $x = ($x + 1) & 0xff;
+         my $stx = $$state[$x];
+         $y = ($stx + $y) & 0xff;
+         my $sty = $$state[$x] = $$state[$y];
+         $$state[$y] = $stx;
+         $_ ^= $$state[($stx + $sty) & 0xff];
+     }
+     $$key{XY} = [ $x, $y ];
+     $$dataPt = pack('C*', @data);
+}
+
+#------------------------------------------------------------------------------
+# Initialize decryption
+# Inputs: 0) ExifTool object reference, 1) Encrypt dictionary reference,
+#         2) ID from file trailer dictionary
+# Returns: error string or undef on success
+sub DecryptInit($$$)
+{
+    my ($exifTool, $encrypt, $id) = @_;
+    unless ($encrypt and ref $encrypt eq 'HASH') {
+        return 'Error loading Encrypt object';
+    }
+    my $filt = $$encrypt{Filter};
+    unless ($filt and $filt =~ s/^\///) {
+        return 'Encrypt dictionary has no Filter!';
+    }
+    my $ver = $$encrypt{V} || 0;
+    my $rev = $$encrypt{R} || 0;
+    $exifTool->FoundTag('Encryption', "$filt v$ver.$rev");
+    unless ($$encrypt{Filter} eq '/Standard') {
+        $$encrypt{Filter} =~ s/^\///;
+        return "PDF '$$encrypt{Filter}' encryption not currently supported";
+    }
+    unless ($$encrypt{O} and $$encrypt{P} and $$encrypt{U}) {
+        return 'Incomplete Encrypt specification';
+    }
+    unless ($ver == 1 or $ver == 2) {
+        return "Encryption algorithm $ver currently not supported";
+    }
+    $id or return "Can't decrypt (no document ID)";
+    unless (eval 'require Digest::MD5') {
+        return 'Install Digest::MD5 to extract encrypted information';
+    }
+    # calculate file-level en/decryption key
+    my $pad = "\x28\xBF\x4E\x5E\x4E\x75\x8A\x41\x64\x00\x4E\x56\xFF\xFA\x01\x08".
+              "\x2E\x2E\x00\xB6\xD0\x68\x3E\x80\x2F\x0C\xA9\xFE\x64\x53\x69\x7A";
+    my $key = $pad . $$encrypt{O} . pack('V', $$encrypt{P}) . $id;
+    my $rep = 1;
+    $$encrypt{meta} = 1; # set flag that Metadata is encrypted
+    if ($rev >= 3) {
+        # in rev 4 (not yet supported), metadata streams may not be encrypted
+        if ($$encrypt{EncryptMetadata} and $$encrypt{EncryptMetadata} =~ /false/i) {
+            delete $$encrypt{meta};     # Meta data isn't encrypted after all
+            $key .= "\xff\xff\xff\xff"; # must add this if metadata not encrypted
+        }
+        $rep += 50; # repeat MD5 50 more times if revision is 3 or greater
+    }
+    my ($len, $i);
+    if ($ver == 1) {
+        $len = 5;
+    } else {
+        $len = $$encrypt{Length} || 40;
+        $len >= 40 or return 'Bad Encrypt Length';
+        $len = int($len / 8);
+    }
+    for ($i=0; $i<$rep; ++$i) {
+        $key = substr(Digest::MD5::md5($key), 0, $len);
+    }
+    # decrypt U to see if a user password is required
+    my $dat;
+    if ($rev >= 3) {
+        $dat = Digest::MD5::md5($pad . $id);
+        RC4Crypt(\$dat, $key);
+        for ($i=1; $i<=19; ++$i) {
+            my @key = unpack('C*', $key);
+            foreach (@key) { $_ ^= $i; }
+            RC4Crypt(\$dat, pack('C*', @key));
+        }
+        $dat .= substr($$encrypt{U}, 16);
+    } else {
+        $dat = $pad;
+        RC4Crypt(\$dat, $key);
+    }
+    $dat eq $$encrypt{U} or return 'Document is password encrypted';
+    $$encrypt{key} = $key;  # save the file-level encryption key
+    $cryptInfo = $encrypt;  # save a reference to the Encrypt object
+    return undef;           # success!
+}
+
+#------------------------------------------------------------------------------
+# Decrypt data
+# Inputs: 0) data reference
+sub Decrypt($)
+{
+    my $dataPt = shift;
+    my $key = $$cryptInfo{key};
+    my $len = length($key) + 5;
+    return unless $lastFetched =~ /^(I\d+ )?(\d+) (\d+)/;
+    $key .= substr(pack('V', $2), 0, 3) . substr(pack('V', $3), 0, 2);
+    $len = 16 if $len > 16;
+    $key = substr(Digest::MD5::md5($key), 0, $len);
+    RC4Crypt($dataPt, $key);
+}
+
+#------------------------------------------------------------------------------
+# Process PDF dictionary extract tag values
+# Inputs: 0) ExifTool object reference, 1) tag table reference
+#         2) dictionary reference, 3) cross-reference table reference,
+#         4) nesting depth
+sub ProcessDict($$$$;$)
+{
+    my ($exifTool, $tagTablePtr, $dict, $xref, $nesting) = @_;
+    my $verbose = $exifTool->Options('Verbose');
+    my @tags = @{$$dict{tags}};
+    my $index = 0;
+    my $next;
+
+    $nesting = ($nesting || 0) + 1;
+    if ($nesting > 50) {
+        WarnOnce($exifTool, 'Nesting too deep (directory ignored)');
+        return;
+    }
+#
+# extract information from all tags in the dictionary
+#
+    for (;;) {
+        my ($tag, $tagInfo);
+        if (@tags) {
+            $tag = shift @tags;
+        } elsif (defined $next and not $next) {
+            $tag = 'Next';
+            $next = 1;
+        } else {
+            last;
+        }
+        if ($$tagTablePtr{$tag}) {
+            $tagInfo = $exifTool->GetTagInfo($tagTablePtr, $tag);
+        }
+        my $val = $$dict{$tag};
+        if ($verbose) {
+            my ($val2, $extra);
+            if (ref $val eq 'SCALAR') {
+                $extra = ", indirect object ($$val)";
+                if ($fetched{$$val}) {
+                    $val2 = "ref($$val)";
+                } elsif ($tag eq 'Next' and not $next) {
+                    # handle 'Next' links after all others
+                    $next = 0;
+                    next;
+                } else {
+                    $fetched{$$val} = 1;
+                    $val = FetchObject($exifTool, $$val, $xref, $tag);
+                    $val2 = '<err>' unless defined $val;
+                }
+            } elsif (ref $val eq 'HASH') {
+                $extra = ', direct dictionary';
+            } elsif (ref $val eq 'ARRAY') {
+                $extra = ', direct array of ' . scalar(@$val) . ' objects';
+            } else {
+                $extra = ', direct object';
+            }
+            my $isSubdir;
+            if (ref $val eq 'HASH') {
+                $isSubdir = 1;
+            } elsif (ref $val eq 'ARRAY') {
+                # recurse into objects in arrays only if they are lists of
+                # dictionaries or indirect objects which could be dictionaries
+                $isSubdir = 1 if @$val;
+                foreach (@$val) {
+                    next if ref $_ eq 'HASH' or ref $_ eq 'SCALAR';
+                    undef $isSubdir;
+                    last;
+                }
+            }
+            if ($isSubdir) {
+                # create bogus subdirectory to recurse into this dict
+                $tagInfo or $tagInfo = {
+                    Name => $tag,
+                    SubDirectory => {
+                        TagTable => 'Image::ExifTool::PDF::Unknown',
+                    },
+                };
+            } elsif (ref $val eq 'ARRAY') {
+                my @list = @$val;
+                foreach (@list) {
+                    $_ = "ref($$_)" if ref $_ eq 'SCALAR';
+                }
+                $val2 = '[' . join(',',@list) . ']';
+            }
+            $exifTool->VerboseInfo($tag, $tagInfo,
+                Value => $val2 || $val,
+                Extra => $extra,
+                Index => $index++,
+            );
+        }
+        unless ($tagInfo) {
+            # add any tag found in Info directory to table
+            next unless $$tagTablePtr{EXTRACT_UNKNOWN};
+            $tagInfo = { Name => $tag };
+            Image::ExifTool::AddTagToTable($tagTablePtr, $tag, $tagInfo);
+        }
+        unless ($$tagInfo{SubDirectory}) {
+            # convert from UTF-16 (big endian) to UTF-8 or Latin if necessary
+            # unless this is binary data (hex-encoded strings would not have been converted)
+            if ($val =~ s/^\xfe\xff// and not $$tagInfo{Binary}) {
+                $val = $exifTool->Unicode2Charset($val, 'MM');
+            }
+            if ($$tagInfo{List}) {
+                # separate tokens in comma or whitespace delimited lists
+                my @values = ($val =~ /,/) ? split /,+\s*/, $val : split ' ', $val;
+                foreach $val (@values) {
+                    $exifTool->FoundTag($tagInfo, $val);
+                }
+            } else {
+                # a tag value
+                $exifTool->FoundTag($tagInfo, $val);
+            }
+            next;
+        }
+        # process the subdirectory
+        my @subDicts;
+        if (ref $val eq 'ARRAY') {
+            @subDicts = @{$val};
+        } else {
+            @subDicts = ( $val );
+        }
+        # loop through all values of this tag
+        for (;;) {
+            my $subDict = shift @subDicts or last;
+            if (ref $subDict eq 'SCALAR') {
+                # load dictionary via an indirect reference
+                $fetched{$$subDict} = 1;
+                $subDict = FetchObject($exifTool, $$subDict, $xref, $tag);
+                $subDict or $exifTool->Warn("Error reading $tag object"), next;
+            }
+            if (ref $subDict eq 'ARRAY') {
+                # convert array of key/value pairs to a hash
+                next if @$subDict < 2;
+                my %hash = ( tags => [] );
+                while (@$subDict >= 2) {
+                    my $key = shift @$subDict;
+                    $key =~ s/^\///;
+                    push @{$hash{tags}}, $key;
+                    $hash{$key} = shift @$subDict;
+                }
+                $subDict = \%hash;
+            } else {
+                next unless ref $subDict eq 'HASH';
+            }
+            my $subTablePtr = GetTagTable($tagInfo->{SubDirectory}->{TagTable});
+            if (not $verbose) {
+                ProcessDict($exifTool, $subTablePtr, $subDict, $xref, $nesting);
+            } elsif ($next) {
+                # handle 'Next' links at this level to avoid deep recursion
+                undef $next;
+                $index = 0;
+                $tagTablePtr = $subTablePtr;
+                $dict = $subDict;
+                @tags = @{$$subDict{tags}};
+                $exifTool->VerboseDir($tag, scalar(@tags));
+            } else {
+                my $oldIndent = $exifTool->{INDENT};
+                my $oldDir = $exifTool->{DIR_NAME};
+                $exifTool->{INDENT} .= '| ';
+                $exifTool->{DIR_NAME} = $tag;
+                $exifTool->VerboseDir($tag, scalar(@{$$subDict{tags}}));
+                ProcessDict($exifTool, $subTablePtr, $subDict, $xref, $nesting);
+                $exifTool->{INDENT} = $oldIndent;
+                $exifTool->{DIR_NAME} = $oldDir;
+            }
+        }
+    }
+#
+# extract information from stream object if it exists
+#
+    return unless $$dict{stream};
+    my $tag = 'stream';
+    # add Subtype (if it exists) to stream name and remove leading '/'
+    ($tag = "$$dict{Subtype}_$tag") =~ s/^\/// if $$dict{Subtype};
+    return unless $$tagTablePtr{$tag};
+    my $tagInfo = $exifTool->GetTagInfo($tagTablePtr, $tag);
+    # decode stream if necessary
+    DecodeStream($exifTool, $dict) or return;
+    # extract information from stream
+    my %dirInfo = (
+        DataPt => \$$dict{stream},
+        DataLen => length $$dict{stream},
+        DirStart => 0,
+        DirLen => length $$dict{stream},
+        Parent => 'PDF',
+    );
+    my $subTablePtr = GetTagTable($tagInfo->{SubDirectory}->{TagTable});
+    unless ($exifTool->ProcessDirectory(\%dirInfo, $subTablePtr)) {
+        $exifTool->Warn("Error processing $$tagInfo{Name} information");
+    }
+}
+
+#------------------------------------------------------------------------------
+# Extract information from PDF file
+# Inputs: 0) ExifTool object reference, 1) dirInfo reference
+# Returns: 0 if not a PDF file, 1 on success, otherwise a negative error number
+sub ReadPDF($$)
+{
+    my ($exifTool, $dirInfo) = @_;
+    my $raf = $$dirInfo{RAF};
+    my $verbose = $exifTool->Options('Verbose');
+    my ($data, $encrypt, $id);
+#
+# validate PDF file
+#
+    $raf->Read($data, 4) == 4 or return 0;
+    $data eq '%PDF' or return 0;
+    $exifTool->SetFileType();   # set the FileType tag
+    my $tagTablePtr = GetTagTable('Image::ExifTool::PDF::Main');
+#
+# read the xref tables referenced from startxref at the end of the file
+#
+    my @xrefOffsets;
+    $raf->Seek(0, 2) or return -2;
+    # the %%EOF must occur within the last 1024 bytes of the file (PDF spec, appendix H)
+    my $len = $raf->Tell();
+    $len = 1024 if $len > 1024;
+    $raf->Seek(-$len, 2) or return -2;
+    $raf->Read($data, $len) == $len or return -3;
+    $data =~ /.*startxref(\x0d\x0a|\x0a\x0a|\x0d|\x0a)(\d+)\1%%EOF/s or return -4;
+    $/ = $1;    # set input record separator
+    push @xrefOffsets, $2;
+    my (%xref, @mainDicts, %loaded);
+    while (@xrefOffsets) {
+        my $offset = shift @xrefOffsets;
+        next if $loaded{$offset};   # avoid infinite recursion
+        unless ($raf->Seek($offset, 0)) {
+            %loaded or return -5;
+            $exifTool->Warn('Bad offset for secondary xref table');
+            next;
+        }
+        unless ($raf->ReadLine($data)) {
+            %loaded or return -6;
+            $exifTool->Warn('Bad offset for secondary xref table');
+            next;
+        }
+        my $loadXRefStream;
+        if ($data eq "xref$/") {
+            # load xref table
+            for (;;) {
+                $raf->ReadLine($data) or return -6;
+                last if $data eq "trailer$/";
+                my ($start, $num) = $data =~ /(\d+)\s+(\d+)/;
+                $num or return -4;
+                my $i;
+                for ($i=0; $i<$num; ++$i) {
+                    $raf->Read($data, 20) == 20 or return -6;
+                    $data =~ /^(\d{10}) (\d{5}) (f|n)/ or return -4;
+                    next if $3 eq 'f';  # ignore free entries
+                    # save the object offset keyed by its reference
+                    my $ref = ($start + $i) . ' ' . int($2) . ' R';
+                    $xref{$ref} = int($1);
+                }
+            }
+            %xref or return -4;
+            $data = '';
+        } elsif ($data =~ s/^(\d+)\s+(\d+)\s+obj//) {
+            # this is a PDF-1.5 cross-reference stream dictionary
+            $loadXRefStream = 1;
+        } else {
+            %loaded or return -4;
+            $exifTool->Warn('Invalid secondary xref table');
+            next;
+        }
+        my $mainDict = ExtractObject($exifTool, \$data, $raf, \%xref);
+        unless ($mainDict) {
+            %loaded or return -8;
+            $exifTool->Warn('Error loading secondary dictionary');
+            next;
+        }
+        if ($loadXRefStream) {
+            # decode and save our XRef stream from PDF-1.5 file
+            # (parse it later as required to avoid wasting time)
+            if ($$mainDict{Type} eq '/XRef' and $$mainDict{W} and
+                @{$$mainDict{W}} > 2 and $$mainDict{Size} and
+                DecodeStream($exifTool, $mainDict))
+            {
+                # create Index entry if it doesn't exist
+                $$mainDict{Index} or $$mainDict{Index} = [ 0, $$mainDict{Size} ];
+                # create 'entry_size' entry for internal use
+                my $w = $$mainDict{W};
+                my $size = 0;
+                foreach (@$w) { $size += $_; }
+                $$mainDict{entry_size} = $size;
+                # save this stream dictionary to use later if required
+                $xref{dicts} = [] unless $xref{dicts};
+                push @{$xref{dicts}}, $mainDict;
+            } else {
+                %loaded or return -9;
+                $exifTool->Warn('Invalid xref stream in secondary dictionary');
+            }
+        }
+        $loaded{$offset} = 1;
+        # load XRef stream in hybrid file if it exists
+        push @xrefOffsets, $$mainDict{XRefStm} if $$mainDict{XRefStm};
+        $encrypt = $$mainDict{Encrypt} if $$mainDict{Encrypt};
+        if ($$mainDict{ID} and ref $$mainDict{ID} eq 'ARRAY') {
+            $id = $mainDict->{ID}->[0];
+        }
+        push @mainDicts, $mainDict;
+        # load previous xref table if it exists
+        push @xrefOffsets, $$mainDict{Prev} if $$mainDict{Prev};
+    }
+#
+# extract encryption information if necessary
+#
+    if ($encrypt) {
+        if (ref $encrypt eq 'SCALAR') {
+            $encrypt = FetchObject($exifTool, $$encrypt, \%xref, 'Encrypt');
+        }
+        # generate Encryption tag information
+        my $err = DecryptInit($exifTool, $encrypt, $id);
+        $err and $exifTool->Warn($err), return -1;
+    }
+#
+# extract the information beginning with each of the main dictionaries
+#
+    my $dict;
+    foreach $dict (@mainDicts) {
+        if ($verbose) {
+            my $n = scalar(@{$$dict{tags}});
+            $exifTool->VPrint(0, "PDF dictionary with $n entries:\n");
+        }
+        ProcessDict($exifTool, $tagTablePtr, $dict, \%xref);
+    }
+    return 1;
+}
+
+#------------------------------------------------------------------------------
+# ReadPDF() warning strings for each error return value
+my %pdfWarning = (
+    # -1 is reserved as error return value with no associated warning
+    -2 => 'Error seeking in file',
+    -3 => 'Error reading file',
+    -4 => 'Invalid xref table',
+    -5 => 'Invalid xref offset',
+    -6 => 'Error reading xref table',
+    -7 => 'Error reading trailer',
+    -8 => 'Error reading main dictionary',
+    -9 => 'Invalid xref stream in main dictionary',
+);
+
+#------------------------------------------------------------------------------
+# Extract information from PDF file
+# Inputs: 0) ExifTool object reference, 1) dirInfo reference
+# Returns: 1 if this was a valid PDF file
+sub ProcessPDF($$)
+{
+    my ($exifTool, $dirInfo) = @_;
+
+    my $oldsep = $/;
+    my $result = ReadPDF($exifTool, $dirInfo);
+    $/ = $oldsep;   # restore input record separator in case it was changed
+    if ($result < 0) {
+        $exifTool->Warn($pdfWarning{$result}) if $pdfWarning{$result};
+        $result = 1;
+    }
+    # clean up and return
+    undef %warnedOnce;
+    undef %streamObjs;
+    undef %fetched;
+    undef $cryptInfo;
+    return $result;
+}
+
+1; # end
+
+
+__END__
+
+=head1 NAME
+
+Image::ExifTool::PDF - Read PDF meta information
+
+=head1 SYNOPSIS
+
+This module is loaded automatically by Image::ExifTool when required.
+
+=head1 DESCRIPTION
+
+This code reads meta information from PDF (Adobe Portable Document Format)
+files.  It supports object streams introduced in PDF-1.5 but only with a
+limited set of Filter and Predictor algorithms, and it decodes encrypted
+information but only with a limited number of algorithms.
+
+=head1 AUTHOR
+
+Copyright 2003-2007, Phil Harvey (phil at owl.phy.queensu.ca)
+
+This library is free software; you can redistribute it and/or modify it
+under the same terms as Perl itself.
+
+=head1 REFERENCES
+
+=over 4
+
+=item L<http://partners.adobe.com/public/developer/pdf/index_reference.html>
+
+=item L<http://www.cr0.net:8040/code/crypto/rc4/>
+
+=back
+
+=head1 SEE ALSO
+
+L<Image::ExifTool::TagNames/PDF Tags>,
+L<Image::ExifTool(3pm)|Image::ExifTool>
+
+=cut
Index: /gsdl/trunk/perllib/cpan/Image/ExifTool/PICT.pm
===================================================================
--- /gsdl/trunk/perllib/cpan/Image/ExifTool/PICT.pm	(revision 16842)
+++ /gsdl/trunk/perllib/cpan/Image/ExifTool/PICT.pm	(revision 16842)
@@ -0,0 +1,1260 @@
+#------------------------------------------------------------------------------
+# File:         PICT.pm
+#
+# Description:  Read PICT meta information
+#
+# Revisions:    10/10/2005 - P. Harvey Created
+#
+# Notes:        Extraction of PICT opcodes is still experimental
+#
+# - size difference in PixPat color table?? (imagemagick reads only 1 long per entry)
+# - other differences in the way imagemagick reads 16-bit images
+#
+# References:   1) http://developer.apple.com/documentation/mac/QuickDraw/QuickDraw-2.html
+#               2) http://developer.apple.com/documentation/QuickTime/INMAC/QT/iqImageCompMgr.a.htm
+#------------------------------------------------------------------------------
+
+package Image::ExifTool::PICT;
+
+use strict;
+use vars qw($VERSION);
+use Image::ExifTool qw(:DataAccess :Utils);
+
+$VERSION = '1.03';
+
+sub ReadPictValue($$$;$);
+
+my ($vers, $extended);  # PICT version number, and extended flag
+my ($verbose, $out, $indent); # used in verbose mode
+
+# ranges of reserved opcodes.
+# opcodes at the start of each range must be defined in the tag table
+my @reserved = (
+    0x0017 => 0x0019, 0x0024 => 0x0027, 0x0035 => 0x0037, 0x003d => 0x003f,
+    0x0045 => 0x0047, 0x004d => 0x004f, 0x0055 => 0x0057, 0x005d => 0x005f,
+    0x0065 => 0x0067, 0x006d => 0x006f, 0x0075 => 0x0077, 0x007d => 0x007f,
+    0x0085 => 0x0087, 0x008d => 0x008f, 0x0092 => 0x0097, 0x00a2 => 0x00af,
+    0x00b0 => 0x00cf, 0x00d0 => 0x00fe, 0x0100 => 0x01ff, 0x0300 => 0x0bfe,
+    0x0c01 => 0x7eff, 0x7f00 => 0x7fff, 0x8000 => 0x80ff, 0x8100 => 0x81ff,
+    0x8201 => 0xffff,
+);
+
+# Apple data structures in PICT images
+my %structs = (
+    Arc => [
+        rect => 'Rect',
+        startAng => 'int16s',
+        arcAng => 'int16s',
+    ],
+    BitMap => [
+        # (no baseAddr)
+        rowBytes => 'int16u',
+        bounds => 'Rect',
+    ],
+    # BitsRect data for PICT version 1
+    BitsRect1 => [
+        bitMap => 'BitMap',
+        srcRect => 'Rect',
+        dstRect => 'Rect',
+        mode => 'int16u',
+        dataSize => 'int16u',
+        bitData => 'binary[$val{dataSize}]',
+    ],
+    # BitsRect data for PICT version 2
+    BitsRect2 => [
+        pixMap => 'PixMap',
+        colorTable => 'ColorTable',
+        srcRect => 'Rect',
+        dstRect => 'Rect',
+        mode => 'int16u',
+        pixData => \ 'GetPixData($val{pixMap}, $raf)',
+    ],
+    # BitsRgn data for PICT version 1
+    BitsRgn1 => [
+        bitMap => 'BitMap',
+        srcRect => 'Rect',
+        dstRect => 'Rect',
+        mode => 'int16u',
+        maskRgn => 'Rgn',
+        dataSize => 'int16u',
+        bitData => 'binary[$val{dataSize}]',
+    ],
+    # BitsRgn data for PICT version 2
+    BitsRgn2 => [
+        pixMap => 'PixMap',
+        colorTable => 'ColorTable',
+        srcRect => 'Rect',
+        dstRect => 'Rect',
+        mode => 'int16u',
+        maskRgn => 'Rgn',
+        pixData => \ 'GetPixData($val{pixMap}, $raf)',
+    ],
+    ColorSpec => [
+        value => 'int16u',
+        rgb => 'RGBColor',
+    ],
+    ColorTable => [
+        ctSeed => 'int32u',
+        ctFlags => 'int16u',
+        ctSize => 'int16u',
+        ctTable => 'ColorSpec[$val{ctSize}+1]',
+    ],
+    # http://developer.apple.com/documentation/QuickTime/INMAC/QT/iqImageCompMgr.a.htm
+    CompressedQuickTime => [
+        size => 'int32u',   # size NOT including size word
+        version => 'int16u',
+        matrix => 'int32u[9]',
+        matteSize => 'int32u',
+        matteRect => 'Rect',
+        mode => 'int16u',
+        srcRect => 'Rect',
+        accuracy => 'int32u',
+        maskSize => 'int32u',
+        matteDescr => 'Int32uData[$val{matteSize} ? 1 : 0]',
+        matteData => 'int8u[$val{matteSize}]',
+        maskRgn => 'int8u[$val{maskSize}]',
+        imageDescr => 'ImageDescription',
+        # size should be $val{imageDescr}->{dataSize}, but this is unreliable
+        imageData => q{binary[$val{size} - 68 - $val{maskSize} - $val{imageDescr}->{size} -
+                    ($val{matteSize} ? $val{mattSize} + $val{matteDescr}->{size} : 0)]
+        },
+    ],
+    DirectBitsRect => [
+        baseAddr => 'int32u',
+        pixMap => 'PixMap',
+        srcRect => 'Rect',
+        dstRect => 'Rect',
+        mode => 'int16u',
+        pixData => \ 'GetPixData($val{pixMap}, $raf)',
+    ],
+    DirectBitsRgn => [
+        baseAddr => 'int32u',
+        pixMap => 'PixMap',
+        srcRect => 'Rect',
+        dstRect => 'Rect',
+        mode => 'int16u',
+        maskRgn => 'Rgn',
+        pixData => \ 'GetPixData($val{pixMap}, $raf)',
+    ],
+    # http://developer.apple.com/technotes/qd/qd_01.html
+    FontName => [
+        size => 'int16u',   # size NOT including size word
+        oldFontID => 'int16u',
+        nameLen => 'int8u',
+        fontName => 'string[$val{nameLen}]',
+        padding => 'binary[$val{size} - $val{nameLen} - 3]',
+    ],
+    # http://developer.apple.com/documentation/QuickTime/APIREF/imagedescription.htm
+    ImageDescription => [
+        size => 'int32u',   # size INCLUDING size word
+        cType => 'string[4]',
+        res1 => 'int32u',
+        res2 => 'int16u',
+        dataRefIndex => 'int16u',
+        version => 'int16u',
+        revision => 'int16u',
+        vendor => 'string[4]',
+        temporalQuality => 'int32u',
+        quality => 'int32u',
+        width => 'int16u',
+        height => 'int16u',
+        hRes => 'fixed32u',
+        vRes => 'fixed32u',
+        dataSize => 'int32u',
+        frameCount => 'int16u',
+        nameLen => 'int8u',
+        compressor => 'string[31]',
+        depth => 'int16u',
+        clutID => 'int16u',
+        clutData => 'binary[$val{size}-86]',
+    ],
+    Int8uText => [
+        val => 'int8u',
+        count => 'int8u',
+        text => 'string[$val{count}]',
+    ],
+    Int8u2Text => [
+        val => 'int8u[2]',
+        count => 'int8u',
+        text => 'string[$val{count}]',
+    ],
+    Int16Data => [
+        size => 'int16u',   # size NOT including size word
+        data => 'int8u[$val{size}]',
+    ],
+    Int32uData => [
+        size => 'int32u',   # size NOT including size word
+        data => 'int8u[$val{size}]',
+    ],
+    LongComment => [
+        kind => 'int16u',
+        size => 'int16u',   # size of data only
+        data => 'binary[$val{size}]',
+    ],
+    PixMap => [
+        # Note: does not contain baseAddr
+        # (except for DirectBits opcodes in which it is loaded separately)
+        rowBytes => 'int16u',
+        bounds => 'Rect',
+        pmVersion => 'int16u',
+        packType => 'int16u',
+        packSize => 'int32u',
+        hRes => 'fixed32s',
+        vRes => 'fixed32s',
+        pixelType => 'int16u',
+        pixelSize => 'int16u',
+        cmpCount => 'int16u',
+        cmpSize => 'int16u',
+        planeBytes => 'int32u',
+        pmTable => 'int32u',
+        pmReserved => 'int32u',
+    ],
+    PixPat => [
+        patType => 'int16u',    # 1 = non-dithered, 2 = dithered
+        pat1Data => 'int8u[8]',
+        # dithered PixPat has RGB entry
+        RGB => 'RGBColor[$val{patType} == 2 ? 1 : 0]',
+        # non-dithered PixPat has other stuff instead
+        nonDithered=> 'PixPatNonDithered[$val{patType} == 2 ? 0 : 1]',
+    ],
+    PixPatNonDithered => [
+        pixMap => 'PixMap',
+        colorTable => 'ColorTable',
+        pixData => \ 'GetPixData($val{pixMap}, $raf)',
+    ],
+    Point => [
+        v => 'int16s',
+        h => 'int16s',
+    ],
+    PointText => [
+        txLoc => 'Point',
+        count => 'int8u',
+        text => 'string[$val{count}]',
+    ],
+    Polygon => [
+        polySize => 'int16u',
+        polyBBox => 'Rect',
+        polyPoints => 'int16u[($val{polySize}-10)/2]',
+    ],
+    Rect => [
+        topLeft => 'Point',
+        botRight => 'Point',
+    ],
+    RGBColor => [
+        red => 'int16u',
+        green => 'int16u',
+        blue => 'int16u',
+    ],
+    Rgn => [
+        rgnSize => 'int16u',
+        rgnBBox => 'Rect',
+        data => 'int8u[$val{rgnSize}-10]',
+    ],
+    ShortLine => [
+        pnLoc => 'Point',
+        dh => 'int8s',
+        dv => 'int8s',
+    ],
+    # http://developer.apple.com/documentation/QuickTime/INMAC/QT/iqImageCompMgr.a.htm
+    UncompressedQuickTime => [
+        size => 'int32u',   # size NOT including size word
+        version => 'int16u',
+        matrix => 'int32u[9]',
+        matteSize => 'int32u',
+        matteRect => 'Rect',
+        matteDescr => 'Int32uData[$val{matteSize} ? 1 : 0]',
+        matteData => 'binary[$val{matteSize}]',
+        subOpcodeData => q{
+            binary[ $val{size} - 50 -
+                    ($val{matteSize} ? $val{mattSize} + $val{matteDescr}->{size} : 0)]
+        },
+    ],
+);
+
+# PICT image opcodes
+%Image::ExifTool::PICT::Main = (
+    PROCESS_PROC => 0,  # set this to zero to omit tags from lookup
+    NOTES => q{
+The PICT format contains no true meta information, except for the possible
+exception of the LongComment opcode.  By default, only ImageWidth,
+ImageHeight and X/YResolution are extracted from a PICT image.  Tags in the
+following table represent image opcodes.  Extraction of these tags is
+experimental, and is only enabled with the Verbose or Unknown options.
+    },
+    0x0000 => {
+        Name => 'Nop',
+        Description => 'No Operation',
+        Format => 'null',
+    },
+    0x0001 => {
+        Name => 'ClipRgn',
+        Description => 'Clipping Region',
+        Format => 'Rgn',
+    },
+    0x0002 => {
+        Name => 'BkPat',
+        Description => 'Background Pattern',
+        Format => 'int8u[8]',
+    },
+    0x0003 => {
+        Name => 'TxFont',
+        Description => 'Font Number',
+        Format => 'int16u',
+    },
+    0x0004 => {
+        Name => 'TxFace',
+        Description => 'Text Font Style',
+        Format => 'int8u',
+    },
+    0x0005 => {
+        Name => 'TxMode',
+        Description => 'Text Source Mode',
+        Format => 'int16u',
+    },
+    0x0006 => {
+        Name => 'SpExtra',
+        Description => 'Extra Space',
+        Format => 'fixed32s',
+    },
+    0x0007 => {
+        Name => 'PnSize',
+        Description => 'Pen Size',
+        Format => 'Point',
+    },
+    0x0008 => {
+        Name => 'PnMode',
+        Description => 'Pen Mode',
+        Format => 'int16u',
+    },
+    0x0009 => {
+        Name => 'PnPat',
+        Description => 'Pen Pattern',
+        Format => 'int8u[8]',
+    },
+    0x000a => {
+        Name => 'FillPat',
+        Description => 'Fill Pattern',
+        Format => 'int8u[8]',
+    },
+    0x000b => {
+        Name => 'OvSize',
+        Description => 'Oval Size',
+        Format => 'Point',
+    },
+    0x000c => {
+        Name => 'Origin',
+        Format => 'Point',
+    },
+    0x000d => {
+        Name => 'TxSize',
+        Description => 'Text Size',
+        Format => 'int16u',
+    },
+    0x000e => {
+        Name => 'FgColor',
+        Description => 'Foreground Color',
+        Format => 'int32u',
+    },
+    0x000f => {
+        Name => 'BkColor',
+        Description => 'Background Color',
+        Format => 'int32u',
+    },
+    0x0010 => {
+        Name => 'TxRatio',
+        Description => 'Text Ratio',
+        Format => 'Rect',
+    },
+    0x0011 => {
+        Name => 'VersionOp',
+        Description => 'Version',
+        Format => 'int8u',
+    },
+    0x0012 => {
+        Name => 'BkPixPat',
+        Description => 'Background Pixel Pattern',
+        Format => 'PixPat',
+    },
+    0x0013 => {
+        Name => 'PnPixPat',
+        Description => 'Pen Pixel Pattern',
+        Format => 'PixPat',
+    },
+    0x0014 => {
+        Name => 'FillPixPat',
+        Description => 'Fill Pixel Pattern',
+        Format => 'PixPat',
+    },
+    0x0015 => {
+        Name => 'PnLocHFrac',
+        Description => 'Fractional Pen Position',
+        Format => 'int16u',
+    },
+    0x0016 => {
+        Name => 'ChExtra',
+        Description => 'Added Width for NonSpace Characters',
+        Format => 'int16u',
+    },
+    0x0017 => {
+        Name => 'Reserved',
+        Format => 'Unknown',
+    },
+    0x001a => {
+        Name => 'RGBFgCol',
+        Description => 'Foreground Color',
+        Format => 'RGBColor',
+    },
+    0x001b => {
+        Name => 'RGBBkCol',
+        Description => 'Background Color',
+        Format => 'RGBColor',
+    },
+    0x001c => {
+        Name => 'HiliteMode',
+        Description => 'Highlight Mode Flag',
+        Format => 'null',
+    },
+    0x001d => {
+        Name => 'HiliteColor',
+        Description => 'Highlight Color',
+        Format => 'RGBColor',
+    },
+    0x001e => {
+        Name => 'DefHilite',
+        Description => 'Use Default Highlight Color',
+        Format => 'null',
+    },
+    0x001f => {
+        Name => 'OpColor',
+        Format => 'RGBColor',
+    },
+    0x0020 => {
+        Name => 'Line',
+        Format => 'Rect',
+    },
+    0x0021 => {
+        Name => 'LineFrom',
+        Format => 'Point',
+    },
+    0x0022 => {
+        Name => 'ShortLine',
+        Format => 'ShortLine',
+    },
+    0x0023 => {
+        Name => 'ShortLineFrom',
+        Format => 'int8u[2]',
+    },
+    0x0024 => {
+        Name => 'Reserved',
+        Format => 'Int16Data',
+    },
+    0x0028 => {
+        Name => 'LongText',
+        Format => 'PointText',
+    },
+    0x0029 => {
+        Name => 'DHText',
+        Format => 'Int8uText',
+    },
+    0x002a => {
+        Name => 'DVText',
+        Format => 'Int8uText',
+    },
+    0x002b => {
+        Name => 'DHDVText',
+        Format => 'Int8u2Text',
+    },
+    0x002c => {
+        Name => 'FontName',
+        Format => 'FontName',
+    },
+    0x002d => {
+        Name => 'LineJustify',
+        Format => 'int8u[10]',
+    },
+    0x002e => {
+        Name => 'GlyphState',
+        Format => 'int8u[8]',
+    },
+    0x002f => {
+        Name => 'Reserved',
+        Format => 'Int16Data',
+    },
+    0x0030 => {
+        Name => 'FrameRect',
+        Format => 'Rect',
+    },
+    0x0031 => {
+        Name => 'PaintRect',
+        Format => 'Rect',
+    },
+    0x0032 => {
+        Name => 'EraseRect',
+        Format => 'Rect',
+    },
+    0x0033 => {
+        Name => 'InvertRect',
+        Format => 'Rect',
+    },
+    0x0034 => {
+        Name => 'FillRect',
+        Format => 'Rect',
+    },
+    0x0035 => {
+        Name => 'Reserved',
+        Format => 'Rect',
+    },
+    0x0038 => {
+        Name => 'FrameSameRect',
+        Format => 'null',
+    },
+    0x0039 => {
+        Name => 'PaintSameRect',
+        Format => 'null',
+    },
+    0x003a => {
+        Name => 'EraseSameRect',
+        Format => 'null',
+    },
+    0x003b => {
+        Name => 'InvertSameRect',
+        Format => 'null',
+    },
+    0x003c => {
+        Name => 'FillSameRect',
+        Format => 'null',
+    },
+    0x003d => {
+        Name => 'Reserved',
+        Format => 'null',
+    },
+    0x0040 => {
+        Name => 'FrameRRect',
+        Format => 'Rect',
+    },
+    0x0041 => {
+        Name => 'PaintRRect',
+        Format => 'Rect',
+    },
+    0x0042 => {
+        Name => 'EraseRRect',
+        Format => 'Rect',
+    },
+    0x0043 => {
+        Name => 'InvertRRect',
+        Format => 'Rect',
+    },
+    0x0044 => {
+        Name => 'FillRRect',
+        Format => 'Rect',
+    },
+    0x0045 => {
+        Name => 'Reserved',
+        Format => 'Rect',
+    },
+    0x0048 => {
+        Name => 'FrameSameRRect',
+        Format => 'null',
+    },
+    0x0049 => {
+        Name => 'PaintSameRRect',
+        Format => 'null',
+    },
+    0x004a => {
+        Name => 'EraseSameRRect',
+        Format => 'null',
+    },
+    0x004b => {
+        Name => 'InvertSameRRect',
+        Format => 'null',
+    },
+    0x004c => {
+        Name => 'FillSameRRect',
+        Format => 'null',
+    },
+    0x004d => {
+        Name => 'Reserved',
+        Format => 'null',
+    },
+    0x0050 => {
+        Name => 'FrameOval',
+        Format => 'Rect',
+    },
+    0x0051 => {
+        Name => 'PaintOval',
+        Format => 'Rect',
+    },
+    0x0052 => {
+        Name => 'EraseOval',
+        Format => 'Rect',
+    },
+    0x0053 => {
+        Name => 'InvertOval',
+        Format => 'Rect',
+    },
+    0x0054 => {
+        Name => 'FillOval',
+        Format => 'Rect',
+    },
+    0x0055 => {
+        Name => 'Reserved',
+        Format => 'Rect',
+    },
+    0x0058 => {
+        Name => 'FrameSameOval',
+        Format => 'null',
+    },
+    0x0059 => {
+        Name => 'PaintSameOval',
+        Format => 'null',
+    },
+    0x005a => {
+        Name => 'EraseSameOval',
+        Format => 'null',
+    },
+    0x005b => {
+        Name => 'InvertSameOval',
+        Format => 'null',
+    },
+    0x005c => {
+        Name => 'FillSameOval',
+        Format => 'null',
+    },
+    0x005d => {
+        Name => 'Reserved',
+        Format => 'null',
+    },
+    0x0060 => {
+        Name => 'FrameArc',
+        Format => 'Arc',
+    },
+    0x0061 => {
+        Name => 'PaintArc',
+        Format => 'Arc',
+    },
+    0x0062 => {
+        Name => 'EraseArc',
+        Format => 'Arc',
+    },
+    0x0063 => {
+        Name => 'InvertArc',
+        Format => 'Arc',
+    },
+    0x0064 => {
+        Name => 'FillArc',
+        Format => 'Arc',
+    },
+    0x0065 => {
+        Name => 'Reserved',
+        Format => 'Arc',
+    },
+    0x0068 => {
+        Name => 'FrameSameArc',
+        Format => 'Point',
+    },
+    0x0069 => {
+        Name => 'PaintSameArc',
+        Format => 'Point',
+    },
+    0x006a => {
+        Name => 'EraseSameArc',
+        Format => 'Point',
+    },
+    0x006b => {
+        Name => 'InvertSameArc',
+        Format => 'Point',
+    },
+    0x006c => {
+        Name => 'FillSameArc',
+        Format => 'Point',
+    },
+    0x006d => {
+        Name => 'Reserved',
+        Format => 'int32u',
+    },
+    0x0070 => {
+        Name => 'FramePoly',
+        Format => 'Polygon',
+    },
+    0x0071 => {
+        Name => 'PaintPoly',
+        Format => 'Polygon',
+    },
+    0x0072 => {
+        Name => 'ErasePoly',
+        Format => 'Polygon',
+    },
+    0x0073 => {
+        Name => 'InvertPoly',
+        Format => 'Polygon',
+    },
+    0x0074 => {
+        Name => 'FillPoly',
+        Format => 'Polygon',
+    },
+    0x0075 => {
+        Name => 'Reserved',
+        Format => 'Polygon',
+    },
+    0x0078 => {
+        Name => 'FrameSamePoly',
+        Format => 'null',
+    },
+    0x0079 => {
+        Name => 'PaintSamePoly',
+        Format => 'null',
+    },
+    0x007a => {
+        Name => 'EraseSamePoly',
+        Format => 'null',
+    },
+    0x007b => {
+        Name => 'InvertSamePoly',
+        Format => 'null',
+    },
+    0x007c => {
+        Name => 'FillSamePoly',
+        Format => 'null',
+    },
+    0x007d => {
+        Name => 'Reserved',
+        Format => 'null',
+    },
+    0x0080 => {
+        Name => 'FrameRgn',
+        Format => 'Rgn',
+    },
+    0x0081 => {
+        Name => 'PaintRgn',
+        Format => 'Rgn',
+    },
+    0x0082 => {
+        Name => 'EraseRgn',
+        Format => 'Rgn',
+    },
+    0x0083 => {
+        Name => 'InvertRgn',
+        Format => 'Rgn',
+    },
+    0x0084 => {
+        Name => 'FillRgn',
+        Format => 'Rgn',
+    },
+    0x0085 => {
+        Name => 'Reserved',
+        Format => 'Rgn',
+    },
+    0x0088 => {
+        Name => 'FrameSameRgn',
+        Format => 'null',
+    },
+    0x0089 => {
+        Name => 'PaintSameRgn',
+        Format => 'null',
+    },
+    0x008a => {
+        Name => 'EraseSameRgn',
+        Format => 'null',
+    },
+    0x008b => {
+        Name => 'InvertSameRgn',
+        Format => 'null',
+    },
+    0x008c => {
+        Name => 'FillSameRgn',
+        Format => 'null',
+    },
+    0x008d => {
+        Name => 'Reserved',
+        Format => 'null',
+    },
+    0x0090 => {
+        Name => 'BitsRect',
+        Description => 'CopyBits with Clipped Rectangle',
+        Format => 'BitsRect#',  # (version-dependent format)
+    },
+    0x0091 => {
+        Name => 'BitsRgn',
+        Description => 'CopyBits with Clipped Region',
+        Format => 'BitsRgn#',   # (version-dependent format)
+    },
+    0x0092 => {
+        Name => 'Reserved',
+        Format => 'Int16Data',
+    },
+    0x0098 => {
+        Name => 'PackBitsRect',
+        Description => 'Packed CopyBits with Clipped Rectangle',
+        Format => 'BitsRect#',  # (version-dependent format)
+    },
+    0x0099 => {
+        Name => 'PackBitsRgn',
+        Description => 'Packed CopyBits with Clipped Region',
+        Format => 'BitsRgn#',   # (version-dependent format)
+    },
+    0x009a => {
+        Name => 'DirectBitsRect',
+        Format => 'DirectBitsRect',
+    },
+    0x009b => {
+        Name => 'DirectBitsRgn',
+        Format => 'DirectBitsRgn',
+    },
+    0x009c => {
+        Name => 'Reserved',
+        Format => 'Int16Data',
+    },
+    0x009d => {
+        Name => 'Reserved',
+        Format => 'Int16Data',
+    },
+    0x009e => {
+        Name => 'Reserved',
+        Format => 'Int16Data',
+    },
+    0x009f => {
+        Name => 'Reserved',
+        Format => 'Int16Data',
+    },
+    0x00a0 => {
+        Name => 'ShortComment',
+        Format => 'int16u',
+    },
+    0x00a1 => [
+        # this list for documentation only [not currently extracted]
+        {
+            # (not actually a full Photohop IRB record it appears, but it does start
+            #  with '8BIM', and does contain resolution information at offset 0x0a)
+            Name => 'LongComment',  # kind = 498
+            Format => 'LongComment',
+            SubDirectory => { TagTable => 'Image::ExifTool::Photoshop::Main' },
+        },
+        {
+            Name => 'LongComment',  # kind = 224
+            Format => 'LongComment',
+            SubDirectory => {
+                TagTable => 'Image::ExifTool::ICC_Profile::Main',
+                Start => '$valuePtr + 4',
+            },
+        },
+    ],
+    0x00a2 => {
+        Name => 'Reserved',
+        Format => 'Int16Data',
+    },
+    0x00b0 => {
+        Name => 'Reserved',
+        Format => 'null',
+    },
+    0x00d0 => {
+        Name => 'Reserved',
+        Format => 'Int32uData',
+    },
+    0x00ff => {
+        Name => 'OpEndPic',
+        Description => 'End of picture',
+        Format => 'null', # 2 for version 2!?
+    },
+    0x0100 => {
+        Name => 'Reserved',
+        Format => 'int16u',
+    },
+    0x0200 => {
+        Name => 'Reserved',
+        Format => 'int32u',
+    },
+    0x02ff => {
+        Name => 'Version',
+        Description => 'Version number of picture',
+        Format => 'int16u',
+    },
+    0x0300 => {
+        Name => 'Reserved',
+        Format => 'int16u',
+    },
+    0x0bff => {
+        Name => 'Reserved',
+        Format => 'int8u[22]',
+    },
+    0x0c00 => {
+        Name => 'HeaderOp',
+        Format => 'int16u[12]',
+    },
+    0x0c01 => {
+        Name => 'Reserved',
+        Format => 'int8u[24]',
+    },
+    0x7f00 => {
+        Name => 'Reserved',
+        Format => 'int8u[254]',
+    },
+    0x8000 => {
+        Name => 'Reserved',
+        Format => 'null',
+    },
+    0x8100 => {
+        Name => 'Reserved',
+        Format => 'Int32uData',
+    },
+    0x8200 => {
+        Name => 'CompressedQuickTime',
+        Format => 'CompressedQuickTime',
+    },
+    0x8201 => {
+        Name => 'UncompressedQuickTime',
+        Format => 'Int32uData',
+    },
+    0xffff => {
+        Name => 'Reserved',
+        Format => 'Int32uData',
+    },
+);
+
+# picture comment 'kind' codes
+# http://developer.apple.com/technotes/qd/qd_10.html
+my %commentKind = (
+    150 => 'TextBegin',
+    151 => 'TextEnd',
+    152 => 'StringBegin',
+    153 => 'StringEnd',
+    154 => 'TextCenter',
+    155 => 'LineLayoutOff',
+    156 => 'LineLayoutOn',
+    157 => 'ClientLineLayout',
+    160 => 'PolyBegin',
+    161 => 'PolyEnd',
+    163 => 'PolyIgnore',
+    164 => 'PolySmooth',
+    165 => 'PolyClose',
+    180 => 'DashedLine',
+    181 => 'DashedStop',
+    182 => 'SetLineWidth',
+    190 => 'PostScriptBegin',
+    191 => 'PostScriptEnd',
+    192 => 'PostScriptHandle',
+    193 => 'PostScriptFile',
+    194 => 'TextIsPostScript',
+    195 => 'ResourcePS',
+    196 => 'PSBeginNoSave',
+    197 => 'SetGrayLevel',
+    200 => 'RotateBegin',
+    201 => 'RotateEnd',
+    202 => 'RotateCenter',
+    210 => 'FormsPrinting',
+    211 => 'EndFormsPrinting',
+    224 => '<ICC Profile>',
+    498 => '<Photoshop Data>',
+    1000 => 'BitMapThinningOff',
+    1001 => 'BitMapThinningOn',
+);
+
+#------------------------------------------------------------------------------
+# Get PixData data
+# Inputs: 0) reference to PixMap, 1) RAF reference
+# Returns: reference to PixData or undef on error
+sub GetPixData($$)
+{
+    my ($pixMap, $raf) = @_;
+    my $packType = $pixMap->{packType};
+    my $rowBytes = $pixMap->{rowBytes} & 0x3fff;    # remove flags bits
+    my $height = $pixMap->{bounds}->{botRight}->{v} -
+                 $pixMap->{bounds}->{topLeft}->{v};
+    my ($data, $size, $buff, $i);
+
+    if ($packType == 1 or $rowBytes < 8) {  # unpacked data
+        $size = $rowBytes * $height;
+        return undef unless $raf->Read($data, $size) == $size;
+    } elsif ($packType == 2) {              # pad byte dropped
+        $size = int($rowBytes * $height * 3 / 4 + 0.5);
+        return undef unless $raf->Read($data, $size) == $size;
+    } else {
+        $data = '';
+        for ($i=0; $i<$height; ++$i) {
+            if ($rowBytes > 250) {
+                $raf->Read($buff,2) == 2 or return undef;
+                $size = unpack('n',$buff);
+            } else {
+                $raf->Read($buff,1) == 1 or return undef;
+                $size = unpack('C',$buff);
+            }
+            $data .= $buff;
+            $raf->Read($buff,$size) == $size or return undef;
+            $data .= $buff;
+        }
+    }
+    return \$data;
+}
+
+#------------------------------------------------------------------------------
+# Read value from PICT file
+# Inputs: 0) RAF reference, 1) tag, 2) format, 3) optional count
+# Returns: value, reference to structure hash, or undef on error
+sub ReadPictValue($$$;$)
+{
+    my ($raf, $tag, $format, $count) = @_;
+    return undef unless $format;
+    unless (defined $count) {
+        if ($format =~ /(.+)\[(.+)\]/s) {
+            $format = $1;
+            $count = $2;
+        } else {
+            $count = 1; # count undefined: assume 1
+        }
+    }
+    my $cntStr = ($count == 1) ? '' : "[$count]";
+    # no size if count is 0
+    my $size = $count ? Image::ExifTool::FormatSize($format) : 0;
+    if (defined $size or $format eq 'null') {
+        my $val;
+        if ($size) {
+            my $buff;
+            $size *= $count;
+            $raf->Read($buff, $size) == $size or return undef;
+            $val = ReadValue(\$buff, 0, $format, $count, $size);
+        } else {
+            $val = '';
+        }
+        if ($verbose) {
+            print $out "${indent}$tag ($format$cntStr)";
+            if ($size) {
+                if (not defined $val) {
+                    print $out " = <undef>\n";
+                } elsif ($format eq 'binary') {
+                    print $out " = <binary data>\n";
+                    if ($verbose > 2) {
+                        my %parms = ( Out => $out );
+                        $parms{MaxLen} = 96 if $verbose < 4;
+                        Image::ExifTool::HexDump(\$val, undef, %parms);
+                    }
+                } else {
+                    print $out " = $val\n";
+                }
+            } else {
+                print $out "\n";
+            }
+        }
+        return \$val if $format eq 'binary' and defined $val;
+        return $val;
+    }
+    $verbose and print $out "${indent}$tag ($format$cntStr):\n";
+    my $struct = $structs{$format} or return undef;
+    my ($c, @vals);
+    for ($c=0; $c<$count; ++$c) {
+        my (%val, $i);
+        for ($i=0; ; $i+=2) {
+            my $tag = $$struct[$i] or last;
+            my $fmt = $$struct[$i+1];
+            my ($cnt, $val);
+            $indent .= '  ';
+            if (ref $fmt) {
+                $val = eval $$fmt;
+                $@ and warn $@;
+                if ($verbose and defined $val) {
+                    printf $out "${indent}$tag (binary[%d]) = <binary data>\n",length($$val);
+                    if ($verbose > 2) {
+                        my %parms = ( Out => $out );
+                        $parms{MaxLen} = 96 if $verbose < 4;
+                        Image::ExifTool::HexDump($val, undef, %parms);
+                    }
+                }
+            } elsif ($fmt =~ /(.+)\[(.+)\]/s) {
+                $fmt = $1;
+                $cnt = eval $2;
+                $@ and warn $@;
+                $val = ReadPictValue($raf, $tag, $fmt, $cnt);
+            } else {
+                $val = ReadPictValue($raf, $tag, $fmt);
+            }
+            $indent = substr($indent, 2);
+            return undef unless defined $val;
+            $val{$tag} = $val;
+        }
+        return \%val if $count == 1;
+        push @vals, \%val;
+    }
+    return \@vals;
+}
+
+#------------------------------------------------------------------------------
+# Extract meta information from a PICT image
+# Inputs: 0) ExifTool object reference, 1) dirInfo reference
+# Returns: 1 on success, 0 if this wasn't a valid PICT image
+sub ProcessPICT($$)
+{
+    my ($exifTool, $dirInfo) = @_;
+    my $raf = $$dirInfo{RAF};
+    $verbose = $exifTool->Options('Verbose');
+    $out = $exifTool->Options('TextOut');
+    $indent = '';
+    my ($buff, $tried, @hdr, $op, $hRes, $vRes);
+
+    # recognize both PICT files and PICT resources (PICT files have a
+    # 512-byte header that we ignore, but PICT resources do not)
+    for (;;) {
+        $raf->Read($buff, 12) == 12 or return 0;
+        @hdr = unpack('x2n5', $buff);
+        $op = pop @hdr;
+        # check for PICT version 1 format
+        if ($op eq 0x1101) {
+            $vers = 1;
+            undef $extended;
+            last;
+        }
+        # check for PICT version 2 format
+        if ($op eq 0x0011) {
+            $raf->Read($buff, 28) == 28 or return 0;
+            if ($buff =~ /^\x02\xff\x0c\x00\xff\xff/) {
+                $vers = 2;
+                undef $extended;
+                last;
+            }
+            if ($buff =~ /^\x02\xff\x0c\x00\xff\xfe/) {
+                $vers = 2;
+                $extended = 1;
+                ($hRes, $vRes) = unpack('x8N2', $buff);
+                last;
+            }
+        }
+        return 0 if $tried;
+        $tried = 1;
+        $raf->Seek(512, 0) or return 0;
+    }
+    # make the bounding rect signed
+    foreach (@hdr) {
+        $_ >= 0x8000 and $_ -= 0x10000;
+    }
+    my $w = $hdr[3] - $hdr[1];
+    my $h = $hdr[2] - $hdr[0];
+    return 0 unless $w > 0 and $h > 0;
+
+    SetByteOrder('MM');
+
+    if ($extended) {
+        # extended version 2 pictures contain resolution information
+        # and image bounds are in 72-dpi equivalent units
+        $hRes = GetFixed32s(\$buff, 8);
+        $vRes = GetFixed32s(\$buff, 12);
+        return 0 unless $hRes and $vRes;
+        $w = int($w * $hRes / 72 + 0.5);
+        $h = int($h * $vRes / 72 + 0.5);
+    }
+    $exifTool->SetFileType();
+    $exifTool->FoundTag('ImageWidth', $w);
+    $exifTool->FoundTag('ImageHeight', $h);
+    $exifTool->FoundTag('XResolution', $hRes) if $hRes;
+    $exifTool->FoundTag('YResolution', $vRes) if $vRes;
+
+    # don't extract image opcodes unless verbose
+    return 1 unless $verbose or $exifTool->Options('Unknown');
+
+    $verbose and printf $out "PICT version $vers%s\n", $extended ? ' extended' : '';
+
+    my $tagTablePtr = GetTagTable('Image::ExifTool::PICT::Main');
+
+    my $success;
+    for (;;) {
+        if ($vers == 1) {
+            $raf->Read($buff, 1) == 1 or last;
+            $op = ord($buff);
+        } else {
+            # must start version 2 opcode on an even byte
+            $raf->Read($buff, 1) if $raf->Tell() & 0x01;
+            $raf->Read($buff, 2) == 2 or last;
+            $op = unpack('n', $buff);
+        }
+        my $tagInfo = $exifTool->GetTagInfo($tagTablePtr, $op);
+        unless ($tagInfo) {
+            my $i;
+            # search for reserved tag info
+            for ($i=0; $i<scalar(@reserved); $i+=2) {
+                next unless $op >= $reserved[$i];
+                last if $op > $reserved[$i+1];
+                $tagInfo = $exifTool->GetTagInfo($tagTablePtr, $reserved[$i]);
+                last;
+            }
+            last unless $tagInfo;
+        }
+        if ($op eq 0xff) {
+            $verbose and print $out "End of picture\n";
+            $success = 1;
+            last;
+        }
+        my $format = $$tagInfo{Format};
+        unless ($format) {
+            $exifTool->Warn("Missing format for $$tagInfo{Name}");
+            last;
+        }
+        # replace version number for version-dependent formats
+        $format =~ s/#$/$vers/;
+        my $wid = $vers * 2;
+        $verbose and printf $out "Tag 0x%.${wid}x, ", $op;
+        my $val = ReadPictValue($raf, $$tagInfo{Name}, $format);
+        unless (defined $val) {
+            $exifTool->Warn("Error reading $$tagInfo{Name} information");
+            last;
+        }
+        if (ref $val eq 'HASH') {
+            # extract JPEG image from CompressedQuickTime imageData
+            if ($$tagInfo{Name} eq 'CompressedQuickTime' and
+                ref $val->{imageDescr} eq 'HASH' and
+                $val->{imageDescr}->{compressor} and
+                $val->{imageDescr}->{compressor} eq 'Photo - JPEG' and
+                ref $val->{imageData} eq 'SCALAR' and
+                $exifTool->ValidateImage($val->{imageData}, 'PreviewImage'))
+            {
+                $exifTool->FoundTag('PreviewImage', $val->{imageData});
+            }
+        } else {
+            # $exifTool->FoundTag($tagInfo, $val);
+        }
+    }
+    $success or $exifTool->Warn('End of picture not found');
+    return 1;
+}
+
+1;  # end
+
+__END__
+
+=head1 NAME
+
+Image::ExifTool::PICT - Read PICT meta information
+
+=head1 SYNOPSIS
+
+This module is used by Image::ExifTool
+
+=head1 DESCRIPTION
+
+This module contains routines required by Image::ExifTool to read PICT
+(Apple Picture) images.
+
+=head1 NOTES
+
+Extraction of PICT opcodes is experimental, and is only enabled with the
+Verbose or the Unknown option.
+
+=head1 AUTHOR
+
+Copyright 2003-2007, Phil Harvey (phil at owl.phy.queensu.ca)
+
+This library is free software; you can redistribute it and/or modify it
+under the same terms as Perl itself.
+
+=head1 REFERENCES
+
+=over 4
+
+=item L<http://developer.apple.com/documentation/mac/QuickDraw/QuickDraw-2.html>
+
+=item L<http://developer.apple.com/documentation/QuickTime/INMAC/QT/iqImageCompMgr.a.htm>
+
+=back
+
+=head1 SEE ALSO
+
+L<Image::ExifTool::TagNames/PICT Tags>,
+L<Image::ExifTool(3pm)|Image::ExifTool>
+
+=cut
+
Index: /gsdl/trunk/perllib/cpan/Image/ExifTool/PNG.pm
===================================================================
--- /gsdl/trunk/perllib/cpan/Image/ExifTool/PNG.pm	(revision 16842)
+++ /gsdl/trunk/perllib/cpan/Image/ExifTool/PNG.pm	(revision 16842)
@@ -0,0 +1,941 @@
+#------------------------------------------------------------------------------
+# File:         PNG.pm
+#
+# Description:  Read and write PNG meta information
+#
+# Revisions:    06/10/2005 - P. Harvey Created
+#               06/23/2005 - P. Harvey Added MNG and JNG support
+#               09/16/2005 - P. Harvey Added write support
+#
+# References:   1) http://www.libpng.org/pub/png/spec/1.2/
+#               2) http://www.faqs.org/docs/png/
+#               3) http://www.libpng.org/pub/mng/
+#               4) http://www.libpng.org/pub/png/spec/register/
+#
+# Notes:        I haven't found a sample PNG image with a 'iTXt' chunk, so
+#               this part of the code is still untested.
+#
+#               Writing meta information in PNG images is a pain in the butt
+#               for a number of reasons:  One biggie is that you have to
+#               decompress then decode the ASCII/hex profile information before
+#               you can edit it, then you have to ASCII/hex-encode, recompress
+#               and calculate a CRC before you can write it out again.  gaaaak.
+#------------------------------------------------------------------------------
+
+package Image::ExifTool::PNG;
+
+use strict;
+use vars qw($VERSION $AUTOLOAD);
+use Image::ExifTool qw(:DataAccess :Utils);
+
+$VERSION = '1.15';
+
+sub ProcessPNG_tEXt($$$);
+sub ProcessPNG_iTXt($$$);
+sub ProcessPNG_Compressed($$$);
+sub CalculateCRC($;$$$);
+sub HexEncode($);
+sub AddChunks($$);
+sub Add_iCCP($$);
+
+my $noCompressLib;
+
+# look up for file type, header chunk and end chunk, based on file signature
+my %pngLookup = (
+    "\x89PNG\r\n\x1a\n" => ['PNG', 'IHDR', 'IEND' ],
+    "\x8aMNG\r\n\x1a\n" => ['MNG', 'MHDR', 'MEND' ],
+    "\x8bJNG\r\n\x1a\n" => ['JNG', 'JHDR', 'IEND' ],
+);
+
+# color type of current image
+$Image::ExifTool::PNG::colorType = -1;
+
+# PNG chunks
+%Image::ExifTool::PNG::Main = (
+    WRITE_PROC => \&Image::ExifTool::DummyWriteProc,
+    GROUPS => { 2 => 'Image' },
+    bKGD => {
+        Name => 'BackgroundColor',
+        ValueConv => 'join(" ",unpack(length($val) < 2 ? "C" : "n*", $val))',
+    },
+    cHRM => {
+        Name => 'PrimaryChromaticities',
+        SubDirectory => { TagTable => 'Image::ExifTool::PNG::PrimaryChromaticities' },
+    },
+    fRAc => {
+        Name => 'FractalParameters',
+        Binary => 1,
+    },
+    gAMA => {
+        Name => 'Gamma',
+        ValueConv => 'my $a=unpack("N",$val);$a ? int(1e9/$a+0.5)/1e4 : $val',
+    },
+    gIFg => {
+        Name => 'GIFGraphicControlExtension',
+        Binary => 1,
+    },
+    gIFt => {
+        Name => 'GIFPlainTextExtension',
+        Binary => 1,
+    },
+    gIFx => {
+        Name => 'GIFApplicationExtension',
+        Binary => 1,
+    },
+    hIST => {
+        Name => 'PaletteHistogram',
+        Binary => 1,
+    },
+    iCCP => {
+        Name => 'ICC_Profile',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::ICC_Profile::Main',
+            ProcessProc => \&ProcessPNG_Compressed,
+        },
+    },
+#   IDAT
+#   IEND
+    IHDR => {
+        Name => 'ImageHeader',
+        SubDirectory => { TagTable => 'Image::ExifTool::PNG::ImageHeader' },
+    },
+    iTXt => {
+        Name => 'InternationalText',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::PNG::TextualData',
+            ProcessProc => \&ProcessPNG_iTXt,
+        },
+    },
+    oFFs => {
+        Name => 'ImageOffset',
+        ValueConv => q{
+            my @a = unpack("NNC",$val);
+            $a[2] = ($a[2] ? "microns" : "pixels");
+            return "$a[0], $a[1] ($a[2])";
+        },
+    },
+    pCAL => {
+        Name => 'PixelCalibration',
+        Binary => 1,
+    },
+    pHYs => {
+        Name => 'PhysicalPixel',
+        SubDirectory => { TagTable => 'Image::ExifTool::PNG::PhysicalPixel' },
+    },
+    PLTE => {
+        Name => 'Palette',
+        ValueConv => 'length($val) <= 3 ? join(" ",unpack("C*",$val)) : \$val',
+    },
+    sBIT => {
+        Name => 'SignificantBits',
+        ValueConv => 'join(" ",unpack("C*",$val))',
+    },
+    sPLT => {
+        Name => 'SuggestedPalette',
+        Binary => 1,
+        PrintConv => 'split("\0",$$val,1)', # extract palette name
+    },
+    sRGB => {
+        Name => 'SRGBRendering',
+        ValueConv => 'unpack("C",$val)',
+        PrintConv => {
+            0 => 'Perceptual',
+            1 => 'Relative Colorimetric',
+            2 => 'Saturation',
+            3 => 'Absolute Colorimetric',
+        },
+    },
+    tEXt => {
+        Name => 'TextualData',
+        SubDirectory => { TagTable => 'Image::ExifTool::PNG::TextualData' },
+    },
+    tIME => {
+        Name => 'ModifyDate',
+        Groups => { 2 => 'Time' },
+        Writable => 1,
+        Shift => 'Time',
+        ValueConv => 'sprintf("%.4d:%.2d:%.2d %.2d:%.2d:%.2d", unpack("nC5", $val))',
+        ValueConvInv => q{
+            my @a = ($val=~/^(\d+):(\d+):(\d+)\s+(\d+):(\d+):(\d+)/);
+            @a == 6 or warn('Invalid date'), return undef;
+            return pack('nC5', @a);
+        },
+        PrintConv => '$self->ConvertDateTime($val)',
+        PrintConvInv => '$val',
+    },
+    tRNS => {
+        Name => 'Transparency',
+        ValueConv => q{
+            return \$val if length($val) > 6;
+            join(" ",unpack($Image::ExifTool::PNG::colorType == 3 ? "C*" : "n*", $val));
+        },
+    },
+    tXMP => {
+        Name => 'XMP',
+        Notes => 'obsolete location specified by older XMP draft',
+        SubDirectory => { TagTable => 'Image::ExifTool::XMP::Main' },
+    },
+    zTXt => {
+        Name => 'CompressedText',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::PNG::TextualData',
+            ProcessProc => \&ProcessPNG_Compressed,
+        },
+    },
+);
+
+# PNG IHDR chunk
+%Image::ExifTool::PNG::ImageHeader = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    GROUPS => { 2 => 'Image' },
+    0 => {
+        Name => 'ImageWidth',
+        Format => 'int32u',
+    },
+    4 => {
+        Name => 'ImageHeight',
+        Format => 'int32u',
+    },
+    8 => 'BitDepth',
+    9 => {
+        Name => 'ColorType',
+        RawConv => '$Image::ExifTool::PNG::colorType = $val',
+        PrintConv => {
+            0 => 'Grayscale',
+            2 => 'RGB',
+            3 => 'Palette',
+            4 => 'Grayscale with Alpha',
+            6 => 'RGB with Alpha',
+        },
+    },
+    10 => {
+        Name => 'Compression',
+        PrintConv => { 0 => 'Deflate/Inflate' },
+    },
+    11 => {
+        Name => 'Filter',
+        PrintConv => { 0 => 'Adaptive' },
+    },
+    12 => {
+        Name => 'Interlace',
+        PrintConv => { 0 => 'Noninterlaced', 1 => 'Adam7 Interlace' },
+    },
+);
+
+# PNG cHRM chunk
+%Image::ExifTool::PNG::PrimaryChromaticities = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    GROUPS => { 2 => 'Image' },
+    FORMAT => 'int32u',
+    0 => { Name => 'WhitePointX', ValueConv => '$val / 100000' },
+    1 => { Name => 'WhitePointY', ValueConv => '$val / 100000' },
+    2 => { Name => 'RedX',        ValueConv => '$val / 100000' },
+    3 => { Name => 'RedY',        ValueConv => '$val / 100000' },
+    4 => { Name => 'GreenX',      ValueConv => '$val / 100000' },
+    5 => { Name => 'GreenY',      ValueConv => '$val / 100000' },
+    6 => { Name => 'BlueX',       ValueConv => '$val / 100000' },
+    7 => { Name => 'BlueY',       ValueConv => '$val / 100000' },
+);
+
+# PNG pHYs chunk
+%Image::ExifTool::PNG::PhysicalPixel = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    GROUPS => { 2 => 'Image' },
+    0 => {
+        Name => 'PixelsPerUnitX',
+        Format => 'int32u',
+    },
+    4 => {
+        Name => 'PixelsPerUnitY',
+        Format => 'int32u',
+    },
+    8 => {
+        Name => 'PixelUnits',
+        PrintConv => { 0 => 'Unknown', 1 => 'Meters' },
+    },
+);
+
+my %unreg = ( Notes => 'unregistered' );
+
+# Tags for PNG tEXt zTXt and iTXt chunks
+# (NOTE: ValueConv is set dynamically, so don't set it here!)
+%Image::ExifTool::PNG::TextualData = (
+    PROCESS_PROC => \&ProcessPNG_tEXt,
+    WRITE_PROC => \&Image::ExifTool::DummyWriteProc,
+    WRITABLE => 'string',
+    PREFERRED => 1, # always add these tags when writing
+    GROUPS => { 2 => 'Image' },
+    NOTES => q{
+The PNG TextualData format allows aribrary tag names to be used.  The tags
+listed below are the only ones that can be written (unless new user-defined
+tags are added via the configuration file), however ExifTool will extract
+any other TextualData tags that are found.
+
+The information for the TextualData tags may be stored as tEXt, zTXt or iTXt
+chunks in the PNG image.  ExifTool will read and edit tags in their original
+form, but tEXt chunks are written by default when creating new tags.
+Compressed zTXt chunks are written only if Compress::Zlib is available, and
+only for profile information or when the -z (Compress) option is specified.
+
+Some of the tags below are not registered as part of the PNG specification,
+but are included here because they are generated by other software such as
+ImageMagick.
+    },
+    Title       => { },
+    Author      => { Groups => { 2 => 'Author' } },
+    Description => { },
+    Copyright   => { Groups => { 2 => 'Author' } },
+   'Creation Time' => {
+        Name => 'CreationTime',
+        Groups => { 2 => 'Time' },
+        Shift => 'Time',
+    },
+    Software    => { },
+    Disclaimer  => { },
+    # change name to differentiate from ExifTool Warning
+    Warning     => { Name => 'PNGWarning', },
+    Source      => { },
+    Comment     => { },
+#
+# The following tags are not part of the original PNG specification,
+# but are written by ImageMagick and other software
+#
+    Artist      => { %unreg, Groups => { 2 => 'Author' } },
+    Document    => { %unreg },
+    Label       => { %unreg },
+    Make        => { %unreg, Groups => { 2 => 'Camera' } },
+    Model       => { %unreg, Groups => { 2 => 'Camera' } },
+    TimeStamp   => { %unreg, Groups => { 2 => 'Time' }, Shift => 'Time' },
+    URL         => { %unreg },
+   'XML:com.adobe.xmp' => {
+        Name => 'XMP',
+        Notes => q{
+            location according to the XMP specification -- this is where ExifTool will
+            add a new XMP chunk if the image didn't already contain XMP
+        },
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::XMP::Main',
+        },
+    },
+   'Raw profile type APP1' => [
+        {
+            # EXIF table must come first because we key on this in ProcessProfile()
+            # (No condition because this is just for BuildTagLookup)
+            Name => 'APP1_Profile',
+            SubDirectory => {
+                TagTable=>'Image::ExifTool::Exif::Main',
+                ProcessProc => \&ProcessProfile,
+            },
+        },
+        {
+            Name => 'APP1_Profile',
+            SubDirectory => {
+                TagTable=>'Image::ExifTool::XMP::Main',
+                ProcessProc => \&ProcessProfile,
+            },
+        },
+    ],
+   'Raw profile type exif' => {
+        Name => 'EXIF_Profile',
+        SubDirectory => {
+            TagTable=>'Image::ExifTool::Exif::Main',
+            ProcessProc => \&ProcessProfile,
+        },
+    },
+   'Raw profile type icc' => {
+        Name => 'ICC_Profile',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::ICC_Profile::Main',
+            ProcessProc => \&ProcessProfile,
+        },
+    },
+   'Raw profile type icm' => {
+        Name => 'ICC_Profile',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::ICC_Profile::Main',
+            ProcessProc => \&ProcessProfile,
+        },
+    },
+   'Raw profile type iptc' => {
+        Name => 'IPTC_Profile',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::Photoshop::Main',
+            ProcessProc => \&ProcessProfile,
+        },
+    },
+   'Raw profile type xmp' => {
+        Name => 'XMP_Profile',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::XMP::Main',
+            ProcessProc => \&ProcessProfile,
+        },
+    },
+);
+
+#------------------------------------------------------------------------------
+# AutoLoad our writer routines when necessary
+#
+sub AUTOLOAD
+{
+    return Image::ExifTool::DoAutoLoad($AUTOLOAD, @_);
+}
+
+#------------------------------------------------------------------------------
+# Found a PNG tag -- extract info from subdirectory or decompress data if necessary
+# Inputs: 0) ExifTool object reference, 1) Pointer to tag table,
+#         2) Tag ID, 3) Tag value, 4) [optional] compressed data flag:
+#            0=not compressed, 1=unknown compression, 2-N=compression with type N-2
+#         5) optional output buffer reference
+# Returns: 1 on success
+sub FoundPNG($$$$;$$)
+{
+    my ($exifTool, $tagTablePtr, $tag, $val, $compressed, $outBuff) = @_;
+    my ($wasCompressed, $deflateErr);
+    return 0 unless defined $val;
+#
+# First, uncompress data if requested
+#
+    my $verbose = $exifTool->Options('Verbose');
+    my $out = $exifTool->Options('TextOut');
+    my $tagInfo = $exifTool->GetTagInfo($tagTablePtr, $tag) ||
+                  # (some software forgets to capitalize first letter)
+                  $exifTool->GetTagInfo($tagTablePtr, ucfirst($tag));
+
+    if ($compressed and $compressed > 1) {
+        if ($compressed == 2) { # Inflate/Deflate compression
+            if (eval 'require Compress::Zlib') {
+                my ($v2, $stat);
+                my $inflate = Compress::Zlib::inflateInit();
+                $inflate and ($v2, $stat) = $inflate->inflate($val);
+                if ($inflate and $stat == Compress::Zlib::Z_STREAM_END()) {
+                    $val = $v2;
+                    $compressed = 0;
+                    $wasCompressed = 1;
+                } else {
+                    $deflateErr = "Error inflating $tag";
+                }
+            } elsif (not $noCompressLib) {
+                $noCompressLib = 1;
+                my $verb = $outBuff ? 'write' : 'decode';
+                $deflateErr = "Install Compress::Zlib to $verb compressed information";
+            } else {
+                $deflateErr = '';   # flag deflate error but no warning
+            }
+        } else {
+            $compressed -= 2;
+            $deflateErr = "Unknown compression method $compressed for $tag";
+        }
+        if ($compressed and $verbose and $tagInfo and $$tagInfo{SubDirectory}) {
+            $exifTool->VerboseDir("Unable to decompress $$tagInfo{Name}", 0, length($val));
+        }
+        $exifTool->Warn($deflateErr) if $deflateErr and not $outBuff;
+    }
+#
+# extract information from subdirectory if available
+#
+    if ($tagInfo) {
+        my $tagName = $$tagInfo{Name};
+        my $processed;
+        if ($$tagInfo{SubDirectory} and not $compressed) {
+            my $len = length $val;
+            if ($verbose and $exifTool->{INDENT} ne '  ') {
+                if ($wasCompressed and $verbose > 2) {
+                    my $name = $tagName;
+                    $wasCompressed and $name = "Decompressed $name";
+                    $exifTool->VerboseDir($name, 0, $len);
+                    my %parms = ( Prefix => $exifTool->{INDENT}, Out => $out );
+                    $parms{MaxLen} = 96 unless $verbose > 3;
+                    Image::ExifTool::HexDump(\$val, undef, %parms);
+                }
+                # don't indent next directory (since it is really the same data)
+                $exifTool->{INDENT} =~ s/..$//;
+            }
+            my $subdir = $$tagInfo{SubDirectory};
+            my $processProc = $$subdir{ProcessProc};
+            # nothing more to do if writing and subdirectory is not writable
+            my $subTable = GetTagTable($$subdir{TagTable});
+            return 1 if $outBuff and not $$subTable{WRITE_PROC};
+            my %subdirInfo = (
+                DataPt => \$val,
+                DirStart => 0,
+                DataLen => $len,
+                DirLen => $len,
+                DirName => $tagName,
+                TagInfo => $tagInfo,
+                ReadOnly => 1, # (only used by WriteXMP)
+                OutBuff => $outBuff,
+            );
+            # no need to re-decompress if already done
+            undef $processProc if $wasCompressed and $processProc eq \&ProcessPNG_Compressed;
+            # rewrite this directory if necessary (but always process TextualData normally)
+            if ($outBuff and not $processProc and $subTable ne \%Image::ExifTool::PNG::TextualData) {
+                return 1 unless $exifTool->{EDIT_DIRS}->{$tagName};
+                $$outBuff = $exifTool->WriteDirectory(\%subdirInfo, $subTable);
+                # if this was an XMP directory, we must make it read-only
+                $tagName eq 'XMP' and Image::ExifTool::XMP::ValidateXMP(\$outBuff,'r');
+                delete $exifTool->{ADD_DIRS}->{$tagName};
+            } else {
+                $processed = $exifTool->ProcessDirectory(\%subdirInfo, $subTable, $processProc);
+            }
+            $compressed = 1;    # pretend this is compressed since it is binary data
+        }
+        if ($outBuff) {
+            my $writable = $tagInfo->{Writable};
+            if ($writable or ($$tagTablePtr{WRITABLE} and
+                not defined $writable and not $$tagInfo{SubDirectory}))
+            {
+                # write new value for this tag if necessary
+                my ($isOverwriting, $newVal);
+                if ($exifTool->{DEL_GROUP}->{PNG}) {
+                    # remove this tag now, but keep in ADD_PNG list to add back later
+                    $isOverwriting = 1;
+                } else {
+                    # remove this from the list of PNG tags to add
+                    delete $exifTool->{ADD_PNG}->{$tag};
+                    # (also handle case of tEXt tags written with lowercase first letter)
+                    delete $exifTool->{ADD_PNG}->{ucfirst($tag)};
+                    my $newValueHash = $exifTool->GetNewValueHash($tagInfo);
+                    $isOverwriting = Image::ExifTool::IsOverwriting($newValueHash);
+                    if (defined $deflateErr) {
+                        $newVal = Image::ExifTool::GetNewValues($newValueHash);
+                        # can only write tag now if unconditionally deleting it
+                        if ($isOverwriting > 0 and not defined $newVal) {
+                            $val = '<deflate error>';
+                        } else {
+                            $isOverwriting = 0; # can't rewrite this compressed text
+                            $exifTool->Warn($deflateErr) if $deflateErr;
+                        }
+                    } else {
+                        if ($isOverwriting < 0) {
+                            $isOverwriting = Image::ExifTool::IsOverwriting($newValueHash, $val);
+                        }
+                        # (must get new value after IsOverwriting() in case it was shifted)
+                        $newVal = Image::ExifTool::GetNewValues($newValueHash);
+                    }
+                }
+                if ($isOverwriting) {
+                    $$outBuff =  (defined $newVal) ? $newVal : '';
+                    ++$exifTool->{CHANGED};
+                    if ($verbose > 1) {
+                        print $out "    - PNG:$tagName = '",$exifTool->Printable($val),"'\n";
+                        print $out "    + PNG:$tagName = '",$exifTool->Printable($newVal),"'\n" if defined $newVal;
+                    }
+                }
+            }
+            if ($$outBuff) {
+                if ($wasCompressed) {
+                    # re-compress the output data
+                    my $deflate;
+                    if (eval 'require Compress::Zlib') {
+                        my $deflate = Compress::Zlib::deflateInit();
+                        if ($deflate) {
+                            $$outBuff = $deflate->deflate($$outBuff);
+                            $$outBuff .= $deflate->flush() if defined $$outBuff;
+                        } else {
+                            undef $$outBuff;
+                        }
+                    }
+                    $$outBuff or $exifTool->Warn("PNG:$tagName not written (compress error)");
+                } elsif ($exifTool->Options('Compress')) {
+                    $exifTool->Warn("PNG:$tagName not compressed (uncompressed tag existed)", 1);
+                }
+            }
+            return 1;
+        }
+        return 1 if $processed;
+    } else {
+        my $name;
+        ($name = $tag) =~ s/\s+(.)/\u$1/g;   # remove white space from tag name
+        $tagInfo = { Name => $name };
+        # make unknown profiles binary data type
+        $$tagInfo{ValueConv} = '\$val' if $tag =~ /^Raw profile type /;
+        Image::ExifTool::AddTagToTable($tagTablePtr, $tag, $tagInfo);
+    }
+#
+# store this tag information
+#
+    if ($verbose) {
+        # temporarily remove subdirectory so it isn't printed in verbose information
+        # since we aren't decoding it anyway;
+        my $subdir = $$tagInfo{SubDirectory};
+        delete $$tagInfo{SubDirectory};
+        $exifTool->VerboseInfo($tag, $tagInfo,
+            Table  => $tagTablePtr,
+            DataPt => \$val,
+        );
+        $$tagInfo{SubDirectory} = $subdir if $subdir;
+    }
+    # set the RawConv dynamically depending on whether this is binary or not
+    my $delRawConv;
+    if ($compressed and not defined $$tagInfo{ValueConv}) {
+        $$tagInfo{RawConv} = '\$val';
+        $delRawConv = 1;
+    }
+    $exifTool->FoundTag($tagInfo, $val);
+    delete $$tagInfo{RawConv} if $delRawConv;
+    return 1;
+}
+
+#------------------------------------------------------------------------------
+# Process encoded PNG profile information
+# Inputs: 0) ExifTool object reference, 1) DirInfo reference, 2) Pointer to tag table
+# Returns: 1 on success
+sub ProcessProfile($$$)
+{
+    my ($exifTool, $dirInfo, $tagTablePtr) = @_;
+    my $dataPt = $$dirInfo{DataPt};
+    my $tagInfo = $$dirInfo{TagInfo};
+    my $outBuff = $$dirInfo{OutBuff};
+    my $tagName = $$tagInfo{Name};
+
+    # ImageMagick 5.3.6 writes profiles with the following headers:
+    # "\nICC Profile\n", "\nIPTC profile\n", "\n\xaa\x01{generic prof\n"
+    # and "\ngeneric profile\n"
+    return 0 unless $$dataPt =~ /^\n(.*?)\n\s*(\d+)\n(.*)/s;
+    my ($profileType, $len) = ($1, $2);
+    # data is encoded in hex, so change back to binary
+    my $buff = pack('H*', join('',split(' ',$3)));
+    my $actualLen = length $buff;
+    if ($len ne $actualLen) {
+        $exifTool->Warn("$tagName is wrong size (should be $len bytes but is $actualLen)");
+        $len = $actualLen;
+    }
+    my $verbose = $exifTool->Options('Verbose');
+    if ($verbose) {
+        if ($verbose > 2) {
+            $exifTool->VerboseDir("Decoded $tagName", 0, $len);
+            my %parms = (
+                Prefix => $exifTool->{INDENT},
+                Out => $exifTool->Options('TextOut'),
+            );
+            $parms{MaxLen} = 96 unless $verbose > 3;
+            Image::ExifTool::HexDump(\$buff, undef, %parms);
+        }
+        # don't indent next directory (since it is really the same data)
+        $exifTool->{INDENT} =~ s/..$//;
+    }
+    my %dirInfo = (
+        Parent   => 'PNG',
+        DataPt   => \$buff,
+        DataLen  => $len,
+        DirStart => 0,
+        DirLen   => $len,
+        Base     => 0,
+        OutFile  => $outBuff,
+    );
+    my $processed = 0;
+    my $oldChanged = $exifTool->{CHANGED};
+    my $exifTable = GetTagTable('Image::ExifTool::Exif::Main');
+    my $editDirs = $exifTool->{EDIT_DIRS};
+    my $addDirs = $exifTool->{ADD_DIRS};
+    if ($tagTablePtr ne $exifTable) {
+        # process non-EXIF and non-APP1 profile as-is
+        if ($outBuff) {
+            # no need to rewrite this if not editing tags in this directory
+            my $dir = $tagName;
+            $dir =~ s/_Profile// unless $dir =~ /^ICC/;
+            return 1 unless $$editDirs{$dir};
+            $$outBuff = $exifTool->WriteDirectory(\%dirInfo, $tagTablePtr);
+            delete $$addDirs{$dir};
+        } else {
+            $processed = $exifTool->ProcessDirectory(\%dirInfo, $tagTablePtr);
+        }
+    } elsif ($buff =~ /^$Image::ExifTool::exifAPP1hdr/) {
+        # APP1 EXIF information
+        return 1 if $outBuff and not $$editDirs{IFD0};
+        my $hdrLen = length($Image::ExifTool::exifAPP1hdr);
+        $dirInfo{DirStart} += $hdrLen;
+        $dirInfo{DirLen} -= $hdrLen;
+        $processed = $exifTool->ProcessTIFF(\%dirInfo);
+        if ($outBuff) {
+            if ($$outBuff) {
+                $$outBuff = $Image::ExifTool::exifAPP1hdr . $$outBuff if $$outBuff;
+            } else {
+                $$outBuff = '' if $processed;
+            }
+            delete $$addDirs{IFD0};
+        }
+    } elsif ($buff =~ /^$Image::ExifTool::xmpAPP1hdr/) {
+        # APP1 XMP information
+        my $hdrLen = length($Image::ExifTool::xmpAPP1hdr);
+        my $tagTablePtr = GetTagTable('Image::ExifTool::XMP::Main');
+        $dirInfo{DirStart} += $hdrLen;
+        $dirInfo{DirLen} -= $hdrLen;
+        if ($outBuff) {
+            return 1 unless $$editDirs{XMP};
+            $$outBuff = $exifTool->WriteDirectory(\%dirInfo, $tagTablePtr);
+            $$outBuff and $$outBuff = $Image::ExifTool::xmpAPP1hdr . $$outBuff;
+            delete $$addDirs{XMP};
+        } else {
+            $processed = $exifTool->ProcessDirectory(\%dirInfo, $tagTablePtr);
+        }
+    } elsif ($buff =~ /^(MM\0\x2a|II\x2a\0)/) {
+        # TIFF information (haven't seen this, but what the heck...)
+        return 1 if $outBuff and not $$editDirs{IFD0};
+        $processed = $exifTool->ProcessTIFF(\%dirInfo);
+        if ($outBuff) {
+            if ($$outBuff) {
+                $$outBuff = $Image::ExifTool::exifAPP1hdr . $$outBuff if $$outBuff;
+            } else {
+                $$outBuff = '' if $processed;
+            }
+            delete $$addDirs{IFD0};
+        }
+    } else {
+        my $profName = $profileType;
+        $profName =~ tr/\x00-\x1f\x7f-\xff/./;
+        $exifTool->Warn("Unknown raw profile '$profName'");
+    }
+    if ($outBuff and $$outBuff) {
+        if ($exifTool->{CHANGED} != $oldChanged) {
+            my $hdr = sprintf("\n%s\n%8d\n", $profileType, length($$outBuff));
+            # hex encode the data
+            $$outBuff = $hdr . HexEncode($outBuff);
+        } else {
+            undef $$outBuff;
+        }
+    }
+    return $processed;
+}
+
+#------------------------------------------------------------------------------
+# Process PNG compressed zTXt or iCCP chunk
+# Inputs: 0) ExifTool object reference, 1) DirInfo reference, 2) Pointer to tag table
+# Returns: 1 on success
+sub ProcessPNG_Compressed($$$)
+{
+    my ($exifTool, $dirInfo, $tagTablePtr) = @_;
+    my ($tag, $val) = split /\0/, ${$$dirInfo{DataPt}}, 2;
+    return 0 unless defined $val;
+    # set compressed to 2 + compression method to decompress the data
+    my $compressed = 2 + unpack('C', $val);
+    my $hdr = $tag . "\0" . substr($val, 0, 1);
+    $val = substr($val, 1); # remove compression method byte
+    # use the PNG chunk tag instead of the embedded tag name for iCCP chunks
+    if ($$dirInfo{TagInfo} and $$dirInfo{TagInfo}->{Name} eq 'ICC_Profile') {
+        $tag = 'iCCP';
+        $tagTablePtr = \%Image::ExifTool::PNG::Main;
+    }
+    my $outBuff = $$dirInfo{OutBuff};
+    my $rtnVal = FoundPNG($exifTool, $tagTablePtr, $tag, $val, $compressed, $outBuff);
+    # add header back onto this chunk if we are writing
+    $$outBuff = $hdr . $$outBuff if $outBuff and $$outBuff;
+    return $rtnVal;
+}
+
+#------------------------------------------------------------------------------
+# Process PNG tEXt chunk
+# Inputs: 0) ExifTool object reference, 1) DirInfo reference, 2) Pointer to tag table
+# Returns: 1 on success
+sub ProcessPNG_tEXt($$$)
+{
+    my ($exifTool, $dirInfo, $tagTablePtr) = @_;
+    my ($tag, $val) = split /\0/, ${$$dirInfo{DataPt}}, 2;
+    my $outBuff = $$dirInfo{OutBuff};
+    my $rtnVal = FoundPNG($exifTool, $tagTablePtr, $tag, $val, undef, $outBuff);
+    # add header back onto this chunk if we are writing
+    $$outBuff = $tag . "\0" . $$outBuff if $outBuff and $$outBuff;
+    return $rtnVal;
+}
+
+#------------------------------------------------------------------------------
+# Process PNG iTXt chunk
+# Inputs: 0) ExifTool object reference, 1) DirInfo reference, 2) Pointer to tag table
+# Returns: 1 on success
+sub ProcessPNG_iTXt($$$)
+{
+    my ($exifTool, $dirInfo, $tagTablePtr) = @_;
+    my ($tag, $dat) = split /\0/, ${$$dirInfo{DataPt}}, 2;
+    return 0 unless defined $dat and length($dat) >= 4;
+    my ($compressed, $meth) = unpack('CC', $dat);
+    my ($lang, $trans, $val) = split /\0/, substr($dat, 2), 3;
+    # set compressed flag so we will decompress it in FoundPNG()
+    $compressed and $compressed = 2 + $meth;
+    my $outBuff = $$dirInfo{OutBuff};
+    my $rtnVal = FoundPNG($exifTool, $tagTablePtr, $tag, $val, $compressed, $outBuff);
+    if ($outBuff and $$outBuff) {
+        $$outBuff = $tag . "\0" . substr($dat, 0, 2) . "$lang\0$trans\0" . $$outBuff;
+    }
+    return $rtnVal;
+}
+
+#------------------------------------------------------------------------------
+# Extract meta information from a PNG image
+# Inputs: 0) ExifTool object reference, 1) dirInfo reference
+# Returns: 1 on success, 0 if this wasn't a valid PNG image, or -1 on write error
+sub ProcessPNG($$)
+{
+    my ($exifTool, $dirInfo) = @_;
+    my $outfile = $$dirInfo{OutFile};
+    my $raf = $$dirInfo{RAF};
+    my $datChunk = '';
+    my $datCount = 0;
+    my $datBytes = 0;
+    my ($sig, $err, $ok);
+
+    # check to be sure this is a valid PNG/MNG/JNG image
+    return 0 unless $raf->Read($sig,8) == 8 and $pngLookup{$sig};
+    if ($outfile) {
+        Write($outfile, $sig) or $err = 1 if $outfile;
+        # can only add tags in Main and TextualData tables
+        $exifTool->{ADD_PNG} = $exifTool->GetNewTagInfoHash(
+            \%Image::ExifTool::PNG::Main,
+            \%Image::ExifTool::PNG::TextualData);
+        # initialize with same directories as JPEG, but PNG tags take priority
+        $exifTool->InitWriteDirs('JPEG','PNG');
+    }
+    my ($fileType, $hdrChunk, $endChunk) = @{$pngLookup{$sig}};
+    $exifTool->SetFileType($fileType);  # set the FileType tag
+    SetByteOrder('MM'); # PNG files are big-endian
+    my $tagTablePtr = GetTagTable('Image::ExifTool::PNG::Main');
+    my $mngTablePtr;
+    if ($fileType ne 'PNG') {
+        $mngTablePtr = GetTagTable('Image::ExifTool::MNG::Main');
+    }
+    my $verbose = $exifTool->Options('Verbose');
+    my $out = $exifTool->Options('TextOut');
+    my ($hbuf, $dbuf, $cbuf, $foundHdr);
+
+    # process the PNG/MNG/JNG chunks
+    undef $noCompressLib;
+    for (;;) {
+        $raf->Read($hbuf,8) == 8 or $exifTool->Warn("Truncated $fileType image"), last;
+        my ($len, $chunk) = unpack('Na4',$hbuf);
+        $len > 0x7fffffff and $exifTool->Warn("Invalid $fileType box size"), last;
+        if ($verbose) {
+            # don't dump image data chunks in verbose mode (only give count instead)
+            if ($datCount and $chunk ne $datChunk) {
+                my $s = $datCount > 1 ? 's' : '';
+                print $out "$fileType $datChunk ($datCount chunk$s, total $datBytes bytes)\n";
+                $datCount = $datBytes = 0;
+                $datChunk = '';
+            }
+            if ($chunk =~ /^(IDAT|JDAT|JDAA)$/) {
+                $datChunk = $chunk;
+                $datCount++;
+                $datBytes += $len;
+            }
+        }
+        if ($outfile) {
+            if ($chunk eq 'IEND') {
+                # add any new chunks immediately before the IEND chunk
+                AddChunks($exifTool, $outfile) or $err = 1;
+            } elsif ($chunk eq 'PLTE' or $chunk eq 'IDAT') {
+                # iCCP chunk must come before PLTE and IDAT
+                # (ignore errors -- will add later as text profile if this fails)
+                Add_iCCP($exifTool, $outfile);
+            }
+        }
+        if ($chunk eq $endChunk) {
+            if ($outfile) {
+                # copy over the rest of the file if necessary
+                Write($outfile, $hbuf) or $err = 1;
+                while ($raf->Read($hbuf, 65536)) {
+                    Write($outfile, $hbuf) or $err = 1;
+                }
+            }
+            $verbose and print $out "$fileType $chunk (end of image)\n";
+            $ok = 1;
+            last;
+        }
+        # read chunk data and CRC
+        unless ($raf->Read($dbuf,$len)==$len and $raf->Read($cbuf, 4)==4) {
+            $exifTool->Warn("Corrupted $fileType image");
+            last;
+        }
+        unless ($foundHdr) {
+            if ($chunk eq $hdrChunk) {
+                $foundHdr = 1;
+            } else {
+                $exifTool->Warn("$fileType image did not start with $hdrChunk");
+                last;
+            }
+        }
+        if ($verbose) {
+            # check CRC when in verbose mode (since we don't care about speed)
+            my $crc = CalculateCRC(\$hbuf, undef, 4);
+            $crc = CalculateCRC(\$dbuf, $crc);
+            $crc == unpack('N',$cbuf) or $exifTool->Warn("Bad CRC for $chunk chunk");
+            if ($datChunk) {
+                Write($outfile, $hbuf, $dbuf, $cbuf) or $err = 1 if $outfile;
+                next;
+            }
+            print $out "$fileType $chunk ($len bytes):\n";
+            if ($verbose > 2) {
+                my %dumpParms = ( Out => $out );
+                $dumpParms{MaxLen} = 96 if $verbose <= 4;
+                Image::ExifTool::HexDump(\$dbuf, undef, %dumpParms);
+            }
+        }
+        # only extract information from chunks in our tables
+        my ($theBuff, $outBuff);
+        $outBuff = \$theBuff if $outfile;
+        if ($$tagTablePtr{$chunk}) {
+            FoundPNG($exifTool, $tagTablePtr, $chunk, $dbuf, undef, $outBuff);
+        } elsif ($mngTablePtr and $$mngTablePtr{$chunk}) {
+            FoundPNG($exifTool, $mngTablePtr, $chunk, $dbuf, undef, $outBuff);
+        }
+        if ($outfile) {
+            if ($theBuff) {
+                $hbuf = pack('Na4',length($theBuff), $chunk);
+                $dbuf = $theBuff;
+                my $crc = CalculateCRC(\$hbuf, undef, 4);
+                $crc = CalculateCRC(\$dbuf, $crc);
+                $cbuf = pack('N', $crc);
+            } elsif (defined $theBuff) {
+                next;   # empty if we deleted the information
+            }
+            Write($outfile, $hbuf, $dbuf, $cbuf) or $err = 1;
+        }
+    }
+    return -1 if $outfile and ($err or not $ok);
+    return 1;   # this was a valid PNG/MNG/JNG image
+}
+
+1;  # end
+
+__END__
+
+=head1 NAME
+
+Image::ExifTool::PNG - Read and write PNG meta information
+
+=head1 SYNOPSIS
+
+This module is used by Image::ExifTool
+
+=head1 DESCRIPTION
+
+This module contains routines required by Image::ExifTool to read and
+write PNG (Portable Network Graphics), MNG (Multi-image Network Graphics)
+and JNG (JPEG Network Graphics) images.
+
+=head1 AUTHOR
+
+Copyright 2003-2007, Phil Harvey (phil at owl.phy.queensu.ca)
+
+This library is free software; you can redistribute it and/or modify it
+under the same terms as Perl itself.
+
+=head1 REFERENCES
+
+=over 4
+
+=item L<http://www.libpng.org/pub/png/spec/1.2/>
+
+=item L<http://www.faqs.org/docs/png/>
+
+=item L<http://www.libpng.org/pub/mng/>
+
+=item L<http://www.libpng.org/pub/png/spec/register/>
+
+=back
+
+=head1 SEE ALSO
+
+L<Image::ExifTool::TagNames/PNG Tags>,
+L<Image::ExifTool::TagNames/MNG Tags>,
+L<Image::ExifTool(3pm)|Image::ExifTool>
+
+=cut
+
Index: /gsdl/trunk/perllib/cpan/Image/ExifTool/PPM.pm
===================================================================
--- /gsdl/trunk/perllib/cpan/Image/ExifTool/PPM.pm	(revision 16842)
+++ /gsdl/trunk/perllib/cpan/Image/ExifTool/PPM.pm	(revision 16842)
@@ -0,0 +1,171 @@
+#------------------------------------------------------------------------------
+# File:         PPM.pm
+#
+# Description:  Read and write PPM meta information
+#
+# Revisions:    09/03/2005 - P. Harvey Created
+#
+# References:   1) http://netpbm.sourceforge.net/doc/ppm.html
+#               2) http://netpbm.sourceforge.net/doc/pgm.html
+#               3) http://netpbm.sourceforge.net/doc/pbm.html
+#------------------------------------------------------------------------------
+
+package Image::ExifTool::PPM;
+
+use strict;
+use vars qw($VERSION);
+use Image::ExifTool qw(:DataAccess :Utils);
+
+$VERSION = '1.04';
+
+#------------------------------------------------------------------------------
+# Read or write information in a PPM/PGM/PBM image
+# Inputs: 0) ExifTool object reference, 1) Directory information reference
+# Returns: 1 on success, 0 if this wasn't a valid PPM file, -1 on write error
+sub ProcessPPM($$)
+{
+    my ($exifTool, $dirInfo) = @_;
+    my $raf = $$dirInfo{RAF};
+    my $outfile = $$dirInfo{OutFile};
+    my $verbose = $exifTool->Options('Verbose');
+    my $out = $exifTool->Options('TextOut');
+    my ($buff, $num, $type, %info);
+#
+# read as much of the image as necessary to extract the header and comments
+#
+    for (;;) {
+        if (defined $buff) {
+            # need to read some more data
+            my $tmp;
+            return 0 unless $raf->Read($tmp, 1024);
+            $buff .= $tmp;
+        } else {
+            return 0 unless $raf->Read($buff, 1024);
+        }
+        # verify this is a valid PPM file
+        return 0 unless $buff =~ /^P([1-6])\s+/g;
+        $num = $1;
+        # note: may contain comments starting with '#'
+        if ($buff =~ /\G#/gc) {
+            # must read more if we are in the middle of a comment
+            next unless $buff =~ /\G ?(.*\n(#.*\n)*)\s*/g;
+            $info{Comment} = $1;
+            next if $buff =~ /\G#/gc;
+        } else {
+            delete $info{Comment};
+        }
+        next unless $buff =~ /\G(\S+)\s+(\S+)\s/g;
+        $info{ImageWidth} = $1;
+        $info{ImageHeight} = $2;
+        $type = [qw{PPM PBM PGM}]->[$num % 3];
+        last if $type eq 'PBM'; # (no MaxVal for PBM images)
+        if ($buff =~ /\G\s*#/gc) {
+            next unless $buff =~ /\G ?(.*\n(#.*\n)*)\s*/g;
+            $info{Comment} = '' unless exists $info{Comment};
+            $info{Comment} .= $1;
+            next if $buff =~ /\G#/gc;
+        }
+        next unless $buff =~ /\G(\S+)\s/g;
+        $info{MaxVal} = $1;
+        last;
+    }
+    # validate numerical values
+    foreach (keys %info) {
+        next if $_ eq 'Comment';
+        return 0 unless $info{$_} =~ /^\d+$/;
+    }
+    if (defined $info{Comment}) {
+        $info{Comment} =~ s/^# ?//mg;   # remove "# " at the start of each line
+        $info{Comment} =~ s/\n$//;      # remove trailing newline
+    }
+    $exifTool->SetFileType($type);
+    my $len = pos($buff);
+#
+# rewrite the file if requested
+#
+    if ($outfile) {
+        my $newValueHash;
+        my $newComment = $exifTool->GetNewValues('Comment', \$newValueHash);
+        my $oldComment = $info{Comment};
+        if (Image::ExifTool::IsOverwriting($newValueHash, $oldComment)) {
+            ++$exifTool->{CHANGED};
+            if ($verbose > 1) {
+                print $out "    - Comment = '$oldComment'\n" if defined $oldComment;
+                print $out "    + Comment = '$newComment'\n" if defined $newComment;
+            }
+        } else {
+            $newComment = $oldComment;  # use existing comment
+        }
+        my $hdr = "P$num\n";
+        if (defined $newComment) {
+            $newComment =~ s/\n/\n# /g;
+            $hdr .= "# $newComment\n";
+        }
+        $hdr .= "$info{ImageWidth} $info{ImageHeight}\n";
+        $hdr .= "$info{MaxVal}\n" if $type ne 'PBM';
+        # write header and start of image
+        Write($outfile, $hdr, substr($buff, $len)) or return -1;
+        # copy over the rest of the image
+        while ($raf->Read($buff, 0x10000)) {
+            Write($outfile, $buff) or return -1;
+        }
+        return 1;
+    }
+#
+# save extracted information
+#
+    if ($verbose > 2) {
+        print $out "$type header ($len bytes):\n";
+        Image::ExifTool::HexDump(\$buff, $len, Out => $out);
+    }
+    my $tag;
+    foreach $tag (qw{Comment ImageWidth ImageHeight MaxVal}) {
+        $exifTool->FoundTag($tag, $info{$tag}) if defined $info{$tag};
+    }
+    return 1;
+}
+
+1;  # end
+
+__END__
+
+=head1 NAME
+
+Image::ExifTool::PPM - Read and write PPM meta information
+
+=head1 SYNOPSIS
+
+This module is used by Image::ExifTool
+
+=head1 DESCRIPTION
+
+This module contains definitions required by Image::ExifTool to read and
+write PPM (Portable Pixel Map), PGM (Portable Gray Map) and PBM (Portable
+BitMap) images.
+
+=head1 AUTHOR
+
+Copyright 2003-2007, Phil Harvey (phil at owl.phy.queensu.ca)
+
+This library is free software; you can redistribute it and/or modify it
+under the same terms as Perl itself.
+
+=head1 REFERENCES
+
+=over 4
+
+=item L<http://netpbm.sourceforge.net/doc/ppm.html>
+
+=item L<http://netpbm.sourceforge.net/doc/pgm.html>
+
+=item L<http://netpbm.sourceforge.net/doc/pbm.html>
+
+=back
+
+=head1 SEE ALSO
+
+L<Image::ExifTool::TagNames/PPM Tags>,
+L<Image::ExifTool(3pm)|Image::ExifTool>
+
+=cut
+
Index: /gsdl/trunk/perllib/cpan/Image/ExifTool/Panasonic.pm
===================================================================
--- /gsdl/trunk/perllib/cpan/Image/ExifTool/Panasonic.pm	(revision 16842)
+++ /gsdl/trunk/perllib/cpan/Image/ExifTool/Panasonic.pm	(revision 16842)
@@ -0,0 +1,626 @@
+#------------------------------------------------------------------------------
+# File:         Panasonic.pm
+#
+# Description:  Panasonic/Leica maker notes tags
+#
+# Revisions:    11/10/2004 - P. Harvey Created
+#
+# References:   1) http://www.compton.nu/panasonic.html (based on FZ10)
+#               2) Derived from DMC-FZ3 samples from dpreview.com
+#               3) http://johnst.org/sw/exiftags/
+#               4) Tels (http://bloodgate.com/) private communication (tests with FZ5)
+#               5) CPAN forum post by 'hardloaf' (http://www.cpanforum.com/threads/2183)
+#               6) http://www.cybercom.net/~dcoffin/dcraw/
+#               7) http://homepage3.nifty.com/kamisaka/makernote/makernote_pana.htm
+#               8) Marcel Coenen private communication (DMC-FZ50)
+#               9) http://forums.dpreview.com/forums/read.asp?forum=1033&message=22756430
+#              10) Jens Duttke private communication (TZ3,FZ30,FZ50)
+#------------------------------------------------------------------------------
+
+package Image::ExifTool::Panasonic;
+
+use strict;
+use vars qw($VERSION);
+use Image::ExifTool::Exif;
+
+$VERSION = '1.26';
+
+sub ProcessPanasonicType2($$$);
+
+# conversions for ShootingMode and SceneMode
+my %shootingMode = (
+    1  => 'Normal',
+    2  => 'Portrait',
+    3  => 'Scenery',
+    4  => 'Sports',
+    5  => 'Night Portrait',
+    6  => 'Program',
+    7  => 'Aperture Priority',
+    8  => 'Shutter Priority',
+    9  => 'Macro',
+    11 => 'Manual',
+    12 => 'Movie Preview', #PH (LZ6)
+    13 => 'Panning',
+    14 => 'Simple', #PH (LZ6)
+    18 => 'Fireworks',
+    19 => 'Party',
+    20 => 'Snow',
+    21 => 'Night Scenery',
+    22 => 'Food', #7
+    23 => 'Baby', #10
+    24 => 'Soft Skin', #PH (LZ6)
+    25 => 'Candlelight', #PH (LZ6)
+    26 => 'Starry Night', #PH (LZ6)
+    27 => 'High Sensitivity', #7 (LZ6)
+    29 => 'Underwater', #7
+    30 => 'Beach', #PH (LZ6)
+    31 => 'Aerial Photo', #PH (LZ6)
+    32 => 'Sunset', #PH (LZ6)
+    33 => 'Pet', #10
+    34 => 'Intelligent ISO', #PH (LZ6)
+);
+
+%Image::ExifTool::Panasonic::Main = (
+    WRITE_PROC => \&Image::ExifTool::Exif::WriteExif,
+    CHECK_PROC => \&Image::ExifTool::Exif::CheckExif,
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
+    WRITABLE => 1,
+    0x01 => {
+        Name => 'ImageQuality',
+        Writable => 'int16u',
+        PrintConv => {
+            2 => 'High',
+            3 => 'Normal',
+            6 => 'Very High', #3 (Leica)
+            7 => 'Raw', #3 (Leica)
+            9 => 'Motion Picture', #PH (LZ6)
+        },
+    },
+    0x02 => {
+        Name => 'FirmwareVersion',
+        Writable => 'undef',
+        Notes => q{
+            for some camera models such as the FZ30 this may be an internal production
+            reference number and not the actual firmware version
+        }, # (ref http://www.stevesforums.com/forums/view_topic.php?id=87764&forum_id=23&)
+        # (can be either binary or ascii -- add decimal points if binary)
+        ValueConv => '$val=~/[\0-\x2f]/ ? join(" ",unpack("C*",$val)) : $val',
+        ValueConvInv => q{
+            $val =~ /(\d+ ){3}\d+/ and $val = pack('C*',split(' ', $val));
+            length($val) == 4 or warn "Version must be 4 numbers\n";
+            return $val;
+        },
+        PrintConv => '$val=~tr/ /./; $val',
+        PrintConvInv => '$val=~tr/./ /; $val',
+    },
+    0x03 => {
+        Name => 'WhiteBalance',
+        Writable => 'int16u',
+        PrintConv => {
+            1 => 'Auto',
+            2 => 'Daylight',
+            3 => 'Cloudy',
+            4 => 'Halogen',
+            5 => 'Manual',
+            8 => 'Flash',
+            10 => 'Black & White', #3 (Leica)
+            11 => 'Manual', #PH (FZ8)
+        },
+    },
+    0x07 => {
+        Name => 'FocusMode',
+        Writable => 'int16u',
+        PrintConv => {
+            1 => 'Auto',
+            2 => 'Manual',
+            4 => 'Auto, Focus button', #4
+            5 => 'Auto, Continuous', #4
+        },
+    },
+    0x0f => {
+        Name => 'AFMode',
+        Writable => 'int8u',
+        Count => 2,
+        PrintConv => { #PH
+            '0 1'   => 'Spot Mode On', # (maybe 9-area for some cameras?)
+            '0 16'  => 'Spot Mode Off or 3-area (high speed)', # (FZ8 is 3-area)
+            '1 0'   => 'Spot Focusing', # (FZ8)
+            '1 1'   => '5-area', # (FZ8)
+            '16'    => 'Normal?', # (only AFMode for DMC-LC20)
+            '16 0'  => '1-area', # (FZ8)
+            '16 16' => '1-area (high speed)', # (FZ8)
+            '32 0'  => '3-area (auto)?', # (DMC-L1 guess)
+            '32 1'  => '3-area (left)?', # (DMC-L1 guess)
+            '32 2'  => '3-area (center)?', # (DMC-L1 guess)
+            '32 3'  => '3-area (right)?', # (DMC-L1 guess)
+        },
+    },
+    0x1a => {
+        Name => 'ImageStabilization',
+        Writable => 'int16u',
+        PrintConv => {
+            2 => 'On, Mode 1',
+            3 => 'Off',
+            4 => 'On, Mode 2',
+        },
+    },
+    0x1c => {
+        Name => 'MacroMode',
+        Writable => 'int16u',
+        PrintConv => {
+            1 => 'On',
+            2 => 'Off',
+            0x101 => 'Tele-Macro', #7
+        },
+    },
+    0x1f => {
+        Name => 'ShootingMode',
+        Writable => 'int16u',
+        PrintConv => \%shootingMode,
+    },
+    0x20 => {
+        Name => 'Audio',
+        Writable => 'int16u',
+        PrintConv => { 1 => 'Yes', 2 => 'No' },
+    },
+    0x21 => { #2
+        Name => 'DataDump',
+        Writable => 0,
+        Binary => 1,
+    },
+    # 0x22 - normally 0, but 2 for 'Simple' ShootingMode in LZ6 sample - PH
+    0x23 => {
+        Name => 'WhiteBalanceBias',
+        Format => 'int16s',
+        Writable => 'int16s',
+        ValueConv => '$val / 3',
+        PrintConv => 'Image::ExifTool::Exif::ConvertFraction($val)',
+        ValueConvInv => '$val * 3',
+        PrintConvInv => 'eval $val',
+    },
+    0x24 => {
+        Name => 'FlashBias',
+        Format => 'int16s',
+        Writable => 'int16s',
+    },
+    0x25 => { #PH
+        Name => 'InternalSerialNumber',
+        Writable => 'undef',
+        Count => 16,
+        Notes => q{
+            this number is unique, and contains the date of manufacture, but is not the
+            same as the number printed on the camera body
+        },
+        PrintConv => q{
+            return $val unless $val=~/^([A-Z]\d{2})(\d{2})(\d{2})(\d{2})(\d{4})/;
+            my $yr = $2 + ($2 < 70 ? 2000 : 1900);
+            return "($1) $yr:$3:$4 no. $5";
+        },
+        PrintConvInv => '$_=$val; tr/A-Z0-9//dc; s/(.{3})(19|20)/$1/; $_',
+    },
+    0x26 => { #PH
+        Name => 'PanasonicExifVersion',
+        Writable => 'undef',
+    },
+    # 0x27 - values: 0 (LZ6,FX10K)
+    0x28 => {
+        Name => 'ColorEffect',
+        Writable => 'int16u',
+        # FX30 manual: (ColorMode) natural, vivid, cool, warm, b/w, sepia
+        PrintConv => {
+            1 => 'Off',
+            2 => 'Warm',
+            3 => 'Cool',
+            4 => 'Black & White',
+            5 => 'Sepia',
+        },
+    },
+    0x29 => { #10
+        Name => 'TimeSincePowerOn',
+        Writable => 'int32u',
+        Notes => q{
+            time in 1/100 sec from when the camera was powered on to when the image is
+            written to memory card
+        },
+        ValueConv => '$val / 100',
+        ValueConvInv => '$val * 100',
+        PrintConv => sub { # convert to format "[DD days ]HH:MM:SS.ss"
+            my $val = shift;
+            my $str = '';
+            if ($val >= 24 * 3600) {
+                my $d = int($val / (24 * 3600));
+                $str .= "$d days ";
+                $val -= $d * 24 * 3600;
+            }
+            my $h = int($val / 3600);
+            $val -= $h * 3600;
+            my $m = int($val / 60);
+            $val -= $m * 60;
+            my $s = int($val);
+            my $f = 100 * ($val - int($val));
+            return sprintf("%s%.2d:%.2d:%.2d.%.2d",$str,$h,$m,$s,$f);
+        },
+        PrintConvInv => sub {
+            my $val = shift;
+            my @vals = ($val =~ /\d+(?:\.\d*)?/g);
+            my $sec = 0;
+            $sec += 24 * 3600 * shift(@vals) if @vals > 3;
+            $sec += 3600 * shift(@vals) if @vals > 2;
+            $sec += 60 * shift(@vals) if @vals > 1;
+            $sec += shift(@vals) if @vals;
+            return $sec;
+        },
+    },
+    0x2a => { #4
+        Name => 'BurstMode',
+        Writable => 'int16u',
+        PrintConv => {
+            0 => 'Off',
+            1 => 'Low/High Quality',
+            2 => 'Infinite',
+        },
+    },
+    0x2b => { #4
+        Name => 'SequenceNumber',
+        Writable => 'int32u',
+    },
+    0x2c => {
+        Name => 'Contrast',
+        Flags => 'PrintHex',
+        Writable => 'int16u',
+        PrintConv => {
+            0 => 'Normal',
+            1 => 'Low',
+            2 => 'High',
+            # 3 - observed with LZ6 in "Aerial Photo" mode - PH
+            # 5 - observed with FX10 in "Underwater" mode - PH
+            0x100 => 'Low',
+            0x110 => 'Normal',
+            0x120 => 'High',
+        }
+    },
+    0x2d => {
+        Name => 'NoiseReduction',
+        Writable => 'int16u',
+        PrintConv => {
+            0 => 'Standard',
+            1 => 'Low',
+            2 => 'High',
+        },
+    },
+    0x2e => { #4
+        Name => 'SelfTimer',
+        Writable => 'int16u',
+        PrintConv => {
+            1 => 'Off',
+            2 => '10s',
+            3 => '2s',
+        },
+    },
+    # 0x2f - values: 1 (LZ6,FX10K)
+    0x30 => { #7
+        Name => 'Rotation',
+        Writable => 'int16u',
+        PrintConv => {
+            1 => 'Horizontal (normal)',
+            6 => 'Rotate 90 CW', #PH (ref 7 gives 270 CW)
+            8 => 'Rotate 270 CW', #PH (ref 7 gives 90 CW)
+        },
+    },
+    # 0x31 - values: 1-5 some sort of mode? (changes with FOC-L) (PH/10)
+    0x32 => { #7
+        Name => 'ColorMode',
+        Writable => 'int16u',
+        PrintConv => {
+            0 => 'Normal',
+            1 => 'Natural',
+            # 2 => observed in LZ6 for some shooting modes
+        },
+    },
+    0x33 => { #10
+        Name => 'BabyAge',
+        Writable => 'string',
+        Notes => 'or pet age', #PH
+        PrintConv => '$val eq "9999:99:99 00:00:00" ? "(not set)" : $val',
+        PrintConvInv => '$val =~ /^\d/ ? $val : "9999:99:99 00:00:00"',
+    },
+    0x34 => { #7/PH
+        Name => 'OpticalZoomMode',
+        Writable => 'int16u',
+        PrintConv => {
+            1 => 'Standard',
+            2 => 'EX Optics',
+        },
+    },
+    0x35 => { #9
+        Name => 'ConversionLens',
+        Writable => 'int16u',
+        PrintConv => { #PH (unconfirmed)
+            1 => 'Off',
+            2 => 'Wide',
+            3 => 'Telephoto',
+            4 => 'Macro',
+        },
+    },
+    0x36 => { #8
+        Name => 'TravelDay',
+        Writable => 'int16u',
+        PrintConv => '$val == 65535 ? "n/a" : $val',
+        PrintConvInv => '$val =~ /(\d+)/ ? $1 : $val',
+    },
+    # 0x37 - values: 0,1,2 (LZ6, 0 for movie preview) and 257 (FX10K)
+    # 0x38 - values: 0,1,2 (LZ6, same as 0x37) and 1,2 (FX10K)
+    0x3a => {
+        Name => 'WorldTimeLocation',
+        Writable => 'int16u',
+        PrintConv => {
+            1 => 'Home',
+            2 => 'Destination',
+        },
+    },
+    # 0x3b - values: 1 (LZ6, FX10K)
+    0x3c => { #PH
+        Name => 'ProgramISO',
+        Writable => 'int16u',
+        PrintConv => '$val == 65535 ? "n/a" : $val',
+        PrintConvInv => '$val eq "n/a" ? 65535 : $val',
+    },
+    # 0x40 Chroma? (ref 7)
+    0x42 => { #7 (DMC-L1)
+        Name => 'FilmMode',
+        Writable => 'int16u',
+        PrintConv => {
+            1 => 'Standard (color)',
+            2 => 'Dynamic (color)',
+            3 => 'Nature (color)',
+            4 => 'Smooth (color)',
+            5 => 'Standard (B&W)',
+            6 => 'Dynamic (B&W)',
+            7 => 'Smooth (B&W)',
+            # 8 => 'My Film 1'? (from owner manual)
+            # 9 => 'My Film 2'?
+        },
+    },
+    0x46 => { #PH/10
+        Name => 'WBAdjustAB',
+        Format => 'int16s',
+        Writable => 'int16u',
+        Notes => 'positive is a shift toward blue',
+    },
+    0x47 => { #PH/10
+        Name => 'WBAdjustGM',
+        Format => 'int16s',
+        Writable => 'int16u',
+        Notes => 'positive is a shift toward green',
+    },
+    0x51 => {
+        Name => 'LensType',
+        Writable => 'string',
+    },
+    0x52 => { #7 (DMC-L1)
+        Name => 'LensSerialNumber',
+        Writable => 'string',
+    },
+    0x53 => { #7 (DMC-L1)
+        Name => 'AccessoryType',
+        Writable => 'string',
+    },
+    0x0e00 => {
+        Name => 'PrintIM',
+        Description => 'Print Image Matching',
+        Writable => 0,
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::PrintIM::Main',
+        },
+    },
+    0x8000 => { #PH
+        Name => 'MakerNoteVersion',
+        Format => 'undef',
+    },
+    0x8001 => { #7/PH/10
+        Name => 'SceneMode',
+        Writable => 'int16u',
+        PrintConv => {
+            0  => 'Off',
+            %shootingMode,
+        },
+    },
+    # 0x8002 - values: 1,2 related to focus? (PH/10)
+    # 0x8003 - values: 1,2 related to focus? (PH/10)
+    0x8004 => { #PH/10
+        Name => 'WBRedLevel',
+        Writable => 'int16u',
+    },
+    0x8005 => { #PH/10
+        Name => 'WBGreenLevel',
+        Writable => 'int16u',
+    },
+    0x8006 => { #PH/10
+        Name => 'WBBlueLevel',
+        Writable => 'int16u',
+    },
+    # 0x8007 - values: 1,2
+    0x8010 => { #PH
+        Name => 'BabyAge',
+        Writable => 'string',
+        Notes => 'or pet age',
+        PrintConv => '$val eq "9999:99:99 00:00:00" ? "(not set)" : $val',
+        PrintConvInv => '$val =~ /^\d/ ? $val : "9999:99:99 00:00:00"',
+    },
+);
+
+# Type 2 tags (ref PH)
+%Image::ExifTool::Panasonic::Type2 = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Image' },
+    FIRST_ENTRY => 0,
+    FORMAT => 'int16u',
+    NOTES => q{
+        This type of maker notes is used by models such as the NV-DS65, PV-D2002,
+        PV-DC3000, PV-DV203, PV-DV401, PV-DV702, PV-L2001, PV-SD4090, PV-SD5000 and
+        iPalm.
+    },
+    0 => {
+        Name => 'MakerNoteType',
+        Format => 'string[4]',
+    },
+    # seems to vary inversely with amount of light, so I'll call it 'Gain' - PH
+    # (minimum is 16, maximum is 136.  Value is 0 for pictures captured from video)
+    3 => 'Gain',
+);
+
+# Tags found in Panasonic RAW images
+%Image::ExifTool::Panasonic::Raw = (
+    GROUPS => { 0 => 'EXIF', 1 => 'IFD0', 2 => 'Image'},
+    WRITE_PROC => \&Image::ExifTool::Exif::WriteExif,
+    CHECK_PROC => \&Image::ExifTool::Exif::CheckExif,
+    WRITE_GROUP => 'IFD0',   # default write group
+    NOTES => 'These tags are found in IFD0 of Panasonic RAW images.',
+    0x01 => {
+        Name => 'PanasonicRawVersion',
+        Writable => 'undef',
+    },
+    0x02 => 'SensorWidth', #5/PH
+    0x03 => 'SensorHeight', #5/PH
+    0x04 => 'SensorTopBorder', #10
+    0x05 => 'SensorLeftBorder', #10
+    0x06 => 'ImageHeight', #5/PH
+    0x07 => 'ImageWidth', #5/PH
+    0x11 => { #10
+        Name => 'RedBalance',
+        Writable => 'int16u',
+        ValueConv => '$val / 256',
+        ValueConvInv => 'int($val * 256 + 0.5)',
+        Notes => 'found in Digilux 2 RAW images',
+    },
+    0x12 => { #10
+        Name => 'BlueBalance',
+        Writable => 'int16u',
+        ValueConv => '$val / 256',
+        ValueConvInv => 'int($val * 256 + 0.5)',
+    },
+    0x17 => { #5
+        Name => 'ISO',
+        Writable => 'int16u',
+    },
+    0x24 => { #6
+        Name => 'WBRedLevel',
+        Writable => 'int16u',
+    },
+    0x25 => { #6
+        Name => 'WBGreenLevel',
+        Writable => 'int16u',
+    },
+    0x26 => { #6
+        Name => 'WBBlueLevel',
+        Writable => 'int16u',
+    },
+    0x10f => {
+        Name => 'Make',
+        Groups => { 2 => 'Camera' },
+        Writable => 'string',
+        DataMember => 'CameraMake',
+        # save this value as an ExifTool member variable
+        RawConv => '$self->{CameraMake} = $val',
+    },
+    0x110 => {
+        Name => 'Model',
+        Description => 'Camera Model Name',
+        Groups => { 2 => 'Camera' },
+        Writable => 'string',
+        DataMember => 'CameraModel',
+        # save this value as an ExifTool member variable
+        RawConv => '$self->{CameraModel} = $val',
+    },
+    0x111 => {
+        Name => 'StripOffsets',
+        Flags => 'IsOffset',
+        OffsetPair => 0x117,  # point to associated byte counts
+        ValueConv => 'length($val) > 32 ? \$val : $val',
+    },
+    0x112 => {
+        Name => 'Orientation',
+        Writable => 'int16u',
+        PrintConv => \%Image::ExifTool::Exif::orientation,
+        Priority => 0,  # so IFD1 doesn't take precedence
+    },
+    0x116 => {
+        Name => 'RowsPerStrip',
+        Priority => 0,
+    },
+    0x117 => {
+        Name => 'StripByteCounts',
+        OffsetPair => 0x111,   # point to associated offset
+        ValueConv => 'length($val) > 32 ? \$val : $val',
+    },
+    0x8769 => {
+        Name => 'ExifOffset',
+        Groups => { 1 => 'ExifIFD' },
+        Flags => 'SubIFD',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::Exif::Main',
+            DirName => 'ExifIFD',
+            Start => '$val',
+        },
+    },
+    0x8825 => {
+        Name => 'GPSInfo',
+        Groups => { 1 => 'GPS' },
+        Flags => 'SubIFD',
+        SubDirectory => {
+            DirName => 'GPS',
+            TagTable => 'Image::ExifTool::GPS::Main',
+            Start => '$val',
+        },
+    },
+);
+
+1;  # end
+
+__END__
+
+=head1 NAME
+
+Image::ExifTool::Panasonic - Panasonic/Leica maker notes tags
+
+=head1 SYNOPSIS
+
+This module is loaded automatically by Image::ExifTool when required.
+
+=head1 DESCRIPTION
+
+This module contains definitions required by Image::ExifTool to interpret
+Panasonic and Leica maker notes in EXIF information.
+
+=head1 AUTHOR
+
+Copyright 2003-2007, Phil Harvey (phil at owl.phy.queensu.ca)
+
+This library is free software; you can redistribute it and/or modify it
+under the same terms as Perl itself.
+
+=head1 REFERENCES
+
+=over 4
+
+=item L<http://www.compton.nu/panasonic.html>
+
+=item L<http://johnst.org/sw/exiftags/>
+
+=item L<http://www.cybercom.net/~dcoffin/dcraw/>
+
+=item L<http://homepage3.nifty.com/kamisaka/makernote/makernote_pana.htm>
+
+=back
+
+=head1 ACKNOWLEDGEMENTS
+
+Thanks to Tels for the information he provided on decoding some tags, and to
+Marcel Coenen and Jens Duttke for their contributions.
+
+=head1 SEE ALSO
+
+L<Image::ExifTool::TagNames/Panasonic Tags>,
+L<Image::ExifTool(3pm)|Image::ExifTool>
+
+=cut
Index: /gsdl/trunk/perllib/cpan/Image/ExifTool/Pentax.pm
===================================================================
--- /gsdl/trunk/perllib/cpan/Image/ExifTool/Pentax.pm	(revision 16842)
+++ /gsdl/trunk/perllib/cpan/Image/ExifTool/Pentax.pm	(revision 16842)
@@ -0,0 +1,1810 @@
+#------------------------------------------------------------------------------
+# File:         Pentax.pm
+#
+# Description:  Pentax/Asahi EXIF maker notes tags
+#
+# Revisions:    11/25/2003 - P. Harvey Created
+#               02/10/2004 - P. Harvey Completely re-done
+#               02/16/2004 - W. Smith Updated (see ref 3)
+#               11/10/2004 - P. Harvey Added support for Asahi cameras
+#               01/10/2005 - P. Harvey Added NikonLens with values from ref 4
+#               03/30/2005 - P. Harvey Added new tags from ref 5
+#               10/04/2005 - P. Harvey Added MOV tags
+#
+# References:   1) Image::MakerNotes::Pentax
+#               2) http://johnst.org/sw/exiftags/ (Asahi cameras)
+#               3) Wayne Smith private communication (Optio 550)
+#               4) http://kobe1995.jp/~kaz/astro/istD.html
+#               5) John Francis (http://www.panix.com/~johnf/raw/index.html) (ist-D/ist-DS)
+#               6) http://www.cybercom.net/~dcoffin/dcraw/
+#               7) Douglas O'Brien private communication (*istD, K10D)
+#               8) Denis Bourez private communication
+#               9) Kazumichi Kawabata private communication
+#              10) David Buret private communication (*istD)
+#              11) http://forums.dpreview.com/forums/read.asp?forum=1036&message=17465929
+#              12) Derby Chang private communication
+#              13) http://homepage3.nifty.com/kamisaka/makernote/makernote_pentax.htm
+#              14) Ger Vermeulen private communication (Optio S6)
+#              15) Barney Garrett private communication (Samsung GX-1S)
+#              16) Axel Kellner private communication (K10D)
+#              17) Cvetan Ivanov private communication (K100D)
+#              18) http://www.gvsoft.homedns.org/exif/makernote-pentax-type3.html
+#              19) Jens Duttke private communication
+#
+# Notes:        See POD documentation at the bottom of this file
+#------------------------------------------------------------------------------
+
+package Image::ExifTool::Pentax;
+
+use strict;
+use vars qw($VERSION);
+use Image::ExifTool::Exif;
+use Image::ExifTool::HP;
+
+$VERSION = '1.61';
+
+sub CryptShutterCount($$);
+
+# pentax lens type codes (ref 4)
+my %pentaxLensType = (
+    '0 0' => 'M-42 or No Lens', #17
+    '1 0' => 'K,M Lens',
+    '2 0' => 'A Series Lens', #7 (from smc PENTAX-A 400mm F5.6)
+    '3 0' => 'SIGMA',
+    '3 17' => 'smc PENTAX-FA SOFT 85mm F2.8',
+    '3 18' => 'smc PENTAX-F 1.7X AF ADAPTER',
+    '3 19' => 'smc PENTAX-F 24-50mm F4',
+    '3 20' => 'smc PENTAX-F 35-80mm F4-5.6',
+    '3 21' => 'smc PENTAX-F 80-200mm F4.7-5.6',
+    '3 22' => 'smc PENTAX-F FISH-EYE 17-28mm F3.5-4.5',
+    '3 23' => 'smc PENTAX-F 100-300mm F4.5-5.6',
+    '3 24' => 'smc PENTAX-F 35-135mm F3.5-4.5',
+    '3 25' => 'smc PENTAX-F 35-105mm F4-5.6 or SIGMA or Tokina',
+    # or '3 25' => SIGMA AF 28-300 F3.5-5.6 DL IF', #11
+    # or '3 25' => 'Tokina 80-200mm F2.8 ATX-Pro', #12
+    # or '3 25' => 'SIGMA 55-200mm F4-5.6 DC', #19
+    '3 26' => 'smc PENTAX-F* 250-600mm F5.6 ED[IF]',
+    '3 27' => 'smc PENTAX-F 28-80mm F3.5-4.5',
+    '3 28' => 'smc PENTAX-F 35-70mm F3.5-4.5',
+    # or '3 28' => 'Tokina 19-35mm F3.5-4.5 AF', #12
+    '3 29' => 'PENTAX-F 28-80mm F3.5-4.5 or SIGMA AF 18-125mm F3.5-5.6 DC', #11 (sigma)
+    '3 30' => 'PENTAX-F 70-200mm F4-5.6',
+    '3 31' => 'smc PENTAX-F 70-210mm F4-5.6',
+    # or '3 31' => 'Tokina AF 730 75-300mm F4.5-5.6',
+    '3 32' => 'smc PENTAX-F 50mm F1.4',
+    '3 33' => 'smc PENTAX-F 50mm F1.7',
+    '3 34' => 'smc PENTAX-F 135mm F2.8 [IF]',
+    '3 35' => 'smc PENTAX-F 28mm F2.8',
+    '3 36' => 'SIGMA 20mm F1.8 EX DG ASPHERICAL RF',
+    '3 38' => 'smc PENTAX-F* 300mm F4.5 ED[IF]',
+    '3 39' => 'smc PENTAX-F* 600mm F4 ED[IF]',
+    '3 40' => 'smc PENTAX-F MACRO 100mm F2.8',
+    '3 41' => 'smc PENTAX-F MACRO 50mm F2.8 or Sigma 50mm F2,8 MACRO', #4,16
+    #'3 44' => 'SIGMA 17-70mm F2.8-4.5 DC MACRO', (Bart Hickman)
+    #'3 44' => 'SIGMA 18-50mm F3.5-5.6 DC, 12-24mm F4.5 EX DG or Tamron 35-90mm F4 AF', #4,12,12
+    #'3 44' => 'SIGMA AF 10-20mm F4-5.6 EX DC', #19
+    '3 44' => 'Tamron 35-90mm F4 AF or various SIGMA models', #4,12,Bart,19
+    '3 46' => 'SIGMA APO 70-200mm F2.8 EX',
+    '3 50' => 'smc PENTAX-FA 28-70mm F4 AL',
+    '3 51' => 'SIGMA 28mm F1.8 EX DG ASPHERICAL MACRO',
+    '3 52' => 'smc PENTAX-FA 28-200mm F3.8-5.6 AL[IF]',
+    # or '3 52' => 'Tamron AF LD 28-200mm F3.8-5.6 (IF) Aspherical (171D), #19
+    '3 53' => 'smc PENTAX-FA 28-80mm F3.5-5.6 AL',
+    '3 247' => 'smc PENTAX-DA FISH-EYE 10-17mm F3.5-4.5 ED[IF]',
+    '3 248' => 'smc PENTAX-DA 12-24mm F4 ED AL[IF]',
+    '3 250' => 'smc PENTAX-DA 50-200mm F4-5.6 ED',
+    '3 251' => 'smc PENTAX-DA 40mm F2.8 Limited',
+    '3 252' => 'smc PENTAX-DA 18-55mm F3.5-5.6 AL',
+    '3 253' => 'smc PENTAX-DA 14mm F2.8 ED[IF]',
+    '3 254' => 'smc PENTAX-DA 16-45mm F4 ED AL',
+    '3 255' => 'SIGMA',
+    # '3 255' => 'SIGMA 18-200mm F3.5-6.3 DC', #8
+    # '3 255' => 'SIGMA DL-II 35-80mm F4-5.6', #12
+    # '3 255' => 'SIGMA DL Zoom 75-300mm F4-5.6', #12
+    # '3 255' => 'SIGMA DF EX Aspherical 28-70mm F2.8', #12
+    # '3 255' => 'SIGMA AF Tele 400mm F5.6 Multi-coated', #19
+    '4 1' => 'smc PENTAX-FA SOFT 28mm F2.8',
+    '4 2' => 'smc PENTAX-FA 80-320mm F4.5-5.6',
+    '4 3' => 'smc PENTAX-FA 43mm F1.9 Limited',
+    '4 6' => 'smc PENTAX-FA 35-80mm F4-5.6',
+    '4 12' => 'smc PENTAX-FA 50mm F1.4', #17
+    '4 15' => 'smc PENTAX-FA 28-105mm F4-5.6 [IF]',
+    '4 16' => 'TAMRON AF 80-210mm F4-5.6 (178D)', #13
+    '4 19' => 'TAMRON SP AF 90mm F2.8 (172E)',
+    '4 20' => 'smc PENTAX-FA 28-80mm F3.5-5.6',
+    '4 22' => 'TOKINA 28-80mm F3.5-5.6', #13
+    '4 23' => 'smc PENTAX-FA 20-35mm F4 AL',
+    '4 24' => 'smc PENTAX-FA 77mm F1.8 Limited',
+    '4 25' => 'TAMRON SP AF 14mm F2.8', #13
+    '4 26' => 'smc PENTAX-FA MACRO 100mm F3.5',
+    '4 27' => 'TAMRON AF28-300mm F/3.5-6.3 LD Aspherical[IF] MACRO (285D)',
+    '4 28' => 'smc PENTAX-FA 35mm F2 AL',
+    '4 29' => 'TAMRON AF 28-200mm F/3.8-5.6 LD Super II MACRO (371D)', #19
+    '4 34' => 'smc PENTAX-FA 24-90mm F3.5-4.5 AL[IF]',
+    '4 35' => 'smc PENTAX-FA 100-300mm F4.7-5.8',
+    '4 36' => 'TAMRON AF70-300mm F/4-5.6 LD MACRO', # both 572D and A17 (Di) - ref 19
+    '4 37' => 'TAMRON SP AF 24-135mm F3.5-5.6 AD AL (190D)', #13
+    '4 38' => 'smc PENTAX-FA 28-105mm F3.2-4.5 AL[IF]',
+    '4 39' => 'smc PENTAX-FA 31mm F1.8AL Limited',
+    '4 41' => 'TAMRON AF 28-200mm Super Zoom F3.8-5.6 Aspherical XR [IF] MACRO (A03)',
+    '4 43' => 'smc PENTAX-FA 28-90mm F3.5-5.6',
+    '4 44' => 'smc PENTAX-FA J 75-300mm F4.5-5.8 AL',
+    '4 45' => 'TAMRON 28-300mm F3.5-6.3 Ultra zoom XR',
+    '4 46' => 'smc PENTAX-FA J 28-80mm F3.5-5.6 AL',
+    '4 47' => 'smc PENTAX-FA J 18-35mm F4-5.6 AL',
+    '4 49' => 'TAMRON SP AF 28-75mm F2.8 XR Di (A09)',
+    '4 51' => 'smc PENTAX-D FA 50mm F2.8 MACRO',
+    '4 52' => 'smc PENTAX-D FA 100mm F2.8 MACRO',
+    '4 244' => 'smc PENTAX-DA 21mm F3.2 AL Limited', #9
+    '4 245' => 'Schneider D-XENON 50-200mm', #15
+    '4 246' => 'Schneider D-XENON 18-55mm', #15
+    '4 247' => 'smc PENTAX-DA 10-17mm F3.5-4.5 ED [IF] Fisheye zoom', #10
+    '4 248' => 'smc PENTAX-DA 12-24mm F4 ED AL [IF]', #10
+    '4 249' => 'TAMRON XR DiII 18-200mm F3.5-6.3 (A14)',
+    '4 250' => 'smc PENTAX-DA 50-200mm F4-5.6 ED', #8
+    '4 251' => 'smc PENTAX-DA 40mm F2.8 Limited', #9
+    '4 252' => 'smc PENTAX-DA 18-55mm F3.5-5.6 AL', #8
+    '4 253' => 'smc PENTAX-DA 14mm F2.8 ED[IF]',
+    '4 254' => 'smc PENTAX-DA 16-45mm F4 ED AL',
+    '5 1' => 'smc PENTAX-FA* 24mm F2 AL[IF]',
+    '5 2' => 'smc PENTAX-FA 28mm F2.8 AL',
+    '5 3' => 'smc PENTAX-FA 50mm F1.7',
+    '5 4' => 'smc PENTAX-FA 50mm F1.4',
+    '5 5' => 'smc PENTAX-FA* 600mm F4 ED[IF]',
+    '5 6' => 'smc PENTAX-FA* 300mm F4.5 ED[IF]',
+    '5 7' => 'smc PENTAX-FA 135mm F2.8 [IF]',
+    '5 8' => 'smc PENTAX-FA MACRO 50mm F2.8',
+    '5 9' => 'smc PENTAX-FA MACRO 100mm F2.8',
+    '5 10' => 'smc PENTAX-FA* 85mm F1.4 [IF]',
+    '5 11' => 'smc PENTAX-FA* 200mm F2.8 ED[IF]',
+    '5 12' => 'smc PENTAX-FA 28-80mm F3.5-4.7',
+    '5 13' => 'smc PENTAX-FA 70-200mm F4-5.6',
+    '5 14' => 'smc PENTAX-FA* 250-600mm F5.6 ED[IF]',
+    '5 15' => 'smc PENTAX-FA 28-105mm F4-5.6',
+    '5 16' => 'smc PENTAX-FA 100-300mm F4.5-5.6',
+    '6 1' => 'smc PENTAX-FA* 85mm F1.4[IF]',
+    '6 2' => 'smc PENTAX-FA* 200mm F2.8 ED[IF]',
+    '6 3' => 'smc PENTAX-FA* 300mm F2.8 ED[IF]',
+    '6 4' => 'smc PENTAX-FA* 28-70mm F2.8 AL',
+    '6 5' => 'smc PENTAX-FA* 80-200mm F2.8 ED[IF]',
+    '6 6' => 'smc PENTAX-FA* 28-70mm F2.8 AL',
+    '6 7' => 'smc PENTAX-FA* 80-200mm F2.8 ED[IF]',
+    '6 8' => 'smc PENTAX-FA 28-70mm F4AL',
+    '6 9' => 'smc PENTAX-FA 20mm F2.8',
+    '6 10' => 'smc PENTAX-FA* 400mm F5.6 ED[IF]',
+    '6 13' => 'smc PENTAX-FA* 400mm F5.6 ED[IF]',
+    '6 14' => 'smc PENTAX-FA* MACRO 200mm F4 ED[IF]',
+    '7 0' => 'smc PENTAX-DA 21mm F3.2 AL Limited', #13
+    '7 238' => 'TAMRON AF 18-250mm F3.5-6.3 Di II LD Aspherical [IF] MACRO', #19
+    '7 243' => 'smc PENTAX-DA 70mm F2.4 Limited', #PH (K10D)
+    '7 244' => 'smc PENTAX-DA 21mm F3.2 AL Limited', #16
+    '8 241' => 'smc PENTAX-DA* 50-135mm F2.8 ED [IF] SDM', #19
+    '8 242' => 'smc PENTAX-DA* 16-50mm F2.8 ED AL [IF] SDM', #19
+);
+
+# Pentax model ID codes (PH)
+my %pentaxModelID = (
+    0x0000d => 'Optio 330/430',
+    0x12926 => 'Optio 230',
+    0x12958 => 'Optio 330GS',
+    0x12962 => 'Optio 450/550',
+    0x1296c => 'Optio S',
+    0x12994 => '*ist D',
+    0x129b2 => 'Optio 33L',
+    0x129bc => 'Optio 33LF',
+    0x129c6 => 'Optio 33WR/43WR/555',
+    0x129d5 => 'Optio S4',
+    0x12a02 => 'Optio MX',
+    0x12a0c => 'Optio S40',
+    0x12a16 => 'Optio S4i',
+    0x12a34 => 'Optio 30',
+    0x12a52 => 'Optio S30',
+    0x12a66 => 'Optio 750Z',
+    0x12a70 => 'Optio SV',
+    0x12a75 => 'Optio SVi',
+    0x12a7a => 'Optio X',
+    0x12a8e => 'Optio S5i',
+    0x12a98 => 'Optio S50',
+    0x12aa2 => '*ist DS',
+    0x12ab6 => 'Optio MX4',
+    0x12ac0 => 'Optio S5n',
+    0x12aca => 'Optio WP',
+    0x12afc => 'Optio S55',
+    0x12b10 => 'Optio S5z',
+    0x12b1a => '*ist DL',
+    0x12b24 => 'Optio S60',
+    0x12b2e => 'Optio S45',
+    0x12b38 => 'Optio S6',
+    0x12b4c => 'Optio WPi', #13
+    0x12b56 => 'BenQ DC X600',
+    0x12b60 => '*ist DS2',
+    0x12b62 => 'Samsung GX-1S',
+    0x12b6a => 'Optio A10',
+    0x12b7e => '*ist DL2',
+    0x12b80 => 'Samsung GX-1L',
+    0x12b9c => 'K100D',
+    0x12b9d => 'K110D',
+    0x12ba2 => 'K100D Super', #19
+    0x12bb0 => 'Optio T10/T20',
+    0x12be2 => 'Optio W10',
+    0x12bf6 => 'Optio M10',
+    0x12c1e => 'K10D',
+    0x12c20 => 'Samsung GX10',
+    0x12c28 => 'Optio S7',
+    0x12c2d => 'Optio L20',
+    0x12c32 => 'Optio M20',
+    0x12c3c => 'Optio W20',
+    0x12c46 => 'Optio A20',
+    0x12c8c => 'Optio M30',
+    0x12c78 => 'Optio E30',
+    0x12c82 => 'Optio T30',
+    0x12c96 => 'Optio W30',
+    0x12ca0 => 'Optio A30',
+    0x12cb4 => 'Optio E40',
+    0x12cbe => 'Optio M40',
+    0x12cc8 => 'Optio Z10',
+    0x12cdc => 'Optio S10',
+    0x12ce6 => 'Optio A40',
+    0x12cf0 => 'Optio V10',
+);
+
+# Pentax city codes - (PH, Optio WP)
+my %pentaxCities = (
+    0 => 'Pago Pago',
+    1 => 'Honolulu',
+    2 => 'Anchorage',
+    3 => 'Vancouver',
+    4 => 'San Fransisco',
+    5 => 'Los Angeles',
+    6 => 'Calgary',
+    7 => 'Denver',
+    8 => 'Mexico City',
+    9 => 'Chicago',
+    10 => 'Miami',
+    11 => 'Toronto',
+    12 => 'New York',
+    13 => 'Santiago',
+    14 => 'Caracus',
+    15 => 'Halifax',
+    16 => 'Buenos Aires',
+    17 => 'Sao Paulo',
+    18 => 'Rio de Janeiro',
+    19 => 'Madrid',
+    20 => 'London',
+    21 => 'Paris',
+    22 => 'Milan',
+    23 => 'Rome',
+    24 => 'Berlin',
+    25 => 'Johannesburg',
+    26 => 'Istanbul',
+    27 => 'Cairo',
+    28 => 'Jerusalem',
+    29 => 'Moscow',
+    30 => 'Jeddah',
+    31 => 'Tehran',
+    32 => 'Dubai',
+    33 => 'Karachi',
+    34 => 'Kabul',
+    35 => 'Male',
+    36 => 'Delhi',
+    37 => 'Colombo',
+    38 => 'Kathmandu',
+    39 => 'Dacca',
+    40 => 'Yangon',
+    41 => 'Bangkok',
+    42 => 'Kuala Lumpur',
+    43 => 'Vientiane',
+    44 => 'Singapore',
+    45 => 'Phnom Penh',
+    46 => 'Ho Chi Minh',
+    47 => 'Jakarta',
+    48 => 'Hong Kong',
+    49 => 'Perth',
+    50 => 'Beijing',
+    51 => 'Shanghai',
+    52 => 'Manila',
+    53 => 'Taipei',
+    54 => 'Seoul',
+    55 => 'Adelaide',
+    56 => 'Tokyo',
+    57 => 'Guam',
+    58 => 'Sydney',
+    59 => 'Noumea',
+    60 => 'Wellington',
+    61 => 'Auckland',
+    62 => 'Lima',
+    63 => 'Dakar',
+    64 => 'Algiers',
+    65 => 'Helsinki',
+    66 => 'Athens',
+    67 => 'Nairobi',
+    68 => 'Amsterdam',
+    69 => 'Stockholm',
+    70 => 'Lisbon', #14
+);
+
+%Image::ExifTool::Pentax::Main = (
+    WRITE_PROC => \&Image::ExifTool::Exif::WriteExif,
+    CHECK_PROC => \&Image::ExifTool::Exif::CheckExif,
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
+    WRITABLE => 1,
+    0x0000 => { #5
+        Name => 'PentaxVersion',
+        Writable => 'int8u',
+        Count => 4,
+        PrintConv => '$val=~tr/ /./; $val',
+        PrintConvInv => '$val=~tr/./ /; $val',
+    },
+    0x0001 => { #PH
+        Name => 'PentaxModelType',
+        Writable => 'int16u',
+        # (values of 0-5 seem to group models into 6 categories, ref 13)
+    },
+    0x0002 => { #PH
+        Name => 'PreviewImageSize',
+        Groups => { 2 => 'Image' },
+        Writable => 'int16u',
+        Count => 2,
+        PrintConv => '$val =~ tr/ /x/; $val',
+    },
+    0x0003 => { #PH
+        Name => 'PreviewImageLength',
+        OffsetPair => 0x0004, # point to associated offset
+        DataTag => 'PreviewImage',
+        Groups => { 2 => 'Image' },
+        Writable => 'int32u',
+        Protected => 2,
+    },
+    0x0004 => { #PH
+        Name => 'PreviewImageStart',
+        IsOffset => 2,  # code to use original base
+        Protected => 2,
+        OffsetPair => 0x0003, # point to associated byte count
+        DataTag => 'PreviewImage',
+        Groups => { 2 => 'Image' },
+        Writable => 'int32u',
+    },
+    0x0005 => { #13
+        Name => 'PentaxModelID',
+        Writable => 'int32u',
+        PrintHex => 1,
+        SeparateTable => 1,
+        PrintConv => \%pentaxModelID,
+    },
+    0x0006 => { #5
+        # Note: Year is int16u in MM byte ordering regardless of EXIF byte order
+        Name => 'Date',
+        Groups => { 2 => 'Time' },
+        Writable => 'undef',
+        Count => 4,
+        Shift => 'Time',
+        DataMember => 'PentaxDate',
+        RawConv => '$$self{PentaxDate} = $val', # save to decrypt ShutterCount
+        ValueConv => 'length($val)==4 ? sprintf("%.4d:%.2d:%.2d",unpack("nC2",$val)) : "Unknown ($val)"',
+        ValueConvInv => 'my @v=split /:/, $val;pack("nC2",$v[0],$v[1],$v[2])',
+    },
+    0x0007 => { #5
+        Name => 'Time',
+        Groups => { 2 => 'Time' },
+        Writable => 'undef',
+        Count => 3,
+        Shift => 'Time',
+        DataMember => 'PentaxTime',
+        RawConv => '$$self{PentaxTime} = $val', # save to decrypt ShutterCount
+        ValueConv => 'length($val)>=3 ? sprintf("%.2d:%.2d:%.2d",unpack("C3",$val)) : "Unknown ($val)"',
+        ValueConvInv => 'pack("C3",split(/:/,$val))',
+    },
+    0x0008 => { #2
+        Name => 'Quality',
+        Writable => 'int16u',
+        PrintConv => {
+            0 => 'Good',
+            1 => 'Better',
+            2 => 'Best',
+            3 => 'TIFF', #5
+            4 => 'RAW', #5
+        },
+    },
+    0x0009 => { #3
+        Name => 'PentaxImageSize',
+        Groups => { 2 => 'Image' },
+        Writable => 'int16u',
+        PrintConv => {
+            0 => '640x480',
+            1 => 'Full', #PH - this can mean 2048x1536 or 2240x1680 or ... ?
+            2 => '1024x768',
+            3 => '1280x960', #PH (Optio WP)
+            4 => '1600x1200',
+            5 => '2048x1536',
+            8 => '2560x1920 or 2304x1728', #PH (Optio WP) or #14
+            9 => '3072x2304', #PH (Optio M30)
+            10 => '3264x2448', #13
+            19 => '320x240', #PH (Optio WP)
+            20 => '2288x1712', #13
+            21 => '2592x1944',
+            22 => '2304x1728 or 2592x1944', #2 or #14
+            23 => '3056x2296', #13
+            25 => '2816x2212 or 2816x2112', #13 or #14
+            27 => '3648x2736', #PH (Optio A20)
+            '0 0' => '2304x1728', #13
+            '4 0' => '1600x1200', #PH (Optio MX4)
+            '5 0' => '2048x1536', #13
+            '8 0' => '2560x1920', #13
+            '32 2' => '960x640', #7
+            '33 2' => '1152x768', #7
+            '34 2' => '1536x1024', #7
+            '35 1' => '2400x1600', #7
+            '36 0' => '3008x2008 or 3040x2024',  #PH
+            '37 0' => '3008x2000', #13
+        },
+    },
+    # 0x000a - (See note below)
+    0x000b => { #3
+        Name => 'PictureMode',
+        Writable => 'int16u',
+        PrintConv => {
+            0 => 'Program', #PH
+            2 => 'Program AE', #13
+            3 => 'Manual', #13
+            5 => 'Portrait',
+            6 => 'Landscape',
+            8 => 'Sport', #PH
+            9 => 'Night Scene',
+            11 => 'Soft', #PH
+            12 => 'Surf & Snow',
+            13 => 'Sunset or Candlelight', #14 (Candlelight)
+            14 => 'Autumn',
+            15 => 'Macro',
+            17 => 'Fireworks',
+            18 => 'Text',
+            19 => 'Panorama', #PH
+            30 => 'Self Portrait', #PH
+            31 => 'Illustrations', #13
+            33 => 'Digital Filter', #13
+            37 => 'Museum', #PH
+            38 => 'Food', #PH
+            40 => 'Green Mode', #PH
+            49 => 'Light Pet', #PH
+            50 => 'Dark Pet', #PH
+            51 => 'Medium Pet', #PH
+            53 => 'Underwater', #PH
+            54 => 'Candlelight', #PH
+            55 => 'Natural Skin Tone', #PH
+            56 => 'Synchro Sound Record', #PH
+            58 => 'Frame Composite', #14
+            '0 0' => 'Auto', #13
+            '0 2' => 'Program AE', #13
+            '5 2' => 'Portrait', #13
+            '6 2' => 'Landscape', #13
+            '9 1' => 'Night Scene', #13
+            '13 1' => 'Candlelight', #13
+            '15 1' => 'Macro', #13
+            '255 0' => 'Digital Filter?', #13
+        },
+    },
+    0x000c => { #PH
+        Name => 'FlashMode',
+        Writable => 'int16u',
+        PrintHex => 1,
+        ValueConv => '$val=~s/ .*//; $val', # ignore 2nd value
+        PrintConv => {
+            0x000 => 'Auto, Did not fire',
+            0x001 => 'Off',
+            0x003 => 'Auto, Did not fire, Red-eye reduction',
+            0x100 => 'Auto, Fired',
+            0x102 => 'On',
+            0x103 => 'Auto, Fired, Red-eye reduction',
+            0x104 => 'On, Red-eye reduction',
+            0x105 => 'On, Wireless',
+            0x108 => 'On, Soft',
+            0x109 => 'On, Slow-sync',
+            0x10a => 'On, Slow-sync, Red-eye reduction',
+            0x10b => 'On, Trailing-curtain Sync',
+        },
+    },
+    0x000d => [ #2
+        {
+            Condition => '$self->{CameraMake} =~ /^PENTAX/',
+            Name => 'FocusMode',
+            Notes => 'Pentax models',
+            Writable => 'int16u',
+            PrintConv => { #PH
+                0 => 'Normal',
+                1 => 'Macro',
+                2 => 'Infinity',
+                3 => 'Manual',
+                5 => 'Pan Focus',
+                16 => 'AF-S', #17
+                17 => 'AF-C', #17
+            },
+        },
+        {
+            Name => 'FocusMode',
+            Writable => 'int16u',
+            Notes => 'Asahi models',
+            PrintConv => { #2
+                0 => 'Normal',
+                1 => 'Macro (1)',
+                2 => 'Macro (2)',
+                3 => 'Infinity',
+            },
+        },
+    ],
+    0x000e => { #7
+        Name => 'AFPointSelected',
+        Writable => 'int16u',
+        PrintConv => {
+            0xffff => 'Auto',
+            0xfffe => 'Fixed Center',
+            1 => 'Upper-left',
+            2 => 'Top',
+            3 => 'Upper-right',
+            4 => 'Left',
+            5 => 'Mid-left',
+            6 => 'Center',
+            7 => 'Mid-right',
+            8 => 'Right',
+            9 => 'Lower-left',
+            10 => 'Bottom',
+            11 => 'Lower-right',
+        },
+    },
+    0x000f => { #PH
+        Name => 'AFPointsInFocus',
+        Writable => 'int16u',
+        PrintHex => 1,
+        PrintConv => {
+            0xffff => 'None',
+            0 => 'Fixed Center or Multiple', #PH/14
+            1 => 'Top-left',
+            2 => 'Top-center',
+            3 => 'Top-right',
+            4 => 'Left',
+            5 => 'Center',
+            6 => 'Right',
+            7 => 'Bottom-left',
+            8 => 'Bottom-center',
+            9 => 'Bottom-right',
+        },
+    },
+    0x0010 => { #PH
+        Name => 'FocusPosition',
+        Writable => 'int16u',
+        Notes => 'related to focus distance but affected by focal length',
+    },
+    0x0012 => { #PH
+        Name => 'ExposureTime',
+        Writable => 'int32u',
+        Priority => 0,
+        ValueConv => '$val * 1e-5',
+        ValueConvInv => '$val * 1e5',
+        # value may be 0xffffffff in Bulb mode (ref 19)
+        PrintConv => '$val > 42949 ? "Unknown (Bulb)" : Image::ExifTool::Exif::PrintExposureTime($val)',
+        PrintConvInv => '$val=~/(unknown|bulb)/i ? $val : eval $val',
+    },
+    0x0013 => { #PH
+        Name => 'FNumber',
+        Writable => 'int16u',
+        Priority => 0,
+        ValueConv => '$val / 10',
+        ValueConvInv => '$val * 10',
+        PrintConv => 'sprintf("%.1f",$val)',
+        PrintConvInv => '$val',
+    },
+    # ISO Tag - Entries confirmed by W. Smith 12 FEB 04
+    0x0014 => {
+        Name => 'ISO',
+        Writable => 'int16u',
+        Priority => 0,
+        PrintConv => {
+            3 => 50, #(NC=Not Confirmed)
+            4 => 64,
+            5 => 80, #(NC)
+            6 => 100,
+            7 => 125, #PH
+            8 => 160, #PH
+            9 => 200,
+            10 => 250, #(NC)
+            11 => 320, #PH
+            12 => 400,
+            13 => 500, #(NC)
+            14 => 640, #(NC)
+            15 => 800,
+            16 => 1000, #(NC)
+            17 => 1250, #(NC)
+            18 => 1600, #PH
+            21 => 3200, #(NC)
+            50 => 50, #PH
+            100 => 100, #PH
+            200 => 200, #PH
+            400 => 400, #PH
+            800 => 800, #PH
+            1600 => 1600, #PH
+            3200 => 3200, #PH
+            # have seen 268 for ISO 1600 on K10D - PH
+        },
+    },
+    # 0x0015 - Related to measured EV? ranges from -2 to 6 if interpreted as signed int (PH)
+    0x0016 => { #PH
+        Name => 'ExposureCompensation',
+        Writable => 'int16u',
+        ValueConv => '($val - 50) / 10',
+        ValueConvInv => 'int($val * 10 + 50.5)',
+        PrintConv => 'Image::ExifTool::Exif::ConvertFraction($val)',
+        PrintConvInv => 'eval $val',
+    },
+    0x0017 => { #3
+        Name => 'MeteringMode',
+        Writable => 'int16u',
+        PrintConv => {
+            0 => 'Multi Segment',
+            1 => 'Center Weighted',
+            2 => 'Spot',
+        },
+    },
+    0x0018 => { #PH
+        Name => 'AutoBracketing',
+        Writable => 'int16u',
+        Count => -1,
+        Notes => q{
+            one or two numbers: exposure bracket step in EV, then extended bracket if
+            available.  Extended bracket values are printed as 'WB-BA', 'WB-GM',
+            'Saturation', 'Sharpness' or 'Contrast' followed by '+1', '+2' or '+3' for
+            step size
+        },
+        ValueConv => sub {
+            my @v = split(' ', shift);
+            if ($v[0] < 10) {
+                $v[0] /= 3;     # 1=.3ev, 2=.7, 3=1...
+            } else {
+                $v[0] -= 9.5;   # 10=.5ev, 11=1.5...
+            }
+            return "@v";
+        },
+        ValueConvInv => sub {
+            my @v = split(' ', shift);
+            if (abs($v[0]-int($v[0])-.5) > 0.05) {
+                $v[0] = int($v[0] * 3 + 0.5);
+            } else {
+                $v[0] = int($v[0] + 10);
+            }
+            return "@v";
+        },
+        PrintConv => sub {
+            my @v = split(' ', shift);
+            $v[0] = sprintf('%.1f', $v[0]) if $v[0];
+            if ($v[1]) {
+                my %s = (1=>'WB-BA',2=>'WB-GM',3=>'Saturation',4=>'Sharpness',5=>'Contrast');
+                my $t = $v[1] >> 8;
+                $v[1] = sprintf('%s+%d', $s{$t} || "Unknown($t)", $v[1] & 0xff);
+            } elsif (defined $v[1]) {
+                $v[1] = 'No Extended Bracket',
+            }
+            return join(' EV, ', @v);
+        },
+        PrintConvInv => sub {
+            my @v = split(/, ?/, shift);
+            $v[0] =~ s/ ?EV//i;
+            if ($v[1]) {
+                my %s = ('WB-BA'=>1,'WB-GM'=>2,Saturation=>3,Sharpness=>4,Contrast=>5);
+                if ($v[1] =~ /^No\b/i) {
+                    $v[1] = 0;
+                } elsif ($v[1] =~ /Unknown\((\d+)\)\+(\d+)/i) {
+                    $v[1] = ($1 << 8) + $2;
+                } elsif ($v[1] =~ /([\w-]+)\+(\d+)/ and $s{$1}) {
+                    $v[1] = ($s{$1} << 8) + $2;
+                } else {
+                    warn "Bad extended bracket\n";
+                }
+            }
+            return "@v";
+        },
+    },
+    0x0019 => { #3
+        Name => 'WhiteBalance',
+        Writable => 'int16u',
+        PrintConv => {
+            0 => 'Auto',
+            1 => 'Daylight',
+            2 => 'Shade',
+            3 => 'Fluorescent', #2
+            4 => 'Tungsten',
+            5 => 'Manual',
+            6 => 'DaylightFluorescent', #13
+            7 => 'DaywhiteFluorescent', #13
+            8 => 'WhiteFluorescent', #13
+            9 => 'Flash', #13
+            10 => 'Cloudy', #13
+            65534 => 'Unknown', #13
+            65535 => 'User Selected', #13
+        },
+    },
+    0x001a => { #5
+        Name => 'WhiteBalanceMode',
+        Writable => 'int16u',
+        PrintConv => {
+            1 => 'Auto (Daylight)',
+            2 => 'Auto (Shade)',
+            3 => 'Auto (Flash)',
+            4 => 'Auto (Tungsten)',
+            7 => 'Auto (DaywhiteFluorescent)', #17 (K100D guess)
+            8 => 'Auto (WhiteFluorescent)', #17 (K100D guess)
+            10 => 'Auto (Cloudy)', #17 (K100D guess)
+            0xffff => 'User-Selected',
+            0xfffe => 'Preset (Fireworks?)', #PH
+            # 0xfffd observed in K100D (ref 17)
+        },
+    },
+    0x001b => { #6
+        Name => 'BlueBalance',
+        Writable => 'int16u',
+        ValueConv => '$val / 256',
+        ValueConvInv => 'int($val * 256 + 0.5)',
+    },
+    0x001c => { #6
+        Name => 'RedBalance',
+        Writable => 'int16u',
+        ValueConv => '$val / 256',
+        ValueConvInv => 'int($val * 256 + 0.5)',
+    },
+    0x001d => [
+        # Would be nice if there was a general way to determine units for FocalLength...
+        {
+            # Optio 30, 33WR, 43WR, 450, 550, 555, 750Z, X
+            Name => 'FocalLength',
+            Condition => '$self->{CameraModel} =~ /^PENTAX Optio (30|33WR|43WR|450|550|555|750Z|X)\b/',
+            Writable => 'int32u',
+            Priority => 0,
+            ValueConv => '$val / 10',
+            ValueConvInv => '$val * 10',
+            PrintConv => 'sprintf("%.1fmm",$val)',
+            PrintConvInv => '$val=~s/mm//;$val',
+        },
+        {
+            # K100D, Optio 230, 330GS, 33L, 33LF, A10, M10, MX, MX4, S, S30,
+            # S4, S4i, S5i, S5n, S5z, S6, S45, S50, S55, S60, SV, Svi, W10, WP,
+            # *ist D, DL, DL2, DS, DS2
+            # (Note: the Optio S6 seems to report the minimum focal length - PH)
+            Name => 'FocalLength',
+            Writable => 'int32u',
+            Priority => 0,
+            ValueConv => '$val / 100',
+            ValueConvInv => '$val * 100',
+            PrintConv => 'sprintf("%.1fmm",$val)',
+            PrintConvInv => '$val=~s/mm//;$val',
+        },
+    ],
+    0x001e => { #3
+        Name => 'DigitalZoom',
+        Writable => 'int16u',
+        ValueConv => '$val / 100', #14
+        ValueConvInv => '$val * 100', #14
+    },
+    0x001f => {
+        Name => 'Saturation',
+        Writable => 'int16u',
+        PrintConv => {
+            0 => 'Low', #PH
+            1 => 'Normal', #PH
+            2 => 'High', #PH
+            3 => 'Med Low', #2
+            4 => 'Med High', #2
+            5 => 'Very Low', #(NC)
+            6 => 'Very High', #(NC)
+            # the *istD has pairs of values - PH
+            '0 0' => 'Low',
+            '1 0' => 'Normal',
+            '2 0' => 'High',
+        },
+    },
+    0x0020 => {
+        Name => 'Contrast',
+        Writable => 'int16u',
+        PrintConv => {
+            0 => 'Low', #PH
+            1 => 'Normal', #PH
+            2 => 'High', #PH
+            3 => 'Med Low', #2
+            4 => 'Med High', #2
+            5 => 'Very Low', #PH
+            6 => 'Very High', #(NC)
+            # the *istD has pairs of values - PH
+            '0 0' => 'Low',
+            '1 0' => 'Normal',
+            '2 0' => 'High',
+        },
+    },
+    0x0021 => {
+        Name => 'Sharpness',
+        Writable => 'int16u',
+        PrintConv => {
+            0 => 'Soft', #PH
+            1 => 'Normal', #PH
+            2 => 'Hard', #PH
+            3 => 'Med Soft', #2
+            4 => 'Med Hard', #2
+            5 => 'Very Soft', #(NC)
+            6 => 'Very Hard', #(NC)
+            # the *istD has pairs of values - PH
+            '0 0' => 'Soft',
+            '1 0' => 'Normal',
+            '2 0' => 'Hard',
+        },
+    },
+    0x0022 => { #PH
+        Name => 'WorldTimeLocation',
+        Groups => { 2 => 'Time' },
+        Writable => 'int16u',
+        PrintConv => {
+            0 => 'Hometown',
+            1 => 'Destination',
+        },
+    },
+    0x0023 => { #PH
+        Name => 'HometownCity',
+        Groups => { 2 => 'Time' },
+        Writable => 'int16u',
+        SeparateTable => 'City',
+        PrintConv => \%pentaxCities,
+    },
+    0x0024 => { #PH
+        Name => 'DestinationCity',
+        Groups => { 2 => 'Time' },
+        Writable => 'int16u',
+        SeparateTable => 'City',
+        PrintConv => \%pentaxCities,
+    },
+    0x0025 => { #PH
+        Name => 'HometownDST',
+        Groups => { 2 => 'Time' },
+        Writable => 'int16u',
+        PrintConv => { 0 => 'No', 1 => 'Yes' },
+    },
+    0x0026 => { #PH
+        Name => 'DestinationDST',
+        Groups => { 2 => 'Time' },
+        Writable => 'int16u',
+        PrintConv => { 0 => 'No', 1 => 'Yes' },
+    },
+    # 0x0027 - could be a 5-byte string software version number for the Optio 550,
+    #          but the offsets are wacky so it is hard to tell (PH)
+    0x0029 => { #5
+        Name => 'FrameNumber',
+        Writable => 'int32u',
+    },
+    # 0x002b - definitely exposure related somehow (PH)
+    0x0032 => { #13
+        Name => 'ImageProcessing',
+        Writable => 'undef',
+        Format => 'int8u',
+        Count => 4,
+        PrintConv => {
+            '0 0' => 'Unprocessed', #PH
+            '0 0 0 0' => 'Unprocessed',
+            '0 0 0 4' => 'Digital Filter',
+            '2 0 0 0' => 'Cropped', #PH
+            '4 0 0 0' => 'Color Filter',
+            '16 0 0 0' => 'Frame Synthesis?',
+        },
+    },
+    # and "\0\0\0\x04" for Digital filter (ref 13)
+    # and "\x04\0\0\0" for Color filter (ref 13) (PH - digital or color filter, K10D)
+    0x0033 => { #PH (K110D/K100D)
+        Name => 'PictureMode',
+        Writable => 'int8u',
+        Count => 3,
+        PrintConv => {
+            # Program dial modes
+            '0 0 0'  => 'Program', # K110D
+            '0 4 0'  => 'Standard', #13
+            '0 5 0'  => 'Portrait', # K110D
+            '0 6 0'  => 'Landscape', # K110D
+            '0 7 0'  => 'Macro', # K110D
+            '0 8 0'  => 'Sport', # K110D
+            '0 9 0'  => 'Night Scene Portrait', # K110D
+            '0 10 0' => 'No Flash', # K110D
+            # SCN modes (menu-selected)
+            '0 11 0' => 'Night Scene', # K100D
+            '0 12 0' => 'Surf & Snow', # K100D
+            '0 13 0' => 'Text', # K100D
+            '0 14 0' => 'Sunset', # K100D
+            '0 15 0' => 'Kids', # K100D
+            '0 16 0' => 'Pet', # K100D
+            '0 17 0' => 'Candlelight', # K100D
+            '0 18 0' => 'Museum', # K100D
+            # AUTO PICT modes (auto-selected)
+            '1 4 0'  => 'Auto PICT (Standard)', #13
+            '1 5 0'  => 'Auto PICT (Portrait)', #7 (K100D)
+            '1 6 0'  => 'Auto PICT (Landscape)', # K110D
+            '1 7 0'  => 'Auto PICT (Macro)', #13
+            '1 8 0'  => 'Auto PICT (Sport)', #13
+            # Manual dial modes
+            '2 0 0'  => 'Program AE', #13
+            '3 0 0'  => 'Green Mode', #16
+            '4 0 0'  => 'Shutter Speed Priority', # K110D
+            '5 0 0'  => 'Aperture Priority', # K110D
+            '8 0 0'  => 'Manual', # K110D
+            '9 0 0'  => 'Bulb', # K110D
+            # *istD modes (ref 7)
+            '2 0 1'  => 'Program AE', # 'User Program AE' according to ref 16
+            '2 1 1'  => 'Hi-speed Program',
+            '2 2 1'  => 'DOF Program',
+            '2 3 1'  => 'MTF Program',
+            '3 0 1'  => 'Green Mode',
+            '4 0 1'  => 'Shutter Speed Priority',
+            '5 0 1'  => 'Aperture Priority',
+            '6 0 1'  => 'Program Tv Shift',
+            '7 0 1'  => 'Program Av Shift',
+            '8 0 1'  => 'Manual',
+            '9 0 1'  => 'Bulb',
+            '10 0 1' => 'Aperture Priority (Off-Auto-Aperture)',
+            '11 0 1' => 'Manual (Off-Auto-Aperture)',
+            '12 0 1' => 'Bulb (Off-Auto-Aperture)',
+            # K10D modes (ref 16)
+            '13 0 0' => 'Shutter & Aperture Priority AE',
+            '13 0 1' => 'Shutter & Aperture Priority AE (1)', #PH guess
+            '15 0 0' => 'Sensitivity Priority AE',
+            '15 0 1' => 'Sensitivity Priority AE (1)',
+            '16 0 0' => 'Flash X-Sync Speed AE',
+            '16 0 1' => 'Flash X-Sync Speed AE (1)', #PH guess
+            # other modes
+            '0 0 1'  => 'Program', #17 (K100D)
+        },
+    },
+    0x0034 => { #7/PH
+        Name => 'DriveMode',
+        Writable => 'int8u',
+        Count => 4,
+        PrintConv => {
+            '0 0 0 0' => 'Single-frame',
+            '1 0 0 0' => 'Continuous',
+            '0 1 0 0' => 'Self-timer (12 sec)',
+            '0 2 0 0' => 'Self-timer (2 sec)',
+            '0 0 1 0' => 'Remote Control?', # needs validation
+            '0 0 0 1' => 'Multiple Exposure',
+        },
+    },
+    0x0037 => { #13
+        Name => 'ColorSpace',
+        Writable => 'int16u',
+        PrintConv => {
+            0 => 'sRGB',
+            1 => 'Adobe RGB',
+        },
+    },
+    0x0038 => { #5 (PEF only)
+        Name => 'ImageAreaOffset',
+        Writable => 'int16u',
+        Count => 2,
+    },
+    0x0039 => { #PH
+        Name => 'RawImageSize',
+        Writable => 'int16u',
+        Count => 2,
+        PrintConv => '$_=$val;s/ /x/;$_',
+    },
+    0x003c => { #7/PH
+        Name => 'AFPointsInFocus',
+        # not writable because I'm not decoding these 4 bytes fully:
+        # Nibble pattern: XSSSYUUU
+        # X = unknown (AF focused flag?, 0 or 1)
+        # SSS = selected AF point bitmask (0x000 or 0x7ff if unused)
+        # Y = unknown (observed 0,6,7,b,e, always 0 if SSS is 0x000 or 0x7ff)
+        # UUU = af points used
+        Format => 'int32u',
+        Notes => '*istD only',
+        ValueConv => '$val & 0x7ff', # ignore other bits for now
+        PrintConv => { BITMASK => {
+            0 => 'Upper-left',
+            1 => 'Top',
+            2 => 'Upper-right',
+            3 => 'Left',
+            4 => 'Mid-left',
+            5 => 'Center',
+            6 => 'Mid-right',
+            7 => 'Right',
+            8 => 'Lower-left',
+            9 => 'Bottom',
+            10 => 'Lower-right',
+        } },
+    },
+    # 0x003d = 8192 (PH, K10D)
+    0x003f => { #PH
+        Name => 'LensType',
+        Writable => 'int8u',
+        Count => 2,
+        SeparateTable => 1,
+        ValueConv => '$val=~s/^(\d+ \d+) 0$/$1/; $val',
+        ValueConvInv => '$val',
+        PrintConv => \%pentaxLensType,
+    },
+    # 0x0041 - increments for each cropped pic (PH)
+    #          (DigitalFilter according to ref 5)
+    0x0047 => { #PH
+        Name => 'CameraTemperature',
+        Writable => 'int8s',
+        PrintConv => '"$val C"',
+        PrintConvInv => '$val=~s/ ?c$//i; $val',
+    },
+    0x0049 => { #13
+        Name => 'NoiseReduction',
+        Writable => 'int16u',
+        PrintConv => { 0 => 'Off', 1 => 'On' },
+    },
+    0x004d => { #PH
+        Name => 'FlashExposureComp',
+        Writable => 'int32s',
+        ValueConv => '$val / 256',
+        ValueConvInv => 'int($val * 256 + ($val > 0 ? 0.5 : -0.5))',
+        PrintConv => '$val ? sprintf("%+.1f", $val) : 0',
+        PrintConvInv => '$val',
+    },
+    0x004f => { #PH
+        Name => 'ImageTone',
+        Writable => 'int16u',
+        PrintConv => {
+            0 => 'Natural',
+            1 => 'Bright',
+        },
+    },
+    0x005c => { #PH
+        Name => 'ShakeReductionInfo',
+        Format => 'undef',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::Pentax::SRInfo',
+        },
+    },
+    0x005d => { #19/PH
+        # (used by all Pentax DSLR's except *istD and *istDS - PH)
+        # The lowest decrypted values observed are 246 for the first image from a
+        # brand new K10D, and 209 for one of the first 20 images from a new K10D,
+        # so either there are ~200 test images shot in the factory, or there is an
+        # offset of ~200 which is applied to this value before encryption
+        Name => 'ShutterCount',
+        Writable => 'undef',
+        Count => 4,
+        Notes => 'raw value is a big-endian 4-byte integer, encrypted using Date and Time',
+        RawConv => 'length($val) == 4 ? unpack("N",$val) : undef',
+        RawConvInv => q{
+            my $val = Image::ExifTool::Pentax::CryptShutterCount($val,$self);
+            return pack('N', $val);
+        },
+        ValueConv => \&CryptShutterCount,
+        ValueConvInv => '$val',
+    },
+    0x0200 => { #5
+        Name => 'BlackPoint',
+        Writable => 'int16u',
+        Count => 4,
+    },
+    0x0201 => { #5
+        Name => 'WhitePoint',
+        Writable => 'int16u',
+        Count => 4,
+    },
+    # 0x0205 - Also stores PictureMode (PH)
+    0x0206 => { #PH
+        Name => 'AEInfo',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::Pentax::AEInfo',
+        },
+    },
+    0x0207 => { #PH
+        Name => 'LensInfo',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::Pentax::LensInfo',
+        },
+    },
+    0x0208 => { #PH
+        Name => 'FlashInfo',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::Pentax::FlashInfo',
+        },
+    },
+    0x0209 => {
+        Name => 'AEMeteringSegments',
+        Format => 'int8u',
+        Count => 16,
+        Notes => q{
+            measurements from each of the 16 AE metering segments, converted to LV
+        },
+        # metering segment locations (ref 19):
+        # +-------------------------+
+        # |           14            |
+        # |    +---+---+---+---+    |
+        # |    | 5 | 3/1\ 2| 4 |    |
+        # |  +-+-+-+-+ - +-+-+-+-+  |
+        # +--+ 9 | 7 ||0|| 6 | 8 +--+
+        # |  +-+-+-+-+ - +-+-+-+-+  |
+        # |    |13 |11\ /10|12 |    |
+        # |    +---+---+---+---+    |
+        # |           15            |
+        # +-------------------------+
+        # convert to approximate LV equivalent (PH):
+        ValueConv => q{
+            my @a;
+            foreach (split ' ', $val) { push @a, $_ / 8 - 6; }
+            return join(' ', @a);
+        },
+        ValueConvInv => q{
+            my @a;
+            foreach (split ' ', $val) { push @a, int(($_ + 6) * 8 + 0.5); }
+            return join(' ', @a);
+        },
+        PrintConv => q{
+            my @a;
+            foreach (split ' ', $val) { push @a, sprintf('%.1f', $_); }
+            return join(' ', @a);
+        },
+        PrintConvInv => '$val',
+    },
+    0x020a => {
+        Name => 'FlashADump',
+        Flags => ['Unknown','Binary'],
+    },
+    0x020b => {
+        Name => 'FlashBDump',
+        Flags => ['Unknown','Binary'],
+    },
+    0x020d => { #PH
+        Name => 'WB_RGGBLevelsDaylight',
+        Writable => 'int16u',
+        Count => 4,
+    },
+    0x020e => { #PH
+        Name => 'WB_RGGBLevelsShade',
+        Writable => 'int16u',
+        Count => 4,
+    },
+    0x020f => { #PH
+        Name => 'WB_RGGBLevelsCloudy',
+        Writable => 'int16u',
+        Count => 4,
+    },
+    0x0210 => { #PH
+        Name => 'WB_RGGBLevelsTungsten',
+        Writable => 'int16u',
+        Count => 4,
+    },
+    0x0211 => { #PH
+        Name => 'WB_RGGBLevelsFluorescentD',
+        Writable => 'int16u',
+        Count => 4,
+    },
+    0x0212 => { #PH
+        Name => 'WB_RGGBLevelsFluorescentN',
+        Writable => 'int16u',
+        Count => 4,
+    },
+    0x0213 => { #PH
+        Name => 'WB_RGGBLevelsFluorescentW',
+        Writable => 'int16u',
+        Count => 4,
+    },
+    0x0214 => { #PH
+        Name => 'WB_RGGBLevelsFlash',
+        Writable => 'int16u',
+        Count => 4,
+    },
+    0x0215 => { #PH
+        Name => 'CameraInfo',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::Pentax::CameraInfo',
+        },
+    },
+    0x0216 => { #PH
+        Name => 'BatteryInfo',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::Pentax::BatteryInfo',
+        },
+    },
+    0x021f => { #19
+        Name => 'AFInfo',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::Pentax::AFInfo',
+        },
+    },
+    0x03fe => { #PH
+        Name => 'DataDump',
+        Writable => 0,
+        PrintConv => '\$val',
+    },
+    0x0402 => { #5
+        Name => 'ToneCurve',
+        PrintConv => '\$val',
+    },
+    0x0403 => { #5
+        Name => 'ToneCurves',
+        PrintConv => '\$val',
+    },
+    0x0e00 => {
+        Name => 'PrintIM',
+        Description => 'Print Image Matching',
+        Writable => 0,
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::PrintIM::Main',
+        },
+    },
+);
+
+# shake reduction information (ref PH)
+%Image::ExifTool::Pentax::SRInfo = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
+    CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
+    WRITABLE => 1,
+    FIRST_ENTRY => 0,
+    NOTES => 'Shake reduction information.',
+    0 => {
+        Name => 'SRResult',
+        PrintConv => { #PH/19
+            0 => 'Not stabilized',
+            BITMASK => {
+                0 => 'Stabilized',
+                6 => 'Not ready',
+            },
+        },
+    },
+    1 => {
+        Name => 'ShakeReduction',
+        PrintConv => { 0 => 'Off', 1 => 'On' },
+    },
+    2 => {
+        Name => 'SR_SWSToSWRTime',
+        # SWS=photometering switch, SWR=shutter release switch
+        # (from http://www.patentstorm.us/patents/6597867-description.html)
+        Notes => q{
+            time from when the shutter button was half pressed to when the shutter was
+            released
+        },
+    },
+);
+
+# auto-exposure information (ref PH)
+%Image::ExifTool::Pentax::AEInfo = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
+    CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
+    WRITABLE => 1,
+    FIRST_ENTRY => 0,
+    0 => {
+        Name => 'AEExposureTime',
+        Notes => 'val = 24 * 2**((32-raw)/8)',
+        ValueConv => '24*exp(-($val-32)*log(2)/8)',
+        ValueConvInv => '-log($val/24)*8/log(2)+32',
+        PrintConv => 'Image::ExifTool::Exif::PrintExposureTime($val)',
+        PrintConvInv => 'eval $val',
+    },
+    1 => {
+        Name => 'AEAperture',
+        Notes => 'val = (2**((raw-4)/16)) / 16',
+        # (same as val=2**((raw-68)/16), but 68 isn't a nice power of 2)
+        ValueConv => 'exp(($val-4)*log(2)/16)/16',
+        ValueConvInv => 'log($val*16)*16/log(2)+4',
+        PrintConv => 'sprintf("%.1f",$val)',
+        PrintConvInv => '$val',
+    },
+    2 => {
+        Name => 'AE_ISO',
+        Notes => 'val = 100 * 2**((raw-32)/8)',
+        ValueConv => '100*exp(($val-32)*log(2)/8)',
+        ValueConvInv => 'log($val/100)*8/log(2)+32',
+        PrintConv => 'int($val + 0.5)',
+        PrintConvInv => '$val',
+    },
+    3 => {
+        Name => 'AEXv',
+        Notes => 'val = (raw-64)/8',
+        ValueConv => '($val-64)/8',
+        ValueConvInv => '$val * 8 + 64',
+    },
+    4 => {
+        Name => 'AEBXv',
+        Format => 'int8s',
+        Notes => 'val = raw / 8',
+        ValueConv => '$val/8',
+        ValueConvInv => '$val * 8',
+    },
+    5 => {
+        Name => 'AEFlashTv',
+        Notes => 'val = 24 * 2**((32-raw)/8)',
+        ValueConv => '24*exp(-($val-32)*log(2)/8)', #19
+        ValueConvInv => '-log($val/24)*8/log(2)+32',
+        PrintConv => 'Image::ExifTool::Exif::PrintExposureTime($val)',
+        PrintConvInv => 'eval $val',
+    },
+    6 => {
+        Name => 'AEProgramMode',
+        PrintConv => {
+            0 => 'Manual/Program AE',
+            1 => 'Aperture Priority/Bulb',
+            2 => 'Shutter Speed Priority',
+            3 => 'Program',
+            35 => 'Standard',
+            43 => 'Portrait',
+            51 => 'Landscape',
+            59 => 'Macro',
+            67 => 'Sport',
+            75 => 'Night Scene Portrait',
+            83 => 'No Flash',
+            91 => 'Night Scene',
+            99 => 'Surf & Snow',
+            107 => 'Text',
+            115 => 'Sunset',
+            123 => 'Kids',
+            131 => 'Pet',
+            139 => 'Candlelight',
+            147 => 'Museum',
+        },
+    },
+    7 => {
+        Name => 'AEExtra',
+        Unknown => 1,
+    },
+);
+
+# lens information (ref PH)
+%Image::ExifTool::Pentax::LensInfo = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
+    CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
+    WRITABLE => 1,
+    FIRST_ENTRY => 0,
+    # (must decode focus distance!)
+    0 => {
+        Name => 'LensType',
+        Format => 'int8u[4]',
+        Priority => 0,
+        Notes => q{
+            for most models the first 2 bytes are LensType, but for the K10D the
+            decoding is a bit different
+        },
+        # this is a bit funny and needs testing with more lenses on the K10D
+        ValueConv => q{
+            my @v = split(' ',$val);
+            if ($v[0] & 0xf0 or $$self{CameraModel} =~ /^PENTAX K10D\b/) {
+                $v[0] &= 0x0f;
+                $v[1] = GetByteOrder() eq 'MM' ? $v[2] * 256 + $v[3] : $v[3] * 256 + $v[2];
+            }
+            return "$v[0] $v[1]";
+        },
+        PrintConv => \%pentaxLensType,
+        SeparateTable => 1,
+    },
+    4 => {
+        Condition => '$$self{CameraModel} !~ /K10D/',
+        Name => 'LensCodes',
+        Format => 'int8u[16]',
+        Notes => 'all models but the K10D',
+    },
+    5 => {
+        Condition => '$$self{CameraModel} =~ /K10D/',
+        Name => 'LensCodes',
+        Format => 'int8u[16]',
+        Notes => 'K10D only',
+    },
+);
+
+# flash information (ref PH)
+%Image::ExifTool::Pentax::FlashInfo = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
+    CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
+    WRITABLE => 1,
+    FIRST_ENTRY => 0,
+    0 => 'FlashStatus',
+    1 => {
+        Name => 'FlashModeCode',
+        PrintConv => {
+            149 => 'On, Wireless', # K10D
+            192 => 'On', # K10D
+            193 => 'On, Red-eye reduction', # *istDS2, K10D
+            194 => 'Auto, Fired', # K100D, K110D
+            200 => 'On, Slow-sync', # K10D
+            201 => 'On, Slow-sync, Red-eye reduction', # K10D
+            202 => 'On, Trailing-curtain Sync', # K10D
+            240 => 'Off (240)', # *istD, *istDS, K100D, K10D
+            241 => 'Off (241)', # *istDL
+            242 => 'Off (242)', # *istDS, *istDL2, K100D, K110D
+            244 => 'Off (244)', # K100D, K110D (either "Auto, Did not fire" or "Off")
+        },
+    },
+    2 => 'ExternalFlashMode',
+    3 => 'InternalFlashMagni',
+    4 => 'TTL_DA_AUp',
+    5 => 'TTL_DA_ADown',
+    6 => 'TTL_DA_BUp',
+    7 => 'TTL_DA_BDown',
+    # ? => 'ExternalFlashMagniA',
+    # ? => 'ExternalFlashMagniB',
+);
+
+# camera manufacture information (ref PH)
+%Image::ExifTool::Pentax::CameraInfo = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
+    CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
+    WRITABLE => 1,
+    FIRST_ENTRY => 0,
+    FORMAT => 'int32u',
+    0 => {
+        Name => 'PentaxModelID',
+        Priority => 0, # (Optio SVi uses incorrect Optio SV ID here)
+        SeparateTable => 1,
+        PrintConv => \%pentaxModelID,
+    },
+    1 => {
+        Name => 'ManufactureDate',
+        ValueConv => q{
+            $val =~ /^(\d{4})(\d{2})(\d{2})$/ and return "$1:$2:$3";
+            # Optio A10 and A20 leave "200" off the year
+            $val =~ /^(\d)(\d{2})(\d{2})$/ and return "200$1:$2:$3";
+            return "Unknown ($val)";
+        },
+        ValueConvInv => '$val=~tr/0-9//dc; $val',
+    },
+    2 => {
+        Name => 'ModelRevision',
+        Format => 'int32u[2]',
+        ValueConv => '$val=~tr/ /./; $val',
+        ValueConvInv => '$val=~tr/./ /; $val',
+    },
+    4 => 'InternalSerialNumber',
+);
+
+# battery information (ref PH)
+%Image::ExifTool::Pentax::BatteryInfo = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
+    CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
+    WRITABLE => 1,
+    FIRST_ENTRY => 0,
+    0 => {
+        Name => 'BatteryType',
+        ValueConv => '$val & 0x7f',
+        ValueConvInv => '$val | 0x80',  # (not sure what this bit means)
+    },
+    1 => {
+        Name => 'BatteryBodyGripStates',
+        Notes => 'body and grip battery state',
+        ValueConv => '($val >> 4) . " " . ($val & 0x0f)',
+        ValueConvInv => 'my @a=split(" ",$val); ($a[0] << 4) + $a[1]',
+    },
+    # internal and grip battery voltage Analogue to Digital measurements,
+    # open circuit and under load
+    2 => 'BatteryADBodyNoLoad',
+    3 => 'BatteryADBodyLoad',
+    4 => 'BatteryADGripNoLoad',
+    5 => 'BatteryADGripLoad',
+);
+
+# auto focus information (ref 19)
+%Image::ExifTool::Pentax::AFInfo = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
+    CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
+    WRITABLE => 1,
+    FIRST_ENTRY => 0,
+    0x0b => {
+        Name => 'AFPointsInFocus',
+        Notes => q{
+            may report two points in focus even though a single AFPoint has been
+            selected, in which case the selected AFPoint is the first reported
+        },
+        PrintConv => {
+            0 => 'None',
+            1 => 'Lower-left, Bottom',
+            2 => 'Bottom',
+            3 => 'Lower-right, Bottom',
+            4 => 'Mid-left, Center',
+            5 => 'Center (horizontal)', #PH (K10D)
+            6 => 'Mid-right, Center',
+            7 => 'Upper-left, Top',
+            8 => 'Top',
+            9 => 'Upper-right, Top',
+            10 => 'Right',
+            11 => 'Lower-left, Mid-left',
+            12 => 'Upper-left, Mid-left',
+            13 => 'Bottom, Center',
+            14 => 'Top, Center',
+            15 => 'Lower-right, Mid-right',
+            16 => 'Upper-right, Mid-right',
+            17 => 'Left',
+            18 => 'Mid-left',
+            19 => 'Center (vertical)', #PH (K10D)
+            20 => 'Mid-right',
+        },
+    },
+);
+
+# tags in Pentax QuickTime videos (PH - tests with Optio WP)
+# (note: very similar to information in Nikon videos)
+%Image::ExifTool::Pentax::MOV = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
+    NOTES => 'This information is found in Pentax MOV video images.',
+    0x00 => {
+        Name => 'Make',
+        Format => 'string[6]',
+        PrintConv => 'ucfirst(lc($val))',
+    },
+    0x26 => {
+        Name => 'ExposureTime',
+        Format => 'int32u',
+        ValueConv => '$val ? 10 / $val : 0',
+        PrintConv => 'Image::ExifTool::Exif::PrintExposureTime($val)',
+    },
+    0x2a => {
+        Name => 'FNumber',
+        Format => 'int32u',
+        ValueConv => '$val / 10',
+        PrintConv => 'sprintf("%.1f",$val)',
+    },
+    0x32 => {
+        Name => 'ExposureCompensation',
+        Format => 'int32s',
+        ValueConv => '$val / 10',
+        PrintConv => 'Image::ExifTool::Exif::ConvertFraction($val)',
+    },
+    0x44 => {
+        Name => 'WhiteBalance',
+        Format => 'int16u',
+        PrintConv => {
+            0 => 'Auto',
+            1 => 'Daylight',
+            2 => 'Shade',
+            3 => 'Fluorescent', #2
+            4 => 'Tungsten',
+            5 => 'Manual',
+        },
+    },
+    0x48 => {
+        Name => 'FocalLength',
+        Writable => 'int32u',
+        ValueConv => '$val / 10',
+        PrintConv => 'sprintf("%.1fmm",$val)',
+    },
+    0xaf => {
+        Name => 'ISO',
+        Format => 'int16u',
+    },
+);
+
+# Pentax type 2 (Casio-like) maker notes (ref 1)
+%Image::ExifTool::Pentax::Type2 = (
+    WRITE_PROC => \&Image::ExifTool::Exif::WriteExif,
+    CHECK_PROC => \&Image::ExifTool::Exif::CheckExif,
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
+    WRITABLE => 'int16u',
+    NOTES => q{
+        These tags are used by the Pentax Optio 330 and 430, and are similar to the
+        tags used by Casio.
+    },
+    0x0001 => {
+        Name => 'RecordingMode',
+        PrintConv => {
+            0 => 'Auto',
+            1 => 'Night Scene',
+            2 => 'Manual',
+        },
+    },
+    0x0002 => {
+        Name => 'Quality',
+        PrintConv => {
+            0 => 'Good',
+            1 => 'Better',
+            2 => 'Best',
+        },
+    },
+    0x0003 => {
+        Name => 'FocusMode',
+        PrintConv => {
+            2 => 'Custom',
+            3 => 'Auto',
+        },
+    },
+    0x0004 => {
+        Name => 'FlashMode',
+        PrintConv => {
+            1 => 'Auto',
+            2 => 'On',
+            4 => 'Off',
+            6 => 'Red-eye reduction',
+        },
+    },
+    # Casio 0x0005 is FlashIntensity
+    # Casio 0x0006 is ObjectDistance
+    0x0007 => {
+        Name => 'WhiteBalance',
+        PrintConv => {
+            0 => 'Auto',
+            1 => 'Daylight',
+            2 => 'Shade',
+            3 => 'Tungsten',
+            4 => 'Fluorescent',
+            5 => 'Manual',
+        },
+    },
+    0x000a => {
+        Name => 'DigitalZoom',
+        Writable => 'int32u',
+    },
+    0x000b => {
+        Name => 'Sharpness',
+        PrintConv => {
+            0 => 'Normal',
+            1 => 'Soft',
+            2 => 'Hard',
+        },
+    },
+    0x000c => {
+        Name => 'Contrast',
+        PrintConv => {
+            0 => 'Normal',
+            1 => 'Low',
+            2 => 'High',
+        },
+    },
+    0x000d => {
+        Name => 'Saturation',
+        PrintConv => {
+            0 => 'Normal',
+            1 => 'Low',
+            2 => 'High',
+        },
+    },
+    0x0014 => {
+        Name => 'ISO',
+        Priority => 0,
+        PrintConv => {
+            10 => 100,
+            16 => 200,
+            50 => 50, #PH
+            100 => 100, #PH
+            200 => 200, #PH
+            400 => 400, #PH
+            800 => 800, #PH
+            1600 => 1600, #PH
+            3200 => 3200, #PH
+        },
+    },
+    0x0017 => {
+        Name => 'ColorFilter',
+        PrintConv => {
+            1 => 'Full',
+            2 => 'Black & White',
+            3 => 'Sepia',
+        },
+    },
+    # Casio 0x0018 is AFPoint
+    # Casio 0x0019 is FlashIntensity
+    0x0e00 => {
+        Name => 'PrintIM',
+        Description => 'Print Image Matching',
+        Writable => 0,
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::PrintIM::Main',
+        },
+    },
+    0x1000 => {
+        Name => 'HometownCityCode',
+        Writable => 'undef',
+        Count => 4,
+    },
+    0x1001 => { #PH
+        Name => 'DestinationCityCode',
+        Writable => 'undef',
+        Count => 4,
+    },
+);
+
+# ASCII-based maker notes of Optio E20 - PH
+%Image::ExifTool::Pentax::Type4 = (
+    PROCESS_PROC => \&Image::ExifTool::HP::ProcessHP,
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
+    NOTES => q{
+        The following few tags are extracted from the wealth of information
+        available in maker notes of the Optio E20.  These maker notes are stored as
+        ASCII text in a format very similar to some HP models.
+    },
+   'F/W Version' => 'FirmwareVersion',
+);
+
+#------------------------------------------------------------------------------
+# Encrypt or decrypt Pentax ShutterCount (symmetrical encryption) - PH
+# Inputs: 0) shutter count value, 1) ExifTool object ref
+# Returns: Encrypted or decrypted ShutterCount
+sub CryptShutterCount($$)
+{
+    my ($val, $exifTool) = @_;
+    # Pentax Date and Time values are used in the encryption
+    return undef unless $$exifTool{PentaxDate} and $$exifTool{PentaxTime} and
+        length($$exifTool{PentaxDate})==4 and length($$exifTool{PentaxTime})>=3;
+    # get Date and Time as integers (after padding Time with a null byte)
+    my $date = unpack('N', $$exifTool{PentaxDate});
+    my $time = unpack('N', $$exifTool{PentaxTime} . "\0");
+    return $val ^ $date ^ (0xffffffff - $time);
+}
+
+
+1; # end
+
+__END__
+
+=head1 NAME
+
+Image::ExifTool::Pentax - Pentax/Asahi maker notes tags
+
+=head1 SYNOPSIS
+
+This module is loaded automatically by Image::ExifTool when required.
+
+=head1 DESCRIPTION
+
+This module contains definitions required by Image::ExifTool to interpret
+Pentax and Asahi maker notes in EXIF information.
+
+=head1 NOTES
+
+I couldn't find a good source for Pentax maker notes information, but I've
+managed to discover a fair bit of information by analyzing sample pictures
+from the Optio 330, Optio 550, Optio S, *istD, *istDs, and through tests
+with my own Optio WP, and with help provided by other ExifTool users (see
+L<ACKNOWLEDGEMENTS>).
+
+The Pentax maker notes are stored in standard EXIF format, but the offsets
+used for some of their cameras are wacky.  The Optio 330 gives the offset
+relative to the offset of the tag in the directory, the Optio WP uses a base
+offset in the middle of nowhere, and the Optio 550 uses different (and
+totally illogical) bases for different menu entries.  Very weird.  (It
+wouldn't surprise me if Pentax can't read their own maker notes!)  Luckily,
+there are only a few entries in the maker notes which are large enough to
+require offsets, so this doesn't affect much useful information.  ExifTool
+attempts to make sense of this fiasco by making an assumption about where
+the information should be stored to deduce the correct offsets.
+
+=head1 REFERENCES
+
+=over 4
+
+=item L<Image::MakerNotes::Pentax|Image::MakerNotes::Pentax>
+
+=item L<http://johnst.org/sw/exiftags/> (Asahi models)
+
+=item L<http://kobe1995.jp/~kaz/astro/istD.html>
+
+=item L<http://www.cybercom.net/~dcoffin/dcraw/>
+
+=item (...plus lots of testing with my Optio WP and K10D!)
+
+=back
+
+=head1 ACKNOWLEDGEMENTS
+
+Thanks to Wayne Smith, John Francis, Douglas O'Brien Cvetan Ivanov and Jens
+Duttke for help figuring out some Pentax tags, Denis Bourez, Kazumichi
+Kawabata, David Buret, Barney Garrett and Axel Kellner for adding to the
+LensType list, and Ger Vermeulen for contributing print conversion values
+for some tags.
+
+=head1 AUTHOR
+
+Copyright 2003-2007, Phil Harvey (phil at owl.phy.queensu.ca)
+
+This library is free software; you can redistribute it and/or modify it
+under the same terms as Perl itself.
+
+=head1 SEE ALSO
+
+L<Image::ExifTool::TagNames/Pentax Tags>,
+L<Image::ExifTool(3pm)|Image::ExifTool>,
+L<Image::Info(3pm)|Image::Info>
+
+=cut
Index: /gsdl/trunk/perllib/cpan/Image/ExifTool/PhotoMechanic.pm
===================================================================
--- /gsdl/trunk/perllib/cpan/Image/ExifTool/PhotoMechanic.pm	(revision 16842)
+++ /gsdl/trunk/perllib/cpan/Image/ExifTool/PhotoMechanic.pm	(revision 16842)
@@ -0,0 +1,257 @@
+#------------------------------------------------------------------------------
+# File:         PhotoMechanic.pm
+#
+# Description:  Read/write Camera Bits Photo Mechanic information
+#
+# Revisions:    10/28/2006 - P. Harvey Created
+#------------------------------------------------------------------------------
+
+package Image::ExifTool::PhotoMechanic;
+
+use strict;
+use vars qw($VERSION);
+use Image::ExifTool qw(:DataAccess :Utils);
+use Image::ExifTool::Exif;
+use Image::ExifTool::IPTC;
+use Image::ExifTool::XMP;
+
+$VERSION = '1.00';
+
+sub ProcessPhotoMechanic($$);
+
+# color class names
+my %colorClasses = (
+    0 => '0 (None)',
+    1 => '1 (Winner)',
+    2 => '2 (Winner alt)',
+    3 => '3 (Superior)',
+    4 => '4 (Superior alt)',
+    5 => '5 (Typical)',
+    6 => '6 (Typical alt)',
+    7 => '7 (Extras)',
+    8 => '8 (Trash)',
+);
+
+# main tag table IPTC-format records in PhotoMechanic trailer
+%Image::ExifTool::PhotoMechanic::Main = (
+    GROUPS => { 2 => 'Image' },
+    PROCESS_PROC => \&Image::ExifTool::IPTC::ProcessIPTC,
+    WRITE_PROC => \&Image::ExifTool::IPTC::WriteIPTC,
+    NOTES => q{
+        The Photo Mechanic trailer contains data in an IPTC-format structure, with
+        soft edit information stored under record number 2.
+    },
+    2 => {
+        Name => 'SoftEdit',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::PhotoMechanic::SoftEdit',
+        },
+    },
+);
+
+# rawCrop coordinate conversions
+my %rawCropConv = (
+    ValueConv => '$val / 655.36',
+    ValueConvInv => 'int($val * 655.36 + 0.5)',
+    PrintConv => 'sprintf("%.3f%%",$val)',
+    PrintConvInv => '$val=~tr/ %//d; $val',
+);
+
+# Record 2 -- PhotoMechanic soft edit information
+%Image::ExifTool::PhotoMechanic::SoftEdit = (
+    GROUPS => { 2 => 'Image' },
+    WRITE_PROC => \&Image::ExifTool::IPTC::WriteIPTC,
+    CHECK_PROC => \&Image::ExifTool::IPTC::CheckIPTC,
+    WRITABLE => 1,
+    FORMAT => 'int32s',
+    209 => {
+        Name => 'RawCropLeft',
+        %rawCropConv,
+    },
+    210 => {
+        Name => 'RawCropTop',
+        %rawCropConv,
+    },
+    211 => {
+        Name => 'RawCropRight',
+        %rawCropConv,
+    },
+    212 => {
+        Name => 'RawCropBottom',
+        %rawCropConv,
+    },
+    213 => 'ConstrainedCropWidth',
+    214 => 'ConstrainedCropHeight',
+    215 => 'FrameNum',
+    216 => {
+        Name => 'Rotation',
+        PrintConv => {
+            0 => '0',
+            1 => '90',
+            2 => '180',
+            3 => '270',
+        },
+    },
+    217 => 'CropLeft',
+    218 => 'CropTop',
+    219 => 'CropRight',
+    220 => 'CropBottom',
+    221 => {
+        Name => 'Tagged',
+        PrintConv => { 0 => 'No', 1 => 'Yes' },
+    },
+    222 => {
+        Name => 'ColorClass',
+        PrintConv => \%colorClasses,
+    },
+    223 => 'Rating',
+);
+
+# PhotoMechanic XMP properties
+%Image::ExifTool::PhotoMechanic::XMP = (
+    GROUPS => { 0 => 'XMP', 1 => 'XMP-photomech', 2 => 'Image' },
+    NAMESPACE => { photomechanic => 'http://ns.camerabits.com/photomechanic/1.0/' },
+    WRITE_PROC => \&Image::ExifTool::XMP::WriteXMP,
+    WRITABLE => 'string',
+    NOTES => q{
+        Below is a list of the observed PhotoMechanic XMP tags.  The actual
+        namespace prefix is "photomechanic" but ExifTool shortens this in
+        the "XMP-photomech" family 1 group name.
+    },
+    CountryCode => { Avoid => 1, Groups => { 2 => 'Location' } },
+    EditStatus  => { },
+    Prefs       => {
+        PrintConv => q{
+            $val =~ s[\s*(\d+):\s*(\d+):\s*(\d+):\s*(\S*)]
+                     [Tagged:$1, ColorClass:$2, Rating:$3, FrameNum:$4];
+            return $val;
+        },
+        PrintConvInv => q{
+            $val =~ s[Tagged:\s*(\d+).*ColorClass:\s*(\d+).*Rating:\s*(\d+).*FrameNum:\s*(\S*)]
+                     [$1:$2:$3:$4]is;
+            return $val;
+        },
+    },
+    TimeCreated => {
+        Avoid => 1,
+        Groups => { 2 => 'Time' },
+        Shift => 'Time',
+        ValueConv => 'Image::ExifTool::Exif::ExifTime($val)',
+        ValueConvInv => 'Image::ExifTool::IPTC::IptcTime($val)',
+    },
+);
+
+#------------------------------------------------------------------------------
+# Read/write PhotoMechanic information in a file
+# Inputs: 0) ExifTool object reference, 1) dirInfo reference
+# Returns: 1 on success, 0 if this file didn't contain PhotoMechanic information
+# - updates DataPos to point to start of PhotoMechanic information
+# - updates DirLen to trailer length
+sub ProcessPhotoMechanic($$)
+{
+    my ($exifTool, $dirInfo) = @_;
+    my $raf = $$dirInfo{RAF};
+    my $offset = $$dirInfo{Offset} || 0;
+    my $outfile = $$dirInfo{OutFile};
+    my $verbose = $exifTool->Options('Verbose');
+    my $out = $exifTool->Options('TextOut');
+    my $rtnVal = 0;
+    my ($buff, $footer);
+
+    for (;;) {
+        # read and validate trailer footer (last 12 bytes)
+        last unless $raf->Seek(-12-$offset, 2) and $raf->Read($footer, 12) == 12;
+        last unless $footer =~ /cbipcbbl$/;
+        my $size = unpack('N', $footer);
+    
+        if ($size & 0x80000000 or not $raf->Seek(-$size-12, 1)) {
+            $exifTool->Warn('Bad PhotoMechanic trailer');
+            last;
+        }
+        unless ($raf->Read($buff, $size) == $size) {
+            $exifTool->Warn('Error reading PhotoMechanic trailer');
+            last;
+        }
+        $rtnVal = 1;    # we read the trailer successfully
+
+        # set variables returned in dirInfo hash
+        $$dirInfo{DataPos} = $raf->Tell() - $size;
+        $$dirInfo{DirLen} = $size + 12;
+    
+        my %dirInfo = (
+            DataPt => \$buff,
+            DataPos => $$dirInfo{DataPos},
+            DirStart => 0,
+            DirLen => $size,
+            Parent => 'PhotoMechanic',
+        );
+        my $tagTablePtr = GetTagTable('Image::ExifTool::PhotoMechanic::Main');
+        if (not $outfile) {
+            # extract trailer information
+            $exifTool->DumpTrailer($dirInfo) if $verbose or $exifTool->{HTML_DUMP};
+            $exifTool->ProcessDirectory(\%dirInfo, $tagTablePtr);
+        } elsif ($exifTool->{DEL_GROUP}->{PhotoMechanic}) {
+            # delete the trailer
+            $verbose and printf $out "  Deleting PhotoMechanic trailer\n";
+            ++$exifTool->{CHANGED};
+        } else {
+            # rewrite the trailer
+            my $newPt;
+            my $newBuff = $exifTool->WriteDirectory(\%dirInfo, $tagTablePtr);
+            if (defined $newBuff) {
+                $newPt = \$newBuff; # write out the modified trailer
+                my $pad = 0x800 - length($newBuff);
+                if ($pad > 0 and not $exifTool->Options('Compact')) {
+                    # pad out to 2kB like PhotoMechanic does
+                    $newBuff .= "\0" x $pad;
+                }
+                # generate new footer
+                $footer = pack('N', length($$newPt)) . 'cbipcbbl';
+            } else {
+                $newPt = \$buff;    # just copy existing trailer
+            }
+            # write out the trailer
+            Write($outfile, $$newPt, $footer) or $rtnVal = -1;
+        }
+        last;
+    }
+    return $rtnVal;
+}
+
+1;  # end
+
+__END__
+
+=head1 NAME
+
+Image::ExifTool::PhotoMechanic - Read/write Photo Mechanic information
+
+=head1 SYNOPSIS
+
+This module is used by Image::ExifTool
+
+=head1 DESCRIPTION
+
+This module contains definitions required by Image::ExifTool to read and
+write information written by the Camera Bits Photo Mechanic software.
+
+=head1 AUTHOR
+
+Copyright 2003-2007, Phil Harvey (phil at owl.phy.queensu.ca)
+
+This library is free software; you can redistribute it and/or modify it
+under the same terms as Perl itself.
+
+=head1 ACKNOWLEDGEMENTS
+
+Many thanks to the great support provided by Camera Bits, and in particular
+for the valuable exchanges with Kirk Baker.  Based on this experience, I can
+say that the technical support offered by Camera Bits is second to none.
+
+=head1 SEE ALSO
+
+L<Image::ExifTool::TagNames/PhotoMechanic Tags>,
+L<Image::ExifTool(3pm)|Image::ExifTool>
+
+=cut
+
Index: /gsdl/trunk/perllib/cpan/Image/ExifTool/Photoshop.pm
===================================================================
--- /gsdl/trunk/perllib/cpan/Image/ExifTool/Photoshop.pm	(revision 16842)
+++ /gsdl/trunk/perllib/cpan/Image/ExifTool/Photoshop.pm	(revision 16842)
@@ -0,0 +1,545 @@
+#------------------------------------------------------------------------------
+# File:         Photoshop.pm
+#
+# Description:  Read/write Photoshop IRB meta information
+#
+# Revisions:    02/06/04 - P. Harvey Created
+#               02/25/04 - P. Harvey Added hack for problem with old photoshops
+#               10/04/04 - P. Harvey Added a bunch of tags (ref Image::MetaData::JPEG)
+#                          but left most of them commented out until I have enough
+#                          information to write PrintConv routines for them to
+#                          display something useful
+#               07/08/05 - P. Harvey Added support for reading PSD files
+#               01/07/06 - P. Harvey Added PSD write support
+#               11/04/06 - P. Harvey Added handling of resource name
+#
+# References:   1) http://www.fine-view.com/jp/lab/doc/ps6ffspecsv2.pdf
+#               2) http://www.ozhiker.com/electronics/pjmt/jpeg_info/irb_jpeg_qual.html
+#               3) Matt Mueller private communication (tests with PS CS2)
+#               4) http://www.fileformat.info/format/psd/egff.htm
+#------------------------------------------------------------------------------
+
+package Image::ExifTool::Photoshop;
+
+use strict;
+use vars qw($VERSION $AUTOLOAD);
+use Image::ExifTool qw(:DataAccess);
+
+$VERSION = '1.32';
+
+sub ProcessPhotoshop($$$);
+sub WritePhotoshop($$$);
+
+# map of where information is stored in PSD image
+my %psdMap = (
+    IPTC         => 'Photoshop',
+    XMP          => 'Photoshop',
+    EXIFInfo     => 'Photoshop',
+    IFD0         => 'EXIFInfo',
+    IFD1         => 'IFD0',
+    ICC_Profile  => 'Photoshop',
+    ExifIFD      => 'IFD0',
+    GPS          => 'IFD0',
+    SubIFD       => 'IFD0',
+    GlobParamIFD => 'IFD0',
+    PrintIM      => 'IFD0',
+    InteropIFD   => 'ExifIFD',
+    MakerNotes   => 'ExifIFD',
+);
+
+# Photoshop APP13 tag table
+# (set Unknown flag for information we don't want to display normally)
+%Image::ExifTool::Photoshop::Main = (
+    GROUPS => { 2 => 'Image' },
+    PROCESS_PROC => \&ProcessPhotoshop,
+    WRITE_PROC => \&WritePhotoshop,
+    0x03e8 => { Unknown => 1, Name => 'Photoshop2Info' },
+    0x03e9 => { Unknown => 1, Name => 'MacintoshPrintInfo' },
+    0x03ea => { Unknown => 1, Name => 'XMLData' }, #PH
+    0x03eb => { Unknown => 1, Name => 'Photoshop2ColorTable' },
+    0x03ed => {
+        Name => 'ResolutionInfo',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::Photoshop::Resolution',
+        },
+    },
+    0x03ee => {
+        Name => 'AlphaChannelsNames',
+        PrintConv => 'Image::ExifTool::Photoshop::ConvertPascalString($val)',
+    },
+    0x03ef => { Unknown => 1, Name => 'DisplayInfo' },
+    0x03f0 => { Unknown => 1, Name => 'PStringCaption' },
+    0x03f1 => { Unknown => 1, Name => 'BorderInformation' },
+    0x03f2 => { Unknown => 1, Name => 'BackgroundColor' },
+    0x03f3 => { Unknown => 1, Name => 'PrintFlags' },
+    0x03f4 => { Unknown => 1, Name => 'BW_HalftoningInfo' },
+    0x03f5 => { Unknown => 1, Name => 'ColorHalftoningInfo' },
+    0x03f6 => { Unknown => 1, Name => 'DuotoneHalftoningInfo' },
+    0x03f7 => { Unknown => 1, Name => 'BW_TransferFunc' },
+    0x03f8 => { Unknown => 1, Name => 'ColorTransferFuncs' },
+    0x03f9 => { Unknown => 1, Name => 'DuotoneTransferFuncs' },
+    0x03fa => { Unknown => 1, Name => 'DuotoneImageInfo' },
+    0x03fb => { Unknown => 1, Name => 'EffectiveBW' },
+    0x03fc => { Unknown => 1, Name => 'ObsoletePhotoshopTag1' },
+    0x03fd => { Unknown => 1, Name => 'EPSOptions' },
+    0x03fe => { Unknown => 1, Name => 'QuickMaskInfo' },
+    0x03ff => { Unknown => 1, Name => 'ObsoletePhotoshopTag2' },
+    0x0400 => { Unknown => 1, Name => 'LayerStateInfo' },
+    0x0401 => { Unknown => 1, Name => 'WorkingPath' },
+    0x0402 => { Unknown => 1, Name => 'LayersGroupInfo' },
+    0x0403 => { Unknown => 1, Name => 'ObsoletePhotoshopTag3' },
+    0x0404 => {
+        Name => 'IPTCData',
+        SubDirectory => {
+            DirName => 'IPTC',
+            TagTable => 'Image::ExifTool::IPTC::Main',
+        },
+    },
+    0x0405 => { Unknown => 1, Name => 'RawImageMode' },
+    0x0406 => { #2
+        Name => 'JPEG_Quality',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::Photoshop::JPEG_Quality',
+        },
+    },
+    0x0408 => { Unknown => 1, Name => 'GridGuidesInfo' },
+    0x0409 => {
+        Name => 'PhotoshopBGRThumbnail',
+        Notes => 'this is a JPEG image, but in BGR format instead of RGB',
+        ValueConv => 'my $img=substr($val,0x1c);$self->ValidateImage(\$img,$tag)',
+    },
+    0x040a => {
+        Name => 'CopyrightFlag',
+        Writable => 'int8u',
+        Groups => { 2 => 'Author' },
+        ValueConv => 'join(" ",unpack("C*", $val))',
+        ValueConvInv => 'pack("C*",split(" ",$val))',
+        PrintConv => { #3
+            0 => 'False',
+            1 => 'True',
+        },
+    },
+    0x040b => {
+        Name => 'URL',
+        Writable => 'string',
+        Groups => { 2 => 'Author' },
+    },
+    0x040c => {
+        Name => 'PhotoshopThumbnail',
+        ValueConv => 'my $img=substr($val,0x1c);$self->ValidateImage(\$img,$tag)',
+    },
+    0x040d => {
+        Name => 'GlobalAngle',
+        Writable => 'int32u',
+        ValueConv => 'unpack("N",$val)',
+        ValueConvInv => 'pack("N",$val)',
+    },
+    0x040e => { Unknown => 1, Name => 'ColorSamplersResource' },
+    0x040f => {
+        Name => 'ICC_Profile',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::ICC_Profile::Main',
+        },
+    },
+    0x0410 => { Unknown => 1, Name => 'Watermark' },
+    0x0411 => { Unknown => 1, Name => 'ICC_Untagged' },
+    0x0412 => { Unknown => 1, Name => 'EffectsVisible' },
+    0x0413 => { Unknown => 1, Name => 'SpotHalftone' },
+    0x0414 => { Unknown => 1, Name => 'IDsBaseValue', Description => "ID's Base Value" },
+    0x0415 => { Unknown => 1, Name => 'UnicodeAlphaNames' },
+    0x0416 => { Unknown => 1, Name => 'IndexedColourTableCount' },
+    0x0417 => { Unknown => 1, Name => 'TransparentIndex' },
+    0x0419 => {
+        Name => 'GlobalAltitude',
+        Writable => 'int32u',
+        ValueConv => 'unpack("N",$val)',
+        ValueConvInv => 'pack("N",$val)',
+    },
+    0x041a => { Unknown => 1, Name => 'Slices' },
+    0x041b => { Unknown => 1, Name => 'WorkflowURL' },
+    0x041c => { Unknown => 1, Name => 'JumpToXPEP' },
+    0x041d => { Unknown => 1, Name => 'AlphaIdentifiers' },
+    0x041e => { Unknown => 1, Name => 'URL_List' },
+    0x0421 => { Unknown => 1, Name => 'VersionInfo' },
+    0x0422 => {
+        Name => 'EXIFInfo', #PH (Found in EPS and PSD files)
+        SubDirectory => {
+            TagTable=> 'Image::ExifTool::Exif::Main',
+            ProcessProc => \&Image::ExifTool::ProcessTIFF,
+            WriteProc => \&Image::ExifTool::WriteTIFF,
+        },
+    },
+    0x0424 => {
+        Name => 'XMP',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::XMP::Main',
+        },
+    },
+    # 0x07d0-0x0bb6 Path information
+    0x0bb7 => { Unknown => 1, Name => 'ClippingPathName' },
+    0x2710 => { Unknown => 1, Name => 'PrintFlagsInfo' },
+);
+
+# Photoshop JPEG quality record (ref 2)
+%Image::ExifTool::Photoshop::JPEG_Quality = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
+    CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
+    FORMAT => 'int16s',
+    GROUPS => { 2 => 'Image' },
+    0 => {
+        Name => 'PhotoshopQuality',
+        Writable => 1,
+        PrintConv => '$val + 4',
+        PrintConvInv => '$val - 4',
+    },
+    1 => {
+        Name => 'PhotoshopFormat',
+        PrintConv => {
+            0x0000 => 'Standard',
+            0x0001 => 'Optimised',
+            0x0101 => 'Progressive',
+        },
+    },
+    2 => {
+        Name => 'ProgressiveScans',
+        PrintConv => {
+            1 => '3 Scans',
+            2 => '4 Scans',
+            3 => '5 Scans',
+        },
+    },
+);
+
+# Photoshop resolution information #PH
+%Image::ExifTool::Photoshop::Resolution = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
+    CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
+    FORMAT => 'int16u',
+    FIRST_ENTRY => 0,
+    WRITABLE => 1,
+    GROUPS => { 2 => 'Image' },
+    0 => {
+        Name => 'XResolution',
+        Format => 'int32u',
+        Priority => 0,
+        ValueConv => '$val / 0x10000',
+        ValueConvInv => 'int($val * 0x10000 + 0.5)',
+        PrintConv => 'int($val * 100 + 0.5) / 100',
+        PrintConvInv => '$val',
+    },
+    2 => {
+        Name => 'DisplayedUnitsX',
+        PrintConv => {
+            1 => 'inches',
+            2 => 'cm',
+        },
+    },
+    4 => {
+        Name => 'YResolution',
+        Format => 'int32u',
+        Priority => 0,
+        ValueConv => '$val / 0x10000',
+        ValueConvInv => 'int($val * 0x10000 + 0.5)',
+        PrintConv => 'int($val * 100 + 0.5) / 100',
+        PrintConvInv => '$val',
+    },
+    6 => {
+        Name => 'DisplayedUnitsY',
+        PrintConv => {
+            1 => 'inches',
+            2 => 'cm',
+        },
+    },
+);
+
+# Photoshop PSD file header
+%Image::ExifTool::Photoshop::Header = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    FORMAT => 'int16u',
+    GROUPS => { 2 => 'Image' },
+    NOTES => 'This information is found in the PSD file header.',
+    6 => 'NumChannels',
+    7 => { Name => 'ImageHeight', Format => 'int32u' },
+    9 => { Name => 'ImageWidth', Format => 'int32u' },
+    11 => 'BitDepth',
+    12 => {
+        Name => 'ColorMode',
+        PrintConv => {
+            0 => 'Bitmap',
+            1 => 'Grayscale',
+            2 => 'Indexed',
+            3 => 'RGB',
+            4 => 'CMYK',
+            7 => 'Multichannel',
+            8 => 'Duotone',
+            9 => 'Lab',
+        },
+    },
+);
+
+# tags for unknown resource types
+%Image::ExifTool::Photoshop::Unknown = (
+    GROUPS => { 2 => 'Unknown' },
+);
+
+#------------------------------------------------------------------------------
+# AutoLoad our writer routines when necessary
+#
+sub AUTOLOAD
+{
+    return Image::ExifTool::DoAutoLoad($AUTOLOAD, @_);
+}
+
+#------------------------------------------------------------------------------
+# Convert pascal string(s) to something we can use
+# Inputs: 1) Pascal string data
+# Returns: Strings, concatenated with ', '
+sub ConvertPascalString($)
+{
+    my $inStr = shift;
+    my $outStr = '';
+    my $len = length($inStr);
+    my $i=0;
+    while ($i < $len) {
+        my $n = ord(substr($inStr, $i, 1));
+        last if $i + $n >= $len;
+        $i and $outStr .= ', ';
+        $outStr .= substr($inStr, $i+1, $n);
+        $i += $n + 1;
+    }
+    return $outStr;
+}
+
+#------------------------------------------------------------------------------
+# Process Photoshop APP13 record
+# Inputs: 0) ExifTool object reference, 1) Reference to directory information
+#         2) Tag table reference
+# Returns: 1 on success
+sub ProcessPhotoshop($$$)
+{
+    my ($exifTool, $dirInfo, $tagTablePtr) = @_;
+    my $dataPt = $$dirInfo{DataPt};
+    my $pos = $$dirInfo{DirStart};
+    my $dirEnd = $pos + $$dirInfo{DirLen};
+    my $verbose = $exifTool->Options('Verbose');
+    my $success = 0;
+
+    SetByteOrder('MM');     # Photoshop is always big-endian
+    $verbose and $exifTool->VerboseDir('Photoshop', 0, $$dirInfo{DirLen});
+
+    # scan through resource blocks:
+    # Format: 0) Type, 4 bytes - '8BIM' (or the rare 'PHUT', 'DCSR' or 'AgHg')
+    #         1) TagID,2 bytes
+    #         2) Name, pascal string padded to even no. bytes
+    #         3) Size, 4 bytes - N
+    #         4) Data, N bytes
+    while ($pos + 8 < $dirEnd) {
+        my $type = substr($$dataPt, $pos, 4);
+        my ($ttPtr, $extra, $val, $name);
+        if ($type eq '8BIM') {
+            $ttPtr = $tagTablePtr;
+        } elsif ($type =~ /^(PHUT|DCSR|AgHg)$/) {
+            $ttPtr = Image::ExifTool::GetTagTable('Image::ExifTool::Photoshop::Unknown');
+        } else {
+            $type =~ s/([^\w])/sprintf("\\x%.2x",ord($1))/ge;
+            $exifTool->Warn(qq{Bad Photoshop IRB resource "$type"});
+            last;
+        }
+        my $tag = Get16u($dataPt, $pos + 4);
+        $pos += 6;  # point to start of name
+        my $nameLen = Get8u($dataPt, $pos);
+        my $namePos = ++$pos;
+        # skip resource block name (pascal string, padded to an even # of bytes)
+        $pos += $nameLen;
+        ++$pos unless $nameLen & 0x01;
+        if ($pos + 4 > $dirEnd) {
+            $exifTool->Warn("Bad Photoshop resource block");
+            last;
+        }
+        my $size = Get32u($dataPt, $pos);
+        $pos += 4;
+        if ($size + $pos > $dirEnd) {
+            $exifTool->Warn("Bad Photoshop resource data size $size");
+            last;
+        }
+        $success = 1;
+        if ($nameLen) {
+            $name = substr($$dataPt, $namePos, $nameLen);
+            $extra = qq{, Name="$name"};
+        } else {
+            $name = '';
+        }
+        my $tagInfo = $exifTool->GetTagInfo($ttPtr, $tag);
+        # append resource name to value if requested (braced by "/#...#/")
+        if ($tagInfo and defined $$tagInfo{SetResourceName} and
+            $$tagInfo{SetResourceName} eq '1' and $name !~ m{/#})
+        {
+            $val = substr($$dataPt, $pos, $size) . '/#' . $name . '#/';
+        }
+        $exifTool->HandleTag($ttPtr, $tag, $val,
+            TagInfo => $tagInfo,
+            Extra   => $extra,
+            DataPt  => $dataPt,
+            DataPos => $$dirInfo{DataPos},
+            Size    => $size,
+            Start   => $pos,
+            Parent  => $$dirInfo{DirName},
+        );
+        $size += 1 if $size & 0x01; # size is padded to an even # bytes
+        $pos += $size;
+    }
+    return $success;
+}
+
+#------------------------------------------------------------------------------
+# extract information from Photoshop PSD file
+# Inputs: 0) ExifTool object reference, 1) dirInfo reference
+# Returns: 1 if this was a valid PSD file, -1 on write error
+sub ProcessPSD($$)
+{
+    my ($exifTool, $dirInfo) = @_;
+    my $raf = $$dirInfo{RAF};
+    my $outfile = $$dirInfo{OutFile};
+    my ($data, $err, $tagTablePtr);
+
+    $raf->Read($data, 30) == 30 or return 0;
+    $data =~ /^8BPS\0\x01/ or return 0;
+    SetByteOrder('MM');
+    $exifTool->SetFileType();    # set the FileType tag
+    my %dirInfo = (
+        DataPt => \$data,
+        DirStart => 0,
+        DirName => 'PSD',
+    );
+    my $len = Get32u(\$data, 26);
+    if ($outfile) {
+        Write($outfile, $data) or $err = 1;
+        $raf->Read($data, $len) == $len or return -1;
+        Write($outfile, $data) or $err = 1; # write color mode data
+        # initialize map of where things are written
+        $exifTool->InitWriteDirs(\%psdMap);
+    } else {
+        # process the header
+        $tagTablePtr = Image::ExifTool::GetTagTable('Image::ExifTool::Photoshop::Header');
+        $dirInfo{DirLen} = 30;
+        $exifTool->ProcessDirectory(\%dirInfo, $tagTablePtr);
+        $raf->Seek($len, 1) or $err = 1;    # skip over color mode data
+    }
+    $raf->Read($data, 4) == 4 or $err = 1;
+    $len = Get32u(\$data, 0);
+    $raf->Read($data, $len) == $len or $err = 1;
+    $tagTablePtr = Image::ExifTool::GetTagTable('Image::ExifTool::Photoshop::Main');
+    $dirInfo{DirLen} = $len;
+    my $rtnVal = 1;
+    if ($outfile) {
+        # rewrite IRB resources
+        $data = WritePhotoshop($exifTool, \%dirInfo, $tagTablePtr);
+        if ($data) {
+            $len = Set32u(length $data);
+            Write($outfile, $len, $data) or $err = 1;
+            # look for trailer and edit if necessary
+            my $trailInfo = Image::ExifTool::IdentifyTrailer($raf);
+            if ($trailInfo) {
+                my $tbuf = '';
+                $$trailInfo{OutFile} = \$tbuf;  # rewrite trailer(s)
+                # rewrite all trailers to buffer
+                if ($exifTool->ProcessTrailers($trailInfo)) {
+                    my $copyBytes = $$trailInfo{DataPos} - $raf->Tell();
+                    if ($copyBytes >= 0) {
+                        # copy remaining PSD file up to start of trailer
+                        while ($copyBytes) {
+                            my $n = ($copyBytes > 65536) ? 65536 : $copyBytes;
+                            $raf->Read($data, $n) == $n or $err = 1;
+                            Write($outfile, $data) or $err = 1;
+                            $copyBytes -= $n;
+                        }
+                        # write the trailer (or not)
+                        $exifTool->WriteTrailerBuffer($trailInfo, $outfile) or $err = 1;
+                    } else {
+                        $exifTool->Warn('Overlapping trailer');
+                        undef $trailInfo;
+                    }
+                } else {
+                    undef $trailInfo;
+                }
+            }
+            unless ($trailInfo) {
+                # copy over the rest of the file
+                while ($raf->Read($data, 65536)) {
+                    Write($outfile, $data) or $err = 1;
+                }
+            }
+        } else {
+            $err = 1;
+        }
+        $rtnVal = -1 if $err;
+    } elsif ($err) {
+        $exifTool->Warn('File format error');
+    } else {
+        ProcessPhotoshop($exifTool, \%dirInfo, $tagTablePtr);
+        # process trailers if they exist
+        my $trailInfo = Image::ExifTool::IdentifyTrailer($raf);
+        $exifTool->ProcessTrailers($trailInfo) if $trailInfo;
+    }
+    return $rtnVal;
+}
+
+1; # end
+
+
+__END__
+
+=head1 NAME
+
+Image::ExifTool::Photoshop - Read/write Photoshop IRB meta information
+
+=head1 SYNOPSIS
+
+This module is loaded automatically by Image::ExifTool when required.
+
+=head1 DESCRIPTION
+
+Photoshop writes its own format of meta information called a Photoshop IRB
+resource which is located in the APP13 record of JPEG files.  This module
+contains the definitions to read this information.
+
+=head1 NOTES
+
+Photoshop IRB blocks may have an associated resource name.  These names are
+usually just an empty string, but if not empty they are displayed in the
+verbose level 2 (or greater) output.  A special C<SetResourceName> flag may
+be set to '1' in the tag information hash to cause the resource name to be
+appended to the value when extracted.  If this is done, the returned value
+has the form "VALUE/#NAME#/".  When writing, the writer routine looks for
+this syntax (if C<SetResourceName> is defined), and and uses the embedded
+name to set the name of the new resource.  This allows the resource names to
+be preserved when copying Photoshop information via user-defined tags.
+
+=head1 AUTHOR
+
+Copyright 2003-2007, Phil Harvey (phil at owl.phy.queensu.ca)
+
+This library is free software; you can redistribute it and/or modify it
+under the same terms as Perl itself.
+
+=head1 REFERENCES
+
+=over 4
+
+=item L<http://www.fine-view.com/jp/lab/doc/ps6ffspecsv2.pdf>
+
+=item L<http://www.ozhiker.com/electronics/pjmt/jpeg_info/irb_jpeg_qual.html>
+
+=item L<http://www.fileformat.info/format/psd/egff.htm>
+
+=back
+
+=head1 SEE ALSO
+
+L<Image::ExifTool::TagNames/Photoshop Tags>,
+L<Image::ExifTool(3pm)|Image::ExifTool>,
+L<Image::MetaData::JPEG(3pm)|Image::MetaData::JPEG>
+
+=cut
Index: /gsdl/trunk/perllib/cpan/Image/ExifTool/PostScript.pm
===================================================================
--- /gsdl/trunk/perllib/cpan/Image/ExifTool/PostScript.pm	(revision 16842)
+++ /gsdl/trunk/perllib/cpan/Image/ExifTool/PostScript.pm	(revision 16842)
@@ -0,0 +1,401 @@
+#------------------------------------------------------------------------------
+# File:         PostScript.pm
+#
+# Description:  Read PostScript meta information
+#
+# Revisions:    07/08/05 - P. Harvey Created
+#
+# References:   1) http://partners.adobe.com/public/developer/en/ps/5002.EPSF_Spec.pdf
+#               2) http://partners.adobe.com/public/developer/en/ps/5001.DSC_Spec.pdf
+#               3) http://partners.adobe.com/public/developer/en/illustrator/sdk/AI7FileFormat.pdf
+#------------------------------------------------------------------------------
+
+package Image::ExifTool::PostScript;
+
+use strict;
+use vars qw($VERSION $AUTOLOAD);
+use Image::ExifTool qw(:DataAccess :Utils);
+
+$VERSION = '1.19';
+
+sub WritePS($$);
+sub CheckPS($$$);
+
+# PostScript tag table
+%Image::ExifTool::PostScript::Main = (
+    WRITE_PROC => \&WritePS,
+    CHECK_PROC => \&CheckPS,
+    PREFERRED => 1, # always add these tags when writing
+    GROUPS => { 2 => 'Image' },
+    # Note: Make all of these tags priority 0 since the first one found at
+    # the start of the file should take priority (in case multiples exist)
+    Author      => { Priority => 0, Groups => { 2 => 'Author' }, Writable => 'string' },
+    BoundingBox => { Priority => 0 },
+    Copyright   => { Priority => 0, Writable => 'string' }, #2
+    CreationDate => {
+        Name => 'CreateDate',
+        Priority => 0,
+        Groups => { 2 => 'Time' },
+        Writable => 'string',
+    },
+    Creator     => { Priority => 0, Writable => 'string' },
+    ImageData   => { Priority => 0 },
+    For         => { Priority => 0, Writable => 'string', Notes => 'for whom the document was prepared'},
+    Keywords    => { Priority => 0, Writable => 'string' },
+    ModDate => {
+        Name => 'ModifyDate',
+        Priority => 0,
+        Groups => { 2 => 'Time' },
+        Writable => 'string',
+    },
+    Pages       => { Priority => 0 },
+    Routing     => { Priority => 0, Writable => 'string' }, #2
+    Subject     => { Priority => 0, Writable => 'string' },
+    Title       => { Priority => 0, Writable => 'string' },
+    Version     => { Priority => 0, Writable => 'string' }, #2
+    # these subdirectories for documentation only
+    BeginPhotoshop => {
+        Name => 'PhotoshopData',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::Photoshop::Main',
+        },
+    },
+    BeginICCProfile => {
+        Name => 'ICC_Profile',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::ICC_Profile::Main',
+        },
+    },
+    begin_xml_packet => {
+        Name => 'XMP',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::XMP::Main',
+        },
+    },
+    TIFFPreview => {
+        Binary => 1,
+        Notes => q{
+            not a real tag ID, but used to represent the TIFF preview extracted from DOS
+            EPS images
+        },
+    },
+);
+
+# composite tags
+%Image::ExifTool::PostScript::Composite = (
+    GROUPS => { 2 => 'Image' },
+    # BoundingBox is in points, not pixels,
+    # but use it anyway if ImageData is not available
+    ImageWidth => {
+        Desire => {
+            0 => 'PostScript:ImageData',
+            1 => 'PostScript:BoundingBox',
+        },
+        ValueConv => 'Image::ExifTool::PostScript::ImageSize(\@val, 0)',
+    },
+    ImageHeight => {
+        Desire => {
+            0 => 'PostScript:ImageData',
+            1 => 'PostScript:BoundingBox',
+        },
+        ValueConv => 'Image::ExifTool::PostScript::ImageSize(\@val, 1)',
+    },
+);
+
+# add our composite tags
+Image::ExifTool::AddCompositeTags('Image::ExifTool::PostScript');
+
+#------------------------------------------------------------------------------
+# AutoLoad our writer routines when necessary
+#
+sub AUTOLOAD
+{
+    return Image::ExifTool::DoAutoLoad($AUTOLOAD, @_);
+}
+
+#------------------------------------------------------------------------------
+# Get image width or height
+# Inputs: 0) value list ref (ImageData, BoundingBox), 1) true to get height
+sub ImageSize($$)
+{
+    my ($vals, $getHeight) = @_;
+    my ($w, $h);
+    if ($$vals[0] and $$vals[0] =~ /^(\d+) (\d+)/) {
+        ($w, $h) = ($1, $2);
+    } elsif ($$vals[1] and $$vals[1] =~ /^(\d+) (\d+) (\d+) (\d+)/) {
+        ($w, $h) = ($3 - $1, $4 - $2);
+    }
+    return $getHeight ? $h : $w;
+}
+
+#------------------------------------------------------------------------------
+# Set PostScript format error warning
+# Inputs: 0) ExifTool object reference, 1) error string
+# Returns: 1
+sub PSErr($$)
+{
+    my ($exifTool, $str) = @_;
+    # set file type if not done already
+    $exifTool->SetFileType('PS') unless $exifTool->GetValue('FileType');
+    $exifTool->Warn("PostScript format error ($str)");
+    return 1;
+}
+
+#------------------------------------------------------------------------------
+# set $/ according to the current file
+# Inputs: 0) RAF reference
+# Returns: Original separator or undefined if on error
+sub SetInputRecordSeparator($)
+{
+    my $raf = shift;
+    my $oldsep = $/;
+    my $pos = $raf->Tell(); # save current position
+    my $data;
+    $raf->Read($data,256) or return undef;
+    my ($a, $d) = (999,999);
+    $a = pos($data), pos($data) = 0 if $data =~ /\x0a/g;
+    $d = pos($data) if $data =~ /\x0d/g;
+    my $diff = $a - $d;
+    if ($diff eq 1) {
+        $/ = "\x0d\x0a";
+    } elsif ($diff eq -1) {
+        $/ = "\x0a\x0d";
+    } elsif ($diff > 0) {
+        $/ = "\x0d";
+    } elsif ($diff < 0) {
+        $/ = "\x0a";
+    } else {
+        return undef;       # error
+    }
+    $raf->Seek($pos, 0);    # restore original position
+    return $oldsep;
+}
+
+#------------------------------------------------------------------------------
+# Extract information from EPS, PS or AI file
+# Inputs: 0) ExifTool object reference, 1) dirInfo reference
+# Returns: 1 if this was a valid PostScript file
+sub ProcessPS($$)
+{
+    my ($exifTool, $dirInfo) = @_;
+    my $raf = $$dirInfo{RAF};
+    my ($data, $dos);
+#
+# determine if this is a postscript file
+#
+    $raf->Read($data, 4) == 4 or return 0;
+    # accept either ASCII or DOS binary postscript file format
+    return 0 unless $data =~ /^(%!PS|%!Ad|\xc5\xd0\xd3\xc6)/;
+    if ($data =~ /^%!Ad/) {
+        # I've seen PS files start with "%!Adobe-PS"...
+        return 0 unless $raf->Read($data, 6) == 6 and $data eq "obe-PS";
+    } elsif ($data =~ /^\xc5\xd0\xd3\xc6/) {
+        # process DOS binary file header
+        # - save DOS header then seek ahead and check PS header
+        $raf->Read($dos, 26) == 26 or return 0;
+        SetByteOrder('II');
+        unless ($raf->Seek(Get32u(\$dos, 0), 0) and
+                $raf->Read($data, 4) == 4 and $data eq '%!PS')
+        {
+            return PSErr($exifTool, 'invalid header');
+        }
+    }
+#
+# set the newline type based on the first newline found in the file
+#
+    my $oldsep = SetInputRecordSeparator($raf);
+    $oldsep or return PSErr($exifTool, 'invalid PS data');
+
+    # set file type (PostScript or EPS)
+    $raf->ReadLine($data) or return 0;
+    $exifTool->SetFileType($data =~ /EPSF/ ? 'EPS' : 'PS');
+#
+# extract TIFF information from DOS header
+#
+    my $tagTablePtr = GetTagTable('Image::ExifTool::PostScript::Main');
+    if ($dos) {
+        my $base = Get32u(\$dos, 16);
+        if ($base) {
+            my $pos = $raf->Tell();
+            # extract the TIFF preview
+            my $len = Get32u(\$dos, 20);
+            my $val = $exifTool->ExtractBinary($base, $len, 'TIFFPreview');
+            if (defined $val and $val =~ /^(MM\0\x2a|II\x2a\0|Binary)/) {
+                $exifTool->HandleTag($tagTablePtr, 'TIFFPreview', $val);
+            } else {
+                $exifTool->Warn('Bad TIFF preview image');
+            }
+            # extract information from TIFF in DOS header
+            # (set Parent to '' to avoid setting FileType tag again)
+            my %dirInfo = (
+                Parent => '',
+                RAF => $raf,
+                Base => $base,
+            );
+            $exifTool->ProcessTIFF(\%dirInfo) or $exifTool->Warn('Bad embedded TIFF');
+            # position file pointer to extract PS information
+            $raf->Seek($pos, 0);
+        }
+    }
+#
+# parse the postscript
+#
+    my ($buff, $mode, $endToken);
+    my (@lines, $altnl);
+    if ($/ eq "\x0d") {
+        $altnl = "\x0a";
+    } else {
+        $/ = "\x0a";        # end on any LF (even if DOS CR+LF)
+        $altnl = "\x0d";
+    }
+    for (;;) {
+        if (@lines) {
+            $data = shift @lines;
+        } else {
+            $raf->ReadLine($data) or last;
+            # check for alternate newlines as efficiently as possible
+            if ($data =~ /$altnl/) {
+                # split into separate lines
+                @lines = split /$altnl/, $data, -1;
+                $data = shift @lines;
+                if (@lines == 1 and $lines[0] eq $/) {
+                    # handle case of DOS newline data inside file using Unix newlines
+                    $data .= $lines[0];
+                    undef @lines;
+                }
+            }
+        }
+        if ($mode) {
+            if (not $endToken) {
+                $buff .= $data;
+                next unless $data =~ m{<\?xpacket end=.(w|r).\?>($/|$)};
+            } elsif ($data !~ /^$endToken/i) {
+                if ($mode eq 'XMP') {
+                    $buff .= $data;
+                } elsif ($mode eq 'Document') {
+                    # ignore embedded documents 
+                } else {
+                    # data is ASCII-hex encoded
+                    $data =~ tr/0-9A-Fa-f//dc;  # remove all but hex characters
+                    $buff .= pack('H*', $data); # translate from hex
+                }
+                next;
+            }
+        } elsif ($data =~ /^(%{1,2})(Begin)(_xml_packet|Photoshop|ICCProfile|Binary)/i) {
+            # the beginning of a data block
+            my %modeLookup = (
+                _xml_packet => 'XMP',
+                iccprofile  => 'ICC_Profile',
+                photoshop   => 'Photoshop',
+            );
+            $mode = $modeLookup{lc($3)};
+            unless ($mode or @lines) {
+                # skip binary data
+                $raf->Seek($1, 1) or last if $data =~ /^%{1,2}BeginBinary:\s*(\d+)/i;
+                next;
+            }
+            $buff = '';
+            $endToken = $1 . ($2 eq 'begin' ? 'end' : 'End') . $3;
+            next;
+        } elsif ($data =~ /^(%{1,2})(Begin)(Document)/i) {
+            $mode = 'Document';
+            next;
+        } elsif ($data =~ /^<\?xpacket begin=.{7,13}W5M0MpCehiHzreSzNTczkc9d/) {
+            # pick up any stray XMP data
+            $mode = 'XMP';
+            $buff = $data;
+            undef $endToken;    # no end token (just look for xpacket end)
+            # XMP could be contained in a single line (if newlines are different)
+            next unless $data =~ m{<\?xpacket end=.(w|r).\?>($/|$)};
+        } elsif ($data =~ /^%%?(\w+): ?(.*)/s and $$tagTablePtr{$1}) {
+            my ($tag, $val) = ($1, $2);
+            # only allow 'ImageData' to have single leading '%'
+            next unless $data =~ /^%%/ or $1 eq 'ImageData';
+            # extract information from PostScript tags in comments
+            $val =~ s/\x0d*\x0a*$//;        # remove trailing CR, LF or CR/LF
+            if ($val =~ s/^\((.*)\)$/$1/) { # remove brackets if necessary
+                $val =~ s/\) \(/, /g;       # convert contained brackets too
+            }
+            $exifTool->HandleTag($tagTablePtr, $tag, $val);
+            next;
+        } else {
+            next;
+        }
+        # extract information from buffered data
+        if ($mode ne 'Document'){
+            my %dirInfo = (
+                DataPt => \$buff,
+                DataLen => length $buff,
+                DirStart => 0,
+            	DirLen => length $buff,
+            	Parent => 'PostScript',
+            );
+            my $subTablePtr = GetTagTable("Image::ExifTool::${mode}::Main");
+            unless ($exifTool->ProcessDirectory(\%dirInfo, $subTablePtr)) {
+                $exifTool->Warn("Error processing $mode information in PostScript file");
+            }
+            undef $buff;
+        }
+        undef $mode;
+    }
+    $/ = $oldsep;   # restore original separator
+    $mode and $mode ne 'Document' and PSErr($exifTool, "unterminated $mode data");
+    return 1;
+}
+
+#------------------------------------------------------------------------------
+# Extract information from EPS file
+# Inputs: 0) ExifTool object reference, 1) dirInfo reference
+# Returns: 1 if this was a valid PostScript file
+sub ProcessEPS($$)
+{
+    return ProcessPS($_[0],$_[1]);
+}
+
+1; # end
+
+
+__END__
+
+=head1 NAME
+
+Image::ExifTool::PostScript - Read PostScript meta information
+
+=head1 SYNOPSIS
+
+This module is loaded automatically by Image::ExifTool when required.
+
+=head1 DESCRIPTION
+
+This code reads meta information from EPS (Encapsulated PostScript), PS
+(PostScript) and AI (Adobe Illustrator) files.
+
+=head1 NOTES
+
+Currently doesn't handle continued lines ("%+" syntax).
+
+=head1 AUTHOR
+
+Copyright 2003-2007, Phil Harvey (phil at owl.phy.queensu.ca)
+
+This library is free software; you can redistribute it and/or modify it
+under the same terms as Perl itself.
+
+=head1 REFERENCES
+
+=over 4
+
+=item L<http://partners.adobe.com/public/developer/en/ps/5002.EPSF_Spec.pdf>
+
+=item L<http://partners.adobe.com/public/developer/en/ps/5001.DSC_Spec.pdf>
+
+=item L<http://partners.adobe.com/public/developer/en/illustrator/sdk/AI7FileFormat.pdf>
+
+=back
+
+=head1 SEE ALSO
+
+L<Image::ExifTool::TagNames/PostScript Tags>,
+L<Image::ExifTool(3pm)|Image::ExifTool>
+
+=cut
Index: /gsdl/trunk/perllib/cpan/Image/ExifTool/PrintIM.pm
===================================================================
--- /gsdl/trunk/perllib/cpan/Image/ExifTool/PrintIM.pm	(revision 16842)
+++ /gsdl/trunk/perllib/cpan/Image/ExifTool/PrintIM.pm	(revision 16842)
@@ -0,0 +1,108 @@
+#------------------------------------------------------------------------------
+# File:         PrintIM.pm
+#
+# Description:  Read PrintIM meta information
+#
+# Revisions:    04/07/2004  - P. Harvey Created
+#------------------------------------------------------------------------------
+
+package Image::ExifTool::PrintIM;
+
+use strict;
+use vars qw($VERSION);
+use Image::ExifTool qw(:DataAccess);
+
+$VERSION = '1.03';
+
+sub ProcessPrintIM($$$);
+
+# PrintIM table (is this proprietary? I can't find any documentation on this)
+%Image::ExifTool::PrintIM::Main = (
+    PROCESS_PROC => \&ProcessPrintIM,
+    GROUPS => { 0 => 'PrintIM', 1 => 'PrintIM', 2 => 'Printing' },
+    PRINT_CONV => 'sprintf("0x%.8x", $val)',
+    TAG_PREFIX => 'PrintIM',
+);
+
+
+#------------------------------------------------------------------------------
+# Process PrintIM IFD
+# Inputs: 0) ExifTool object reference, 1) reference to directory information
+#         2) pointer to tag table
+# Returns: 1 on success
+sub ProcessPrintIM($$$)
+{
+    my ($exifTool, $dirInfo, $tagTablePtr) = @_;
+    my $dataPt = $$dirInfo{DataPt};
+    my $offset = $$dirInfo{DirStart};
+    my $size = $$dirInfo{DirLen};
+    my $verbose = $exifTool->Options('Verbose');
+
+    unless ($size > 15) {
+        $exifTool->Warn('Bad PrintIM data');
+        return 0;
+    }
+    my $header = substr($$dataPt, $offset, 7);
+    unless ($header eq 'PrintIM') {
+        $exifTool->Warn('Invalid PrintIM header');
+        return 0;
+    }
+    # check size of PrintIM block
+    my $num = Get16u($dataPt, $offset + 14);
+    if ($size < 16 + $num * 6) {
+        # size is too big, maybe byte ordering is wrong
+        ToggleByteOrder();
+        $num = Get16u($dataPt, $offset + 14);
+        if ($size < 16 + $num * 6) {
+            $exifTool->Warn('Bad PrintIM size');
+            return 0;
+        }
+    }
+    $verbose and $exifTool->VerboseDir('PrintIM', $num);
+    my $n;
+    for ($n=0; $n<$num; ++$n) {
+        my $pos = $offset + 16 + $n * 6;
+        my $tag = Get16u($dataPt, $pos);
+        my $val = Get32u($dataPt, $pos + 2);
+        $exifTool->HandleTag($tagTablePtr, $tag, $val,
+            Index  => $n,
+            DataPt => $dataPt,
+            Size   => 4,
+            Start  => $pos + 2,
+        );
+    }
+    return 1;
+}
+
+
+
+1;  # end
+
+__END__
+
+=head1 NAME
+
+Image::ExifTool::PrintIM - Read PrintIM meta information
+
+=head1 SYNOPSIS
+
+This module is loaded automatically by Image::ExifTool when required.
+
+=head1 DESCRIPTION
+
+This module contains definitions required by Image::ExifTool to interpret
+Print Image Matching meta information.
+
+=head1 AUTHOR
+
+Copyright 2003-2007, Phil Harvey (phil at owl.phy.queensu.ca)
+
+This library is free software; you can redistribute it and/or modify it
+under the same terms as Perl itself.
+
+=head1 SEE ALSO
+
+L<Image::ExifTool::TagNames/PrintIM Tags>,
+L<Image::ExifTool(3pm)|Image::ExifTool>
+
+=cut
Index: /gsdl/trunk/perllib/cpan/Image/ExifTool/QuickTime.pm
===================================================================
--- /gsdl/trunk/perllib/cpan/Image/ExifTool/QuickTime.pm	(revision 16842)
+++ /gsdl/trunk/perllib/cpan/Image/ExifTool/QuickTime.pm	(revision 16842)
@@ -0,0 +1,739 @@
+#------------------------------------------------------------------------------
+# File:         QuickTime.pm
+#
+# Description:  Read QuickTime, MP4 and M4A meta information
+#
+# Revisions:    10/04/2005 - P. Harvey Created
+#               12/19/2005 - P. Harvey Added MP4 support
+#               09/22/2006 - P. Harvey Added M4A support
+#
+# References:   1) http://developer.apple.com/documentation/QuickTime/
+#               2) http://search.cpan.org/dist/MP4-Info-1.04/
+#               3) http://www.geocities.com/xhelmboyx/quicktime/formats/mp4-layout.txt
+#               4) http://wiki.multimedia.cx/index.php?title=Apple_QuickTime
+#------------------------------------------------------------------------------
+
+package Image::ExifTool::QuickTime;
+
+use strict;
+use vars qw($VERSION);
+use Image::ExifTool qw(:DataAccess :Utils);
+use Image::ExifTool::Exif;
+
+$VERSION = '1.12';
+
+sub FixWrongFormat($);
+sub ProcessMOV($$;$);
+
+# information for time/date-based tags (time zero is Jan 1, 1904)
+my %timeInfo = (
+    Groups => { 2 => 'Time' },
+    # Note: This value will be in UTC if generated by a system that is aware of the time zone
+    ValueConv => 'ConvertUnixTime($val - ((66 * 365 + 17) * 24 * 3600))',
+    PrintConv => '$self->ConvertDateTime($val)',
+);
+# information for duration tags
+my %durationInfo = (
+    ValueConv => '$self->{TimeScale} ? $val / $self->{TimeScale} : $val',
+    PrintConv => '$self->{TimeScale} ? sprintf("%.2fs", $val) : $val',
+);
+
+# QuickTime atoms
+%Image::ExifTool::QuickTime::Main = (
+    PROCESS_PROC => \&Image::ExifTool::QuickTime::ProcessMOV,
+    GROUPS => { 2 => 'Video' },
+    NOTES => q{
+        These tags are used in QuickTime MOV and MP4 videos, and QTIF images.  Tags
+        with a question mark after their name are not extracted unless the Unknown
+        option is set.
+    },
+    free => { Unknown => 1, Binary => 1 },
+    skip => { Unknown => 1, Binary => 1 },
+    wide => { Unknown => 1, Binary => 1 },
+    ftyp => { #MP4
+        Name => 'FrameType',
+        Unknown => 1,
+        Notes => 'MP4 only',
+        Binary => 1,
+    },
+    pnot => {
+        Name => 'Preview',
+        SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Preview' },
+    },
+    PICT => {
+        Name => 'PreviewPICT',
+        Binary => 1,
+    },
+    moov => {
+        Name => 'Movie',
+        SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Movie' },
+    },
+    mdat => { Unknown => 1, Binary => 1 },
+);
+
+# atoms used in QTIF files
+%Image::ExifTool::QuickTime::ImageFile = (
+    PROCESS_PROC => \&Image::ExifTool::QuickTime::ProcessMOV,
+    GROUPS => { 2 => 'Image' },
+    NOTES => 'Tags used in QTIF QuickTime Image Files.',
+    idsc => {
+        Name => 'ImageDescription',
+        SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::ImageDesc' },
+    },
+    idat => {
+        Name => 'ImageData',
+        Binary => 1,
+    },
+    iicc => {
+        Name => 'ICC_Profile',
+        SubDirectory => { TagTable => 'Image::ExifTool::ICC_Profile::Main' },
+    },
+);
+
+# image description data block
+%Image::ExifTool::QuickTime::ImageDesc = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    GROUPS => { 2 => 'Image' },
+    4 => { Name => 'CompressorID',  Format => 'string[4]' },
+    20 => { Name => 'VendorID',     Format => 'string[4]' },
+    28 => { Name => 'Quality',      Format => 'int32u' },
+    32 => { Name => 'ImageWidth',   Format => 'int16u' },
+    34 => { Name => 'ImageHeight',  Format => 'int16u' },
+    36 => { Name => 'XResolution',  Format => 'int32u' },
+    40 => { Name => 'YResolution',  Format => 'int32u' },
+    48 => { Name => 'FrameCount',   Format => 'int16u' },
+    50 => { Name => 'NameLength',   Format => 'int8u' },
+    51 => { Name => 'Compressor',   Format => 'string[$val{46}]' },
+    82 => { Name => 'BitDepth',     Format => 'int16u' },
+);
+
+# preview data block
+%Image::ExifTool::QuickTime::Preview = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    GROUPS => { 2 => 'Image' },
+    FORMAT => 'int16u',
+    0 => {
+        Name => 'PreviewDate',
+        Format => 'int32u',
+        %timeInfo,
+    },
+    2 => 'PreviewVersion',
+    3 => {
+        Name => 'PreviewAtomType',
+        Format => 'string[4]',
+    },
+    5 => 'PreviewAtomIndex',
+);
+
+# movie atoms
+%Image::ExifTool::QuickTime::Movie = (
+    PROCESS_PROC => \&Image::ExifTool::QuickTime::ProcessMOV,
+    GROUPS => { 2 => 'Video' },
+    mvhd => {
+        Name => 'MovieHeader',
+        SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::MovieHdr' },
+    },
+    trak => {
+        Name => 'Track',
+        SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Track' },
+    },
+    udta => {
+        Name => 'UserData',
+        SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::UserData' },
+    },
+);
+
+# movie header data block
+%Image::ExifTool::QuickTime::MovieHdr = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    GROUPS => { 2 => 'Video' },
+    FORMAT => 'int32u',
+    0 => { Name => 'Version', Format => 'int8u' },
+    1 => {
+        Name => 'CreateDate',
+        %timeInfo,
+    },
+    2 => {
+        Name => 'ModifyDate',
+        %timeInfo,
+    },
+    3 => {
+        Name => 'TimeScale',
+        RawConv => '$self->{TimeScale} = $val',
+    },
+    4 => { Name => 'Duration', %durationInfo },
+    5 => {
+        Name => 'PreferredRate',
+        ValueConv => '$val / 0x10000',
+    },
+    6 => {
+        Name => 'PreferredVolume',
+        Format => 'int16u',
+        ValueConv => '$val / 256',
+        PrintConv => 'sprintf("%.2f%%", $val * 100)',
+    },
+    18 => { Name => 'PreviewTime',      %durationInfo },
+    19 => { Name => 'PreviewDuration',  %durationInfo },
+    20 => { Name => 'PosterTime',       %durationInfo },
+    21 => { Name => 'SelectionTime',    %durationInfo },
+    22 => { Name => 'SelectionDuration',%durationInfo },
+    23 => { Name => 'CurrentTime',      %durationInfo },
+    24 => 'NextTrackID',
+);
+
+# track atoms
+%Image::ExifTool::QuickTime::Track = (
+    PROCESS_PROC => \&Image::ExifTool::QuickTime::ProcessMOV,
+    GROUPS => { 2 => 'Video' },
+    tkhd => {
+        Name => 'TrackHeader',
+        SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::TrackHdr' },
+    },
+    udta => {
+        Name => 'UserData',
+        SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::UserData' },
+    },
+    mdia => { #MP4
+        Name => 'Media',
+        SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Media' },
+    },
+);
+
+# track header data block
+%Image::ExifTool::QuickTime::TrackHdr = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    GROUPS => { 1 => 'Track#', 2 => 'Video' },
+    FORMAT => 'int32u',
+    0 => {
+        Name => 'TrackVersion',
+        Format => 'int8u',
+        Priority => 0,
+    },
+    1 => {
+        Name => 'TrackCreateDate',
+        Priority => 0,
+        %timeInfo,
+    },
+    2 => {
+        Name => 'TrackModifyDate',
+        Priority => 0,
+        %timeInfo,
+    },
+    3 => {
+        Name => 'TrackID',
+        Priority => 0,
+    },
+    5 => {
+        Name => 'TrackDuration',
+        Priority => 0,
+        %durationInfo,
+    },
+    8 => {
+        Name => 'TrackLayer',
+        Format => 'int16u',
+        Priority => 0,
+    },
+    9 => {
+        Name => 'TrackVolume',
+        Format => 'int16u',
+        Priority => 0,
+        ValueConv => '$val / 256',
+        PrintConv => 'sprintf("%.2f%%", $val * 100)',
+    },
+    19 => {
+        Name => 'ImageWidth',
+        Priority => 0,
+        RawConv => \&FixWrongFormat,
+    },
+    20 => {
+        Name => 'ImageHeight',
+        Priority => 0,
+        RawConv => \&FixWrongFormat,
+    },
+);
+
+# user data atoms
+%Image::ExifTool::QuickTime::UserData = (
+    PROCESS_PROC => \&Image::ExifTool::QuickTime::ProcessMOV,
+    GROUPS => { 2 => 'Video' },
+    NOTES => q{
+        Tag ID's beginning with the copyright symbol (hex 0xa9) are multi-language
+        text, but ExifTool only extracts the text from the first language in the
+        record.  ExifTool will extract any multi-language user data tags found, even
+        if they don't exist in this table.
+    },
+    "\xa9cpy" => 'Copyright',
+    "\xa9day" => 'CreateDate',
+    "\xa9dir" => 'Director',
+    "\xa9ed1" => 'Edit1',
+    "\xa9ed2" => 'Edit2',
+    "\xa9ed3" => 'Edit3',
+    "\xa9ed4" => 'Edit4',
+    "\xa9ed5" => 'Edit5',
+    "\xa9ed6" => 'Edit6',
+    "\xa9ed7" => 'Edit7',
+    "\xa9ed8" => 'Edit8',
+    "\xa9ed9" => 'Edit9',
+    "\xa9fmt" => 'Format',
+    "\xa9inf" => 'Information',
+    "\xa9prd" => 'Producer',
+    "\xa9prf" => 'Performers',
+    "\xa9req" => 'Requirements',
+    "\xa9src" => 'Source',
+    "\xa9wrt" => 'Writer',
+    name => 'Name',
+    WLOC => {
+        Name => 'WindowLocation',
+        Format => 'int16u',
+    },
+    LOOP => {
+        Name => 'LoopStyle',
+        Format => 'int32u',
+        PrintConv => {
+            1 => 'Normal',
+            2 => 'Palindromic',
+        },
+    },
+    SelO => {
+        Name => 'PlaySelection',
+        Format => 'int8u',
+    },
+    AllF => {
+        Name => 'PlayAllFrames',
+        Format => 'int8u',
+    },
+    meta => {
+        Name => 'Meta',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::QuickTime::Meta',
+            HasVersion => 1, # must skip 4-byte version number header
+        },
+    },
+   'ptv '=> {
+        Name => 'PrintToVideo',
+        SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Video' },
+    },
+    # hnti => 'HintInfo',
+    # hinf => 'HintTrackInfo',
+    TAGS => [
+        {
+            # these tags were initially discovered in a Pentax movie, but
+            # seem very similar to those used by Nikon
+            Name => 'PentaxTags',
+            Condition => '$$valPt =~ /^PENTAX DIGITAL CAMERA\0/',
+            SubDirectory => {
+                TagTable => 'Image::ExifTool::Pentax::MOV',
+                ByteOrder => 'LittleEndian',
+            },
+        },
+        {
+            Name => 'NikonTags',
+            Condition => '$$valPt =~ /^NIKON DIGITAL CAMERA\0/',
+            SubDirectory => {
+                TagTable => 'Image::ExifTool::Nikon::MOV',
+                ByteOrder => 'LittleEndian',
+            },
+        },
+        {
+            Name => 'SanyoMOV',
+            Condition => q{
+                $$valPt =~ /^SANYO DIGITAL CAMERA\0/ and
+                $self->{VALUE}->{FileType} eq "MOV"
+            },
+            SubDirectory => {
+                TagTable => 'Image::ExifTool::Sanyo::MOV',
+                ByteOrder => 'LittleEndian',
+            },
+        },
+        {
+            Name => 'SanyoMP4',
+            Condition => q{
+                $$valPt =~ /^SANYO DIGITAL CAMERA\0/ and
+                $self->{VALUE}->{FileType} eq "MP4"
+            },
+            SubDirectory => {
+                TagTable => 'Image::ExifTool::Sanyo::MP4',
+                ByteOrder => 'LittleEndian',
+            },
+        },
+        {
+            Name => 'UnknownTags',
+            Unknown => 1,
+            Binary => 1
+        },
+    ],
+);
+
+# meta atoms
+%Image::ExifTool::QuickTime::Meta = (
+    PROCESS_PROC => \&Image::ExifTool::QuickTime::ProcessMOV,
+    GROUPS => { 2 => 'Video' },
+    ilst => {
+        Name => 'InfoList',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::QuickTime::InfoList',
+            HasData => 1, # process atoms as containers with 'data' elements
+        },
+    },
+);
+
+# info list atoms
+# -> these atoms are unique, and contain one or more 'data' atoms
+%Image::ExifTool::QuickTime::InfoList = (
+    PROCESS_PROC => \&Image::ExifTool::QuickTime::ProcessMOV,
+    GROUPS => { 2 => 'Audio' },
+    "\xa9ART" => 'Artist',
+    "\xa9alb" => 'Album',
+    "\xa9cmt" => 'Comment',
+    "\xa9com" => 'Composer',
+    "\xa9day" => 'Year',
+    "\xa9des" => 'Description', #4
+    "\xa9gen" => 'Genre',
+    "\xa9grp" => 'Grouping',
+    "\xa9lyr" => 'Lyrics',
+    "\xa9nam" => 'Title',
+    "\xa9too" => 'Encoder',
+    "\xa9trk" => 'Track',
+    "\xa9wrt" => 'Composer',
+    '----' => {
+        Name => 'iTunesInfo',
+        SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::iTunesInfo' },
+    },
+    aART => 'AlbumArtist',
+    apid => 'AppleStoreID',
+    auth => 'Author',
+    covr => 'CoverArt',
+    cpil => {
+        Name => 'Compilation',
+        PrintConv => { 0 => 'No', 1 => 'Yes' },
+    },
+    cprt => 'Copyright',
+    disk => {
+        Name => 'DiskNumber',
+        ValueConv => 'length($val) >= 6 ? join(" of ",unpack("x2nn",$val)) : \$val',
+    },
+    dscp => 'Description',
+    gnre => 'Genre',
+    perf => 'Performer',
+    pgap => {
+        Name => 'PlayGap',
+        PrintConv => {
+            0 => 'Insert Gap',
+            1 => 'No Gap',
+        },
+    },
+    rtng => 'Rating', # int
+    titl => 'Title',
+    tmpo => 'BeatsPerMinute', # int
+    trkn => {
+        Name => 'TrackNumber',
+        ValueConv => 'length($val) >= 6 ? join(" of ",unpack("x2nn",$val)) : \$val',
+    },
+);
+
+# info list atoms
+%Image::ExifTool::QuickTime::iTunesInfo = (
+    PROCESS_PROC => \&Image::ExifTool::QuickTime::ProcessMOV,
+    GROUPS => { 2 => 'Audio' },
+);
+
+# print to video data block
+%Image::ExifTool::QuickTime::Video = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    GROUPS => { 2 => 'Video' },
+    0 => {
+        Name => 'DisplaySize',
+        PrintConv => {
+            0 => 'Normal',
+            1 => 'Double Size',
+            2 => 'Half Size',
+            3 => 'Full Screen',
+            4 => 'Current Size',
+        },
+    },
+    6 => {
+        Name => 'SlideShow',
+        PrintConv => {
+            0 => 'No',
+            1 => 'Yes',
+        },
+    },
+);
+
+# MP4 media
+%Image::ExifTool::QuickTime::Media = (
+    PROCESS_PROC => \&Image::ExifTool::QuickTime::ProcessMOV,
+    GROUPS => { 2 => 'Video' },
+    NOTES => 'MP4 only (most tags unknown because ISO charges for the specification).',
+    minf => {
+        Name => 'Minf',
+        SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Minf' },
+    },
+);
+
+%Image::ExifTool::QuickTime::Minf = (
+    PROCESS_PROC => \&Image::ExifTool::QuickTime::ProcessMOV,
+    GROUPS => { 2 => 'Video' },
+    NOTES => 'MP4 only (most tags unknown because ISO charges for the specification).',
+    dinf => {
+        Name => 'Dinf',
+        SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Dinf' },
+    },
+    stbl => {
+        Name => 'Stbl',
+        SubDirectory => { TagTable => 'Image::ExifTool::QuickTime::Stbl' },
+    },
+);
+
+%Image::ExifTool::QuickTime::Stbl = (
+    PROCESS_PROC => \&Image::ExifTool::QuickTime::ProcessMOV,
+    GROUPS => { 2 => 'Video' },
+    NOTES => 'MP4 only (most tags unknown because ISO charges for the specification).',
+);
+
+%Image::ExifTool::QuickTime::Dinf = (
+    PROCESS_PROC => \&Image::ExifTool::QuickTime::ProcessMOV,
+    GROUPS => { 2 => 'Video' },
+    NOTES => 'MP4 only (most tags unknown because ISO charges for the specification).',
+);
+
+#------------------------------------------------------------------------------
+# Fix incorrect format for ImageWidth/Height as written by Pentax
+sub FixWrongFormat($)
+{
+    my $val = shift;
+    return undef unless $val;
+    if ($val & 0xffff0000) {
+        $val = unpack('n',pack('N',$val));
+    }
+    return $val;
+}
+
+#------------------------------------------------------------------------------
+# Process a QuickTime atom
+# Inputs: 0) ExifTool object reference, 1) directory information reference
+#         2) optional tag table reference
+# Returns: 1 on success
+sub ProcessMOV($$;$)
+{
+    my ($exifTool, $dirInfo, $tagTablePtr) = @_;
+    my $raf = $$dirInfo{RAF};
+    my $dataPt = $$dirInfo{DataPt};
+    my $verbose = $exifTool->Options('Verbose');
+    my $dataPos = $$dirInfo{Base} || 0;
+    my ($buff, $tag, $size, $track);
+
+    # more convenient to package data as a RandomAccess file
+    $raf or $raf = new File::RandomAccess($dataPt);
+    # skip leading 4-byte version number if necessary
+    ($raf->Read($buff,4) == 4 and $dataPos += 4) or return 0 if $$dirInfo{HasVersion};
+    # read size/tag name atom header
+    $raf->Read($buff,8) == 8 or return 0;
+    $dataPos += 8;
+    $tagTablePtr or $tagTablePtr = GetTagTable('Image::ExifTool::QuickTime::Main');
+    ($size, $tag) = unpack('Na4', $buff);
+    if ($dataPt) {
+        $verbose and $exifTool->VerboseDir($$dirInfo{DirName});
+    } else {
+        # check on file type if called with a RAF
+        $$tagTablePtr{$tag} or return 0;
+        if ($tag eq 'ftyp') {
+            # read ahead 4 bytes to see if this is an M4A file
+            my $ftyp = 'MP4';
+            if ($raf->Read($buff, 4) == 4) {
+                $raf->Seek(-4, 1);
+                $ftyp = 'M4A' if $buff eq 'M4A ';
+            }
+            $exifTool->SetFileType($ftyp);  # MP4 or M4A
+        } else {
+            $exifTool->SetFileType();       # MOV
+        }
+        SetByteOrder('MM');
+    }
+    for (;;) {
+        if ($size < 8) {
+            last if $size == 0;
+            $size == 1 or $exifTool->Warn('Invalid atom size'), last;
+            $raf->Read($buff, 8) == 8 or last;
+            $dataPos += 8;
+            my ($hi, $lo) = unpack('NN', $buff);
+            if ($hi or $lo > 0x7fffffff) {
+                $exifTool->Warn('End of processing at large atom');
+                last;
+            }
+            $size = $lo;
+        }
+        $size -= 8;
+        my $tagInfo = $exifTool->GetTagInfo($tagTablePtr, $tag);
+        # generate tagInfo if Unknown option set
+        if (not defined $tagInfo and ($exifTool->{OPTIONS}->{Unknown} or
+            $tag =~ /^\xa9/))
+        {
+            my $name = $tag;
+            $name =~ s/([\x00-\x1f\x7f-\xff])/'x'.unpack('H*',$1)/eg;
+            if ($name =~ /^xa9(.*)/) {
+                $tagInfo = {
+                    Name => "UserData_$1",
+                    Description => "User Data $1",
+                };
+            } else {
+                $tagInfo = {
+                    Name => "Unknown_$name",
+                    Description => "Unknown $name",
+                    Unknown => 1,
+                    Binary => 1,
+                };
+            }
+            Image::ExifTool::AddTagToTable($tagTablePtr, $tag, $tagInfo);
+        }
+        # load values only if associated with a tag (or verbose) and < 16MB long
+        if ((defined $tagInfo or $verbose) and $size < 0x1000000) {
+            my $val;
+            unless ($raf->Read($val, $size) == $size) {
+                $exifTool->Warn("Truncated '$tag' data");
+                last;
+            }
+            # use value to get tag info if necessary
+            $tagInfo or $tagInfo = $exifTool->GetTagInfo($tagTablePtr, $tag, \$val);
+            my $hasData = ($$dirInfo{HasData} and $val =~ /^\0...data\0/s);
+            if ($verbose and not $hasData) {
+                $exifTool->VerboseInfo($tag, $tagInfo,
+                    Value => $val,
+                    DataPt => \$val,
+                    DataPos => $dataPos,
+                );
+            }
+            if ($tagInfo) {
+                my $subdir = $$tagInfo{SubDirectory};
+                if ($subdir) {
+                    my %dirInfo = (
+                        DataPt => \$val,
+                        DirStart => 0,
+                        DirLen => $size,
+                        DirName => $$tagInfo{Name},
+                        HasData => $$subdir{HasData},
+                        HasVersion => $$subdir{HasVersion},
+                        # Base needed for IsOffset tags in binary data
+                        Base => $dataPos,
+                    );
+                    if ($$subdir{ByteOrder} and $$subdir{ByteOrder} =~ /^Little/) {
+                        SetByteOrder('II');
+                    }
+                    if ($$tagInfo{Name} eq 'Track') {
+                        $track or $track = 0;
+                        $exifTool->{SET_GROUP1} = 'Track' . (++$track);
+                    }
+                    my $subTable = GetTagTable($$subdir{TagTable});
+                    $exifTool->ProcessDirectory(\%dirInfo, $subTable);
+                    delete $exifTool->{SET_GROUP1};
+                    SetByteOrder('MM');
+                } elsif ($hasData) {
+                    # handle atoms containing 'data' tags
+                    my $pos = 0;
+                    for (;;) {
+                        last if $pos + 16 > $size;
+                        my ($len, $type, $flags) = unpack("x${pos}Na4N", $val);
+                        last if $pos + $len > $size;
+                        my $value;
+                        if ($type eq 'data' and $len >= 16) {
+                            $pos += 16;
+                            $len -= 16;
+                            $value = substr($val, $pos, $len);
+                            # format flags: 0x0=binary, 0x1=text, 0xd=image, 0x15=boolean 
+                            if ($flags == 0x0015) {
+                                $value = $len ? ReadValue(\$value, $len-1, 'int8u', 1, 1) : '';
+                            } elsif ($flags != 0x01 and not $$tagInfo{ValueConv}) {
+                                # make binary data a scalar reference unless a ValueConv exists
+                                my $buf = $value;
+                                $value = \$buf;
+                            }
+                        }
+                        $exifTool->VerboseInfo($tag, $tagInfo,
+                            Value => ref $value ? $$value : $value,
+                            DataPt => \$val,
+                            DataPos => $dataPos,
+                            Start => $pos,
+                            Size => $len,
+                            Extra => sprintf(", Type='$type', Flags=0x%x",$flags)
+                        ) if $verbose;
+                        $exifTool->FoundTag($tagInfo, $value) if defined $value;
+                        $pos += $len;
+                    }
+                } else {
+                    if ($tag =~ /^\xa9/) {
+                        # parse international text to extract first string
+                        my $len = unpack('n', $val);
+                        # $len should include 4 bytes for length and type words,
+                        # but Pentax forgets to add these in, so allow for this
+                        $len += 4 if $len == $size - 4;
+                        $val = substr($val, 4, $len - 4) if $len <= $size;
+                    } elsif ($$tagInfo{Format}) {
+                        $val = ReadValue(\$val, 0, $$tagInfo{Format}, $$tagInfo{Count}, length($val));
+                    }
+                    $exifTool->FoundTag($tagInfo, $val);
+                }
+            }
+        } else {
+            $raf->Seek($size, 1) or $exifTool->Warn("Truncated '$tag' data"), last;
+        }
+        $raf->Read($buff, 8) == 8 or last;
+        $dataPos += $size + 8;
+        ($size, $tag) = unpack('Na4', $buff);
+    }
+    return 1;
+}
+
+#------------------------------------------------------------------------------
+# Process a QuickTime Image File
+# Inputs: 0) ExifTool object reference, 1) directory information reference
+# Returns: 1 on success
+sub ProcessQTIF($$)
+{
+    my $table = GetTagTable('Image::ExifTool::QuickTime::ImageFile');
+    return ProcessMOV($_[0], $_[1], $table);
+}
+
+1;  # end
+
+__END__
+
+=head1 NAME
+
+Image::ExifTool::QuickTime - Read QuickTime and MP4 meta information
+
+=head1 SYNOPSIS
+
+This module is used by Image::ExifTool
+
+=head1 DESCRIPTION
+
+This module contains routines required by Image::ExifTool to extract
+information from QuickTime and MP4 video, and M4A audio files.
+
+=head1 BUGS
+
+The MP4 support is rather pathetic since the specification documentation is
+not freely available (yes, ISO sucks).
+
+=head1 AUTHOR
+
+Copyright 2003-2007, Phil Harvey (phil at owl.phy.queensu.ca)
+
+This library is free software; you can redistribute it and/or modify it
+under the same terms as Perl itself.
+
+=head1 REFERENCES
+
+=over 4
+
+=item L<http://developer.apple.com/documentation/QuickTime/>
+
+=back
+
+=head1 SEE ALSO
+
+L<Image::ExifTool::TagNames/QuickTime Tags>,
+L<Image::ExifTool(3pm)|Image::ExifTool>
+
+=cut
+
Index: /gsdl/trunk/perllib/cpan/Image/ExifTool/README
===================================================================
--- /gsdl/trunk/perllib/cpan/Image/ExifTool/README	(revision 16842)
+++ /gsdl/trunk/perllib/cpan/Image/ExifTool/README	(revision 16842)
@@ -0,0 +1,561 @@
+--------------------------------------------------------------------------------
+File:         Image/ExifTool/README
+
+Description:  ExifTool support modules documentation
+
+The ExifTool support modules are loaded by ExifTool to interpret various formats
+of meta information.
+
+The tables in these files are used as lookups based on the tag ID values.  The
+hash keys are the tag IDs (in decimal or hexadecimal if the ID is numerical as
+with EXIF tables, or the tag name if the ID is ASCII as with XMP tables).  In
+the case of a BinaryData table, the IDs are numerical and specify offsets into
+the binary data block.  The corresponding hash value provides information about
+the tag (explained later).
+
+Twenty-one special keys (PROCESS_PROC, WRITE_PROC, CHECK_PROC, GROUPS, FORMAT,
+FIRST_ENTRY, TAG_PREFIX, PRINT_CONV, DID_TAG_ID, WRITABLE, NOTES, IS_OFFSET,
+EXTRACT_UNKNOWN, NAMESPACE, PREFERRED, PARENT, PRIORITY, WRITE_GROUP, LANG_INFO,
+VARS and DATAMEMBER) are used to provide additional information about a table.
+The special keys have names that are all capitalized to avoid possible conflicts
+with tag keys.  Below is an explanation of the meaning of each special key:
+
+  PROCESS_PROC : Reference to a function used to process the directory for this
+  table.  If PROCESS_PROC is not given, \&Image::ExifTool::Exif::ProcessExif is
+  assumed.  If PROCESS_PROC is set to 0, the tags are not added to the lookup.
+  The process proc returns 1 on success or 0 on failure, and takes 3 arguments:
+  0) reference to the ExifTool object, 1) reference to a directory information
+  hash (with the following entries:), 2) reference to the tag table hash.
+
+    Name        - Tag name for this SubDirectory entry (for verbose messages)
+    Base        - Base offset for pointers from start of file
+    DataPt      - Reference to data block containing directory (may be undef)
+    DataPos     - Position of data block within file (relative to Base)
+    DataLen     - Length of data block in bytes
+    DirStart    - Offset to start of directory from start of data block
+    DirLen      - Length of directory data within block
+    DirName     - Name of this directory
+    OutFile     - Output file or scalar reference
+    Parent      - Name of parent directory
+    RAF         - Reference to File::RandomAccess object if available
+    NewDataPos  - File position of new data (write proc only)
+    Fixup       - Reference to hash of offset fixups (used in EXIF writing only)
+    FixBase     - Flag set to attempt to fix base offsets
+    FixOffsets  - Evaluated for each value pointer to patch maker note offsets
+    LastIFD     - Used by WriteExif() to return offset of last IFD written
+    ImageData   - Used by WriteExif() to avoid buffering large image data blocks
+
+  WRITE_PROC : Reference to a function to write all new tags for this directory.
+  The write proc returns the new directory data or undefined on error.  It takes
+  the same arguments as the process proc above except that the second argument
+  (reference to directory information hash) is optional, and if specified gives
+  information about the source directory for tags to be copied to the output.
+
+  CHECK_PROC : Reference to a function which validates Raw values for writing.
+  The function takes three arguments: 0) ExifTool object reference, 1) tagInfo
+  reference, 2) value reference, and returns undefined (and possibly modifies
+  the input value) if successful, or an error message if there was a format
+  problem.
+
+  GROUPS : A hash lookup for the default group names for all entries in this
+  table. If not specified, the Group 0 and 1 names will be set automatically
+  according to the name of the module.
+
+  FORMAT : Specifies the default tag Format and corresponding pointer increment
+  for entries in a BinaryData table, and defaults to 'int8u' if not specified.
+  The possible values of FORMAT are:
+
+    int8s       - Signed 8-bit integer                    (EXIF 'SBYTE')
+    int8u       - Unsigned 8-bit integer                  (EXIF 'BYTE')
+    int16s      - Signed 16-bit integer                   (EXIF 'SSHORT')
+    int16u      - Unsigned 16-bit integer                 (EXIF 'SHORT')
+    int32s      - Signed 32-bit integer                   (EXIF 'SLONG')
+    int32u      - Unsigned 32-bit integer                 (EXIF 'LONG')
+    int64s      - Signed 64-bit integer                   (BigTIFF 'SLONG8')
+    int64u      - Unsigned 64-bit integer                 (BigTIFF 'LONG8')
+    rational32s - Rational consisting of 2 int16s values
+    rational32u - Rational consisting of 2 int16u values
+    rational64s - Rational consisting of 2 int32s values  (EXIF 'SRATIONAL')
+    rational64u - Rational consisting of 2 int32u values  (EXIF 'RATIONAL')
+    fixed16s    - Signed 16-bit fixed point value
+    fixed16u    - Unsigned 16-bit fixed point value
+    fixed32s    - Signed 32-bit fixed point value
+    fixed32u    - Unsigned 32-bit fixed point value
+    float       - 32-bit IEEE floating point value        (EXIF 'FLOAT')
+    double      - 64-bit IEEE floating point value        (EXIF 'DOUBLE')
+    extended    - 80-bit extended floating float
+    ifd         - Unsigned 32-bit integer sub-IFD pointer (EXIF 'IFD')
+    ifd8        - Unsigned 64-bit integer sub-IFD pointer (BigTIFF 'IFD8')
+    string      - Series of 8-bit ASCII characters        (EXIF 'ASCII')
+    undef       - Undefined-format binary data            (EXIF 'UNDEFINED')
+    binary      - Binary data
+
+  FIRST_ENTRY : Specifies the index for the first tag entry in a binary table.
+  This value is only used if the Unknown option is set to 2 or higher, and
+  allows the binary data to be scanned for unknown tag entries.
+
+  TAG_PREFIX : Prefix for names of unknown tags.
+
+  PRINT_CONV : Default print conversion tags where PrintConv isn't specified.
+
+  DID_TAG_ID : Used by GetTagID() as a flag to indicate that TagID's have been
+  defined for all tags in this table.  This may be set in the table definition
+  to prevent GetTagID() from assigning TagID's.
+
+  WRITABLE : Indicates that all tags in this table are writable.  This is the
+  same as setting the Writable flag for each individual tag in the table, except
+  for SubDirectory tags which are not made Writable.
+
+  NOTES : Notes to introduce the table in the TagNames documentation.  Pod
+  formatting codes B<> and C<> may be used in this text.
+
+  IS_OFFSET : Reference to list of TagID's representing offsets for binary
+  data tables only.  Not used for EXIF tables.
+
+  EXTRACT_UNKNOWN : Used in PDF tables to specify a directory where all unknown
+  tags should be extracted.
+
+  NAMESPACE : Namespace prefix for tags in the XMP table.  If this isn't a
+  standard (pre-defined) namespace, then the URI must be defined as well.  To do
+  this, the NAMESPACE value is a reference to either a list or a hash, where the
+  first element (or hash key) is the namespace prefix and and the second element
+  (or hash value) is the URI.
+
+  PREFERRED : Set to true if the tags in this table should always be added when
+  writing information.  Overrides the order specified by SetNewGroups().  When
+  this feature is used, it may also be desireable to specify a preferred group
+  when calling InitWriteDirs() to write these tags; this avoids creating other
+  directories for tags which are already being creating in the preferred group.
+
+  PARENT : Used internally to store the parent table name of a user-defined tag
+  table so the appropriate module can be loaded as required.
+
+  PRIORITY : Default Priority for all tags in this table.
+
+  WRITE_GROUP : Default WriteGroup for all tags in the table.
+
+  LANG_INFO : Code reference to a routine which returns a reference to a
+  language-specific tag information hash.  The routine takes two arguments: a
+  reference to the non-specific tagInfo hash, and the language code. Used only
+  in tables which support tag name language extensions (ie. MIE and XMP).
+
+  VARS : Hash used to store additional parameters.  Individual modules may use
+  this to store any parameters they want.  The following additional parameters
+  have been defined, and may be used by any module:
+
+    INDEX - Use "Index" instead of "Tag ID" for column heading in tag name docs.
+
+    NO_ID - Avoid printing "Tag ID" column in tag name documentation.
+
+  DATAMEMBER : BinaryData tables only.  A reference to a list of tag ID's which
+  must be extracted as data members when writing.
+
+The remaining entries in a tag table are the tag IDs with their associated
+information.  The information may exist in one of three forms:  1) A simple
+scalar which is the name of the tag, 2) A reference to a hash of information
+describing this tag, or 3) a reference to a list of hashes which contain
+Condition expressions allowing the appropriate hash to be selected to suit the
+conditions.  The following is a description of possible hash entries.  All
+entries are optional, except for the tag Name which is required if the tag ID is
+numerical.
+
+  Name          : The tag name.  Tag names need not be unique.  If they aren't
+                  unique, duplicate tags overwrite values of previous tags
+                  unless the Duplicates option is set or the new tag has lower
+                  Priority.  With Duplicates set, to allow multiple tags with
+                  the same name to exist in the tag information hash, the key of
+                  the previous tag is changed to the form "TagName (N)", where N
+                  starts at 1 and increments for subsequent duplicate tags.  A
+                  tag name should start with an uppercase letter, and contain
+                  only the charcters in the set [A-Za-z0-9_-].
+
+  Description   : A more readable description of tag name.  If a tag doesn't
+                  specify a Description, then the tag Name is used instead, with
+                  spaces inserted between the words.
+
+  Notes         : Notes for this tag in the HTML TagNames documentation.
+
+  Groups        : Hash lookup for group names for this tag.
+
+  Format        : Only valid for BinaryData, EXIF and IPTC tables. For a Binary
+                  or EXIF table, this gives the format that is used to convert
+                  the binary data, and is one of the FORMAT types specified
+                  above. For BinaryData tables, the format may have a size in
+                  trailing brackets which is a perl expression to be evaluated.
+                  The expression may access any of the previous table entries
+                  through a %val hash, or the data size via $size. For example,
+                  'string[$val{3}]' defines a string with length given by the
+                  table entry with tag index '3'. If not specified, the format
+                  of an EXIF entry is taken from the EXIF information, and the
+                  format of a BinaryData entry is taken from the FORMAT
+                  specified for the table (or int8u if FORMAT is not specified).
+
+  Count         : Used when writing EXIF information to specify the number
+                  values to write, or the number of characters in a fixed-length
+                  string.  A value of -1 indicates that the count is variable
+                  and should be determined by the number of values provided.
+                  Note that this count that corresponds to the specified Format,
+                  so if a different-sized Writable format is defined, the actual
+                  count written to the file will be different.
+
+  Flags         : Flags to specify characteristics for this tag.  May be a
+                  simple scalar flag name, a reference to a list of flag names,
+                  or a reference to a hash of flag/value pairs. If not a hash
+                  reference, the flag value is set to 1. Flags are expanded for
+                  faster access at run time into members of the tagInfo hash,
+                  and may be written directly as members if desired. The
+                  available flag names are:
+
+                  'Avoid' - avoid creating this tag if possible.
+
+                  'Binary' - set to 1 for binary data.  This has the same effect
+                  as setting ValueConv to '\$val', but it it a bit cleaner and
+                  avoids dummy ValueConvInv entries for writable tags.  Has no
+                  effect if ValueConv is defined for the tag.
+
+                  'DataMember' - name of exiftool data member associated with
+                  this tag if it should be stored as a special data member when
+                  writing information.  Necessary only if the value of the tag
+                  affects other written information.  Currently only used for
+                  tags in EXIF tables.
+
+                  'DataTag' - associated tag name containing data for offset or
+                  byte count tags.
+
+                  'Drop' - set to 1 for tags that should be excluded when
+                  rebuilding maker notes when copying all tags.
+
+                  'EntryBased' - set to 1 if the offset for this value is based
+                  on the IFD entry position.  This allows individual values to
+                  be entry-based even though some others aren't (as with the
+                  Casio PrintIM information).
+
+                  'IsOffset' - flag set if the tag represents an offset to some
+                  data, and causes value will be adjusted to an absolute file
+                  offset.  If set to 2, the offset base of the parent directory
+                  is used even if the base changes for the current directory
+                  (only some maker notes are this messed up).
+
+                  'List' - indicates that duplicate entries of this tag are
+                  allowed, and will be accumulated in a list.  Note that for XMP
+                  information, 3 different types of lists are supported and the
+                  List value specifies the type: 'Bag', 'Seq' or 'Alt'.
+
+                  'MakerNotes' - set if this tag is maker note data.
+
+                  'NotIFD' - set for 'MakerNotes' tags only if the data is not
+                  in standard EXIF IFD format.
+                  
+                  'OffsetPair' - set if the tag represents half of an offset/
+                  byte count pair.  Data for these tags must be handled
+                  separately.  Value is the tagID for the paired tag.
+
+                  'Permanent' - flag indicates that a tag is permanent, and
+                  can't be added or deleted from the file.  By default, all
+                  MakerNotes tags are permanent unless otherwise specified.
+
+                  'PrintHex' - specifies that unknown PrintConv values should
+                  be printed in hex (ie. 'Unknown (0x0)')
+
+                  'Priority' - gives the priority of this tag while reading.  If
+                  set to zero, this tag will not override the value of previous
+                  tags with the same name.  If the priority is greater than
+                  zero, this tag won't be overridden by subsequent tags unless
+                  their priority is equal to or greater than this priority.  A
+                  special feature is that Priority 0 tags are automatically
+                  incremented to Priority 1 if they exist in the IFD of the full
+                  resolution image (as determined by SubFileType).
+
+                  'Protected' - bit mask to protect tags from writing:
+                  Bit 0x01 indicates an 'unsafe' tag, which is not set via
+                  SetNewValuesFromFile() unless specified explicitly.
+                  Bit 0x02 indicates a 'protected' tag, which should not be set
+                  directly by the user.
+
+                  'SeparateTable' - set to list PrintConv values in a separate
+                  table in the HTML documentation.  Value is 1 for a table name
+                  of 'Module TagName', or 'TAG' for 'Module TAG', or 'MODULE
+                  TAG' to fully specify the table name.
+
+                  'SetResourceName' - [Photoshop tags only] set to 1 to append
+                  resource name to the extracted value (ie. 'VALUE/#NAME#/').
+                  Also allows resource name to be appended when writing new
+                  values.  May be set to any value other than 1 for a default
+                  resource name to use when writing if an appended name is not
+                  provided.
+
+                  'SubIFD' - used in writing to determine that the tag specifies
+                  an offset to a sub-IFD.  When this flag is set, the Group1
+                  name gives the name of the IFD.
+
+                  'Unknown' - this is an unknown tag (only extracted when the
+                  Unknown option is set).
+
+  RawConv       : Used to convert the Raw value at extraction time (while the
+                  image file is still open, unlike ValueConv and PrintConv below
+                  which are done later only if the value is requested).  May be
+                  a scalar expression using $val (the Raw tag value), $self (the
+                  current ExifTool object) and $tag (the tag name), or a code
+                  reference with $val and $self as arguments.  For Composite
+                  tags, $val is a reference to a hash of component tag names,
+                  and @val may be used to access the Raw values of these tags.
+                  The returned value may be a scalar which is used as the new
+                  Raw value, a scalar reference to a binary data value, a hash
+                  reference for Composite tags, an ARRAY reference for a list of
+                  values, or undefined to indicate that the tag should be
+                  ignored.  Also, RawConv may generate Warning or Error tags,
+                  while ValueConv and PrintConv may not. Note: RawConv should
+                  only be used if necessary (in general, only if the tag may be
+                  ignored).  It is preferable to use ValueConv instead of
+                  RawConv because ValueConv is only executed if the tag value is
+                  requested, while RawConv is executed for all extracted tags.
+
+  ValueConv     : Used to convert the Raw value to a useable form. May be a hash
+                  reference to act as a lookup table, a scalar which is
+                  evaluated as a Perl expression, or a code reference to a
+                  subroutine.  May also be a list reference, in which case the
+                  value is split at whitespace into a list of values and each
+                  value is converted by the associated entry in the ValueConv
+                  list.  If a hash reference is used and the Raw value doesn't
+                  appear as one of the keys, then the converted value is set to
+                  "Unknown (X)", where X is the Raw value (unless a special
+                  'BITMASK' key exists, in which case the hash referenced by
+                  'BITMASK' is used to decode individual value bits).  In an
+                  expression, $self is a reference to the current ExifTool
+                  object, $val is the Raw value, and $tag is the tag name.  The
+                  subroutine takes 2 arguments: the Raw value and a reference to
+                  the current ExifTool object.  The expression or subroutine is
+                  evaluated when and if the tag value is requested (ie. only
+                  after all extraction is complete), so if necessary at this
+                  time the values of all other tags are available via calls to
+                  $self->GetValue("Tag","Raw"). (Note: In theory, types other
+                  than "Raw" may be accessed, but they are slower and may lead
+                  to cyclical dependencies so they should be avoided). When
+                  evaluated, the expression or subroutine returns a scalar for
+                  the converted value, or a scalar reference to a binary data
+                  value (see the 'Binary' flag).  The return value should always
+                  be defined -- use RawConv instead to return undef if it is
+                  necessary to test the value for validity, otherwise an undef
+                  tag may hide a previously defined value when the Duplicates
+                  option is not enabled.  Composite tags which Require or Desire
+                  other tags may access the ValueConv, PrintConv and Raw values
+                  of these tags through the elements of the @val, @prt and @raw
+                  lists respectively (only if there was no RawConv or returned a
+                  hash reference).  For these tags, $val may be used in an
+                  expression to represent $val[0], and the first argument passed
+                  for a code reference is a reference to @val.  If ValueConv is
+                  not specified, the Raw value is not converted.
+
+  PrintConv     : This entry is similar to ValueConv above, except that it is
+                  used to further convert the tag value to a human readable
+                  form.  It can be either a hash lookup, a scalar Perl
+                  expression, a code reference or a list reference.  In this
+                  expression, $self, $val and $tag may be used as with
+                  ValueConv, but if ValueConv was defined then $val is the
+                  ValueConv value instead of the Raw value.  The returned value
+                  should always be defined.  Note that the print conversion is
+                  only done if the PrintConv option is enabled (which it is by
+                  default), and if the result of the ValueConv is not a scalar
+                  reference.  If it is a list reference, then the converted
+                  values are joined by '; ' in the output string.
+
+  RawConvInv    : The inverse of RawConv.  This should only be used in very rare
+                  situations when the raw value can not be predetermined.
+                  Unlike the other inverse conversions which are applied in
+                  SetNewValue(), this conversion is applied in WriteInfo() as
+                  the file is being written.  This is important because it means
+                  that FILE_TYPE and any DataMember tags ocurring before this
+                  tag in the file are available.  Beware that if the return
+                  value is not defined, the tag will be deleted unless there is
+                  specific logic to avoid this (currently, only EXIF and Binary
+                  data directories handle this case).
+
+  ValueConvInv  : The inverse of ValueConv.  Only necessary for Writable tags
+                  when ValueConv is specified (except WriteAlso tags).  Note
+                  that DataMember tags may NOT be used in the inverse
+                  conversions because these conversions are done before the
+                  input file is parsed. Instead, a Condition or RawConvInv must
+                  be used.
+
+  PrintConvInv  : The inverse of PrintConv.  Only necessary if for Writable tags
+                  when PrintConv is specified (unless WriteAlso is used).
+
+  Condition     : If given, specifies scalar which is evaluated as a Perl
+                  expression at extraction time to decide whether the tag is
+                  valid.  If used in a list of alternate tag definitions, the
+                  first list entry with a true condition is taken.  If no
+                  condition exists, then a 'true' condition is assumed.  The
+                  expression may use $self to access the ExifTool object, and
+                  $oldVal to access the previous Raw value of the tag (if it
+                  exists).  The first 48 bytes of the raw data value are
+                  accessible through the reference $valPt for EXIF and Jpeg2000
+                  tags only.  EXIF tags may also reference the format string and
+                  value count through $format and $count.  Note that if the
+                  value is writable and $valPt is used, the tag must have a
+                  Format (unless 'undef' or 'string'), and a Count (unless 1 or
+                  length of the 'undef' or 'string'), so the raw data may be
+                  generated for evaluating the Condition.
+
+  Require       : [Composite tags only] A hash reference specifying the tags
+                  required to calculate the composite tag value.  The hash
+                  values are the names of the required tags, and the keys
+                  specify the indices where the tag values are stored in the
+                  @val list used in the ValueConv and/or PrintConv expression.
+                  The composite value is only calculated if the values for all
+                  Require'd tags are defined.  Require and Desire tag names may
+                  be prefixed by an optional group family 0 or 1 name followed
+                  by a colon.  Case IS significant.  A special feature allows
+                  a scalar tag name to be used instead of the hash reference
+                  when only the 0th tag is defined.  For example, the following
+                  two definitions are equivalent:
+
+                        Require => { 0 => 'XMP:Title' },
+                        Require => 'XMP:Title',
+
+  Desire        : [Composite tags only] This is the same as Require except that
+                  the value is still calculated even if the specified tags don't
+                  exist.  Beware that the elements of @val, @prt and @raw may be
+                  undefined for Desire'd tags.  Both Require and Desire may
+                  exist together, however be sure to specify unique keys for
+                  each tag since they are used as indices into the @val, @prt
+                  and @raw lists common to both Require'd and Desire'd tags.
+
+  Shift         : [Writable tags only] Specifies type of shift to apply if this
+                  value may be shifted.  Set to 'Time' for shifting date/time
+                  tags.
+
+  Writable      : Indicates this tag can be written (or not written if value is
+                  set to zero), and for EXIF-type tables gives format for
+                  writing.  Writable may be set to 1 for MakerNotes information
+                  because the existing format is always used, however providing
+                  a format is desireable because it is used in validating the
+                  value.  For EXIF tables, the Writable flag may be different
+                  than the Format flag, in which case Format is used for
+                  converting the binary value and Writable specifies the format
+                  code written to the EXIF IFD.  For SubDirectories in EXIF
+                  information, this flag is only defined if the SubDirectory is
+                  writable as a block, or if the SubDirectory can not be edited
+                  (in which case Writable is set to 0).
+
+  WriteAlso     : Used for writable tag to specify other tags to write when this
+                  tag is written.  The value is a hash reference.  The hash keys
+                  are the names of the tags to write, and the values are
+                  evaluated to obtain the ValueConv values of each tag (or undef
+                  to delete the tag).  In the eval, $val may be used as the Raw
+                  value of the parent tag.  This will write Protected tags that
+                  aren't directly writable (Protected bit 0x02 set).
+
+  WriteCheck    : If given, specifies a scalar which is evaluated as a Perl
+                  expression for a one-time validatation the value being
+                  written.  The expression has access to 3 variables: $val is
+                  the value to be written, $self is the ExifTool object, and
+                  $tagInfo is the tag information hash reference.  It returns an
+                  error string or undef if the value is good.
+
+  WriteCondition: [Writable EXIF tags only] Specifies a condition to be
+                  evaluated before the tag can be written to a specific file.
+                  The condition may use $self to reference the ExifTool object,
+                  and returns true if it is OK for writing. Unlike WriteCheck
+                  which is done only once when the new value is set, this
+                  condition is evaluated before the information is written in
+                  each file.
+
+  WriteGroup    : [Required for writable EXIF tags] Specifies the IFD where the
+                  information gets written by default.
+
+  OffsetPair    : Used in EXIF table to specify the tagID for the corresponding
+                  offset or length tag.
+
+  DataTag       : Used in EXIF table to specify the tag name of the data
+                  associated with offset/length tags.
+
+  Struct        : [XMP tags only] Used by XMP writer code to specify name of XMP
+                  structure so the PropertyPath can be determined.
+
+  Units         : [MIE tags only] Reference to a list of valid units strings.
+                  The default units are the first item in the list.
+
+  TagID         : [reserved] This entry is used internally by GetTagID() to
+                  cache tag ID numbers for speed.
+
+  Table         : [reserved] Reference to parent tag table.
+
+  PropertyPath  : [reserved] Used internally by XMP writer to save property path
+                  name.
+
+  Module        : [reserved] Used internally to store module name for writable
+                  Composite tags.
+
+  LangCode      : [reserved] Used internally to indicate language code for
+                  alternate language tags (MIE and XMP only)
+
+  SubDirectory {  If it exists, this specifies the start of a new subdirectory.
+                  It contains a collection of variables which specify the type
+                  and location of the subdirectory.  These variables are
+                  described below:
+
+     TagTable   : Specifies the name of the tag table lookup for the new
+                  subdirectory.  If not specified, the parent tag table is used.
+
+     Start      : The offset to the start of the subdirectory relative to the
+                  current Base.  This is a Perl expression which may use
+                  $valuePtr to represent the location of the tag value in the
+                  file, or $val for the value itself.  If not specified, a Start
+                  of '$valuePtr' is assumed.
+
+     OffsetPt   : If specified, this is a Perl expression that gives the
+                  position of a 32-bit word in the current directory that is
+                  added to the Start position to get the position of the new
+                  subdirectory.  The expression should use the position of the
+                  current tag ($valuePtr).
+
+     Base       : This specifies the base offset for all pointers in the
+                  subdirectory.  This need not be specified if the offset is the
+                  same as the current directory, which is normally the case.
+                  May use $start to represent the subdirectory start location
+                  relative to the current base.  If this is defined, the
+                  automatic validation of base offsets is disabled for maker
+                  notes directories.
+
+     EntryBased : Flag indicating that the offsets are based on the indidual
+                  directory entry position, so offsets are incremented by 12
+                  times the corresponding entry index.
+
+     MaxSubdirs : Maximum number of subdirectories specified by the current tag
+                  (if the tag specifies multiple values).  If not specified, the
+                  tag value ($val) is used as-is.  If MaxSubdirs is specified,
+                  then one subdirectory is parsed for each value found up to the
+                  maximum number specified.
+
+     ByteOrder  : Specifies byte ordering if different than than the rest of the
+                  file.  Must be either BigEndian, LittleEndian or Unknown.  If
+                  Unknown is specified, the byte order will be determined from
+                  the directory count (however, this can not be done if OffsetPt
+                  is specified).
+
+     Validate   : If given, specifies Perl expression which is used to validate
+                  the subdirectory data.  The following variables may be used in
+                  the expression: $val (value of the tag), $dirData (reference
+                  to directory data), $subdirStart (offset to subdirectory
+                  start) and $size (size of subdirectory).
+
+     ProcessProc: If given, specifies processing procedure used to decode this
+                  subdirectory data.  This overrides the default procedure
+                  specified by PROCESS_PROC in the tag table.
+
+     DirName    : Name of this subdirectory.  If not specified, the name is
+                  taken from the tag name.  DirName is important because it is
+                  used when writing to compare against names in the directory
+                  map to determine which directories need to be edited.
+
+     FixBase    : Flag set if base offsets should be fixed.  Used to add a
+                  constant to maker notes offsets to fix damage done by some
+                  image editing utilities. (maker notes only)
+
+     FixOffsets : Expression to evaluate for each value pointer to patch
+                  problems with some EXIF maker note offsets.  May access the
+                  following variables: $valuePtr, $valEnd, $size, $tagID and
+                  $wFlag.  May return undef when writing to ignore the entry.
+  }
+
+--------------------------------------------------------------------------------
Index: /gsdl/trunk/perllib/cpan/Image/ExifTool/RIFF.pm
===================================================================
--- /gsdl/trunk/perllib/cpan/Image/ExifTool/RIFF.pm	(revision 16842)
+++ /gsdl/trunk/perllib/cpan/Image/ExifTool/RIFF.pm	(revision 16842)
@@ -0,0 +1,618 @@
+#------------------------------------------------------------------------------
+# File:         RIFF.pm
+#
+# Description:  Read RIFF/WAV/AVI meta information
+#
+# Revisions:    09/14/2005 - P. Harvey Created
+#
+# References:   1) http://www.exif.org/Exif2-2.PDF
+#               2) http://www.vlsi.fi/datasheets/vs1011.pdf
+#               3) http://www.music-center.com.br/spec_rif.htm
+#               4) http://www.codeproject.com/audio/wavefiles.asp
+#               5) http://msdn.microsoft.com/archive/en-us/directx9_c/directx/htm/avirifffilereference.asp
+#               6) http://research.microsoft.com/invisible/tests/riff.h.htm
+#               7) http://www.onicos.com/staff/iz/formats/wav.html
+#               8) http://graphics.cs.uni-sb.de/NMM/dist-0.9.1/Docs/Doxygen/html/mmreg_8h-source.html
+#               9) http://developers.videolan.org/vlc/vlc/doc/doxygen/html/codecs_8h-source.html
+#------------------------------------------------------------------------------
+
+package Image::ExifTool::RIFF;
+
+use strict;
+use vars qw($VERSION);
+use Image::ExifTool qw(:DataAccess :Utils);
+
+$VERSION = '1.10';
+
+# type of current stream
+$Image::ExifTool::RIFF::streamType = '';
+
+my %riffEncoding = ( #2
+    0x01 => 'Microsoft PCM',
+    0x02 => 'Microsoft ADPCM',
+    0x03 => 'Microsoft IEEE float',
+    0x04 => 'Compaq VSELP', #4
+    0x05 => 'IBM CVSD', #4
+    0x06 => 'Microsoft a-Law',
+    0x07 => 'Microsoft u-Law',
+    0x08 => 'Microsoft DTS', #4
+    0x09 => 'DRM', #4
+    0x0a => 'WMA 9 Speech', #9
+    0x10 => 'OKI-ADPCM',
+    0x11 => 'Intel IMA/DVI-ADPCM',
+    0x12 => 'Videologic Mediaspace ADPCM', #4
+    0x13 => 'Sierra ADPCM', #4
+    0x14 => 'Antex G.723 ADPCM', #4
+    0x15 => 'DSP Solutions DIGISTD',
+    0x16 => 'DSP Solutions DIGIFIX',
+    0x17 => 'Dialoic OKI ADPCM', #6
+    0x18 => 'Media Vision ADPCM', #6
+    0x19 => 'HP CU', #7
+    0x20 => 'Yamaha ADPCM', #6
+    0x21 => 'SONARC Speech Compression', #6
+    0x22 => 'DSP Group True Speech', #6
+    0x23 => 'Echo Speech Corp.', #6
+    0x24 => 'Virtual Music Audiofile AF36', #6
+    0x25 => 'Audio Processing Tech.', #6
+    0x26 => 'Virtual Music Audiofile AF10', #6
+    0x27 => 'Aculab Prosody 1612', #7
+    0x28 => 'Merging Tech. LRC', #7
+    0x30 => 'Dolby AC2',
+    0x31 => 'Microsoft GSM610',
+    0x32 => 'MSN Audio', #6
+    0x33 => 'Antex ADPCME', #6
+    0x34 => 'Control Resources VQLPC', #6
+    0x35 => 'DSP Solutions DIGIREAL', #6
+    0x36 => 'DSP Solutions DIGIADPCM', #6
+    0x37 => 'Control Resources CR10', #6
+    0x38 => 'Natural MicroSystems VBX ADPCM', #6
+    0x39 => 'Crystal Semiconductor IMA ADPCM', #6
+    0x3a => 'Echo Speech ECHOSC3', #6
+    0x3b => 'Rockwell ADPCM',
+    0x3c => 'Rockwell DIGITALK',
+    0x3d => 'Xebec Multimedia', #6
+    0x40 => 'Antex G.721 ADPCM',
+    0x41 => 'Antex G.728 CELP',
+    0x42 => 'Microsoft MSG723', #7
+    0x45 => 'ITU-T G.726', #9
+    0x50 => 'Microsoft MPEG',
+    0x51 => 'RT23 or PAC', #7
+    0x52 => 'InSoft RT24', #4
+    0x53 => 'InSoft PAC', #4
+    0x55 => 'MP3',
+    0x59 => 'Cirrus', #7
+    0x60 => 'Cirrus Logic', #6
+    0x61 => 'ESS Tech. PCM', #6
+    0x62 => 'Voxware Inc.', #6
+    0x63 => 'Canopus ATRAC', #6
+    0x64 => 'APICOM G.726 ADPCM',
+    0x65 => 'APICOM G.722 ADPCM',
+    0x66 => 'Microsoft DSAT', #6
+    0x67 => 'Micorsoft DSAT DISPLAY', #6
+    0x69 => 'Voxware Byte Aligned', #7
+    0x70 => 'Voxware AC8', #7
+    0x71 => 'Voxware AC10', #7
+    0x72 => 'Voxware AC16', #7
+    0x73 => 'Voxware AC20', #7
+    0x74 => 'Voxware MetaVoice', #7
+    0x75 => 'Voxware MetaSound', #7
+    0x76 => 'Voxware RT29HW', #7
+    0x77 => 'Voxware VR12', #7
+    0x78 => 'Voxware VR18', #7
+    0x79 => 'Voxware TQ40', #7
+    0x80 => 'Soundsoft', #6
+    0x81 => 'Voxware TQ60', #7
+    0x82 => 'Microsoft MSRT24', #7
+    0x83 => 'AT&T G.729A', #7
+    0x84 => 'Motion Pixels MVI MV12', #7
+    0x85 => 'DataFusion G.726', #7
+    0x86 => 'DataFusion GSM610', #7
+    0x88 => 'Iterated Systems Audio', #7
+    0x89 => 'Onlive', #7
+    0x91 => 'Siemens SBC24', #7
+    0x92 => 'Sonic Foundry Dolby AC3 APDIF', #7
+    0x93 => 'MediaSonic G.723', #8
+    0x94 => 'Aculab Prosody 8kbps', #8
+    0x97 => 'ZyXEL ADPCM', #7,
+    0x98 => 'Philips LPCBB', #7
+    0x99 => 'Studer Professional Audio Packed', #7
+    0xa0 => 'Malden PhonyTalk', #8
+    0x100 => 'Rhetorex ADPCM', #6
+    0x101 => 'IBM u-Law', #3
+    0x102 => 'IBM a-Law', #3
+    0x103 => 'IBM ADPCM', #3
+    0x111 => 'Vivo G.723', #7
+    0x112 => 'Vivo Siren', #7
+    0x123 => 'Digital G.723', #7
+    0x125 => 'Sanyo LD ADPCM', #8
+    0x130 => 'Sipro Lab ACEPLNET', #8
+    0x131 => 'Sipro Lab ACELP4800', #8
+    0x132 => 'Sipro Lab ACELP8V3', #8
+    0x133 => 'Sipro Lab G.729', #8
+    0x134 => 'Sipro Lab G.729A', #8
+    0x135 => 'Sipro Lab Kelvin', #8
+    0x140 => 'Dictaphone G.726 ADPCM', #8
+    0x150 => 'Qualcomm PureVoice', #8
+    0x151 => 'Qualcomm HalfRate', #8
+    0x155 => 'Ring Zero Systems TUBGSM', #8
+    0x160 => 'Microsoft Audio1', #8
+    0x200 => 'Creative Labs ADPCM', #6
+    0x202 => 'Creative Labs FASTSPEECH8', #6
+    0x203 => 'Creative Labs FASTSPEECH10', #6
+    0x210 => 'UHER ADPCM', #8
+    0x220 => 'Quarterdeck Corp.', #6
+    0x230 => 'I-Link VC', #8
+    0x240 => 'Aureal Semiconductor Raw Sport', #8
+    0x250 => 'Interactive Products HSX', #8
+    0x251 => 'Interactive Products RPELP', #8
+    0x260 => 'Consistent CS2', #8
+    0x270 => 'Sony SCX', #8
+    0x300 => 'Fujitsu FM TOWNS SND', #6
+    0x400 => 'Brooktree Digital', #6
+    0x450 => 'QDesign Music', #8
+    0x680 => 'AT&T VME VMPCM', #7
+    0x681 => 'AT&T TCP', #8
+    0x1000 => 'Olivetti GSM', #6
+    0x1001 => 'Olivetti ADPCM', #6
+    0x1002 => 'Olivetti CELP', #6
+    0x1003 => 'Olivetti SBC', #6
+    0x1004 => 'Olivetti OPR', #6
+    0x1100 => 'Lernout & Hauspie', #6
+    0x1400 => 'Norris Comm. Inc.', #6
+    0x1401 => 'ISIAudio', #7
+    0x1500 => 'AT&T Soundspace Music Compression', #7
+    0x2000 => 'FAST Multimedia DVM', #7
+    0xfffe => 'Extensible', #7
+    0xffff => 'Development', #4
+);
+
+# RIFF info
+%Image::ExifTool::RIFF::Main = (
+    PROCESS_PROC => \&Image::ExifTool::RIFF::ProcessChunks,
+    NOTES => q{
+        Windows WAV and AVI files are RIFF format files.  Meta information embedded
+        in two types of RIFF C<LIST> chunks: C<INFO> and C<exif>.  As well, some
+        information about the audio content is extracted from the C<fmt > chunk.
+    },
+   'fmt ' => {
+        Name => 'AudioFormat',
+        SubDirectory => { TagTable => 'Image::ExifTool::RIFF::AudioFormat' },
+    },
+    LIST_INFO => {
+        Name => 'Info',
+        SubDirectory => { TagTable => 'Image::ExifTool::RIFF::Info' },
+    },
+    LIST_exif => {
+        Name => 'Exif',
+        SubDirectory => { TagTable => 'Image::ExifTool::RIFF::Exif' },
+    },
+    LIST_hdrl => { # AVI header LIST chunk
+        Name => 'Hdrl',
+        SubDirectory => { TagTable => 'Image::ExifTool::RIFF::Hdrl' },
+    },
+);
+
+# Format and Audio Stream Format chunk data
+%Image::ExifTool::RIFF::AudioFormat = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    GROUPS => { 2 => 'Audio' },
+    FORMAT => 'int16u',
+    0 => {
+        Name => 'Encoding',
+        PrintHex => 1,
+        PrintConv => \%riffEncoding,
+    },
+    1 => 'NumChannels',
+    2 => {
+        Name => 'SampleRate',
+        Format => 'int32u',
+    },
+    4 => {
+        Name => 'AvgBytesPerSec',
+        Format => 'int32u',
+    },
+   # uninteresting
+   # 6 => 'BlockAlignment',
+    7 => 'BitsPerSample',
+);
+
+# Sub chunks of INFO LIST chunk
+%Image::ExifTool::RIFF::Info = (
+    PROCESS_PROC => \&Image::ExifTool::RIFF::ProcessChunks,
+    GROUPS => { 2 => 'Audio' },
+    IARL => 'ArchivalLocation',
+    IART => { Name => 'Artist',    Groups => { 2 => 'Author' } },
+    ICMS => 'Commissioned',
+    ICMT => 'Comment',
+    ICOP => { Name => 'Copyright', Groups => { 2 => 'Author' } },
+    ICRD => {
+        Name => 'DateCreated',
+        Groups => { 2 => 'Time' },
+        ValueConv => '$_=$val; s/-/:/g; s/\0.*//s; $_',
+    },
+    ICRP => 'Cropped',
+    IDIM => 'Dimensions',
+    IDPI => 'DotsPerInch',
+    IENG => 'Engineer',
+    IGNR => 'Genre',
+    IKEY => 'Keywords',
+    ILGT => 'Lightness',
+    IMED => 'Medium',
+    INAM => 'Title',
+    IPLT => 'NumColors',
+    IPRD => 'Product',
+    ISBJ => 'Subject',
+    ISFT => 'Software',
+    ISHP => 'Sharpness',
+    ISRC => 'Source',
+    ISRF => 'SourceForm',
+    ITCH => 'Technician',
+);
+
+# Sub chunks of EXIF LIST chunk
+%Image::ExifTool::RIFF::Exif = (
+    PROCESS_PROC => \&Image::ExifTool::RIFF::ProcessChunks,
+    GROUPS => { 2 => 'Audio' },
+    ever => 'ExifVersion',
+    erel => 'RelatedImageFile',
+    etim => { Name => 'TimeCreated', Groups => { 2 => 'Time' } },
+    ecor => { Name => 'Make',        Groups => { 2 => 'Camera' } },
+    emdl => { Name => 'Model',       Groups => { 2 => 'Camera' } },
+    emnt => { Name => 'MakerNotes',  Binary => 1 },
+    eucm => {
+        Name => 'UserComment',
+        PrintConv => 'Image::ExifTool::Exif::ConvertExifText($self,$val)',
+    },
+);
+
+# Sub chunks of hdrl LIST chunk
+%Image::ExifTool::RIFF::Hdrl = (
+    PROCESS_PROC => \&Image::ExifTool::RIFF::ProcessChunks,
+    GROUPS => { 2 => 'Image' },
+    avih => {
+        Name => 'AVIHeader',
+        SubDirectory => { TagTable => 'Image::ExifTool::RIFF::AVIHeader' },
+    },
+    IDIT => {
+        Name => 'DateTimeOriginal',
+        Description => 'Date/Time Original',
+        Groups => { 2 => 'Time' },
+        ValueConv => 'Image::ExifTool::RIFF::ConvertRIFFDate($val)',
+        PrintConv => '$self->ConvertDateTime($val)',
+    },
+    ISMP => 'TimeCode',
+    LIST_strl => {
+        Name => 'Stream',
+        SubDirectory => { TagTable => 'Image::ExifTool::RIFF::Stream' },
+    },
+);
+
+%Image::ExifTool::RIFF::AVIHeader = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    GROUPS => { 2 => 'Video' },
+    FORMAT => 'int32u',
+    FIRST_ENTRY => 0,
+    0 => {
+        Name => 'FrameRate',
+        ValueConv => '$val ? 1e6 / $val : undef',
+        PrintConv => 'int($val * 1000 + 0.5) / 1000',
+    },
+    1 => {
+        Name => 'MaxDataRate',
+        PrintConv => 'sprintf("%.4g kB/s",$val / 1024)',
+    },
+  # 2 => 'PaddingGranularity',
+  # 3 => 'Flags',
+    4 => 'FrameCount',
+  # 5 => 'InitialFrames',
+    6 => 'StreamCount',
+  # 7 => 'SuggestedBufferSize',
+    8 => 'ImageWidth',
+    9 => 'ImageHeight',
+);
+
+%Image::ExifTool::RIFF::Stream = (
+    PROCESS_PROC => \&Image::ExifTool::RIFF::ProcessChunks,
+    GROUPS => { 2 => 'Image' },
+    strh => {
+        Name => 'StreamHeader',
+        SubDirectory => { TagTable => 'Image::ExifTool::RIFF::StreamHeader' },
+    },
+    strn => 'StreamName',
+    strd => { #PH
+        Name => 'StreamData',
+        SubDirectory => { TagTable => 'Image::ExifTool::RIFF::StreamData' },
+    },
+    strf => [
+        {
+            Name => 'AudioFormat',
+            Condition => '$Image::ExifTool::RIFF::streamType eq "auds"',
+            SubDirectory => { TagTable => 'Image::ExifTool::RIFF::AudioFormat' },
+        },
+        {
+            Name => 'VideoFormat',
+            Condition => '$Image::ExifTool::RIFF::streamType eq "vids"',
+            SubDirectory => { TagTable => 'Image::ExifTool::BMP::Main' },
+        },
+    ],
+);
+
+%Image::ExifTool::RIFF::StreamHeader = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    GROUPS => { 2 => 'Video' },
+    FORMAT => 'int32u',
+    FIRST_ENTRY => 0,
+    0 => {
+        Name => 'StreamType',
+        Format => 'string[4]',
+        RawConv => '$Image::ExifTool::RIFF::streamType = $val',
+        PrintConv => {
+            auds => 'Audio',
+            mids => 'MIDI',
+            txts => 'Text',
+            vids => 'Video',
+        },
+    },
+    1 => {
+        Name => 'Codec',
+        Format => 'string[4]',
+    },
+  # 2 => 'StreamFlags',
+  # 3 => 'StreamPriority',
+  # 3.5 => 'Language',
+  # 4 => 'InitialFrames',
+  # 5 => 'Scale',
+  # 6 => 'Rate',
+  # 7 => 'Start',
+  # 8 => 'Length',
+  # 9 => 'SuggestedBufferSize',
+    10 => 'Quality',
+    11 => {
+        Name => 'SampleSize',
+        PrintConv => '$val ? "$val byte" . ($val==1 ? "" : "s") : "Variable"',
+    },
+  # 12 => { Name => 'Frame', Format => 'int16u[4]' },
+);
+
+%Image::ExifTool::RIFF::StreamData = ( #PH
+    PROCESS_PROC => \&Image::ExifTool::RIFF::ProcessStreamData,
+    GROUPS => { 2 => 'Video' },
+    NOTES => 'This chunk contains EXIF information in FujiFilm F30 AVI files.',
+    AVIF => {
+        Name => 'AVIF',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::Exif::Main',
+            DirName => 'IFD0',
+            Start => 8,
+            ByteOrder => 'II',
+        },
+    },
+);
+
+# RIFF composite tags
+%Image::ExifTool::RIFF::Composite = (
+    Duration => {
+        Require => {
+            0 => 'FrameRate',
+            1 => 'FrameCount',
+        },
+        ValueConv => '$val[0] ? $val[1] / $val[0] : undef',
+        PrintConv => 'sprintf("%.2fs",$val)',
+    },
+);
+
+# add our composite tags
+Image::ExifTool::AddCompositeTags('Image::ExifTool::RIFF');
+
+
+#------------------------------------------------------------------------------
+# Convert RIFF date to EXIF format
+my %monthNum = (
+    Jan=>1, Feb=>2, Mar=>3, Apr=>4, May=>5, Jun=>6,
+    Jul=>7, Aug=>8, Sep=>9, Oct=>10,Nov=>11,Dec=>12
+);
+sub ConvertRIFFDate($)
+{
+    my $val = shift;
+    my @part = split ' ', $val;
+    my $mon;
+    if (@part >= 5 and $mon = $monthNum{ucfirst(lc($part[1]))}) {
+        # the standard AVI date format (ie. "Mon Mar 10 15:04:43 2003")
+        $val = sprintf("%.4d:%.2d:%.2d %s", $part[4],
+                       $mon, $part[2], $part[3]);
+    } elsif ($val =~ m{(\d{4})/\s*(\d+)/\s*(\d+)/?\s+(\d+):\s*(\d+)\s*(P?)}) {
+        # but the Casio QV-3EX writes dates like "2001/ 1/27  1:42PM",
+        # and the Casio EX-Z30 writes "2005/11/28/ 09:19"... doh!
+        $val = sprintf("%.4d:%.2d:%.2d %.2d:%.2d:00",$1,$2,$3,$4+($6?12:0),$5);
+    }
+    return $val;
+}
+
+#------------------------------------------------------------------------------
+# Process stream data
+# Inputs: 0) ExifTool object ref, 1) dirInfo reference, 2) tag table ref
+# Returns: 1 on success
+sub ProcessStreamData($$$)
+{
+    my ($exifTool, $dirInfo, $tagTablePtr) = @_;
+    my $dataPt = $$dirInfo{DataPt};
+    my $start = $$dirInfo{DirStart};
+    my $size = $$dirInfo{DirLen};
+    return 0 if $size < 4;
+    if ($exifTool->Options('Verbose')) {
+        $exifTool->VerboseDir($$dirInfo{DirName}, 0, $size);
+    }
+    my $tag = substr($$dataPt, $start, 4);
+    my $tagInfo = $exifTool->GetTagInfo($tagTablePtr, $tag);
+    my $subdir = $$tagInfo{SubDirectory};
+    if ($subdir) {
+        my $offset = $$subdir{Start} || 0;
+        my $baseShift = $$dirInfo{DataPos} + $$dirInfo{DirStart} + $offset;
+        my %subdirInfo = (
+            DataPt  => $dataPt,
+            DataPos => $$dirInfo{DataPos} - $baseShift,
+            Base    => ($$dirInfo{Base} || 0) + $baseShift,
+            DataLen => $$dirInfo{DataLen},
+            DirStart=> $$dirInfo{DirStart} + $offset,
+            DirLen  => $$dirInfo{DirLen} - $offset,
+            DirName => $$subdir{DirName},
+            Parent  => $$dirInfo{DirName},
+        );
+        # (we could set FIRST_EXIF_POS to $subdirInfo{Base} here to make
+        #  htmlDump offsets relative to EXIF base if we wanted...)
+        my $subTable = GetTagTable($$subdir{TagTable});
+        $exifTool->ProcessDirectory(\%subdirInfo, $subTable);
+    }
+    return 1;
+}
+
+#------------------------------------------------------------------------------
+# Process RIFF chunks
+# Inputs: 0) ExifTool object reference, 1) directory information reference
+#         2) tag table reference
+# Returns: 1 on success
+sub ProcessChunks($$$)
+{
+    my ($exifTool, $dirInfo, $tagTablePtr) = @_;
+    my $dataPt = $$dirInfo{DataPt};
+    my $start = $$dirInfo{DirStart};
+    my $size = $$dirInfo{DirLen};
+    my $end = $start + $size;
+
+    if ($exifTool->Options('Verbose')) {
+        $exifTool->VerboseDir($$dirInfo{DirName}, 0, $size);
+    }
+    while ($start + 8 < $end) {
+        my $tag = substr($$dataPt, $start, 4);
+        my $len = Get32u($dataPt, $start + 4);
+        $start += 8;
+        if ($start + $len > $end) {
+            $exifTool->Warn("Bad $tag chunk");
+            return 0;
+        }
+        if ($tag eq 'LIST' and $len >= 4) {
+            $tag .= '_' . substr($$dataPt, $start, 4);
+            $len -= 4;
+            $start += 4;
+        }
+        $exifTool->HandleTag($tagTablePtr, $tag, undef,
+            DataPt => $dataPt,
+            DataPos => $$dirInfo{DataPos},
+            Start => $start,
+            Size => $len,
+        );
+        ++$len if $len & 0x01;  # must account for padding if odd number of bytes
+        $start += $len;
+    }
+    return 1;
+}
+
+#------------------------------------------------------------------------------
+# Extract information from a RIFF file
+# Inputs: 0) ExifTool object reference, 1) DirInfo reference
+# Returns: 1 on success, 0 if this wasn't a valid RIFF file
+sub ProcessRIFF($$)
+{
+    my ($exifTool, $dirInfo) = @_;
+    my $raf = $$dirInfo{RAF};
+    my ($buff, $err);
+    my %types = ( 'WAVE' => 'WAV', 'AVI ' => 'AVI' );
+
+    # verify this is a valid RIFF file
+    return 0 unless $raf->Read($buff, 12) == 12;
+    return 0 unless $buff =~ /^RIFF....(.{4})/s;
+    $exifTool->SetFileType($types{$1}); # set type to 'WAV', 'AVI' or 'RIFF'
+    $Image::ExifTool::RIFF::streamType = '';    # initialize stream type
+    SetByteOrder('II');
+    my $tagTablePtr = GetTagTable('Image::ExifTool::RIFF::Main');
+    my $pos = 12;
+#
+# Read chunks in RIFF image until we get to the 'data' chunk
+#
+    for (;;) {
+        $raf->Read($buff, 8) == 8 or $err=1, last;
+        $pos += 8;
+        my ($tag, $len) = unpack('a4V', $buff);
+        # special case: construct new tag name from specific LIST type
+        if ($tag eq 'LIST') {
+            $raf->Read($buff, 4) == 4 or $err=1, last;
+            $pos += 4;
+            $tag .= "_$buff";
+            $len -= 4;  # already read 4 bytes (the LIST type)
+        }
+        $exifTool->VPrint(0, "RIFF '$tag' chunk ($len bytes of data):\n");
+        # stop when we hit the audio data or AVI index or AVI movie data
+        if ($tag eq 'data' or $tag eq 'idx1' or $tag eq 'LIST_movi') {
+            $exifTool->VPrint(0, "(end of parsing)\n");
+            last;
+        }
+        my $tagInfo = $exifTool->GetTagInfo($tagTablePtr, $tag);
+        # RIFF chunks are padded to an even number of bytes
+        my $len2 = $len + ($len & 0x01);
+        if ($tagInfo and $$tagInfo{SubDirectory}) {
+            $raf->Read($buff, $len2) == $len2 or $err=1, last;
+            my %dirInfo = (
+                DataPt => \$buff,
+                DataPos => $pos,
+                DirStart => 0,
+                DirLen => $len,
+            );
+            my $tagTablePtr = GetTagTable($tagInfo->{SubDirectory}->{TagTable});
+            $exifTool->ProcessDirectory(\%dirInfo, $tagTablePtr);
+        } else {
+            $raf->Seek($len2, 1) or $err=1, last;
+        }
+        $pos += $len2;
+    }
+    $err and $exifTool->Warn('Error reading RIFF file (corrupted?)');
+    return 1;
+}
+
+1;  # end
+
+__END__
+
+=head1 NAME
+
+Image::ExifTool::RIFF - Read RIFF/WAV/AVI meta information
+
+=head1 SYNOPSIS
+
+This module is used by Image::ExifTool
+
+=head1 DESCRIPTION
+
+This module contains routines required by Image::ExifTool to extract
+information from RIFF-based (Resource Interchange File Format) files,
+including Windows WAV audio and AVI video files.
+
+=head1 AUTHOR
+
+Copyright 2003-2007, Phil Harvey (phil at owl.phy.queensu.ca)
+
+This library is free software; you can redistribute it and/or modify it
+under the same terms as Perl itself.
+
+=head1 REFERENCES
+
+=over 4
+
+=item L<http://www.exif.org/Exif2-2.PDF>
+
+=item L<http://www.vlsi.fi/datasheets/vs1011.pdf>
+
+=item L<http://www.music-center.com.br/spec_rif.htm>
+
+=item L<http://www.codeproject.com/audio/wavefiles.asp>
+
+=item L<http://msdn.microsoft.com/archive/en-us/directx9_c/directx/htm/avirifffilereference.asp>
+
+=back
+
+=head1 SEE ALSO
+
+L<Image::ExifTool::TagNames/RIFF Tags>,
+L<Image::ExifTool(3pm)|Image::ExifTool>
+
+=cut
+
Index: /gsdl/trunk/perllib/cpan/Image/ExifTool/Real.pm
===================================================================
--- /gsdl/trunk/perllib/cpan/Image/ExifTool/Real.pm	(revision 16842)
+++ /gsdl/trunk/perllib/cpan/Image/ExifTool/Real.pm	(revision 16842)
@@ -0,0 +1,731 @@
+#------------------------------------------------------------------------------
+# File:         Real.pm
+#
+# Description:  Read Real audio/video meta information
+#
+# Revisions:    05/16/2006 - P. Harvey Created
+#
+# References:   1) http://www.getid3.org/
+#               2) https://common.helixcommunity.org/nonav/2003/HCS_SDK_r5/htmfiles/rmff.htm
+#------------------------------------------------------------------------------
+
+package Image::ExifTool::Real;
+
+use strict;
+use vars qw($VERSION);
+use Image::ExifTool qw(:DataAccess :Utils);
+use Image::ExifTool::Canon;
+
+$VERSION = '1.01';
+
+sub ProcessRealMeta($$$);
+sub ProcessRealProperties($$$);
+
+# Real property types (ref PH)
+my %propertyType = (
+    0 => 'int32u',
+    2 => 'string',
+);
+
+# Real Metadata property types
+my %metadataFormat = (
+    1 => 'string',  # text
+    2 => 'string',  # text list
+    3 => 'flag',    # 1 or 4 byte integer
+    4 => 'int32u',  # 4-byte integer
+    5 => 'undef',   # binary data
+    6 => 'string',  # URL
+    7 => 'string',  # date
+    8 => 'string',  # file name
+    9 =>  undef,    # grouping
+    10 => undef,    # reference
+);
+
+# Real Metadata property flag bit descriptions
+my %metadataFlag = (
+    0 => 'Read Only',
+    1 => 'Private',
+    2 => 'Type Descriptor',
+);
+
+
+# tags used in RealMedia (RM, RV and RMVB) files
+%Image::ExifTool::Real::Media = (
+    GROUPS => { 2 => 'Video' },
+    NOTES => q{
+        These B<Tag ID>'s are Chunk ID's used in RealMedia and RealVideo (RM, RV and
+        RMVB) files.
+    },
+    CONT => { SubDirectory => { TagTable => 'Image::ExifTool::Real::ContentDescr' } },
+    MDPR => { SubDirectory => { TagTable => 'Image::ExifTool::Real::MediaProps' } },
+    PROP => { SubDirectory => { TagTable => 'Image::ExifTool::Real::Properties' } },
+    RJMD => { SubDirectory => { TagTable => 'Image::ExifTool::Real::Metadata' } },
+);
+
+# pseudo-tags used in RealAudio (RA) files
+%Image::ExifTool::Real::Audio = (
+    GROUPS => { 2 => 'Audio' },
+    NOTES => q{
+        Tags in the following table reference information extracted from various
+        versions of RealAudio (RA) files.
+    },
+    '.ra3' => { Name => 'RA3', SubDirectory => { TagTable => 'Image::ExifTool::Real::AudioV3' } },
+    '.ra4' => { Name => 'RA4', SubDirectory => { TagTable => 'Image::ExifTool::Real::AudioV4' } },
+    '.ra5' => { Name => 'RA5', SubDirectory => { TagTable => 'Image::ExifTool::Real::AudioV5' } },
+);
+
+# pseudo-tags used in RealMedia Metafiles and RealMedia Plug-in Metafiles (RAM and RPM)
+%Image::ExifTool::Real::Metafile = (
+    GROUPS => { 2 => 'Video' },
+    NOTES => q{
+        Tags representing information extracted from Real Audio Metafile and
+        RealMedia Plug-in Metafile (RAM and RPM) files.
+    },
+    txt => 'Text',
+    url => 'URL',
+);
+
+%Image::ExifTool::Real::Properties = (
+    GROUPS => { 1 => 'Real-PROP', 2 => 'Video' },
+    PROCESS_PROC => \&Image::ExifTool::Canon::ProcessSerialData,
+    FORMAT => 'int32u',
+    0  => 'MaxBitRate',
+    1  => 'AvgBitRate',
+    2  => 'MaxPacketSize',
+    3  => 'AvgPacketSize',
+    4  => 'NumPackets',
+    5 => { Name => 'Duration',      ValueConv => '$val / 1000' },
+    6 => { Name => 'Preroll',       ValueConv => '$val / 1000' },
+    7 => { Name => 'IndexOffset',   Unknown => 1 },
+    8 => { Name => 'DataOffset',    Unknown => 1 },
+    9 => { Name => 'NumStreams',    Format => 'int16u' },
+    10 => {
+        Name => 'Flags',
+        Format => 'int16u',
+        PrintConv => { BITMASK => {
+            0 => 'Allow Recording',
+            1 => 'Perfect Play',
+            2 => 'Live',
+            3 => 'Allow Download', #PH (from rmeditor dump)
+        } },
+    },
+);
+
+%Image::ExifTool::Real::MediaProps = (
+    GROUPS => { 1 => 'Real-MDPR', 2 => 'Video' },
+    PROCESS_PROC => \&Image::ExifTool::Canon::ProcessSerialData,
+    FORMAT => 'int32u',
+    PRIORITY => 0,  # first stream takes priority
+    0  => { Name => 'StreamNumber',  Format => 'int16u' },
+    1  => { Name => 'StreamMaxBitRate' },
+    2  => { Name => 'StreamAvgBitRate' },
+    3  => { Name => 'StreamMaxPacketSize' },
+    4  => { Name => 'StreamAvgPacketSize' },
+    5  => { Name => 'StreamStartTime' },
+    6  => { Name => 'StreamPreroll',       ValueConv => '$val / 1000' },
+    7  => { Name => 'StreamDuration',      FValueConv => '$val / 1000' },
+    8  => { Name => 'StreamNameLen', Format => 'int8u', Unknown => 1 },
+    9  => { Name => 'StreamName',    Format => 'string[$val{8}]' },
+    10 => { Name => 'StreamMimeLen', Format => 'int8u', Unknown => 1 },
+    11 => {
+        Name => 'StreamMimeType',
+        Format => 'string[$val{10}]',
+        RawConv => '$self->{RealStreamMime} = $val',
+    },
+    12 => { Name => 'FileInfoLen', Unknown => 1 },
+    13 => {
+        Name => 'FileInfoLen2',
+        # if this condition fails, subsequent tags will not be processed
+        Condition => '$self->{RealStreamMime} eq "logical-fileinfo"',
+        Unknown => 1,
+    },
+    14 => {
+        Name => 'FileInfoVersion',
+        Format => 'int16u',
+    },
+    15 => {
+        Name => 'PhysicalStreams',
+        Format => 'int16u',
+        Unknown => 1,
+    },
+    16 => {
+        Name => 'PhysicalStreamNumbers',
+        Format => 'int16u[$val{15}]',
+        Unknown => 1,
+    },
+    17 => {
+        Name => 'DataOffsets',
+        Format => 'int32u[$val{15}]',
+        Unknown => 1,
+    },
+    18 => {
+        Name => 'NumRules',
+        Format => 'int16u',
+        Unknown => 1,
+    },
+    19 => {
+        Name => 'PhysicalStreamNumberMap',
+        Format => 'int16u[$val{18}]',
+        Unknown => 1,
+    },
+    20 => {
+        Name => 'NumProperties',
+        Format => 'int16u',
+        Unknown => 1,
+    },
+    21 => {
+        Name => 'FileInfoProperties',
+        Format => 'undef[$val{13}-$val{15}*6-$val{18}*2-12]',
+        SubDirectory => { TagTable => 'Image::ExifTool::Real::FileInfo' },
+    },
+);
+
+# Observed FileInfo properties (ref PH)
+%Image::ExifTool::Real::FileInfo = (
+    GROUPS => { 1 => 'Real-MDPR', 2 => 'Video' },
+    PROCESS_PROC => \&ProcessRealProperties,
+    NOTES => q{
+        The following tags have been observed in the FileInfo properties, but any
+        other existing information will also be extracted.
+    },
+    Indexable       => { PrintConv => { 0 => 'False', 1 => 'True' } },
+    Keywords        => { },
+    Description     => { },
+   'File ID'        => { Name => 'FileID' },
+   'Content Rating' => {
+        Name => 'ContentRating',
+        PrintConv => {
+            0 => 'No Rating',
+            1 => 'All Ages',
+            2 => 'Older Children',
+            3 => 'Younger Teens',
+            4 => 'Older Teens',
+            5 => 'Adult Supervision Recommended',
+            6 => 'Adults Only',
+        },
+    },
+    Audiences       => { },
+    audioMode       => { Name => 'AudioMode' },
+   'Creation Date'  => {
+        Name => 'CreateDate',
+        Groups => { 2 => 'Time' },
+        ValueConv => q{
+            $val =~ m{(\d+)/(\d+)/(\d+)\s+(\d+):(\d+):(\d+)} ?
+            sprintf("%.4d:%.2d:%.2d %.2d:%.2d:%.2d",$3,$2,$1,$4,$5,$6) : $val
+        },
+        PrintConv => '$self->ConvertDateTime($val)',
+    },
+   'Generated By'   => { Name => 'Software' },
+   'Modification Date' => {
+        Name => 'ModifyDate',
+        Groups => { 2 => 'Time' },
+        ValueConv => q{
+            $val =~ m{(\d+)/(\d+)/(\d+)\s+(\d+):(\d+):(\d+)} ?
+            sprintf("%.4d:%.2d:%.2d %.2d:%.2d:%.2d",$3,$2,$1,$4,$5,$6) : $val
+        },
+        PrintConv => '$self->ConvertDateTime($val)',
+    },
+   'Target Audiences' => { Name => 'TargetAudiences' },
+   'Audio Format'   => { Name => 'AudioFormat' },
+   'Video Quality'  => { Name => 'VideoQuality' },
+    videoMode       => { Name => 'VideoMode' },
+);
+
+%Image::ExifTool::Real::ContentDescr = (
+    GROUPS => { 1 => 'Real-CONT', 2 => 'Video' },
+    PROCESS_PROC => \&Image::ExifTool::Canon::ProcessSerialData,
+    FORMAT => 'int16u',
+    0 => { Name => 'TitleLen',      Unknown => 1 },
+    1 => { Name => 'Title',         Format => 'string[$val{0}]' },
+    2 => { Name => 'AuthorLen',     Unknown => 1 },
+    3 => { Name => 'Author',        Format => 'string[$val{2}]', Groups => { 2 => 'Author' } },
+    4 => { Name => 'CopyrightLen',  Unknown => 1 },
+    5 => { Name => 'Copyright',     Format => 'string[$val{4}]', Groups => { 2 => 'Author' } },
+    6 => { Name => 'CommentLen',    Unknown => 1 },
+    7 => { Name => 'Comment',       Format => 'string[$val{6}]' },
+);
+
+# Real RJMD meta information (ref PH)
+%Image::ExifTool::Real::Metadata = (
+    GROUPS => { 1 => 'Real-RJMD', 2 => 'Video' },
+    PROCESS_PROC => \&ProcessRealMeta,
+    NOTES => q{
+        The tags below represent information which has been observed in the Real
+        Metadata format, but ExifTool will extract any information it finds in this
+        format.  (As far as I can tell from the referenced documentation, string
+        values should be plain text, but this is not the case for the only sample
+        file I have been able to obtain containing this information.  These tags
+        could also be split into separate sub-directories, but this will wait until
+        I have better documentation or a more complete set of samples.)
+    },
+   'Album/Name'     => 'AlbumName',
+   'Track/Category' => 'TrackCategory',
+   'Track/Comments' => 'TrackComments',
+   'Track/Lyrics'   => 'TrackLyrics',
+);
+
+%Image::ExifTool::Real::AudioV3 = (
+    GROUPS => { 1 => 'Real-RA3', 2 => 'Audio' },
+    PROCESS_PROC => \&Image::ExifTool::Canon::ProcessSerialData,
+    FORMAT => 'int8u',
+    0  => { Name => 'Channels',       Format => 'int16u' },
+    1  => { Name => 'Unknown',        Format => 'int16u[3]', Unknown => 1 },
+    2  => { Name => 'BytesPerMinute', Format => 'int16u' },
+    3  => { Name => 'AudioBytes',     Format => 'int32u' },
+    4  => { Name => 'TitleLen',       Unknown => 1 },
+    5  => { Name => 'Title',          Format => 'string[$val{4}]' },
+    6  => { Name => 'ArtistLen',      Unknown => 1 },
+    7  => { Name => 'Artist',         Format => 'string[$val{6}]', Groups => { 2 => 'Author' } },
+    8  => { Name => 'CopyrightLen',   Unknown => 1 },
+    9  => { Name => 'Copyright',      Format => 'string[$val{8}]', Groups => { 2 => 'Author' } },
+    10 => { Name => 'CommentLen',     Unknown => 1 },
+    11 => { Name => 'Comment',        Format => 'string[$val{10}]' },
+);
+
+%Image::ExifTool::Real::AudioV4 = (
+    GROUPS => { 1 => 'Real-RA4', 2 => 'Audio' },
+    PROCESS_PROC => \&Image::ExifTool::Canon::ProcessSerialData,
+    FORMAT => 'int16u',
+    0  => { Name => 'FourCC1',        Format => 'undef[4]', Unknown => 1 },
+    1  => { Name => 'AudioFileSize',  Format => 'int32u',   Unknown => 1 },
+    2  => { Name => 'Version2',       Unknown => 1 },
+    3  => { Name => 'HeaderSize',     Format => 'int32u',   Unknown => 1 },
+    4  => { Name => 'CodecFlavorID',  Unknown => 1 },
+    5  => { Name => 'CodedFrameSize', Format => 'int32u',   Unknown => 1 },
+    6  => { Name => 'AudioBytes',     Format => 'int32u' },
+    7  => { Name => 'BytesPerMinute', Format => 'int32u' },
+    8  => { Name => 'Unknown',        Format => 'int32u',   Unknown => 1 },
+    9  => { Name => 'SubPacketH',     Unknown => 1 },
+    10 => 'AudioFrameSize',
+    11 => { Name => 'SubPacketSize',  Unknown => 1 },
+    12 => { Name => 'Unknown',        Unknown => 1 },
+    13 => 'SampleRate',
+    14 => { Name => 'Unknown',        Unknown => 1 },
+    15 => 'BitsPerSample',
+    16 => 'Channels',
+    17 => { Name => 'FourCC2Len',     Format => 'int8u',    Unknown => 1 },
+    18 => { Name => 'FourCC2',        Format => 'undef[4]', Unknown => 1 },
+    19 => { Name => 'FourCC3Len',     Format => 'int8u',    Unknown => 1 },
+    20 => { Name => 'FourCC3',        Format => 'undef[4]', Unknown => 1 },
+    21 => { Name => 'Unknown',        Format => 'int8u',    Unknown => 1 },
+    22 => { Name => 'Unknown',        Unknown => 1 },
+    23 => { Name => 'TitleLen',       Format => 'int8u',    Unknown => 1 },
+    24 => { Name => 'Title',          Format => 'string[$val{23}]' },
+    25 => { Name => 'ArtistLen',      Format => 'int8u',    Unknown => 1 },
+    26 => { Name => 'Artist',         Format => 'string[$val{25}]', Groups => { 2 => 'Author' } },
+    27 => { Name => 'CopyrightLen',   Format => 'int8u',    Unknown => 1 },
+    28 => { Name => 'Copyright',      Format => 'string[$val{27}]', Groups => { 2 => 'Author' } },
+    29 => { Name => 'CommentLen',     Format => 'int8u',    Unknown => 1 },
+    30 => { Name => 'Comment',        Format => 'string[$val{29}]' },
+);
+
+%Image::ExifTool::Real::AudioV5 = (
+    GROUPS => { 1 => 'Real-RA5', 2 => 'Audio' },
+    PROCESS_PROC => \&Image::ExifTool::Canon::ProcessSerialData,
+    FORMAT => 'int16u',
+    0  => { Name => 'FourCC1',        Format => 'undef[4]', Unknown => 1 },
+    1  => { Name => 'AudioFileSize',  Format => 'int32u',   Unknown => 1 },
+    2  => { Name => 'Version2',       Unknown => 1 },
+    3  => { Name => 'HeaderSize',     Format => 'int32u',   Unknown => 1 },
+    4  => { Name => 'CodecFlavorID',  Unknown => 1 },
+    5  => { Name => 'CodedFrameSize', Format => 'int32u',   Unknown => 1 },
+    6  => { Name => 'AudioBytes',     Format => 'int32u' },
+    7  => { Name => 'BytesPerMinute', Format => 'int32u' },
+    8  => { Name => 'Unknown',        Format => 'int32u',   Unknown => 1 },
+    9  => { Name => 'SubPacketH',     Unknown => 1 },
+    10 => { Name => 'FrameSize',      Unknown => 1 },
+    11 => { Name => 'SubPacketSize',  Unknown => 1 },
+    12 => 'SampleRate',
+    13 => { Name => 'SampleRate2',    Unknown => 1 },
+    14 => { Name => 'BitsPerSample',  Format => 'int32u' },
+    15 => 'Channels',
+    16 => { Name => 'Genr',           Format => 'int32u',   Unknown => 1 },
+    17 => { Name => 'FourCC3',        Format => 'undef[4]', Unknown => 1 },
+);
+
+#------------------------------------------------------------------------------
+# Process Real NameValueProperties
+# Inputs: 0) ExifTool object reference, 1) dirInfo ref, 2) tag table ref
+# Returns: 1 on success
+sub ProcessRealProperties($$$)
+{
+    my ($exifTool, $dirInfo, $tagTablePtr) = @_;
+    my $dataPt = $$dirInfo{DataPt};
+    my $dirLen = $$dirInfo{DirLen};
+    my $pos = $$dirInfo{DirStart};
+    my $verbose = $exifTool->Options('Verbose');
+
+    $verbose and $exifTool->VerboseDir('RealProperties', undef, $dirLen);
+
+    while ($pos + 6 <= $dirLen) {
+
+        # get property size and version
+        my ($size, $vers) = unpack("x${pos}Nn", $$dataPt);
+        last if $size < 6;
+        unless ($vers == 0) {
+            $pos += $size;
+            next;
+        }
+        $pos += 6;
+
+        my $tagLen = unpack("x${pos}C", $$dataPt);
+        ++$pos;
+
+        last if $pos + $tagLen > $dirLen;
+        my $tag = substr($$dataPt, $pos, $tagLen);
+        $pos += $tagLen;
+
+        last if $pos + 6 > $dirLen;
+        my ($type, $valLen) = unpack("x${pos}Nn", $$dataPt);
+        $pos += 6;
+
+        last if $pos + $valLen > $dirLen;
+        my $format = $propertyType{$type} || 'undef';
+        my $count = int($valLen / Image::ExifTool::FormatSize($format));
+        my $val = ReadValue($dataPt, $pos, $format, $count, $dirLen-$pos);
+
+        my $tagInfo = $exifTool->GetTagInfo($tagTablePtr, $tag);
+        unless ($tagInfo) {
+            my $tagName;
+            ($tagName = $tag) =~ s/\s+//g;
+            next unless $tagName =~ /^\w+$/;    # ignore crazy names
+            $tagInfo = { Name => ucfirst($tagName) };
+            Image::ExifTool::AddTagToTable($tagTablePtr, $tag, $tagInfo);
+        }
+        if ($verbose) {
+            $exifTool->VerboseInfo($tag, $tagInfo,
+                Table  => $tagTablePtr,
+                Value  => $val,
+                DataPt => $dataPt,
+                Size   => $valLen,
+                Start  => $pos,
+                Addr   => $pos + $$dirInfo{DataPos},
+                Format => $format,
+                Count  => $count,
+            );
+        }
+        $exifTool->FoundTag($tagInfo, $val);
+        $pos += $valLen;
+    }
+    return 1;
+}
+
+#------------------------------------------------------------------------------
+# Process Real metadata properties
+# Inputs: 0) ExifTool object reference, 1) dirInfo ref, 2) tag table ref
+# Returns: 1 on success
+sub ProcessRealMeta($$$)
+{
+    my ($exifTool, $dirInfo, $tagTablePtr) = @_;
+    my $dataPt = $$dirInfo{DataPt};
+    my $dataPos = $$dirInfo{DataPos};
+    my $pos = $$dirInfo{DirStart};
+    my $dirEnd = $pos + $$dirInfo{DirLen};
+    my $verbose = $exifTool->Options('Verbose');
+    my $prefix = $$dirInfo{Prefix} || '';
+    $prefix and $prefix .= '/';
+
+    $verbose and $exifTool->VerboseDir('RealMetadata', undef, $$dirInfo{DirLen});
+
+    for (;;) {
+        last if $pos + 28 > $dirEnd;
+        # extract fixed-position metadata structure members
+        my ($size, $type, $flags, $valuePos, $subPropPos, $numSubProps, $nameLen)
+            = unpack("x${pos}N7", $$dataPt);
+        # make pointers relative to data start
+        $valuePos += $pos;
+        $subPropPos += $pos;
+        # validate what we have read so far
+        last if $pos + $size > $dirEnd;
+        last if $pos + 28 + $nameLen > $dirEnd;
+        last if $valuePos <  $pos + 28 + $nameLen;
+        last if $valuePos + 4 > $dirEnd;
+        my $tag = substr($$dataPt, $pos + 28, $nameLen);
+        $tag =~ s/\0.*//s;  # truncate at null
+        $tag = $prefix . $tag;
+        my $valueLen = unpack("x${valuePos}N", $$dataPt);
+        $valuePos += 4; # point at value itself
+        last if $valuePos + $valueLen > $dirEnd;
+
+        my $format = $metadataFormat{$type};
+        my $tagInfo = $exifTool->GetTagInfo($tagTablePtr, $tag);
+        unless ($tagInfo) {
+            my $tagName = $tag;
+            $tagName =~ tr/A-Za-z0-9//dc;
+            $tagInfo = { Name => ucfirst($tagName) };
+            Image::ExifTool::AddTagToTable($tagTablePtr, $tag, $tagInfo);
+        }
+        if ($verbose) {
+            $format = 'undef' unless defined $format;
+            $flags = Image::ExifTool::DecodeBits($flags, \%metadataFlag);
+        }
+        if ($valueLen and $format) {
+            # (a flag can be 1 or 4 bytes)
+            if ($format eq 'flag') {
+                $format = ($valueLen == 4) ? 'int32u' : 'int8u';
+            } elsif ($type == 7 and $tagInfo) {
+                # add PrintConv and ValueConv for "date" type
+                $$tagInfo{ValueConv} or $$tagInfo{ValueConv} = q{
+                    $val =~ /^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/ ?
+                    sprintf("%.4d:%.2d:%.2d %.2d:%.2d:%.2d",$1,$2,$3,$4,$5,$6) :
+                    $val;
+                };
+                $$tagInfo{PrintConv} or $$tagInfo{PrintConv} = '$self->ConvertDateTime($val)';
+            }
+            my $count = int($valueLen / Image::ExifTool::FormatSize($format));
+            my $val = ReadValue($dataPt, $valuePos, $format, $count, $dirEnd-$valuePos);
+            $exifTool->HandleTag($tagTablePtr, $tag, $val,
+                DataPt => $dataPt,
+                DataPos => $dataPos,
+                Start => $valuePos,
+                Size => $valueLen,
+                Format => "type=$type, flags=$flags",
+            );
+        }
+        # extract sub-properties
+        if ($numSubProps) {
+            my $dirStart = $valuePos + $valueLen + $numSubProps * 8;
+            my %dirInfo = (
+                DataPt => $dataPt,
+                DataPos => $dataPos,
+                DirStart => $dirStart,
+                DirLen => $pos + $size - $dirStart,
+                Prefix => $tag,
+            );
+            $exifTool->ProcessDirectory(\%dirInfo, $tagTablePtr);
+        }
+        $pos += $size;  # step to next Metadata structure
+    }
+    unless ($pos == $dirEnd) {
+        $exifTool->Warn('Format error in Real Metadata');
+        return 0;
+    }
+    return 1;
+}
+
+#------------------------------------------------------------------------------
+# Read information frame a Real file
+# Inputs: 0) ExifTool object reference, 1) Directory information reference
+# Returns: 1 on success, 0 if this wasn't a valid Real file
+sub ProcessReal($$)
+{
+    my ($exifTool, $dirInfo) = @_;
+    my $raf = $$dirInfo{RAF};
+    my ($buff, $tag, $vers, $extra, @mimeTypes, %dirCount);
+
+    $raf->Read($buff, 8) == 8 or return 0;
+    $buff =~ m{^(\.RMF|\.ra\xfd|pnm://|rtsp://|http://)} or return 0;
+
+    my ($type, $tagTablePtr);
+    if ($1 eq '.RMF') {
+        $tagTablePtr = GetTagTable('Image::ExifTool::Real::Media');
+        $type = 'RM';
+    } elsif ($1 eq ".ra\xfd") {
+        $tagTablePtr = GetTagTable('Image::ExifTool::Real::Audio');
+        $type = 'RA';
+    } else {
+        $tagTablePtr = GetTagTable('Image::ExifTool::Real::Metafile');
+        my $ext = $exifTool->{FILE_EXT};
+        $type = ($ext and $ext eq 'RPM') ? 'RPM' : 'RAM';
+        require Image::ExifTool::PostScript;
+        my $oldSep = Image::ExifTool::PostScript::SetInputRecordSeparator($raf);
+        $raf->Seek(0,0);
+        while ($raf->ReadLine($buff)) {
+            last if length $buff > 256;
+            next unless $buff ;
+            chomp $buff;
+            if ($type) {
+                # must be a Real file type if protocol is http
+                return 0 if $buff =~ /^http/ and $buff !~ /\.(ra|rm|rv|rmvb|smil)$/i;
+                $exifTool->SetFileType($type);
+                undef $type;
+            }
+            # save URL or Text from RAM file
+            my $tag = $buff =~ m{^[a-z]{3,4}://} ? 'url' : 'txt';
+            $exifTool->HandleTag($tagTablePtr, $tag, $buff);
+        }
+        $/ = $oldSep if $oldSep;
+        return 1;
+    }
+
+    $exifTool->SetFileType($type);
+    SetByteOrder('MM');
+    my $verbose = $exifTool->Options('Verbose');
+#
+# Process RealAudio file
+#
+    if ($type eq 'RA') {
+        ($vers, $extra) = unpack('x4nn', $buff);
+        $tag = ".ra$vers";
+        my $fpos = $raf->Tell();
+        unless ($raf->Read($buff, 512)) {
+            $exifTool->Warn('Error reading audio header');
+            return 1;
+        }
+        my $tagInfo = $exifTool->GetTagInfo($tagTablePtr, $tag);
+        if ($verbose > 2) {
+            $exifTool->VerboseInfo($tag, $tagInfo, DataPt => \$buff, DataPos => $fpos);
+        }
+        if ($tagInfo) {
+            my $subTablePtr = GetTagTable($tagInfo->{SubDirectory}->{TagTable});
+            my %dirInfo = (
+                DataPt   => \$buff,
+                DataPos  => $fpos,
+                DirLen   => length $buff,
+                DirStart => 0,
+            );
+            $exifTool->ProcessDirectory(\%dirInfo, $subTablePtr);
+        } else {
+            $exifTool->Warn('Unsupported RealAudio version');
+        }
+        return 1;
+    }
+#
+# Process RealMedia file
+#
+    # skip the rest of the RM header
+    my $size = unpack('x4N', $buff);
+    unless ($raf->Seek($size - 8, 1)) {
+        $exifTool->Warn('Error seeking in file');
+        return 0;
+    }
+
+    # Process RealMedia chunks
+    for (;;) {
+        $raf->Read($buff, 10) == 10 or last;
+        ($tag, $size, $vers) = unpack('a4Nn', $buff);
+        last if $tag eq "\0\0\0\0";
+        if ($verbose) {
+            $exifTool->VPrint(0, "$tag chunk ($size bytes):\n");
+        } else {
+            last if $tag eq 'DATA'; # stop normal parsing at DATA tag
+        }
+        if ($size & 0x80000000) {
+            $exifTool->Warn('Bad chunk header');
+            last;
+        }
+        my $tagInfo = $exifTool->GetTagInfo($tagTablePtr, $tag);
+        if ($tagInfo and $$tagInfo{SubDirectory}) {
+            my $fpos = $raf->Tell();
+            unless ($raf->Read($buff, $size-10) == $size-10) {
+                $exifTool->Warn("Error reading $tag chunk");
+                last;
+            }
+            if ($verbose > 2) {
+                $exifTool->VerboseInfo($tag, $tagInfo, DataPt => \$buff, DataPos => $fpos);
+            }
+            my $subTablePtr = GetTagTable($tagInfo->{SubDirectory}->{TagTable});
+            my %dirInfo = (
+                DataPt   => \$buff,
+                DataPos  => $fpos,
+                DirLen   => length $buff,
+                DirStart => 0,
+            );
+            if ($dirCount{$tag}) {
+                $exifTool->{SET_GROUP1} = '+' . ++$dirCount{$tag};
+            } else {
+                $dirCount{$tag} = 1;
+            }
+            $exifTool->ProcessDirectory(\%dirInfo, $subTablePtr);
+            delete $exifTool->{SET_GROUP1};
+            # keep track of stream MIME types
+            my $mime = $exifTool->{RealStreamMime};
+            if ($mime) {
+                delete $exifTool->{RealStreamMime};
+                $mime =~ s/\0.*//s;
+                push @mimeTypes, $mime unless $mime =~ /^logical-/;
+            }
+        } else {
+            unless ($raf->Seek($size-10, 1)) {
+                $exifTool->Warn('Error seeking in file');
+                last;
+            }
+        }
+    }
+    # override MIMEType with stream MIME type if we only have one stream
+    if (@mimeTypes == 1 and length $mimeTypes[0]) {
+        $exifTool->{VALUE}->{MIMEType} = $mimeTypes[0];
+        $verbose and $exifTool->VPrint(0, "  MIMEType = $mimeTypes[0]\n");
+    }
+#
+# Process footer containing Real metadata and ID3 information
+#
+    if ($raf->Seek(-140, 2) and $raf->Read($buff, 12) == 12 and $buff =~ /^RMJE/) {
+        my $metaSize = unpack('x8N', $buff);
+        if ($raf->Seek(-$metaSize-12, 1) and
+            $raf->Read($buff, $metaSize) == $metaSize and
+            $buff =~ /^RJMD/)
+        {
+            my %dirInfo = (
+                DataPt => \$buff,
+                DataPos => $raf->Tell() - $metaSize,
+                DirStart => 8,
+                DirLen => length($buff) - 8,
+            );
+            my $tagTablePtr = GetTagTable('Image::ExifTool::Real::Metadata');
+            $exifTool->ProcessDirectory(\%dirInfo, $tagTablePtr);
+        } else {
+            $exifTool->Warn('Bad metadata footer');
+        }
+        if ($raf->Seek(-128, 2) and $raf->Read($buff, 128) == 128 and $buff =~ /^TAG/) {
+            $exifTool->VPrint(0, "ID3v1:\n");
+            my %dirInfo = (
+                DataPt => \$buff,
+                DirStart => 0,
+                DirLen => length($buff),
+            );
+            my $tagTablePtr = GetTagTable('Image::ExifTool::ID3::v1');
+            $exifTool->ProcessDirectory(\%dirInfo, $tagTablePtr);
+        }
+    }
+    return 1;
+}
+
+1;  # end
+
+__END__
+
+=head1 NAME
+
+Image::ExifTool::Real - Read Real audio/video meta information
+
+=head1 SYNOPSIS
+
+This module is used by Image::ExifTool
+
+=head1 DESCRIPTION
+
+This module contains the routines required by Image::ExifTool to read meta
+information in RealAudio (RA), RealMedia (RM, RV and RMVB) and RealMedia
+Metafile (RAM and RPM) files.
+
+=head1 NOTES
+
+There must be a bug in the software that wrote the Metadata used in the test
+file t/images/Real.rm because the TrackLyricsDataSize word is written
+little-endian, but the Real format is big-endian.
+
+=head1 AUTHOR
+
+Copyright 2003-2007, Phil Harvey (phil at owl.phy.queensu.ca)
+
+This library is free software; you can redistribute it and/or modify it
+under the same terms as Perl itself.
+
+=head1 REFERENCES
+
+=over 4
+
+=item L<http://www.getid3.org/>
+
+=item L<https://common.helixcommunity.org/nonav/2003/HCS_SDK_r5/htmfiles/rmff.htm>
+
+=back
+
+=head1 SEE ALSO
+
+L<Image::ExifTool::TagNames/Real Tags>,
+L<Image::ExifTool(3pm)|Image::ExifTool>
+
+=cut
+
Index: /gsdl/trunk/perllib/cpan/Image/ExifTool/Ricoh.pm
===================================================================
--- /gsdl/trunk/perllib/cpan/Image/ExifTool/Ricoh.pm	(revision 16842)
+++ /gsdl/trunk/perllib/cpan/Image/ExifTool/Ricoh.pm	(revision 16842)
@@ -0,0 +1,439 @@
+#------------------------------------------------------------------------------
+# File:         Ricoh.pm
+#
+# Description:  Ricoh EXIF maker notes tags
+#
+# Revisions:    03/28/2005 - P. Harvey Created
+#
+# References:   1) http://www.ozhiker.com/electronics/pjmt/jpeg_info/ricoh_mn.html
+#               2) http://homepage3.nifty.com/kamisaka/makernote/makernote_ricoh.htm
+#------------------------------------------------------------------------------
+
+package Image::ExifTool::Ricoh;
+
+use strict;
+use vars qw($VERSION);
+use Image::ExifTool qw(:DataAccess :Utils);
+use Image::ExifTool::Exif;
+
+$VERSION = '1.05';
+
+sub ProcessRicohText($$$);
+sub ProcessRicohRMETA($$$);
+
+%Image::ExifTool::Ricoh::Main = (
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
+    WRITE_PROC => \&Image::ExifTool::Exif::WriteExif,
+    CHECK_PROC => \&Image::ExifTool::Exif::CheckExif,
+    WRITABLE => 1,
+    NOTES => 'These tags are used by Ricoh Caplio camera models.',
+    0x0001 => { Name => 'MakerNoteType',    Writable => 'string' },
+    0x0002 => { Name => 'MakerNoteVersion', Writable => 'string' },
+    0x0e00 => {
+        Name => 'PrintIM',
+        Writable => 0,
+        Description => 'Print Image Matching',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::PrintIM::Main',
+        },
+    },
+    0x1001 => {
+        Name => 'ImageInfo',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::Ricoh::ImageInfo',
+        },
+    },
+    0x1003 => {
+        Name => 'Sharpness',
+        Writable => 'int32u',
+        PrintConv => {
+            0 => 'Sharp',
+            1 => 'Normal',
+            2 => 'Soft',
+        },
+    },
+    0x2001 => [
+        {
+            Name => 'RicohSubdir',
+            Condition => '$self->{CameraModel} !~ /^Caplio RR1\b/',
+            SubDirectory => {
+                Validate => '$val =~ /^\[Ricoh Camera Info\]/',
+                TagTable => 'Image::ExifTool::Ricoh::Subdir',
+                Start => '$valuePtr + 20',
+                ByteOrder => 'BigEndian',
+            },
+        },
+        {
+            Name => 'RicohRR1Subdir',
+            SubDirectory => {
+                Validate => '$val =~ /^\[Ricoh Camera Info\]/',
+                TagTable => 'Image::ExifTool::Ricoh::Subdir',
+                Start => '$valuePtr + 20',
+                ByteOrder => 'BigEndian',
+                # the Caplio RR1 uses a different base address -- doh!
+                Base => '$start-20',
+            },
+        },
+    ],
+);
+
+# Ricoh image info (ref 2)
+%Image::ExifTool::Ricoh::ImageInfo = (
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Image' },
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
+    CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
+    WRITABLE => 1,
+    PRIORITY => 0,
+    FORMAT => 'int8u',
+    FIRST_ENTRY => 0,
+    IS_OFFSET => [ 28 ],   # tag 28 is 'IsOffset'
+    0 => {
+        Name => 'RicohImageWidth',
+        Format => 'int16u',
+    },
+    2 => {
+        Name => 'RicohImageHeight',
+        Format => 'int16u',
+    },
+    6 => {
+        Name => 'RicohDate',
+        Groups => { 2 => 'Time' },
+        Format => 'int8u[7]',
+        # (what an insane way to encode the date)
+        ValueConv => q{
+            sprintf("%.2x%.2x:%.2x:%.2x %.2x:%.2x:%.2x",
+                    split(' ', $val));
+        },
+        ValueConvInv => q{
+            my @vals = ($val =~ /(\d{1,2})/g);
+            push @vals, 0 if @vals < 7;
+            join(' ', map(hex, @vals));
+        },
+    },
+    28 => {
+        Name => 'PreviewImageStart',
+        Format => 'int16u',
+        Flags => 'IsOffset',
+        OffsetPair => 30,   # associated byte count tagID
+        DataTag => 'PreviewImage',
+        Protected => 2,
+    },
+    30 => {
+        Name => 'PreviewImageLength',
+        Format => 'int16u',
+        OffsetPair => 28,   # point to associated offset
+        DataTag => 'PreviewImage',
+        Protected => 2,
+    },
+    32 => {
+        Name => 'FlashMode',
+        PrintConv => {
+            0 => 'Off',
+            1 => 'Auto', #PH
+            2 => 'On',
+        },
+    },
+    33 => {
+        Name => 'Macro',
+        PrintConv => { 0 => 'Off', 1 => 'On' },
+    },
+    34 => {
+        Name => 'Sharpness',
+        PrintConv => {
+            0 => 'Sharp',
+            1 => 'Normal',
+            2 => 'Soft',
+        },
+    },
+    38 => {
+        Name => 'WhiteBalance',
+        PrintConv => {
+            0 => 'Auto',
+            1 => 'Daylight',
+            2 => 'Cloudy',
+            3 => 'Tungsten',
+            4 => 'Fluorescent',
+            # 5 (One Pushes, flake setting?)
+            # 7 (details setting?)
+        },
+    },
+    39 => {
+        Name => 'ISOSetting',
+        PrintConv => {
+            0 => 'Auto',
+            1 => 64,
+            2 => 100,
+            4 => 200,
+            6 => 400,
+            7 => 800,
+            8 => 1600,
+        },
+    },
+    40 => {
+        Name => 'Saturation',
+        PrintConv => {
+            0 => 'High',
+            1 => 'Normal',
+            2 => 'Low',
+            3 => 'None (B&W)',
+        },
+    },
+);
+
+# NOTE: this subdir is not currently writable because the offsets would require
+# special code to handle the funny start location and base offset
+%Image::ExifTool::Ricoh::Subdir = (
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
+    WRITE_PROC => \&Image::ExifTool::Exif::WriteExif,
+    CHECK_PROC => \&Image::ExifTool::Exif::CheckExif,
+    0x0004 => { Name => 'RicohDateTime1', Groups => { 2 => 'Time' } }, #PH
+    0x0005 => { Name => 'RicohDateTime2', Groups => { 2 => 'Time' } }, #PH
+    # 0x000E ProductionNumber? (ref 2)
+);
+
+# Ricoh text-type maker notes (PH)
+%Image::ExifTool::Ricoh::Text = (
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
+    PROCESS_PROC => \&ProcessRicohText,
+    NOTES => q{
+        Ricoh RDC models such as the RDC-i700, RDC-5000, RDC-6000, RDC-7 and
+        RDC-4300 use a text-based format for their maker notes instead of the IFD
+        format used by the Caplio models.  Below is a list of known tags in this
+        information.
+    },
+    Rev => 'Revision',
+    Rv => 'Revision',
+    Rg => 'RedGain',
+    Gg => 'GreenGain',
+    Bg => 'BlueGain',
+);
+
+%Image::ExifTool::Ricoh::RMETA = (
+    GROUPS => { 0 => 'APP5', 1 => 'RMETA', 2 => 'Image' },
+    PROCESS_PROC => \&Image::ExifTool::Ricoh::ProcessRicohRMETA,
+    NOTES => q{
+        The Ricoh Caplio Pro G3 has the ability to add custom fields to the APP5
+        "RMETA" segment of JPEG images.  While only a few observed tags have been
+        defined below, ExifTool will extract any information found here.
+    },
+    'Sign type' => { Name => 'SignType', PrintConv => {
+        1 => 'Directional',
+        2 => 'Warning',
+        3 => 'Information',
+    } },
+    Location => { PrintConv => {
+        1 => 'Verge',
+        2 => 'Gantry',
+        3 => 'Central reservation',
+        4 => 'Roundabout',
+    } },
+    Lit => { PrintConv => {
+        1 => 'Yes',
+        2 => 'No',
+    } },
+    Condition => { PrintConv => {
+        1 => 'Good',
+        2 => 'Fair',
+        3 => 'Poor',
+        4 => 'Damaged',
+    } },
+    Azimuth => { PrintConv => {
+        1 => 'N',
+        2 => 'NNE',
+        3 => 'NE',
+        4 => 'ENE',
+        5 => 'E',
+        6 => 'ESE',
+        7 => 'SE',
+        8 => 'SSE',
+        9 => 'S',
+        10 => 'SSW',
+        11 => 'SW',
+        12 => 'WSW',
+        13 => 'W',
+        14 => 'WNW',
+        15 => 'NW',
+        16 => 'NNW',
+    } },
+);
+
+#------------------------------------------------------------------------------
+# Process Ricoh text-based maker notes
+# Inputs: 0) ExifTool object reference
+#         1) Reference to directory information hash
+#         2) Pointer to tag table for this directory
+# Returns: 1 on success, otherwise returns 0 and sets a Warning
+sub ProcessRicohText($$$)
+{
+    my ($exifTool, $dirInfo, $tagTablePtr) = @_;
+    my $dataPt = $$dirInfo{DataPt};
+    my $dataLen = $$dirInfo{DataLen};
+    my $dirStart = $$dirInfo{DirStart} || 0;
+    my $dirLen = $$dirInfo{DirLen} || $dataLen - $dirStart;
+    my $verbose = $exifTool->Options('Verbose');
+
+    my $data = substr($$dataPt, $dirStart, $dirLen);
+    return 1 if $data =~ /^\0/;     # blank Ricoh maker notes
+    # validate text maker notes
+    unless ($data =~ /^(Rev|Rv)/) {
+        $exifTool->Warn('Bad Ricoh maker notes');
+        return 0;
+    }
+    my $pos = 0;
+    while ($data =~ m/([A-Z][a-z]{1,2})([0-9A-F]+);/sg) {
+        my $tag = $1;
+        my $val = $2;
+        my $tagInfo = $exifTool->GetTagInfo($tagTablePtr, $tag);
+        if ($verbose) {
+            $exifTool->VerboseInfo($tag, $tagInfo,
+                Table  => $tagTablePtr,
+                Value  => $val,
+            );
+        }
+        unless ($tagInfo) {
+            next unless $exifTool->{OPTIONS}->{Unknown};
+            $tagInfo = {
+                Name => "Ricoh_Text_$tag",
+                Unknown => 1,
+                PrintConv => 'length($val) > 60 ? substr($val,0,55) . "[...]" : $val',
+            };
+            # add tag information to table
+            Image::ExifTool::AddTagToTable($tagTablePtr, $tag, $tagInfo);
+        }
+        $exifTool->FoundTag($tagInfo, $val);
+    }
+    return 1;
+}
+
+#------------------------------------------------------------------------------
+# Process Ricoh APP5 RMETA information
+# Inputs: 0) ExifTool object reference
+#         1) Reference to directory information hash
+#         2) Pointer to tag table for this directory
+# Returns: 1 on success, otherwise returns 0 and sets a Warning
+sub ProcessRicohRMETA($$$)
+{
+    my ($exifTool, $dirInfo, $tagTablePtr) = @_;
+    my $dataPt = $$dirInfo{DataPt};
+    my $dirStart = $$dirInfo{DirStart};
+    my $dataLen = length($$dataPt);
+    my $verbose = $exifTool->Options('Verbose');
+
+    $dataLen > 6 or $exifTool->Warn('Truncated Ricoh RMETA data'), return 0;
+    my $byteOrder = substr($$dataPt, $dirStart, 2);
+    SetByteOrder($byteOrder) or $exifTool->Warn('Bad Ricoh RMETA data'), return 0;
+    my (@tags, @vals, @nums, $valPos);
+    my $pos = $dirStart + 6;
+    while ($pos <= $dataLen - 4) {
+        my $type = Get16u($dataPt, $pos);
+        my $size = Get16u($dataPt, $pos + 2);
+        $pos += 4;
+        $size -= 2;
+        if ($size < 0 or $pos + $size > $dataLen) {
+            $exifTool->Warn('Corrupted Ricoh RMETA data');
+            last;
+        }
+        if ($type eq 1) {
+            # save the tag names
+            my $tags = substr($$dataPt, $pos, $size);
+            $tags =~ s/\0+$//;  # remove trailing nulls
+            @tags = split /\0/, $tags;
+        } elsif ($type eq 2) {
+            # save the ASCII tag values
+            my $vals = substr($$dataPt, $pos, $size);
+            $vals =~ s/\0+$//;
+            @vals = split /\0/, $vals;
+            $valPos = $pos; # save position of first ASCII value
+        } elsif ($type eq 3) {
+            # save the numerical tag values
+            my $nums = substr($$dataPt, $pos, $size);
+            @nums = unpack($byteOrder eq 'MM' ? 'n*' : 'v*', $nums);
+        } elsif ($type eq 0) {
+            $pos += 2;  # why 2 extra bytes?
+        }
+        $pos += $size;
+    }
+    if (@tags or @vals) {
+        if (@tags != @vals) {
+            my ($nt, $nv) = (scalar(@tags), scalar(@vals));
+            $exifTool->Warn("Number of tags ($nt) and values ($nv) differs in Ricoh RMETA");
+        }
+        # find next tag in null-delimited list
+        # unpack numerical values from block of int16u values
+        my ($tag, $name, $val);
+        foreach $tag (@tags) {
+            $val = shift @vals;
+            last unless defined $val;
+            ($name = $tag) =~ s/\b([a-z])/\U$1/gs;  # make capitalize all words
+            $name =~ s/ (\w)/\U$1/g;                # remove special characters
+            $name = 'RMETA_Unknown' unless length($name);
+            my $num = shift @nums;
+            my $tagInfo = $exifTool->GetTagInfo($tagTablePtr, $tag);
+            if ($tagInfo) {
+                # make sure print conversion is defined
+                $$tagInfo{PrintConv} = { } unless $$tagInfo{PrintConv};
+            } else {
+                # create tagInfo hash
+                $tagInfo = { Name => $name, PrintConv => { } };
+                Image::ExifTool::AddTagToTable($tagTablePtr, $tag, $tagInfo);
+            }
+            # use string value directly if no numerical value
+            $num = $val unless defined $num;
+            # add conversion for this value (replacing any existing entry)
+            $tagInfo->{PrintConv}->{$num} = $val;
+            if ($verbose) {
+                $exifTool->VerboseInfo($tag, $tagInfo,
+                    Table   => $tagTablePtr,
+                    Value   => $num,
+                    DataPt  => $dataPt,
+                    DataPos => $$dirInfo{DataPos},
+                    Start   => $valPos,
+                    Size    => length($val),
+                );
+            }
+            $exifTool->FoundTag($tagInfo, $num);
+            $valPos += length($val) + 1;
+        }
+    }
+    return 1;
+}
+
+1;  # end
+
+__END__
+
+=head1 NAME
+
+Image::ExifTool::Ricoh - Ricoh EXIF maker notes tags
+
+=head1 SYNOPSIS
+
+This module is loaded automatically by Image::ExifTool when required.
+
+=head1 DESCRIPTION
+
+This module contains definitions required by Image::ExifTool to
+interpret Ricoh maker notes EXIF meta information.
+
+=head1 AUTHOR
+
+Copyright 2003-2007, Phil Harvey (phil at owl.phy.queensu.ca)
+
+This library is free software; you can redistribute it and/or modify it
+under the same terms as Perl itself.
+
+=head1 REFERENCES
+
+=over 4
+
+=item L<http://www.ozhiker.com/electronics/pjmt/jpeg_info/ricoh_mn.html>
+
+=back
+
+=head1 SEE ALSO
+
+L<Image::ExifTool::TagNames/Ricoh Tags>,
+L<Image::ExifTool(3pm)|Image::ExifTool>
+
+=cut
Index: /gsdl/trunk/perllib/cpan/Image/ExifTool/Sanyo.pm
===================================================================
--- /gsdl/trunk/perllib/cpan/Image/ExifTool/Sanyo.pm	(revision 16842)
+++ /gsdl/trunk/perllib/cpan/Image/ExifTool/Sanyo.pm	(revision 16842)
@@ -0,0 +1,377 @@
+#------------------------------------------------------------------------------
+# File:         Sanyo.pm
+#
+# Description:  Sanyo EXIF maker notes tags
+#
+# Revisions:    04/06/2004  - P. Harvey Created
+#
+# Reference:    http://www.exif.org/makernotes/SanyoMakerNote.html
+#------------------------------------------------------------------------------
+
+package Image::ExifTool::Sanyo;
+
+use strict;
+use vars qw($VERSION);
+use Image::ExifTool::Exif;
+
+$VERSION = '1.10';
+
+my %offOn = (
+    0 => 'Off',
+    1 => 'On',
+);
+
+%Image::ExifTool::Sanyo::Main = (
+    WRITE_PROC => \&Image::ExifTool::Exif::WriteExif,
+    CHECK_PROC => \&Image::ExifTool::Exif::CheckExif,
+    WRITABLE => 1,
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
+    0x00ff => {
+        # this is an absolute offset in the JPG file... odd - PH
+        Name => 'MakerNoteOffset',
+        Writable => 'int32u',
+    },
+    0x0100 => {
+        Name => 'SanyoThumbnail',
+        Writable => 'undef',
+        WriteCheck => '$self->CheckImage(\$val)',
+        ValueConv => '$self->ValidateImage(\$val,$tag)',
+        ValueConvInv => '$val',
+    },
+    0x0200 => {
+        Name => 'SpecialMode',
+        Writable => 'int32u',
+        Count => 3,
+    },
+    0x0201 => {
+        Name => 'SanyoQuality',
+        Flags => 'PrintHex',
+        Writable => 'int16u',
+        PrintConv => {
+            0x0000 => 'Normal/Very Low',
+            0x0001 => 'Normal/Low',
+            0x0002 => 'Normal/Medium Low',
+            0x0003 => 'Normal/Medium',
+            0x0004 => 'Normal/Medium High',
+            0x0005 => 'Normal/High',
+            0x0006 => 'Normal/Very High',
+            0x0007 => 'Normal/Super High',
+            0x0100 => 'Fine/Very Low',
+            0x0101 => 'Fine/Low',
+            0x0102 => 'Fine/Medium Low',
+            0x0103 => 'Fine/Medium',
+            0x0104 => 'Fine/Medium High',
+            0x0105 => 'Fine/High',
+            0x0106 => 'Fine/Very High',
+            0x0107 => 'Fine/Super High',
+            0x0200 => 'Super Fine/Very Low',
+            0x0201 => 'Super Fine/Low',
+            0x0202 => 'Super Fine/Medium Low',
+            0x0203 => 'Super Fine/Medium',
+            0x0204 => 'Super Fine/Medium High',
+            0x0205 => 'Super Fine/High',
+            0x0206 => 'Super Fine/Very High',
+            0x0207 => 'Super Fine/Super High',
+        },
+    },
+    0x0202 => {
+        Name => 'Macro',
+        Writable => 'int16u',
+        PrintConv => {
+            0 => 'Normal',
+            1 => 'Macro',
+            2 => 'View',
+            3 => 'Manual',
+        },
+    },
+    0x0204 => {
+        Name => 'DigitalZoom',
+        Writable => 'rational64u',
+    },
+    0x0207 => 'SoftwareVersion',
+    0x0208 => 'PictInfo',
+    0x0209 => 'CameraID',
+    0x020e => {
+        Name => 'SequentialShot',
+        Writable => 'int16u',
+        PrintConv => {
+            0 => 'None',
+            1 => 'Standard',
+            2 => 'Best',
+            3 => 'Adjust Exposure',
+        },
+    },
+    0x020f => {
+        Name => 'WideRange',
+        Writable => 'int16u',
+        PrintConv => \%offOn,
+    },
+    0x0210 => {
+        Name => 'ColorAdjustmentMode',
+        Writable => 'int16u',
+        PrintConv => \%offOn,
+    },
+    0x0213 => {
+        Name => 'QuickShot',
+        Writable => 'int16u',
+        PrintConv => \%offOn,
+    },
+    0x0214 => {
+        Name => 'SelfTimer',
+        Writable => 'int16u',
+        PrintConv => \%offOn,
+    },
+    0x0216 => {
+        Name => 'VoiceMemo',
+        Writable => 'int16u',
+        PrintConv => \%offOn,
+    },
+    0x0217 => {
+        Name => 'RecordShutterRelease',
+        Writable => 'int16u',
+        PrintConv => {
+            0 => 'Record while down',
+            1 => 'Press start, press stop',
+        },
+    },
+    0x0218 => {
+        Name => 'FlickerReduce',
+        Writable => 'int16u',
+        PrintConv => \%offOn,
+    },
+    0x0219 => {
+        Name => 'OpticalZoomOn',
+        Writable => 'int16u',
+        PrintConv => \%offOn,
+    },
+    0x021b => {
+        Name => 'DigitalZoomOn',
+        Writable => 'int16u',
+        PrintConv => \%offOn,
+    },
+    0x021d => {
+        Name => 'LightSourceSpecial',
+        Writable => 'int16u',
+        PrintConv => \%offOn,
+    },
+    0x021e => {
+        Name => 'Resaved',
+        Writable => 'int16u',
+        PrintConv => {
+            0 => 'No',
+            1 => 'Yes',
+        },
+    },
+    0x021f => {
+        Name => 'SceneSelect',
+        Writable => 'int16u',
+        PrintConv => {
+            0 => 'Off',
+            1 => 'Sport',
+            2 => 'TV',
+            3 => 'Night',
+            4 => 'User 1',
+            5 => 'User 2',
+            6 => 'Lamp', #PH
+        },
+    },
+    0x0223 => {
+        Name => 'ManualFocusDistance',
+        Writable => 'rational64u',
+    },
+    0x0224 => {
+        Name => 'SequenceShotInterval',
+        Writable => 'int16u',
+        PrintConv => {
+            0 => '5 frames/sec',
+            1 => '10 frames/sec',
+            2 => '15 frames/sec',
+            3 => '20 frames/sec',
+        },
+    },
+    0x0225 => {
+        Name => 'FlashMode',
+        Writable => 'int16u',
+        PrintConv => {
+            0 => 'Auto',
+            1 => 'Force',
+            2 => 'Disabled',
+            3 => 'Red eye',
+        },
+    },
+    0x0e00 => {
+        Name => 'PrintIM',
+        Description => 'Print Image Matching',
+        Writable => 0,
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::PrintIM::Main',
+        },
+    },
+    0x0f00 => {
+        Name => 'DataDump',
+        Writable => 0,
+        Binary => 1,
+    },
+);
+
+# tags in Sanyo MOV videos (PH - observations from an E6 sample)
+# (note: very similar to information in Nikon/Pentax videos)
+%Image::ExifTool::Sanyo::MOV = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
+    NOTES => q{
+        This information is found in Sanyo MOV video images, and is very similar to
+        information found in Pentax MOV and Nikon QT videos.
+    },
+    0x00 => {
+        Name => 'Make',
+        Format => 'string[5]',
+        PrintConv => 'ucfirst(lc($val))',
+    },
+    0x18 => {
+        Name => 'Model',
+        Description => 'Camera Model Name',
+        Format => 'string[8]',
+    },
+    0x26 => {
+        Name => 'ExposureTime',
+        Format => 'int32u',
+        ValueConv => '$val ? 10 / $val : 0',
+        PrintConv => 'Image::ExifTool::Exif::PrintExposureTime($val)',
+    },
+    0x2a => {
+        Name => 'FNumber',
+        Format => 'int32u',
+        ValueConv => '$val * 0.1',
+        PrintConv => 'sprintf("%.1f",$val)',
+    },
+    0x32 => {
+        Name => 'ExposureCompensation',
+        Format => 'int32s',
+        ValueConv => '$val * 0.1',
+        PrintConv => 'Image::ExifTool::Exif::ConvertFraction($val)',
+    },
+    0x44 => {
+        Name => 'WhiteBalance',
+        Format => 'int16u',
+        PrintConv => {
+            0 => 'Auto',
+            1 => 'Daylight',
+            2 => 'Shade',
+            3 => 'Fluorescent', #2
+            4 => 'Tungsten',
+            5 => 'Manual',
+        },
+    },
+    0x48 => {
+        Name => 'FocalLength',
+        Writable => 'int32u',
+        ValueConv => '$val * 0.1',
+        PrintConv => 'sprintf("%.1fmm",$val)',
+    },
+);
+
+# tags in Sanyo MP4 videos (PH - from C4, C5 and HD1A samples)
+# (there is still a lot more information here that could be decoded!)
+%Image::ExifTool::Sanyo::MP4 = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
+    NOTES => 'This information is found in Sanyo MP4 video images.',
+    0x00 => {
+        Name => 'Make',
+        Format => 'string[5]',
+        PrintConv => 'ucfirst(lc($val))',
+    },
+    0x18 => {
+        Name => 'Model',
+        Description => 'Camera Model Name',
+        Format => 'string[8]',
+    },
+    # (0x2e has values 0x31, 0x33 and 0x3c in my samples, but
+    # some of the shutter speeds should be around 1/500 or so)
+    0x32 => {
+        Name => 'FNumber',
+        Format => 'int32u',
+        ValueConv => '$val / 10',
+        PrintConv => 'sprintf("%.1f",$val)',
+    },
+    0x6a => {
+        Name => 'ISO',
+        Format => 'int32u',
+    },
+# (these tags are shifted by +1 byte for the C4 compared to the HD1A, so we can't use them)
+#     0xfe => {
+#         Name => 'ThumbnailLength',
+#         Format => 'int32u',
+#     },
+#     0x102 => {
+#         Name => 'ThumbnailOffset',
+#         IsOffset => 1,
+#         Format => 'int32u',
+#         RawConv => '$val + 0xf2',
+#     },
+    # so instead, look for the JPEG header using brute force...
+    0x800 => {
+        Name => 'ThumbnailImage',
+        Notes => 'position varies',
+        Format => 'undef[$size - 0x800]',
+        ValueConv => '$val=~s/.*(?=\xff\xd8\xff\xc4)//; $self->ValidateImage(\$val,$tag)',
+    },
+);
+
+#------------------------------------------------------------------------------
+# Patch incorrect offsets in J1, J2, J4, S1, S3 and S4 maker notes
+# Inputs: 0) valuePtr, 1) end of previous value, 2) value size, 3) tag ID, 4) write flag
+sub FixOffsets($$$$;$)
+{
+    my ($valuePtr, $valEnd, $size, $tagID, $wFlag) = @_;
+    # ignore existing offsets and calculate reasonable values instead
+    if ($tagID == 0x100) {
+        # just ignore the SanyoThumbnail when writing (pointer is garbage)
+        $_[0] = undef if $wFlag;
+    } else {
+        $_[0] = $valEnd;    # set value pointer to next logical location
+        ++$size if $size & 0x01;
+        $_[1] += $size;     # update end-of-value pointer
+    }
+}
+
+
+1;  # end
+
+__END__
+
+=head1 NAME
+
+Image::ExifTool::Sanyo - Sanyo EXIF maker notes tags
+
+=head1 SYNOPSIS
+
+This module is loaded automatically by Image::ExifTool when required.
+
+=head1 DESCRIPTION
+
+This module contains definitions required by Image::ExifTool to interpret
+Sanyo maker notes in EXIF information.
+
+=head1 AUTHOR
+
+Copyright 2003-2007, Phil Harvey (phil at owl.phy.queensu.ca)
+
+This library is free software; you can redistribute it and/or modify it
+under the same terms as Perl itself.
+
+=head1 REFERENCES
+
+=over 4
+
+=item L<http://www.exif.org/makernotes/SanyoMakerNote.html>
+
+=back
+
+=head1 SEE ALSO
+
+L<Image::ExifTool::TagNames/Sanyo Tags>,
+L<Image::ExifTool(3pm)|Image::ExifTool>
+
+=cut
Index: /gsdl/trunk/perllib/cpan/Image/ExifTool/Shift.pl
===================================================================
--- /gsdl/trunk/perllib/cpan/Image/ExifTool/Shift.pl	(revision 16842)
+++ /gsdl/trunk/perllib/cpan/Image/ExifTool/Shift.pl	(revision 16842)
@@ -0,0 +1,574 @@
+#------------------------------------------------------------------------------
+# File:         Shift.pl
+#
+# Description:  ExifTool time shifting routines
+#
+# Revisions:    10/28/2005 - P. Harvey Created
+#------------------------------------------------------------------------------
+
+package Image::ExifTool;
+
+use strict;
+
+sub ShiftTime($$$;$);
+
+#------------------------------------------------------------------------------
+# apply shift to value in new value hash
+# Inputs: 0) shift type, 1) shift string, 2) value to shift, 3) new value hash ref
+# Returns: error string or undef on success and updates value in new value hash
+sub ApplyShift($$$;$)
+{
+    my ($func, $shift, $val, $newValueHash) = @_;
+
+    # get shift direction from first character in shift string
+    $shift =~ s/^(\+|-)// or return 'Bad shift string (no sign)';
+    my $pre = $1;
+    my $dir = ($pre eq '+') ? 1 : -1;
+    my $tagInfo = $$newValueHash{TagInfo};
+    my $tag = $$tagInfo{Name};
+    my $self = $$newValueHash{Self};    # (used in eval)
+    my $shiftOffset;
+    if ($$newValueHash{ShiftOffset}) {
+        $shiftOffset = $$newValueHash{ShiftOffset};
+    } else {
+        $shiftOffset = $$newValueHash{ShiftOffset} = { };
+    }
+
+    # initialize handler for eval warnings
+    local $SIG{'__WARN__'} = \&SetWarning;
+    SetWarning(undef);
+
+    # shift is applied to ValueConv value, so we must ValueConv-Shift-ValueConvInv
+    my ($type, $err);
+    foreach $type ('ValueConv','Shift','ValueConvInv') {
+        if ($type eq 'Shift') {
+            #### eval ShiftXxx function
+            $err = eval "Shift$func(\$val, \$shift, \$dir, \$shiftOffset)";
+        } elsif ($$tagInfo{$type}) {
+            my $conv = $$tagInfo{$type};
+            if (ref $conv eq 'CODE') {
+                $val = &$conv($val, $self);
+            } else {
+                return "Can't handle $type for $tag in ApplyShift()" if ref $$tagInfo{$type};
+                #### eval ValueConv/ValueConvInv ($val, $self)
+                $val = eval $$tagInfo{$type};
+            }
+        } else {
+            next;
+        }
+        # handle errors
+        $err and return $err;
+        $@ and SetWarning($@);
+        GetWarning() and return CleanWarning();
+    }
+    # update value in new value hash
+    $newValueHash->{Value} = [ $val ];
+    return undef;   # success
+}
+
+#------------------------------------------------------------------------------
+# Check date/time shift
+# Inputs: 0) shift type, 1) shift string (without sign)
+# Returns: updated shift string, or undef on error (and may update shift)
+sub CheckShift($$)
+{
+    my ($type, $shift) = @_;
+    my $err;
+    if ($type eq 'Time') {
+        return "No shift direction" unless $shift =~ s/^(\+|-)//;
+        # do a test shift to validate the shift string
+        my $testTime = '2005:11:02 09:00:13.25-04:00';
+        $err = ShiftTime($testTime, $shift, $1 eq '+' ? 1 : -1);
+    } else {
+        $err = "Unknown shift type ($type)";
+    }
+    return $err;
+}
+
+#------------------------------------------------------------------------------
+# return the number of days in a month
+# Inputs: 0) month number (Jan=1, may be outside range), 1) year
+# Returns: number of days in month
+sub DaysInMonth($$)
+{
+    my ($mon, $year) = @_;
+    my @days = (31,28,31,30,31,30,31,31,30,31,30,31);
+    # adjust to the range [0,11]
+    while ($mon < 1)  { $mon += 12; --$year; }
+    while ($mon > 12) { $mon -= 12; ++$year; }
+    # return standard number of days unless february on a leap year
+    return $days[$mon-1] unless $mon == 2 and not $year % 4;
+    # leap years don't occur on even centuries except every 400 years
+    return 29 if $year % 100 or not $year % 400;
+    return 28;
+}
+
+#------------------------------------------------------------------------------
+# split times into corresponding components: YYYY MM DD hh mm ss tzh tzm
+# Inputs: 0) date/time or shift string 1) reference to list for returned components
+#         2) optional reference to list of time components (if shift string)
+# Returns: true on success
+# Returned components are 0-Y, 1-M, 2-D, 3-hr, 4-min, 5-sec, 6-tzhr, 7-tzmin
+sub SplitTime($$;$)
+{
+    my ($val, $vals, $time) = @_;
+    # insert zeros if missing in shift string
+    if ($time) {
+        $val =~ s/(^|[-+:\s]):/${1}0:/g;
+        $val =~ s/:([:\s]|$)/:0$1/g;
+    }
+    # change dashes to colons in date (for XMP dates)
+    if ($val =~ s/^(\d{4})-(\d{2})-(\d{2})/$1:$2:$3/) {
+        $val =~ tr/T/ /;    # change 'T' separator to ' '
+    }
+    # add space before timezone to split it into a separate word
+    $val =~ s/(\+|-)/ $1/;
+    my @words = split ' ', $val;
+    my $err = 1;
+    my @v;
+    for (;;) {
+        my $word = shift @words;
+        last unless defined $word;
+        # split word into separate numbers (allow decimal points but no signs)
+        my @vals = $word =~ /(?=\d|\.\d)\d*(?:\.\d*)?/g or last;
+        if ($word =~ /^(\+|-)/) {
+            # this is the timezone
+            (defined $v[6] or @vals > 2) and $err = 1, last;
+            my $sign = ($1 ne '-') ? 1 : -1;
+            # apply sign to both minutes and seconds
+            $v[6] = $sign * shift(@vals);
+            $v[7] = $sign * (shift(@vals) || 0);
+        } elsif ((@words and $words[0] =~ /^\d+/) or # there is a time word to follow
+            (not $time and $vals[0] =~ /^\d{3}/) or # first value is year (3 or more digits)
+            ($time and not defined $$time[3] and not defined $v[0])) # we don't have a time
+        {
+            # this is a date (must come first)
+            (@v or @vals > 3) and $err = 1, last;
+            not $time and @vals != 3 and $err = 1, last;
+            $v[2] = pop(@vals);     # take day first if only one specified
+            $v[1] = pop(@vals) || 0;
+            $v[0] = pop(@vals) || 0;
+        } else {
+            # this is a time (can't come after timezone)
+            (defined $v[3] or defined $v[6] or @vals > 3) and $err = 1, last;
+            not $time and @vals != 3 and $err = 1, last;
+            $v[3] = shift(@vals);   # take hour first if only one specified
+            $v[4] = shift(@vals) || 0;
+            $v[5] = shift(@vals) || 0;
+        }
+        $err = 0;
+    }
+    return 0 if $err or not @v;
+    if ($time) {
+        # zero any required shift entries which aren't yet defined
+        $v[0] = $v[1] = $v[2] = 0 if defined $$time[0] and not defined $v[0];
+        $v[3] = $v[4] = $v[5] = 0 if defined $$time[3] and not defined $v[3];
+        $v[6] = $v[7] = 0 if defined $$time[6] and not defined $v[6];
+    }
+    @$vals = @v;    # return split time components
+    return 1;
+}
+
+#------------------------------------------------------------------------------
+# shift date/time by components
+# Inputs: 0) split date/time list ref, 1) split shift list ref,
+#         2) shift direction, 3) reference to output list of shifted components
+#         4) number of decimal points in seconds
+# Returns: error string or undef on success
+sub ShiftComponents($$$$$)
+{
+    my ($time, $shift, $dir, $toTime, $dec) = @_;
+    # min/max for Y, M, D, h, m, s
+    my @min = (    0, 1, 1, 0, 0, 0);
+    my @max = (10000,12,28,24,60,60);
+    my $i;
+#
+# apply the shift
+#
+    my $c = 0;
+    for ($i=0; $i<@$time; ++$i) {
+        my $v = ($$time[$i] || 0) + $dir * ($$shift[$i] || 0) + $c;
+        # handle fractional values by propagating remainders downwards
+        if ($v != int($v) and $i < 5) {
+            my $iv = int($v);
+            $c = ($v - $iv) * $max[$i+1];
+            $v = $iv;
+        } else {
+            $c = 0;
+        }
+        $$toTime[$i] = $v;
+    }
+    # round off seconds to the required number of decimal points
+    my $sec = $$toTime[5];
+    if (defined $sec and $sec != int($sec)) {
+        my $mult = 10 ** $dec;
+        $$toTime[5] = int($sec * $mult + 0.5 * ($sec <=> 0)) / $mult;
+    }
+#
+# handle overflows, starting with least significant number first (seconds)
+#
+    $c = 0;
+    for ($i=5; $i>=0; $i--) {
+        defined $$time[$i] or $c = 0, next;
+        # apply shift and adjust for previous overflow
+        my $v = $$toTime[$i] + $c;
+        $c = 0; # set carry to zero
+        # adjust for over/underflow
+        my ($min, $max) = ($min[$i], $max[$i]);
+        if ($v < $min) {
+            if ($i == 2) {  # 2 = day of month
+                do {
+                    # add number of days in previous month
+                    --$c;
+                    my $mon = $$toTime[$i-1] + $c;
+                    $v += DaysInMonth($mon, $$toTime[$i-2]);
+                } while ($v < 1);
+            } else {
+                my $fc = ($v - $min) / $max;
+                # carry ($c) must be largest integer equal to or less than $fc
+                $c = int($fc);
+                --$c if $c > $fc;
+                $v -= $c * $max;
+            }
+        } elsif ($v >= $max + $min) {
+            if ($i == 2) {
+                for (;;) {
+                    # test against number of days in current month
+                    my $mon = $$toTime[$i-1] + $c;
+                    my $days = DaysInMonth($mon, $$toTime[$i-2]);
+                    last if $v <= $days;
+                    $v -= $days;
+                    ++$c;
+                    last if $v <= 28;
+                }
+            } else {
+                my $fc = ($v - $max - $min) / $max;
+                # carry ($c) must be smallest integer greater than $fc
+                $c = int($fc);
+                ++$c if $c <= $fc;
+                $v -= $c * $max;
+            }
+        }
+        $$toTime[$i] = $v;  # save the new value
+    }
+    # handle overflows in timezone
+    if (defined $$toTime[6]) {
+        my $m = $$toTime[6] * 60 + $$toTime[7];
+        $m += 0.5 * ($m <=> 0);     # avoid round-off errors
+        $$toTime[6] = int($m / 60);
+        $$toTime[7] = int($m - $$toTime[6] * 60);
+    }
+    return undef;   # success
+}
+
+#------------------------------------------------------------------------------
+# Shift date/time string
+# Inputs: 0) date/time string, 1) shift string, 2) shift direction (+1 or -1)
+#         3) reference to ShiftOffset hash (with Date, DateTime, Time, Timezone keys)
+# Returns: error string or undef on success and date/time string is updated
+sub ShiftTime($$$;$)
+{
+    local $_;
+    my ($val, $shift, $dir, $shiftOffset) = @_;
+    my (@time, @shift, @toTime, $mode, $needShiftOffset, $dec);
+#
+# figure out what we are dealing with (time, date or date/time)
+#
+    SplitTime($val, \@time) or return "Invalid time string ($val)";
+    if (defined $time[0]) {
+        $mode = defined $time[3] ? 'DateTime' : 'Date';
+    } elsif (defined $time[3]) {
+        $mode = 'Time';
+    }
+    # get number of digits after the seconds decimal point
+    if (defined $time[5] and $time[5] =~ /\.(\d+)/) {
+        $dec = length($1);
+    } else {
+        $dec = 0;
+    }
+    if ($shiftOffset) {
+        $needShiftOffset = 1 unless defined $$shiftOffset{$mode};
+        $needShiftOffset = 1 if defined $time[6] and not defined $$shiftOffset{Timezone};
+    } else {
+        $needShiftOffset = 1;
+    }
+    if ($needShiftOffset) {
+#
+# apply date/time shift the hard way
+#
+        SplitTime($shift, \@shift, \@time) or return "Invalid shift string ($shift)";
+
+        # change 'Z' timezone to '+00:00' only if necessary
+        if (@shift > 6 and @time <= 6) {
+            $time[6] = $time[7] = 0 if $val =~ s/Z$/\+00:00/;
+        }
+        my $err = ShiftComponents(\@time, \@shift, $dir, \@toTime, $dec);
+        $err and return $err;
+#
+# calculate and save the shift offsets for next time
+#
+        if ($shiftOffset) {
+            if (defined $time[0] or defined $time[3]) {
+                my @tm1 = (0, 0, 0, 1, 0, 2000);
+                my @tm2 = (0, 0, 0, 1, 0, 2000);
+                if (defined $time[0]) {
+                    @tm1[3..5] = reverse @time[0..2];
+                    @tm2[3..5] = reverse @toTime[0..2];
+                    --$tm1[4]; # month should start from 0
+                    --$tm2[4];
+                }
+                my $diff = 0;
+                if (defined $time[3]) {
+                    @tm1[0..2] = reverse @time[3..5];
+                    @tm2[0..2] = reverse @toTime[3..5];
+                    # handle fractional seconds separately
+                    $diff = $tm2[0] - int($tm2[0]) - ($tm1[0] - int($tm1[0]));
+                    $tm1[0] = int($tm1[0]);
+                    $tm2[0] = int($tm2[0]);
+                }
+                eval q{
+                    require Time::Local;
+                    $diff += Time::Local::timegm(@tm2) - Time::Local::timegm(@tm1);
+                };
+                # not a problem if we failed here since we'll just try again next time,
+                # so don't return error message
+                unless (@$) {
+                    my $mode;
+                    if (defined $time[0]) {
+                        $mode = defined $time[3] ? 'DateTime' : 'Date';
+                    } else {
+                        $mode = 'Time';
+                    }
+                    $$shiftOffset{$mode} = $diff;
+                }
+            }
+            if (defined $time[6]) {
+                $$shiftOffset{Timezone} = ($toTime[6] - $time[6]) * 60 +
+                                           $toTime[7] - $time[7];
+            }
+        }
+
+    } else {
+#
+# apply shift from previously calculated offsets
+#
+        if ($$shiftOffset{Timezone} and @time <= 6) {
+            # change 'Z' timezone to '+00:00' only if necessary
+            $time[6] = $time[7] = 0 if $val =~ s/Z$/\+00:00/;
+        }
+        # apply the previous date/time shift if necessary
+        if ($mode) {
+            my @tm = (0, 0, 0, 1, 0, 2000);
+            if (defined $time[0]) {
+                @tm[3..5] = reverse @time[0..2];
+                --$tm[4]; # month should start from 0
+            }
+            @tm[0..2] = reverse @time[3..5] if defined $time[3];
+            # save fractional seconds
+            my $frac = $tm[0] - int($tm[0]);
+            $tm[0] = int($tm[0]);
+            my $tm;
+            eval q{
+                require Time::Local;
+                $tm = Time::Local::timegm(@tm) + $frac;
+            };
+            $@ and return CleanWarning($@);
+            $tm += $$shiftOffset{$mode};    # apply the shift
+            $tm < 0 and return 'Shift results in negative time';
+            # strip off fractional seconds after rounding to desired precision
+            # (only round if the result isn't integral)
+            $frac = $tm - int($tm);
+            if ($frac) {
+                # round off to required number of decimal points
+                my $tmf = $tm + 0.5 * 10 ** (-$dec);
+                $tm = int($tmf);
+                $frac = $tm - $tmf;
+            }
+            @tm = gmtime($tm);
+            @toTime = reverse @tm[0..5];
+            $toTime[0] += 1900;
+            ++$toTime[1];
+            $toTime[5] += $frac;    # add the fractional seconds back in
+        }
+        # apply the previous timezone shift if necessary
+        if (defined $time[6]) {
+            my $m = $time[6] * 60 + $time[7];
+            $m += $$shiftOffset{Timezone};
+            $m += 0.5 * ($m <=> 0);     # avoid round-off errors
+            $toTime[6] = int($m / 60);
+            $toTime[7] = int($m - $toTime[6] * 60);
+        }
+    }
+#
+# insert shifted time components back into original string
+#
+    my ($i, $err);
+    for ($i=0; $i<@toTime; ++$i) {
+        next unless defined $time[$i] and defined $toTime[$i];
+        my ($v, $d, $s);
+        if ($i != 6) {  # not timezone hours
+            last unless $val =~ /((?=\d|\.\d)\d*(\.\d*)?)/g;
+            next if $toTime[$i] == $time[$i];
+            $v = $1;    # value
+            $d = $2;    # decimal part of value
+            $s = '';    # no sign
+        } else {
+            last if $time[$i] == $toTime[$i] and $time[$i+1] == $toTime[$i+1];
+            last unless $val =~ /((?:\+|-)(?=\d|\.\d)\d*(\.\d*)?)/g;
+            $v = $1;
+            $d = $2;
+            if ($toTime[6] >= 0 and $toTime[7] >= 0) {
+                $s = '+';
+            } else {
+                $s = '-';
+                $toTime[6] = -$toTime[6];
+                $toTime[7] = -$toTime[7];
+            }
+        }
+        my $nv = $toTime[$i];
+        my $pos = pos $val;
+        my $len = length $v;
+        my $sig = $len - length $s;
+        my $dec = $d ? length($d) - 1 : 0;
+        my $newNum = sprintf($dec ? "$s%0$sig.${dec}f" : "$s%0${sig}d", $nv);
+        length($newNum) != $len and $err = 1;
+        substr($val, $pos - $len, $len) = $newNum;
+        pos($val) = $pos;
+    }
+    $err and return "Error packing shifted time ($val)";
+    $_[0] = $val;   # return shifted value
+    return undef;   # success!
+}
+
+
+1; # end
+
+__END__
+
+=head1 NAME
+
+Image::ExifTool::Shift.pl - ExifTool time shifting routines
+
+=head1 DESCRIPTION
+
+This module contains routines used by ExifTool to shift date and time
+values.
+
+=head1 DETAILS
+
+Time shifts are applied to standard EXIF-formatted date/time values (ie.
+C<2005:03:14 18:55:00>).  Date-only and time-only values may also be
+shifted, and an optional timezone (ie. C<-05:00>) is also supported.  Here
+are some general rules and examples to explain how shift strings are
+interpreted:
+
+Date-only values are shifted using the following formats:
+
+    'Y:M:D'     - shift date by 'Y' years, 'M' months and 'D' days
+    'M:D'       - shift months and days only
+    'D'         - shift specified number of days
+
+Time-only values are shifted using the following formats:
+
+    'h:m:s'     - shift time by 'h' hours, 'm' minutes and 's' seconds
+    'h:m'       - shift hours and minutes only
+    'h'         - shift specified number of hours
+
+Timezone shifts are specified in the following formats:
+
+    '+h:m'      - shift timezone by 'h' hours and 'm' minutes
+    '-h:m'      - negative shift of timezone hours and minutes
+    '+h'        - shift timezone hours only
+    '-h'        - negative shift of timezone hours only
+
+A valid shift value consists of one or two arguments, separated by a space.
+If only one is provided, it is assumed to be a time shift when applied to a
+time-only or a date/time value, or a date shift when applied to a date-only
+value.  For example:
+
+    '7'         - shift by 1 hour if applied to a time or date/time
+                  value, or by one day if applied to a date value
+    '2:0'       - shift 2 hours (time, date/time), or 2 months (date)
+    '5:0:0'     - shift 5 hours (time, date/time), or 5 years (date)
+    '0:0:1'     - shift 1 sec (time, date/time), or 1 day (date)
+
+If two arguments are given, the date shift is first, followed by the time
+shift:
+
+    '3:0:0 0'         - shift date by 3 years
+    '0 15:30'         - shift time by 15 hours and 30 minutes
+    '1:0:0 0:0:0+5:0' - shift date by 1 year and timezone by 5 hours
+
+A date shift is simply ignored if applied to a time value or visa versa.
+
+Numbers specified in shift fields may contain a decimal point:
+
+    '1.5'       - 1 hour 30 minutes (time, date/time), or 1 day (date)
+    '2.5 0'     - 2 days 12 hours (date/time), 12 hours (time) or
+                  2 days (date)
+
+And to save typing, a zero is assumed for any missing numbers:
+
+    '1::'       - shift by 1 hour (time, date/time) or 1 year (date)
+    '26:: 0'    - shift date by 26 years
+    '+:30       - shift timezone by 30 minutes
+
+Below are some specific examples applied to real date and/or time values
+('Dir' is the applied shift direction: '+' is positive, '-' is negative):
+
+     Original Value         Shift   Dir    Shifted Value
+    ---------------------  -------  ---  ---------------------
+    '20:30:00'             '5'       +   '01:30:00'
+    '2005:01:27'           '5'       +   '2005:02:01'
+    '11:54:00'             '2.5 0'   -   '23:54:00'
+    '2005:11:02'           '2.5 0'   -   '2005:10:31'
+    '2005:11:02 11:54:00'  '2.5 0'   -   '2005:10:30 23:54:00'
+    '2004:02:28 08:00:00'  '1 1.3'   +   '2004:02:29 09:18:00'
+    '07:00:00'             '-5'      +   '07:00:00'
+    '07:00:00+01:00'       '-5'      +   '07:00:00-04:00'
+    '07:00:00Z'            '+2:30'   -   '07:00:00-02:30'
+    '1970:01:01'           '35::'    +   '2005:01:01'
+    '2005:01:01'           '400'     +   '2006:02:05'
+    '10:00:00.00'          '::1.33'  +   '09:59:58.67'
+
+=head1 NOTES
+
+The format of the original date/time value is not changed when the time
+shift is applied.  This means that the length of the date/time string will
+not change, and only the numbers in the string will be modified.  The only
+exception to this rule is that a 'Z' timezone is changed to '+00:00'
+notation if a timezone shift is applied.  A timezone will not be added to
+the date/time string.
+
+=head1 TRICKY
+
+This module is perhaps more complicated than it needs to be because it is
+designed to be very flexible in the way time shifts are specified and
+applied...
+
+The ability to shift dates by Y years, M months, etc, is somewhat
+contradictory to the goal of maintaining a constant shift for all time
+values when applying a batch shift.  This is because shifting by 1 month can
+be equivalent to anything from 28 to 31 days, and 1 year can be 365 or 366
+days, depending on the starting date.
+
+The inconsistency is handled by shifting the first tag found with the actual
+specified shift, then calculating the equivalent time difference in seconds
+for this shift and applying this difference to subsequent tags in a batch
+conversion.  So if it works as designed, the behaviour should be both
+intuitive and mathematically correct, and the user shouldn't have to worry
+about details such as this (in keeping with Perl's "do the right thing"
+philosophy).
+
+=head1 AUTHOR
+
+Copyright 2003-2007, Phil Harvey (phil at owl.phy.queensu.ca)
+
+This library is free software; you can redistribute it and/or modify it
+under the same terms as Perl itself.
+
+=head1 SEE ALSO
+
+L<Image::ExifTool(3pm)|Image::ExifTool>
+
+=cut
Index: /gsdl/trunk/perllib/cpan/Image/ExifTool/Shortcuts.pm
===================================================================
--- /gsdl/trunk/perllib/cpan/Image/ExifTool/Shortcuts.pm	(revision 16842)
+++ /gsdl/trunk/perllib/cpan/Image/ExifTool/Shortcuts.pm	(revision 16842)
@@ -0,0 +1,254 @@
+#------------------------------------------------------------------------------
+# File:         Shortcuts.pm
+#
+# Description:  ExifTool shortcut tags
+#
+# Revisions:    02/07/2004 - P. Harvey Moved out of Exif.pm
+#               09/15/2004 - P. Harvey Added D70Boring from Greg Troxel
+#               01/11/2005 - P. Harvey Added Canon20D from Christian Koller
+#               03/03/2005 - P. Harvey Added user defined shortcuts
+#               03/26/2005 - P. Harvey Added Nikon from Tom Christiansen
+#------------------------------------------------------------------------------
+
+package Image::ExifTool::Shortcuts;
+
+use strict;
+use vars qw($VERSION);
+
+$VERSION = '1.16';
+
+# this is a special table used to define command-line shortcuts
+%Image::ExifTool::Shortcuts::Main = (
+    # this shortcut allows the three common date/time tags to be shifted at once
+    AllDates => [
+        'DateTimeOriginal',
+        'CreateDate',
+        'ModifyDate',
+    ],
+    # This is a shortcut to some common information which is useful in most images
+    Common => [
+        'FileName',
+        'FileSize',
+        'Model',
+        'DateTimeOriginal',
+        'ImageSize',
+        'Quality',
+        'FocalLength',
+        'ShutterSpeed',
+        'Aperture',
+        'ISO',
+        'WhiteBalance',
+        'Flash',
+    ],
+    # This shortcut provides the same information as the Canon utilities
+    Canon => [
+        'FileName',
+        'Model',
+        'DateTimeOriginal',
+        'ShootingMode',
+        'ShutterSpeed',
+        'Aperture',
+        'MeteringMode',
+        'ExposureCompensation',
+        'ISO',
+        'Lens',
+        'FocalLength',
+        'ImageSize',
+        'Quality',
+        'FlashOn',
+        'FlashType',
+        'ConditionalFEC',
+        'RedEyeReduction',
+        'ShutterCurtainHack',
+        'WhiteBalance',
+        'FocusMode',
+        'Contrast',
+        'Sharpness',
+        'Saturation',
+        'ColorTone',
+        'FileSize',
+        'FileNumber',
+        'DriveMode',
+        'OwnerName',
+        'SerialNumber',
+    ],
+    # courtesy of Christian Koller
+    Canon20D => [
+        'FileName',
+        'Model',
+        'DateTimeOriginal',
+        'ShootingMode',
+        'ShutterSpeedValue', #changed for 20D
+        'ApertureValue', #changed for 20D
+        'MeteringMode',
+        'ExposureCompensation',
+        'ISO',
+        'Lens',
+        'FocalLength',
+        #'ImageSize', #wrong in CR2
+        'ExifImageWidth', #instead
+        'ExifImageLength', #instead
+        'Quality',
+        'FlashOn',
+        'FlashType',
+        'ConditionalFEC',
+        'RedEyeReduction',
+        'ShutterCurtainHack',
+        'WhiteBalance',
+        'FocusMode',
+        'Contrast',
+        'Sharpness',
+        'Saturation',
+        'ColorTone',
+        'ColorSpace', # new
+        'LongExposureNoiseReduction', #new
+        'FileSize',
+        'FileNumber',
+        'DriveMode',
+        'OwnerName',
+        'SerialNumber',
+    ],
+    Nikon => [
+        'Model',
+        'SubSecDateTimeOriginal',
+        'ShutterCount',
+        'LensSpec',
+        'FocalLength',
+        'ImageSize',
+        'ShutterSpeed',
+        'Aperture',
+        'ISO',
+        'NoiseReduction',
+        'ExposureProgram',
+        'ExposureCompensation',
+        'WhiteBalance',
+        'WhiteBalanceFineTune',
+        'ShootingMode',
+        'Quality',
+        'MeteringMode',
+        'FocusMode',
+        'ImageOptimization',
+        'ToneComp',
+        'ColorHue',
+        'ColorSpace',
+        'HueAdjustment',
+        'Saturation',
+        'Sharpness',
+        'Flash',
+        'FlashMode',
+        'FlashExposureComp',
+    ],
+    # This shortcut may be useful when copying tags between files to either
+    # copy the maker notes as a block or prevent it from being copied
+    MakerNotes => [
+        'MakerNotes',   # (for RIFF MakerNotes)
+        'MakerNoteCanon',
+        'MakerNoteCasio',
+        'MakerNoteCasio2',
+        'MakerNoteFujiFilm',
+        'MakerNoteHP',
+        'MakerNoteHP2',
+        'MakerNoteHP4',
+        'MakerNoteHP6',
+        'MakerNoteJVC',
+        'MakerNoteJVCText',
+        'MakerNoteKodak1a',
+        'MakerNoteKodak1b',
+        'MakerNoteKodak2',
+        'MakerNoteKodak3',
+        'MakerNoteKodak4',
+        'MakerNoteKodak5',
+        'MakerNoteKodak6a',
+        'MakerNoteKodak6b',
+        'MakerNoteKodak7',
+        'MakerNoteKodakIFD',
+        'MakerNoteKodakUnknown',
+        'MakerNoteKyocera',
+        'MakerNoteMinolta',
+        'MakerNoteMinolta2',
+        'MakerNoteMinolta3',
+        'MakerNoteNikon',
+        'MakerNoteNikon2',
+        'MakerNoteNikon3',
+        'MakerNoteOlympus',
+        'MakerNoteOlympus2',
+        'MakerNoteLeica',
+        'MakerNotePanasonic',
+        'MakerNotePanasonic2',
+        'MakerNotePentax',
+        'MakerNotePentax2',
+        'MakerNotePentax3',
+        'MakerNotePentax4',
+        'MakerNoteRicoh',
+        'MakerNoteRicohText',
+        'MakerNoteSanyo',
+        'MakerNoteSanyoC4',
+        'MakerNoteSanyoPatch',
+        'MakerNoteSigma',
+        'MakerNoteSony',
+        'MakerNoteSonySRF',
+        'MakerNoteSonySR2',
+        'MakerNoteUnknown',
+    ],
+);
+
+# load user-defined shortcuts if available
+if (defined %Image::ExifTool::Shortcuts::UserDefined) {
+    my $shortcut;
+    foreach $shortcut (keys %Image::ExifTool::Shortcuts::UserDefined) {
+        my $val = $Image::ExifTool::Shortcuts::UserDefined{$shortcut};
+        # also allow simple aliases
+        $val = [ $val ] unless ref $val eq 'ARRAY';
+        # save the user-defined shortcut or alias
+        $Image::ExifTool::Shortcuts::Main{$shortcut} = $val;
+    }
+}
+
+
+1; # end
+
+__END__
+
+=head1 NAME
+
+Image::ExifTool::Shortcuts - ExifTool shortcut tags
+
+=head1 SYNOPSIS
+
+This module is required by Image::ExifTool.
+
+=head1 DESCRIPTION
+
+This module contains definitions for tag name shortcuts used by
+Image::ExifTool.  You can customize this file to add your own shortcuts.
+
+Individual users may also add their own shortcuts to the .ExifTool_config
+file in their home directory (or the directory specified by the
+EXIFTOOL_HOME environment variable).  The shortcuts are defined in a hash
+called %Image::ExifTool::Shortcuts::UserDefined.  The keys of the hash are
+the shortcut names, and the elements are either tag names or references to
+lists of tag names.
+
+An example shortcut definition in .ExifTool_config:
+
+    %Image::ExifTool::Shortcuts::UserDefined = (
+        MyShortcut => ['createdate','exif:exposuretime','aperture'],
+        MyAlias => 'FocalLengthIn35mmFormat',
+    );
+
+In this example, MyShortcut is a shortcut for the CreateDate,
+EXIF:ExposureTime and Aperture tags, and MyAlias is a shortcut for
+FocalLengthIn35mmFormat.
+
+=head1 AUTHOR
+
+Copyright 2003-2007, Phil Harvey (phil at owl.phy.queensu.ca)
+
+This library is free software; you can redistribute it and/or modify it
+under the same terms as Perl itself.
+
+=head1 SEE ALSO
+
+L<Image::ExifTool(3pm)|Image::ExifTool>
+
+=cut
Index: /gsdl/trunk/perllib/cpan/Image/ExifTool/Sigma.pm
===================================================================
--- /gsdl/trunk/perllib/cpan/Image/ExifTool/Sigma.pm	(revision 16842)
+++ /gsdl/trunk/perllib/cpan/Image/ExifTool/Sigma.pm	(revision 16842)
@@ -0,0 +1,245 @@
+#------------------------------------------------------------------------------
+# File:         Sigma.pm
+#
+# Description:  Sigma/Foveon EXIF maker notes tags
+#
+# Revisions:    04/06/2004 - P. Harvey Created
+#               02/20/2007 - PH added SD14 tags
+#
+# Reference:    http://www.x3f.info/technotes/FileDocs/MakerNoteDoc.html
+#------------------------------------------------------------------------------
+
+package Image::ExifTool::Sigma;
+
+use strict;
+use vars qw($VERSION);
+use Image::ExifTool::Exif;
+
+$VERSION = '1.04';
+
+%Image::ExifTool::Sigma::Main = (
+    WRITE_PROC => \&Image::ExifTool::Exif::WriteExif,
+    CHECK_PROC => \&Image::ExifTool::Exif::CheckExif,
+    WRITABLE => 'string',
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
+    0x0002 => 'SerialNumber',
+    0x0003 => 'DriveMode',
+    0x0004 => 'ResolutionMode',
+    0x0005 => 'AFMode',
+    0x0006 => 'FocusSetting',
+    0x0007 => 'WhiteBalance',
+    0x0008 => {
+        Name => 'ExposureMode',
+        PrintConv => { #PH
+            A => 'Aperture-priority AE',
+            M => 'Manual',
+            P => 'Program AE',
+            S => 'Shutter speed priority AE',
+        },
+    },
+    0x0009 => {
+        Name => 'MeteringMode',
+        PrintConv => { #PH
+            A => 'Average',
+            C => 'Center-weighted average',
+            8 => 'Multi-segment',
+        },
+    },
+    0x000a => 'Lens',
+    0x000b => 'ColorSpace',
+    0x000c => [
+        {
+            Name => 'ExposureCompensation',
+            Condition => '$$self{CameraModel} !~ /SD14$/',
+            ValueConv => '$val =~ s/Expo:\s*//, $val',
+            ValueConvInv => 'IsFloat($val) ? sprintf("Expo:%+.1f",$val) : undef',
+        },
+        { #PH
+            Name => 'UnknownCompensation',
+            Writable => 'rational64s',
+            Unknown => 1,
+        },
+    ],
+    0x000d => [
+        {
+            Name => 'Contrast',
+            Condition => '$$self{CameraModel} !~ /SD14$/',
+            ValueConv => '$val =~ s/Cont:\s*//, $val',
+            ValueConvInv => 'IsFloat($val) ? sprintf("Cont:%+.1f",$val) : undef',
+        },
+        { #PH
+            Name => 'Contrast',
+            Writable => 'rational64s',
+        },
+    ],
+    0x000e => [
+        {
+            Name => 'Shadow',
+            Condition => '$$self{CameraModel} !~ /SD14$/',
+            ValueConv => '$val =~ s/Shad:\s*//, $val',
+            ValueConvInv => 'IsFloat($val) ? sprintf("Shad:%+.1f",$val) : undef',
+        },
+        { #PH
+            Name => 'Shadow',
+            Writable => 'rational64s',
+        },
+    ],
+    0x000f => [
+        {
+            Name => 'Highlight',
+            Condition => '$$self{CameraModel} !~ /SD14$/',
+            ValueConv => '$val =~ s/High:\s*//, $val',
+            ValueConvInv => 'IsFloat($val) ? sprintf("High:%+.1f",$val) : undef',
+        },
+        { #PH
+            Name => 'Highlight',
+            Writable => 'rational64s',
+        },
+    ],
+    0x0010 => [
+        {
+            Name => 'Saturation',
+            Condition => '$$self{CameraModel} !~ /SD14$/',
+            ValueConv => '$val =~ s/Satu:\s*//, $val',
+            ValueConvInv => 'IsFloat($val) ? sprintf("Satu:%+.1f",$val) : undef',
+        },
+        { #PH
+            Name => 'Saturation',
+            Writable => 'rational64s',
+        },
+    ],
+    0x0011 => [
+        {
+            Name => 'Sharpness',
+            Condition => '$$self{CameraModel} !~ /SD14$/',
+            ValueConv => '$val =~ s/Shar:\s*//, $val',
+            ValueConvInv => 'IsFloat($val) ? sprintf("Shar:%+.1f",$val) : undef',
+        },
+        { #PH
+            Name => 'Sharpness',
+            Writable => 'rational64s',
+        },
+    ],
+    0x0012 => [
+        {
+            Name => 'X3FillLight',
+            Condition => '$$self{CameraModel} !~ /SD14$/',
+            ValueConv => '$val =~ s/Fill:\s*//, $val',
+            ValueConvInv => 'IsFloat($val) ? sprintf("Fill:%+.1f",$val) : undef',
+        },
+        { #PH
+            Name => 'X3FillLight',
+            Writable => 'rational64s',
+        },
+    ],
+    0x0014 => [
+        {
+            Name => 'ColorAdjustment',
+            Condition => '$$self{CameraModel} !~ /SD14$/',
+            ValueConv => '$val =~ s/CC:\s*//, $val',
+            ValueConvInv => 'IsInt($val) ? "CC:$val" : undef',
+        },
+        { #PH
+            Name => 'ColorAdjustment',
+            Writable => 'rational64s',
+            Count => 3,
+        },
+    ],
+    0x0015 => 'AdjustmentMode',
+    0x0016 => {
+        Name => 'Quality',
+        ValueConv => '$val =~ s/Qual:\s*//, $val',
+        ValueConvInv => 'IsInt($val) ? "Qual:$val" : undef',
+    },
+    0x0017 => 'Firmware',
+    0x0018 => 'Software',
+    0x0019 => 'AutoBracket',
+    # 0x001a - int32u: 1884 to 2016
+    # 0x001b - int32u: 20688 to 45335
+    # 0x001c - int16u[2]: "640 480"
+    0x001d => { #PH
+        Name => 'MakerNoteVersion',
+        Format => 'undef',
+    },
+    # 0x001e - int16u: 0
+    # 0x001f - string: ""
+    # 0x0020-21 - string: " "
+    0x0022 => { #PH
+        Name => 'AdjustmentMode',
+        Priority => 0,
+        Unknown => 1,
+    },
+    # 0x0023 - string: 10, 131, 150, 152, 169
+    # 0x0024-25 - string: ""
+    # 0x0026-2e - int32u: 0
+    # 0x002f - int32u: 0, 1
+    0x0030 => 'LensApertureRange', #PH
+    0x0031 => { #PH
+        Name => 'FNumber',
+        Writable => 'rational64u',
+        PrintConv => 'sprintf("%.1f",$val)',
+        PrintConvInv => '$val',
+    },
+    0x0032 => { #PH
+        Name => 'ExposureTime',
+        Writable => 'rational64u',
+        PrintConv => 'Image::ExifTool::Exif::PrintExposureTime($val)',
+        PrintConvInv => 'eval $val',
+    },
+    # 0x0033 - string: "3909" to "12337687"
+    # 0x0034 - int32u: 0,1,2,3 or 4 (possibly AFPoint?)
+    0x0035 => { #PH
+        Name => 'ExposureCompensation',
+        Writable => 'rational64s',
+    },
+    # 0x0036 - string: "                    "
+    # 0x0037-39 - string: ""
+    0x003a => { #PH (guess!)
+        Name => 'FlashExposureComp',
+        Writable => 'rational64s',
+    },
+    0x003b => { #PH (how is this different from other Firmware?)
+        Name => 'Firmware',
+        Priority => 0,
+    },
+    0x003c => 'WhiteBalance', #PH
+);
+
+1;  # end
+
+__END__
+
+=head1 NAME
+
+Image::ExifTool::Sigma - Sigma/Foveon EXIF maker notes tags
+
+=head1 SYNOPSIS
+
+This module is loaded automatically by Image::ExifTool when required.
+
+=head1 DESCRIPTION
+
+This module contains definitions required by Image::ExifTool to interpret
+Sigma and Foveon maker notes in EXIF information.
+
+=head1 AUTHOR
+
+Copyright 2003-2007, Phil Harvey (phil at owl.phy.queensu.ca)
+
+This library is free software; you can redistribute it and/or modify it
+under the same terms as Perl itself.
+
+=head1 REFERENCES
+
+=over 4
+
+=item L<http://www.x3f.info/technotes/FileDocs/MakerNoteDoc.html>
+
+=back
+
+=head1 SEE ALSO
+
+L<Image::ExifTool::TagNames/Sigma Tags>,
+L<Image::ExifTool(3pm)|Image::ExifTool>
+
+=cut
Index: /gsdl/trunk/perllib/cpan/Image/ExifTool/SigmaRaw.pm
===================================================================
--- /gsdl/trunk/perllib/cpan/Image/ExifTool/SigmaRaw.pm	(revision 16842)
+++ /gsdl/trunk/perllib/cpan/Image/ExifTool/SigmaRaw.pm	(revision 16842)
@@ -0,0 +1,474 @@
+#------------------------------------------------------------------------------
+# File:         SigmaRaw.pm
+#
+# Description:  Read Sigma/Foveon RAW (X3F) meta information
+#
+# Revisions:    10/16/2005 - P. Harvey Created
+#
+# References:   1) http://www.x3f.info/technotes/FileDocs/X3F_Format.pdf
+#------------------------------------------------------------------------------
+
+package Image::ExifTool::SigmaRaw;
+
+use strict;
+use vars qw($VERSION);
+use Image::ExifTool qw(:DataAccess :Utils);
+
+$VERSION = '1.03';
+
+sub ProcessX3FHeader($$$);
+sub ProcessX3FDirectory($$$);
+sub ProcessX3FProperties($$$);
+
+# main X3F sections (plus header stuff)
+%Image::ExifTool::SigmaRaw::Main = (
+    PROCESS_PROC => \&ProcessX3FDirectory,
+    NOTES => 'These tags are used in Sigma and Foveon RAW (.X3F) images.',
+    Header => {
+        SubDirectory => { TagTable => 'Image::ExifTool::SigmaRaw::Header' },
+    },
+    HeaderExt => {
+        SubDirectory => { TagTable => 'Image::ExifTool::SigmaRaw::HeaderExt' },
+    },
+    PROP => {
+        Name => 'Properties',
+        SubDirectory => { TagTable => 'Image::ExifTool::SigmaRaw::Properties' },
+    },
+    IMAG => {
+        Name => 'PreviewImage',
+        Binary => 1,
+    },
+    IMA2 => {
+        Name => 'PreviewImage',
+        Binary => 1,
+    },
+);
+
+# common X3F header structure
+%Image::ExifTool::SigmaRaw::Header = (
+    PROCESS_PROC => \&ProcessX3FHeader,
+    FORMAT => 'int32u',
+    1 => {
+        Name => 'FileVersion',
+        ValueConv => '($val >> 16) . "." . ($val & 0xffff)',
+    },
+    7 => 'ImageWidth',
+    8 => 'ImageHeight',
+    9 => 'Rotation',
+    10 => {
+        Name => 'WhiteBalance',
+        Format => 'string[32]',
+    },
+);
+
+# extended header tags
+%Image::ExifTool::SigmaRaw::HeaderExt = (
+    GROUPS => { 2 => 'Camera' },
+    NOTES => 'Extended header data found in version 2.1 and 2.2 files',
+    0 => 'Unused',
+    1 => { Name => 'ExposureAdjust',PrintConv => 'sprintf("%.2f",$val)' },
+    2 => { Name => 'Contrast',      PrintConv => 'sprintf("%.2f",$val)' },
+    3 => { Name => 'Shadow',        PrintConv => 'sprintf("%.2f",$val)' },
+    4 => { Name => 'Highlight',     PrintConv => 'sprintf("%.2f",$val)' },
+    5 => { Name => 'Saturation',    PrintConv => 'sprintf("%.2f",$val)' },
+    6 => { Name => 'Sharpness',     PrintConv => 'sprintf("%.2f",$val)' },
+    7 => { Name => 'RedAdjust',     PrintConv => 'sprintf("%.2f",$val)' },
+    8 => { Name => 'GreenAdjust',   PrintConv => 'sprintf("%.2f",$val)' },
+    9 => { Name => 'BlueAdjust',    PrintConv => 'sprintf("%.2f",$val)' },
+   10 => { Name => 'X3FillLight',   PrintConv => 'sprintf("%.2f",$val)' },
+);
+
+# PROP tags
+%Image::ExifTool::SigmaRaw::Properties = (
+    PROCESS_PROC => \&ProcessX3FProperties,
+    GROUPS => { 2 => 'Camera' },
+    AEMODE => {
+        Name => 'MeteringMode',
+        PrintConv => {
+            8 => '8-segment',
+            C => 'Center-weighted average',
+            A => 'Average',
+        },
+    },
+    AFAREA      => 'AFArea', # observed: CENTER_V
+    AFINFOCUS   => 'AFInFocus', # observed: H
+    AFMODE      => 'FocusMode',
+    AP_DESC     => 'ApertureDisplayed',
+    APERTURE => {
+        Name => 'FNumber',
+        Groups => { 2 => 'Image' },
+        PrintConv => 'sprintf("%.1f",$val)',
+    },
+    BRACKET     => 'BracketShot',
+    BURST       => 'BurstShot',
+    CAMMANUF    => 'Make',
+    CAMMODEL    => 'Model',
+    CAMNAME     => 'CameraName',
+    CAMSERIAL   => 'SerialNumber',
+    COLORSPACE  => 'ColorSpace', # observed: sRGB
+    DRIVE => {
+        Name => 'DriveMode',
+        PrintConv => {
+            SINGLE => 'Single Shot',
+            MULTI  => 'Multi Shot',
+            '2S'   => '2-sec Timer',
+            '10S'  => '10-sec Timer',
+            UP     => 'Mirror Up',
+            AB     => 'Auto Bracket',
+            OFF    => 'Off',
+        },
+    },
+    EVAL_STATE  => 'EvalState', # observed: POST-EXPOSURE
+    EXPCOMP => {
+        Name => 'ExposureCompensation',
+        Groups => { 2 => 'Image' },
+        PrintConv => 'Image::ExifTool::Exif::ConvertFraction($val)',
+    },
+    EXPNET => {
+        Name => 'NetExposureCompensation',
+        Groups => { 2 => 'Image' },
+        PrintConv => 'Image::ExifTool::Exif::ConvertFraction($val)',
+    },
+    EXPTIME => {
+        Name => 'IntegrationTime',
+        Groups => { 2 => 'Image' },
+        ValueConv => '$val * 1e-6', # convert from usec
+        PrintConv => 'Image::ExifTool::Exif::PrintExposureTime($val)',
+    },
+    FIRMVERS    => 'FirmwareVersion',
+    FLASH => {
+        Name => 'FlashMode',
+        PrintConv => 'ucfirst(lc($val))',
+    },
+    FLASHEXPCOMP=> 'FlashExpComp',
+    FLASHPOWER  => 'FlashPower',
+    FLASHTTLMODE=> 'FlashTTLMode', # observed: ON
+    FLASHTYPE   => 'FlashType', # observed: NONE
+    FLENGTH => {
+        Name => 'FocalLength',
+        PrintConv => 'sprintf("%.1fmm",$val)',
+    },
+    FLEQ35MM    => 'FocalLengthIn35mmFormat',
+    FOCUS => {
+        Name => 'Focus',
+        PrintConv => {
+            AF => 'Auto-focus Locked',
+           'NO LOCK' => "Auto-focus Didn't Lock",
+            M => 'Manual',
+        },
+    },
+    IMAGERBOARDID => 'ImagerBoardID',
+    IMAGERTEMP  => 'SensorTemperature',
+    ISO         => 'ISO',
+    LENSARANGE  => 'LensApertureRange',
+    LENSFRANGE  => 'LensFocalRange',
+    LENSMODEL   => 'LensID',
+    PMODE => {
+        Name => 'ExposureProgram',
+        PrintConv => {
+            P => 'Program',
+            A => 'Aperture Priority',
+            S => 'Shutter Priority',
+            M => 'Manual',
+        },
+    },
+    RESOLUTION => {
+        Name => 'Quality',
+        PrintConv => {
+            LOW => 'Low',
+            MED => 'Medium',
+            HI  => 'High',
+        },
+    },
+    SENSORID    => 'SensorID',
+    SH_DESC     => 'ShutterSpeedDisplayed',
+    SHUTTER => {
+        Name => 'ExposureTime',
+        Groups => { 2 => 'Image' },
+        PrintConv => 'Image::ExifTool::Exif::PrintExposureTime($val)',
+    },
+    TIME => {
+        Name => 'DateTimeOriginal',
+        Groups => { 2 => 'Time' },
+        Description => 'Date/Time Original',
+        ValueConv => 'ConvertUnixTime($val)',
+        PrintConv => '$self->ConvertDateTime($val)',
+    },
+    WB_DESC     => 'WhiteBalance',
+    VERSION_BF  => 'VersionBF',
+);
+
+#------------------------------------------------------------------------------
+# Extract null-terminated unicode string from list of characters
+# Inputs: 0) ExifTool object ref, 1) list ref, 2) position in list
+# Returns: Converted string
+sub ExtractUnicodeString($$$)
+{
+    my ($exifTool, $chars, $pos) = @_;
+    my $i;
+    for ($i=$pos; $i<@$chars; ++$i) {
+        last unless $$chars[$i];
+    }
+    my $buff = pack('v*',@$chars[$pos..$i-1]);
+    my $val = $exifTool->Unicode2Charset($buff, 'II');
+    return $val;
+}
+
+#------------------------------------------------------------------------------
+# Process an X3F header
+# Inputs: 0) ExifTool object reference, 1) DirInfo reference, 2) tag table ref
+# Returns: 1 on success
+sub ProcessX3FHeader($$$)
+{
+    my ($exifTool, $dirInfo, $tagTablePtr) = @_;
+    my $verbose = $exifTool->Options('Verbose');
+
+    # process the static header structure first
+    $exifTool->ProcessBinaryData($dirInfo, $tagTablePtr);
+
+    # process extended data if available
+    if ($$dirInfo{DirLen} >= 232) {
+        $verbose and $exifTool->VerboseDir('X3F HeaderExt', 32);
+        $tagTablePtr = GetTagTable('Image::ExifTool::SigmaRaw::HeaderExt');
+        my $dataPt = $$dirInfo{DataPt};
+        my @vals = unpack('x72C32V32', $$dataPt);
+        my $i;
+        my $unused = 0;
+        for ($i=0; $i<32; ++$i) {
+            $vals[$i] or ++$unused, next;
+            my $val = $vals[$i+32];
+            # convert value 0x40000000 => 2 ** 1, 0x3f800000 => 2 ** 0, 0x3f000000 => 2 ** -1
+            if ($val) {
+                my $sign;
+                if ($val & 0x80000000) {
+                    $sign = -1;
+                    $val &= 0x7fffffff;
+                } else {
+                    $sign = 1;
+                }
+                $val = $sign * 2 ** (($val - 0x3f800000) / 0x800000);
+            }
+            $exifTool->HandleTag($tagTablePtr, $vals[$i], $val,
+                Index => $i,
+                DataPt => $dataPt,
+                Start => 104 + $i * 4,
+                Size => 4,
+            );
+        }
+        $exifTool->VPrint(0, "$exifTool->{INDENT}($unused entries unused)\n");
+    }
+    return 1;
+}
+
+#------------------------------------------------------------------------------
+# Process an X3F properties
+# Inputs: 0) ExifTool object reference, 1) DirInfo reference, 2) tag table ref
+# Returns: 1 on success
+sub ProcessX3FProperties($$$)
+{
+    my ($exifTool, $dirInfo, $tagTablePtr) = @_;
+    my $dataPt = $$dirInfo{DataPt};
+    my $size = length($$dataPt);
+    my $verbose = $exifTool->Options('Verbose');
+    my $unknown = $exifTool->Options('Unknown');
+
+    unless ($size >= 24 and $$dataPt =~ /^SECp/) {
+        $exifTool->Warn('Bad properties header');
+        return 0;
+    }
+    my ($entries, $fmt, $len) = unpack('x8V2x4V', $$dataPt);
+    unless ($size >= 24 + 8 * $entries + $len) {
+        $exifTool->Warn('Truncated Property directory');
+        return 0;
+    }
+    $verbose and $exifTool->VerboseDir('Properties', $entries);
+    $fmt == 0 or $exifTool->Warn("Unsupported character format $fmt"), return 0;
+    my $charPos = 24 + 8 * $entries;
+    my @chars = unpack('v*',substr($$dataPt, $charPos, $len * 2));
+    my $index;
+    for ($index=0; $index<$entries; ++$index) {
+        my ($namePos, $valPos) = unpack('V2',substr($$dataPt, $index*8 + 24, 8));
+        if ($namePos >= @chars or $valPos >= @chars) {
+            $exifTool->Warn('Bad Property pointer');
+            return 0;
+        }
+        my $tag = ExtractUnicodeString($exifTool, \@chars, $namePos);
+        my $val = ExtractUnicodeString($exifTool, \@chars, $valPos);
+        if (not $$tagTablePtr{$tag} and $unknown and $tag =~ /^\w+$/) {
+            my $tagInfo = {
+                Name => "SigmaRaw_$tag",
+                Description => Image::ExifTool::MakeDescription('SigmaRaw', $tag),
+                Unknown => 1,
+                Writable => 0,  # can't write unknown tags
+            };
+            # add tag information to table
+            Image::ExifTool::AddTagToTable($tagTablePtr, $tag, $tagInfo);
+        }
+        
+        $exifTool->HandleTag($tagTablePtr, $tag, $val,
+            Index => $index,
+            DataPt => $dataPt,
+            Start => $charPos + 2 * $valPos,
+            Size => 2 * (length($val) + 1),
+        );
+    }
+    return 1;
+}
+
+#------------------------------------------------------------------------------
+# Process an X3F directory
+# Inputs: 0) ExifTool object reference, 1) DirInfo reference, 2) tag table ref
+# Returns: error string or undef on success
+sub ProcessX3FDirectory($$$)
+{
+    my ($exifTool, $dirInfo, $tagTablePtr) = @_;
+    my $raf = $$dirInfo{RAF};
+    my $verbose = $exifTool->Options('Verbose');
+
+    $raf->Seek($$dirInfo{DirStart}, 0) or return 'Error seeking to directory start';
+
+    # parse the X3F directory structure
+    my ($buff, $ver, $entries, $index);
+    $raf->Read($buff, 12) == 12 or return 'Truncated X3F image';
+    $buff =~ /^SECd/ or return 'Bad section header';
+    ($ver, $entries) = unpack('x4V2', $buff);
+    $verbose and $exifTool->VerboseDir('X3F Subsection', $entries);
+    my $dir;
+    $raf->Read($dir, $entries * 12) == $entries * 12 or return 'Truncated X3F directory';
+    for ($index=0; $index<$entries; ++$index) {
+        my $pos = $index * 12;
+        my ($offset, $len, $tag) = unpack("x${pos}V2a4", $dir);
+        my $tagInfo = $exifTool->GetTagInfo($tagTablePtr, $tag);
+        if ($verbose) {
+            $exifTool->VPrint(0, "$exifTool->{INDENT}$index) $tag Subsection ($len bytes):\n");
+            if ($verbose > 2) {
+                $raf->Seek($offset, 0) or return 'Error seeking';
+                my $n = $verbose > 3 ? $len : 64;
+                $n = $len if $n > $len;
+                $raf->Read($buff, $n) == $n or return 'Truncated image';
+                Image::ExifTool::HexDump(\$buff, undef,
+                    Prefix => $exifTool->{INDENT},
+                    Out => $exifTool->Options('TextOut'),
+                );
+            }
+        }
+        next unless $tagInfo;
+        $raf->Seek($offset, 0) or return "Error seeking for $$tagInfo{Name}";
+        if  ($$tagInfo{Name} eq 'PreviewImage') {
+            # check image header to see if this is a JPEG preview image
+            $raf->Read($buff, 28) == 28 or return 'Error reading PreviewImage header';
+            # igore all image data but JPEG compressed (type 18)
+            next unless $buff =~ /^SECi.{4}\x02\0\0\0\x12/s;
+            $len -= 28;
+        }
+        $raf->Read($buff, $len) == $len or return "Error reading $$tagInfo{Name} data";
+        my $subdir = $$tagInfo{SubDirectory};
+        if ($subdir) {
+            my %dirInfo = ( DataPt => \$buff );
+            my $subTable = GetTagTable($$subdir{TagTable});
+            $exifTool->ProcessDirectory(\%dirInfo, $subTable);
+        } else {
+            $exifTool->FoundTag($tagInfo, $buff);
+        }
+    }
+    return undef;
+}
+
+#------------------------------------------------------------------------------
+# Extract information from a Sigma raw (X3F) image
+# Inputs: 0) ExifTool object reference, 1) DirInfo reference
+# Returns: 1 on success, 0 if this wasn't a valid X3F image
+sub ProcessX3F($$)
+{
+    my ($exifTool, $dirInfo) = @_;
+    my $raf = $$dirInfo{RAF};
+    my $buff;
+
+    return 0 unless $raf->Read($buff, 40) == 40;
+    return 0 unless $buff =~ /^FOVb/;
+
+    SetByteOrder('II');
+    $exifTool->SetFileType();
+
+    # check version number
+    my $ver = unpack('x4V',$buff);
+    $ver = ($ver >> 16) . '.' . ($ver & 0xffff);
+    if ($ver >= 3) {
+        $exifTool->Warn("Can't read version $ver X3F image");
+        return 1;
+    }
+    # read version 2.1/2.2 extended header
+    if ($ver > 2) {
+        my $buf2;
+        unless ($raf->Read($buf2, 192) == 192) {
+            $exifTool->Warn('Error reading extended header');
+            return 1;
+        }
+        $buff .= $buf2;
+    }
+    # process header information
+    my $tagTablePtr = GetTagTable('Image::ExifTool::SigmaRaw::Main');
+    my $tagInfo = $exifTool->GetTagInfo($tagTablePtr, 'Header');
+    my $subdir = GetTagTable('Image::ExifTool::SigmaRaw::Header');
+    my %dirInfo = (
+        DataPt => \$buff,
+        DirStart => 0,
+        DirLen => length($buff),
+    );
+    $exifTool->ProcessDirectory(\%dirInfo, $subdir);
+    # read the directory pointer
+    $raf->Seek(-4, 2);
+    unless ($raf->Read($buff, 4) == 4) {
+        $exifTool->Warn('Error reading X3F dir pointer');
+        return 1;
+    }
+    my $offset = unpack('V', $buff);
+    %dirInfo = (
+        RAF => $raf,
+        DirStart => $offset,
+    );
+    # process the X3F subsections
+    my $err = $exifTool->ProcessDirectory(\%dirInfo, $tagTablePtr);
+    $err and $exifTool->Warn($err);
+    return 1;
+}
+
+1;  # end
+
+__END__
+
+=head1 NAME
+
+Image::ExifTool::SigmaRaw - Read Sigma/Foveon RAW (X3F) meta information
+
+=head1 SYNOPSIS
+
+This module is loaded automatically by Image::ExifTool when required.
+
+=head1 DESCRIPTION
+
+This module contains definitions required by Image::ExifTool to read
+Sigma and Foveon X3F images.
+
+=head1 AUTHOR
+
+Copyright 2003-2007, Phil Harvey (phil at owl.phy.queensu.ca)
+
+This library is free software; you can redistribute it and/or modify it
+under the same terms as Perl itself.
+
+=head1 REFERENCES
+
+=over 4
+
+=item L<http://www.x3f.info/technotes/FileDocs/X3F_Format.pdf>
+
+=back
+
+=head1 SEE ALSO
+
+L<Image::ExifTool::TagNames/SigmaRaw Tags>,
+L<Image::ExifTool::Sigma(3pm)|Image::ExifTool::Sigma>,
+L<Image::ExifTool(3pm)|Image::ExifTool>
+
+=cut
Index: /gsdl/trunk/perllib/cpan/Image/ExifTool/Sony.pm
===================================================================
--- /gsdl/trunk/perllib/cpan/Image/ExifTool/Sony.pm	(revision 16842)
+++ /gsdl/trunk/perllib/cpan/Image/ExifTool/Sony.pm	(revision 16842)
@@ -0,0 +1,421 @@
+#------------------------------------------------------------------------------
+# File:         Sony.pm
+#
+# Description:  Sony EXIF Maker Notes tags
+#
+# Revisions:    04/06/2004  - P. Harvey Created
+#
+# References:   1) http://www.cybercom.net/~dcoffin/dcraw/
+#               2) http://homepage3.nifty.com/kamisaka/makernote/makernote_sony.htm
+#               3) Thomas Bodenmann private communication
+#------------------------------------------------------------------------------
+
+package Image::ExifTool::Sony;
+
+use strict;
+use vars qw($VERSION);
+use Image::ExifTool qw(:DataAccess :Utils);
+use Image::ExifTool::Exif;
+use Image::ExifTool::Minolta;
+
+$VERSION = '1.10';
+
+sub ProcessSRF($$$);
+sub ProcessSR2($$$);
+
+my %sonyLensIDs;    # filled in based on Minolta LensID's
+
+%Image::ExifTool::Sony::Main = (
+    WRITE_PROC => \&Image::ExifTool::Exif::WriteExif,
+    CHECK_PROC => \&Image::ExifTool::Exif::CheckExif,
+    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
+    0x0e00 => {
+        Name => 'PrintIM',
+        Description => 'Print Image Matching',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::PrintIM::Main',
+        },
+    },
+    # 0xb020 string with observed values "Standard", "None" and "Real"
+    0xb021 => { #2
+        Name => 'ColorTemperature',
+        PrintConv => '$val ? $val : "Auto"',
+    },
+    0xb023 => { #PH (A100)
+        Name => 'SceneMode',
+        PrintConv => {
+            0 => 'Manual (P,A,S or M)',
+            1 => 'Portrait',
+            4 => 'Sunset',
+            5 => 'Sports',
+            6 => 'Landscape',
+            8 => 'Macro',
+            16 => 'Auto',
+            17 => 'Night Portrait',
+        },
+    },
+    0xb024 => { #PH (A100)
+        Name => 'ZoneMatching',
+        PrintConv => {
+            0 => 'ISO Setting Used',
+            1 => 'High Key',
+            2 => 'Low Key',
+        },
+    },
+    0xb025 => { #PH (A100)
+        Name => 'DynamicRangeOptimizer',
+        PrintConv => {
+            0 => 'Off',
+            1 => 'Standard',
+            2 => 'Advanced',
+        },
+    },
+    0xb026 => { #PH (A100)
+        Name => 'ImageStabilization',
+        PrintConv => { 0 => 'Off', 1 => 'On' },
+    },
+    0xb027 => { #2
+        Name => 'LensID',
+        PrintConv => \%sonyLensIDs,
+    },
+    0xb028 => { #2
+        # (used by the DSLR-A100)
+        Name => 'MinoltaMakerNote',
+        Flags => 'SubIFD',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::Minolta::Main',
+            Start => '$val',
+        },
+    },
+    0xb029 => { #2
+        Name => 'ColorMode',
+        Writable => 'int32u',
+        PrintConv => {
+            0 => 'Standard',
+            1 => 'Vivid',
+            2 => 'Portrait',
+            3 => 'Landscape',
+            4 => 'Sunset',
+            5 => 'Night Scene',
+            6 => 'B&W',
+            7 => 'Adobe RGB',
+        },
+    },
+    0xb040 => { #2
+        Name => 'Macro',
+        PrintConv => { 0 => 'Off', 1 => 'On' },
+    },
+    0xb041 => { #2
+        Name => 'ExposureMode',
+        PrintConv => {
+            0 => 'Auto',
+            5 => 'Landscape',
+            6 => 'Program',
+            7 => 'Aperture Priority',
+            8 => 'Shutter Priority',
+            9 => 'Night Scene',
+            15 => 'Manual',
+        },
+    },
+    0xb047 => { #2
+        Name => 'Quality',
+        PrintConv => {
+            0 => 'Normal',
+            1 => 'Fine',
+        },
+    },
+    0xb04e => { #2
+        Name => 'LongExposureNoiseReduction',
+        PrintConv => { 0 => 'Off', 1 => 'On' },
+    },
+);
+
+# tag table for Sony RAW Format
+%Image::ExifTool::Sony::SRF = (
+    PROCESS_PROC => \&ProcessSRF,
+    GROUPS => { 0 => 'MakerNotes', 1 => 'SRF#', 2 => 'Camera' },
+    NOTES => q{
+        The maker notes in SRF (Sony Raw Format) images contain 7 IFD's (with family
+        1 group names SRF0 through SRF6).  SRF0 through SRF5 use these Sony tags,
+        while SRF6 uses standard EXIF tags.  All information other than SRF0 is
+        encrypted, but thanks to Dave Coffin the decryption algorithm is known.
+    },
+    0 => {
+        Name => 'SRF2_Key',
+        Notes => 'key to decrypt maker notes from the start of SRF2',
+        RawConv => '$self->{SRF2_Key} = $val',
+    },
+    1 => {
+        Name => 'DataKey',
+        Notes => 'key to decrypt the rest of the file from the end of the maker notes',
+        RawConv => '$self->{SRFDataKey} = $val',
+    },
+);
+
+# tag table for Sony RAW 2 Format Private IFD (ref 1)
+%Image::ExifTool::Sony::SR2Private = (
+    PROCESS_PROC => \&ProcessSR2,
+    GROUPS => { 0 => 'MakerNotes', 1 => 'SR2', 2 => 'Camera' },
+    NOTES => q{
+        The SR2 format uses the DNGPrivateData tag to reference a private IFD
+        containing these tags.
+    },
+    0x7200 => {
+        Name => 'SR2SubIFDOffset',
+        # (adjusting offset messes up calculations for AdobeSR2 in DNG images)
+        # Flags => 'IsOffset',
+        OffsetPair => 0x7201,
+        RawConv => '$self->{SR2SubIFDOffset} = $val',
+    },
+    0x7201 => {
+        Name => 'SR2SubIFDLength',
+        OffsetPair => 0x7200,
+        RawConv => '$self->{SR2SubIFDLength} = $val',
+    },
+    0x7221 => {
+        Name => 'SR2SubIFDKey',
+        Format => 'int32u',
+        Notes => 'key to decrypt SR2SubIFD',
+        RawConv => '$self->{SR2SubIFDKey} = $val',
+    },
+);
+
+%Image::ExifTool::Sony::SR2SubIFD = (
+    GROUPS => { 0 => 'MakerNotes', 1 => 'SR2', 2 => 'Camera' },
+    NOTES => 'Tags in the encrypted SR2SubIFD',
+    0x7303 => 'WB_GRBGLevels', #1
+    0x74c0 => { #PH
+        Name => 'SR2DataIFD',
+        Flags => 'SubIFD',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::Sony::SR2DataIFD',
+            Start => '$val',
+            MaxSubdirs => 6,
+        },
+    },
+    0x74a0 => 'MaxApertureAtMaxFocal', #PH
+    0x74a1 => 'MaxApertureAtMinFocal', #PH
+);
+
+%Image::ExifTool::Sony::SR2DataIFD = (
+    GROUPS => { 0 => 'MakerNotes', 1 => 'SR2', 2 => 'Camera' },
+    0x7770 => 'ColorMode', #PH
+);
+
+# fill in Sony LensID lookup based on Minolta values
+{
+    my $id;
+    foreach $id (keys %Image::ExifTool::Minolta::minoltaLensIDs) {
+        # higher numbered lenses are missing last digit of ID
+        my $sonyID = ($id < 10000) ? $id : int($id / 10);
+        $sonyLensIDs{$sonyID} = $Image::ExifTool::Minolta::minoltaLensIDs{$id};
+    }
+}
+
+#------------------------------------------------------------------------------
+# decrypt Sony data (ref 1)
+# Inputs: 0) data reference, 1) start offset, 2) data length, 3) decryption key
+# Returns: nothing (original data buffer is updated with decrypted data)
+sub Decrypt($$$$)
+{
+    my ($dataPt, $start, $len, $key) = @_;
+    my ($i, $j, @pad);
+    my $words = $len / 4;
+
+    for ($i=0; $i<4; ++$i) {
+        my $lo = ($key & 0xffff) * 0x0edd + 1;
+        my $hi = ($key >> 16) * 0x0edd + ($key & 0xffff) * 0x02e9 + ($lo >> 16);
+        $pad[$i] = $key = (($hi & 0xffff) << 16) + ($lo & 0xffff);
+    }
+    $pad[3] = ($pad[3] << 1 | ($pad[0]^$pad[2]) >> 31) & 0xffffffff;
+    for ($i=4; $i<0x7f; ++$i) {
+        $pad[$i] = (($pad[$i-4]^$pad[$i-2]) << 1 |
+                    ($pad[$i-3]^$pad[$i-1]) >> 31) & 0xffffffff;
+    }
+    my @data = unpack("x$start N$words", $$dataPt);
+    for ($i=0x7f,$j=0; $j<$words; ++$i,++$j) {
+        $data[$j] ^= $pad[$i & 0x7f] = $pad[($i+1) & 0x7f] ^ $pad[($i+65) & 0x7f];
+    }
+    substr($$dataPt, $start, $words*4) = pack('N*', @data);
+}
+
+#------------------------------------------------------------------------------
+# Process SRF maker notes
+# Inputs: 0) ExifTool object reference, 1) reference to directory information
+#         2) pointer to tag table
+# Returns: 1 on success
+sub ProcessSRF($$$)
+{
+    my ($exifTool, $dirInfo, $tagTablePtr) = @_;
+    my $dataPt = $$dirInfo{DataPt};
+    my $dirLen = $$dirInfo{DirLen};
+    my $start = $$dirInfo{DirStart};
+    my $verbose = $exifTool->Options('Verbose');
+
+    # process IFD chain
+    my ($ifd, $success);
+    for ($ifd=0; ; ) {
+        my $srf = $$dirInfo{DirName} = "SRF$ifd";
+        my $srfTable = $tagTablePtr;
+        # SRF6 uses standard EXIF tags
+        $srfTable = GetTagTable('Image::ExifTool::Exif::Main') if $ifd == 6;
+        $exifTool->{SET_GROUP1} = $srf;
+        $success = Image::ExifTool::Exif::ProcessExif($exifTool, $dirInfo, $srfTable);
+        delete $exifTool->{SET_GROUP1};
+        last unless $success;
+#
+# get pointer to next IFD
+#
+        my $count = Get16u($dataPt, $$dirInfo{DirStart});
+        my $dirEnd = $$dirInfo{DirStart} + 2 + $count * 12;
+        last if $dirEnd + 4 > length($$dataPt);
+        my $nextIFD = Get32u($dataPt, $dirEnd);
+        last unless $nextIFD;
+        $nextIFD -= $$dirInfo{DataPos}; # adjust for position of makernotes data
+        $$dirInfo{DirStart} = $nextIFD;
+#
+# decrypt next IFD data if necessary
+#
+        ++$ifd;
+        my ($key, $len);
+        if ($ifd == 1) {
+            # get the key to decrypt IFD1
+            my $cp = $start + 0x8ddc;    # why?
+            my $ip = $cp + 4 * unpack("x$cp C", $$dataPt);
+            $key = unpack("x$ip N", $$dataPt);
+            $len = $cp + $nextIFD;  # decrypt up to $cp
+        } elsif ($ifd == 2) {
+            # get the key to decrypt IFD2
+            $key = $exifTool->{SRF2_Key};
+            $len = length($$dataPt) - $nextIFD; # decrypt rest of maker notes
+        } else {
+            next;   # no decryption needed
+        }
+        # decrypt data
+        Decrypt($dataPt, $nextIFD, $len, $key) if defined $key;
+        next unless $verbose > 2;
+        # display decrypted data in verbose mode
+        $exifTool->VerboseDir("Decrypted SRF$ifd", 0, $nextIFD + $len);
+        my %parms = (
+            Prefix => "$exifTool->{INDENT}  ",
+            Start => $nextIFD,
+            DataPos => $$dirInfo{DataPos},
+            Out => $exifTool->Options('TextOut'),
+        );
+        $parms{MaxLen} = 96 unless $verbose > 3;
+        Image::ExifTool::HexDump($dataPt, $len, %parms);
+    }
+}
+
+#------------------------------------------------------------------------------
+# Process SR2 data
+# Inputs: 0) ExifTool object reference, 1) reference to directory information
+#         2) pointer to tag table
+# Returns: 1 on success
+sub ProcessSR2($$$)
+{
+    my ($exifTool, $dirInfo, $tagTablePtr) = @_;
+    my $dataPt = $$dirInfo{DataPt};
+    my $dataPos = $$dirInfo{DataPos};
+    my $dataLen = $$dirInfo{DataLen} || length $$dataPt;
+    my $dirLen = $$dirInfo{DirLen};
+    my $verbose = $exifTool->Options('Verbose');
+    my $result = Image::ExifTool::Exif::ProcessExif($exifTool, $dirInfo, $tagTablePtr);
+    return $result unless $result;
+    my $offset = $exifTool->{SR2SubIFDOffset};
+    my $length = $exifTool->{SR2SubIFDLength};
+    my $key = $exifTool->{SR2SubIFDKey};
+    my $raf = $$dirInfo{RAF};
+    my $base = $$dirInfo{Base} || 0;
+    if ($offset and $length and defined $key) {
+        my $buff;
+        # read encrypted SR2SubIFD from file
+        if (($raf and $raf->Seek($offset+$base, 0) and
+                $raf->Read($buff, $length) == $length) or
+            # or read from data (when processing Adobe DNGPrivateData)
+            ($offset - $dataPos >= 0 and $offset - $dataPos + $length < $dataLen and 
+                ($buff = substr($$dataPt, $offset - $dataPos, $length))))
+        {
+            Decrypt(\$buff, 0, $length, $key);
+            # display decrypted data in verbose mode
+            if ($verbose > 2) {
+                $exifTool->VerboseDir("Decrypted SR2SubIFD", 0, $length);
+                my %parms = (
+                    Out => $exifTool->{OPTIONS}->{TextOut},
+                    Prefix => $exifTool->{INDENT},
+                    Addr => $offset + $base,
+                );
+                $parms{MaxLen} = 96 unless $verbose > 3;
+                Image::ExifTool::HexDump(\$buff, $length, %parms);
+            }
+            my %dirInfo = (
+                Base => $base,
+                DataPt => \$buff,
+                DataLen => length $buff,
+                DirStart => 0,
+                DirName => 'SR2SubIFD',
+                DataPos => $offset,
+            );
+            my $subTable = Image::ExifTool::GetTagTable('Image::ExifTool::Sony::SR2SubIFD');
+            $result = $exifTool->ProcessDirectory(\%dirInfo, $subTable);
+
+        } else {
+            $exifTool->Warn('Error reading SR2 data');
+        }
+    }
+    delete $exifTool->{SR2SubIFDOffset};
+    delete $exifTool->{SR2SubIFDLength};
+    delete $exifTool->{SR2SubIFDKey};
+    return $result;
+}
+
+1; # end
+
+__END__
+
+=head1 NAME
+
+Image::ExifTool::Sony - Sony EXIF maker notes tags
+
+=head1 SYNOPSIS
+
+This module is loaded automatically by Image::ExifTool when required.
+
+=head1 DESCRIPTION
+
+This module contains definitions required by Image::ExifTool to
+interpret Sony maker notes EXIF meta information.
+
+=head1 NOTES
+
+The Sony maker notes use the standard EXIF IFD structure, but unfortunately
+the entries are large blocks of binary data for which I can find no
+documentation.  You can use "exiftool -v3" to dump these blocks in hex.
+
+=head1 AUTHOR
+
+Copyright 2003-2007, Phil Harvey (phil at owl.phy.queensu.ca)
+
+This library is free software; you can redistribute it and/or modify it
+under the same terms as Perl itself.
+
+=head1 REFERENCES
+
+=over 4
+
+=item L<http://www.cybercom.net/~dcoffin/dcraw/>
+
+=item L<http://homepage3.nifty.com/kamisaka/makernote/makernote_sony.htm>
+
+=back
+
+=head1 ACKNOWLEDGEMENTS
+
+Thanks to Thomas Bodenmann for providing information about the LensID's.
+
+=head1 SEE ALSO
+
+L<Image::ExifTool::TagNames/Sony Tags>,
+L<Image::ExifTool(3pm)|Image::ExifTool>
+
+=cut
Index: /gsdl/trunk/perllib/cpan/Image/ExifTool/TagLookup.pm
===================================================================
--- /gsdl/trunk/perllib/cpan/Image/ExifTool/TagLookup.pm	(revision 16842)
+++ /gsdl/trunk/perllib/cpan/Image/ExifTool/TagLookup.pm	(revision 16842)
@@ -0,0 +1,3875 @@
+#------------------------------------------------------------------------------
+# File:         TagLookup.pm
+#
+# Description:  Fast lookup for ExifTool tags
+#
+# Notes:        This lookup is used to look up tag names for writing
+#
+# Revisions:    12/31/2004  - P. Harvey Created
+#------------------------------------------------------------------------------
+
+package Image::ExifTool::TagLookup;
+
+use strict;
+require Exporter;
+
+use vars qw($VERSION @ISA @EXPORT_OK);
+use Image::ExifTool qw(:Utils);
+
+$VERSION = '1.04';
+@ISA = qw(Exporter);
+@EXPORT_OK = qw(FindTagInfo TagExists);
+
+#
+# Note: the lists below were generated by Image::ExifTool::BuildTagLookup
+#
+
+#++++ Begin automatically generated code ++++
+
+# list of tables containing writable tags
+my @tableList = (
+	'Image::ExifTool::APP12::Ducky',
+	'Image::ExifTool::Canon::CameraInfo',
+	'Image::ExifTool::Canon::CameraInfo1DmkIII',
+	'Image::ExifTool::Canon::CameraInfo2',
+	'Image::ExifTool::Canon::CameraSettings',
+	'Image::ExifTool::Canon::ColorBalance',
+	'Image::ExifTool::Canon::ColorBalance1',
+	'Image::ExifTool::Canon::ColorBalance2',
+	'Image::ExifTool::Canon::ColorBalance3',
+	'Image::ExifTool::Canon::ColorInfo',
+	'Image::ExifTool::Canon::ColorInfoD30',
+	'Image::ExifTool::Canon::FaceDetect1',
+	'Image::ExifTool::Canon::FaceDetect2',
+	'Image::ExifTool::Canon::FileInfo',
+	'Image::ExifTool::Canon::Flags',
+	'Image::ExifTool::Canon::FocalLength',
+	'Image::ExifTool::Canon::Main',
+	'Image::ExifTool::Canon::ModifiedInfo',
+	'Image::ExifTool::Canon::MyColors',
+	'Image::ExifTool::Canon::Panorama',
+	'Image::ExifTool::Canon::PreviewImageInfo',
+	'Image::ExifTool::Canon::Processing',
+	'Image::ExifTool::Canon::ShotInfo',
+	'Image::ExifTool::CanonCustom::Functions10D',
+	'Image::ExifTool::CanonCustom::Functions1D',
+	'Image::ExifTool::CanonCustom::Functions2',
+	'Image::ExifTool::CanonCustom::Functions20D',
+	'Image::ExifTool::CanonCustom::Functions30D',
+	'Image::ExifTool::CanonCustom::Functions350D',
+	'Image::ExifTool::CanonCustom::Functions400D',
+	'Image::ExifTool::CanonCustom::Functions5D',
+	'Image::ExifTool::CanonCustom::FunctionsD30',
+	'Image::ExifTool::CanonCustom::PersonalFuncValues',
+	'Image::ExifTool::CanonCustom::PersonalFuncs',
+	'Image::ExifTool::CanonRaw::ExposureInfo',
+	'Image::ExifTool::CanonRaw::FlashInfo',
+	'Image::ExifTool::CanonRaw::ImageFormat',
+	'Image::ExifTool::CanonRaw::ImageInfo',
+	'Image::ExifTool::CanonRaw::Main',
+	'Image::ExifTool::CanonRaw::MakeModel',
+	'Image::ExifTool::CanonRaw::RawJpgInfo',
+	'Image::ExifTool::CanonRaw::TimeStamp',
+	'Image::ExifTool::CanonVRD::Ver1',
+	'Image::ExifTool::CanonVRD::Ver2',
+	'Image::ExifTool::Casio::Main',
+	'Image::ExifTool::Casio::Type2',
+	'Image::ExifTool::Composite',
+	'Image::ExifTool::DNG::AdobeData',
+	'Image::ExifTool::Exif::Main',
+	'Image::ExifTool::Extra',
+	'Image::ExifTool::FotoStation::Main',
+	'Image::ExifTool::FotoStation::SoftEdit',
+	'Image::ExifTool::FujiFilm::Main',
+	'Image::ExifTool::GPS::Main',
+	'Image::ExifTool::IPTC::ApplicationRecord',
+	'Image::ExifTool::IPTC::EnvelopeRecord',
+	'Image::ExifTool::IPTC::NewsPhoto',
+	'Image::ExifTool::JFIF::Main',
+	'Image::ExifTool::Kodak::CameraInfo',
+	'Image::ExifTool::Kodak::Main',
+	'Image::ExifTool::Kodak::SubIFD0',
+	'Image::ExifTool::Kodak::SubIFD1',
+	'Image::ExifTool::Kodak::SubIFD2',
+	'Image::ExifTool::Kodak::SubIFD3',
+	'Image::ExifTool::Kodak::SubIFD5',
+	'Image::ExifTool::Kodak::Type2',
+	'Image::ExifTool::Kodak::Type3',
+	'Image::ExifTool::Kodak::Type4',
+	'Image::ExifTool::Kodak::Type7',
+	'Image::ExifTool::MIE::Audio',
+	'Image::ExifTool::MIE::Camera',
+	'Image::ExifTool::MIE::Doc',
+	'Image::ExifTool::MIE::Extender',
+	'Image::ExifTool::MIE::Flash',
+	'Image::ExifTool::MIE::GPS',
+	'Image::ExifTool::MIE::Geo',
+	'Image::ExifTool::MIE::Image',
+	'Image::ExifTool::MIE::Lens',
+	'Image::ExifTool::MIE::Main',
+	'Image::ExifTool::MIE::Orient',
+	'Image::ExifTool::MIE::Preview',
+	'Image::ExifTool::MIE::Thumbnail',
+	'Image::ExifTool::MIE::UTM',
+	'Image::ExifTool::MIE::Video',
+	'Image::ExifTool::Minolta::CameraSettings',
+	'Image::ExifTool::Minolta::CameraSettings5D',
+	'Image::ExifTool::Minolta::CameraSettings7D',
+	'Image::ExifTool::Minolta::CameraSettingsA100',
+	'Image::ExifTool::Minolta::Main',
+	'Image::ExifTool::MinoltaRaw::PRD',
+	'Image::ExifTool::MinoltaRaw::RIF',
+	'Image::ExifTool::MinoltaRaw::WBG',
+	'Image::ExifTool::Nikon::AFInfo',
+	'Image::ExifTool::Nikon::ColorBalance1',
+	'Image::ExifTool::Nikon::ColorBalance2',
+	'Image::ExifTool::Nikon::ColorBalance3',
+	'Image::ExifTool::Nikon::GEM',
+	'Image::ExifTool::Nikon::LensData00',
+	'Image::ExifTool::Nikon::Main',
+	'Image::ExifTool::Nikon::MultiExposure',
+	'Image::ExifTool::Nikon::PreviewImage',
+	'Image::ExifTool::Nikon::ROC',
+	'Image::ExifTool::Nikon::Scan',
+	'Image::ExifTool::Nikon::Type2',
+	'Image::ExifTool::NikonCapture::Brightness',
+	'Image::ExifTool::NikonCapture::ColorBoost',
+	'Image::ExifTool::NikonCapture::CropData',
+	'Image::ExifTool::NikonCapture::DLightingHQ',
+	'Image::ExifTool::NikonCapture::DLightingHS',
+	'Image::ExifTool::NikonCapture::Main',
+	'Image::ExifTool::NikonCapture::NoiseReduction',
+	'Image::ExifTool::NikonCapture::PhotoEffects',
+	'Image::ExifTool::NikonCapture::RedEyeData',
+	'Image::ExifTool::NikonCapture::UnsharpData',
+	'Image::ExifTool::NikonCapture::WBAdjData',
+	'Image::ExifTool::Olympus::CameraSettings',
+	'Image::ExifTool::Olympus::Equipment',
+	'Image::ExifTool::Olympus::FETags',
+	'Image::ExifTool::Olympus::FocusInfo',
+	'Image::ExifTool::Olympus::ImageProcessing',
+	'Image::ExifTool::Olympus::Main',
+	'Image::ExifTool::Olympus::RawDevelopment',
+	'Image::ExifTool::Olympus::RawDevelopment2',
+	'Image::ExifTool::Olympus::RawInfo',
+	'Image::ExifTool::PNG::Main',
+	'Image::ExifTool::PNG::TextualData',
+	'Image::ExifTool::Panasonic::Main',
+	'Image::ExifTool::Panasonic::Raw',
+	'Image::ExifTool::Pentax::AEInfo',
+	'Image::ExifTool::Pentax::AFInfo',
+	'Image::ExifTool::Pentax::BatteryInfo',
+	'Image::ExifTool::Pentax::CameraInfo',
+	'Image::ExifTool::Pentax::FlashInfo',
+	'Image::ExifTool::Pentax::LensInfo',
+	'Image::ExifTool::Pentax::Main',
+	'Image::ExifTool::Pentax::SRInfo',
+	'Image::ExifTool::Pentax::Type2',
+	'Image::ExifTool::PhotoMechanic::SoftEdit',
+	'Image::ExifTool::PhotoMechanic::XMP',
+	'Image::ExifTool::Photoshop::JPEG_Quality',
+	'Image::ExifTool::Photoshop::Main',
+	'Image::ExifTool::Photoshop::Resolution',
+	'Image::ExifTool::PostScript::Main',
+	'Image::ExifTool::Ricoh::ImageInfo',
+	'Image::ExifTool::Ricoh::Main',
+	'Image::ExifTool::Sanyo::Main',
+	'Image::ExifTool::Sigma::Main',
+	'Image::ExifTool::Sony::Main',
+	'Image::ExifTool::XMP::DICOM',
+	'Image::ExifTool::XMP::Lightroom',
+	'Image::ExifTool::XMP::MediaPro',
+	'Image::ExifTool::XMP::Microsoft',
+	'Image::ExifTool::XMP::aux',
+	'Image::ExifTool::XMP::cc',
+	'Image::ExifTool::XMP::crs',
+	'Image::ExifTool::XMP::dc',
+	'Image::ExifTool::XMP::dex',
+	'Image::ExifTool::XMP::exif',
+	'Image::ExifTool::XMP::iptcCore',
+	'Image::ExifTool::XMP::pdf',
+	'Image::ExifTool::XMP::photoshop',
+	'Image::ExifTool::XMP::tiff',
+	'Image::ExifTool::XMP::xmp',
+	'Image::ExifTool::XMP::xmpBJ',
+	'Image::ExifTool::XMP::xmpDM',
+	'Image::ExifTool::XMP::xmpMM',
+	'Image::ExifTool::XMP::xmpPLUS',
+	'Image::ExifTool::XMP::xmpRights',
+	'Image::ExifTool::XMP::xmpTPg',
+);
+
+# lookup for all writable tags
+my %tagLookup = (
+	'abspeakaudiofilepath' => { 164 => 'absPeakAudioFilePath' },
+	'accessorytype' => { 126 => 0x53 },
+	'actionadvised' => { 54 => 0x2a },
+	'activearea' => { 48 => 0xc68d },
+	'addaspectratioinfo' => { 25 => 0x80e },
+	'addoriginaldecisiondata' => { 25 => 0x80f, 26 => 0x11, 27 => 0x13, 30 => 0x14 },
+	'address' => { 75 => 'Address' },
+	'adjustmentmode' => { 146 => [0x15,0x22] },
+	'advancedraw' => { 109 => 0x76a43203 },
+	'advisory' => { 162 => 'Advisory' },
+	'ae_iso' => { 128 => 0x2 },
+	'aeaperture' => { 128 => 0x1 },
+	'aebautocancel' => { 25 => 0x104 },
+	'aebbracketvalue' => { 22 => 0x11 },
+	'aebsequence' => { 23 => 0x9, 24 => 0x9, 25 => 0x105, 26 => 0x8, 27 => 0x9, 30 => 0x9, 31 => 0x7 },
+	'aebshotcount' => { 25 => 0x106 },
+	'aebxv' => { 128 => 0x4 },
+	'aeexposuretime' => { 128 => 0x0 },
+	'aeextra' => { 128 => 0x7 },
+	'aeflashtv' => { 128 => 0x5 },
+	'aelock' => { 115 => 0x201 },
+	'aemeteringsegments' => { 134 => 0x209 },
+	'aeprogrammode' => { 128 => 0x6 },
+	'aesetting' => { 4 => 0x21 },
+	'aexv' => { 128 => 0x3 },
+	'afareamode' => { 92 => 0x0 },
+	'afareas' => { 115 => 0x304 },
+	'afassist' => { 23 => 0x5, 31 => 0x5 },
+	'afassistbeam' => { 25 => 0x50e, 26 => 0x4, 27 => 0x5, 28 => 0x4, 29 => 0x4, 30 => 0x5 },
+	'afduringliveview' => { 25 => 0x511 },
+	'afexpansionwithselectedpoint' => { 25 => 0x508 },
+	'afmicroadjustment' => { 25 => 0x507 },
+	'afmode' => { 126 => 0xf, 146 => 0x5 },
+	'afonaelockbuttonswitch' => { 25 => 0x702 },
+	'afpoint' => { 4 => 0x13, 44 => 0x18, 92 => 0x1, 118 => 0x308 },
+	'afpointactivationarea' => { 24 => 0x11, 30 => 0x11 },
+	'afpointautoselection' => { 25 => 0x50b },
+	'afpointbrightness' => { 25 => 0x50d },
+	'afpointdisplayduringfocus' => { 25 => 0x50c },
+	'afpointillumination' => { 24 => 0xa },
+	'afpointregistration' => { 23 => 0x7 },
+	'afpoints' => { 86 => 0x10 },
+	'afpointselected' => { 134 => 0xe },
+	'afpointselection' => { 24 => 0xb },
+	'afpointselectionmethod' => { 25 => 0x50f, 26 => 0xc, 27 => 0xd, 30 => 0xd },
+	'afpointsinfocus' => { 22 => 0xe, 92 => 0x2, 129 => 0xb, 134 => [0xf,0x3c] },
+	'afpointsinfocus5d' => { 1 => 0x38 },
+	'afpointspotmetering' => { 24 => 0xd },
+	'afresponse' => { 98 => 0xad },
+	'afresult' => { 120 => 0x1038 },
+	'afsearch' => { 115 => 0x303 },
+	'aiservoimagepriority' => { 25 => 0x503 },
+	'aiservotrackingmethod' => { 25 => 0x504 },
+	'aiservotrackingsensitivity' => { 24 => 0x14, 25 => 0x502 },
+	'album' => { 164 => 'album' },
+	'alreadyapplied' => { 154 => 'AlreadyApplied' },
+	'alttapename' => { 164 => 'altTapeName' },
+	'alttimecodetimeformat' => { 164 => 'altTimecodeTimeFormat' },
+	'alttimecodetimevalue' => { 164 => 'altTimecodeTimeValue' },
+	'antialiasstrength' => { 48 => 0xc632 },
+	'aperturerange' => { 25 => 0x10d },
+	'aperturevalue' => { 48 => 0x9202, 120 => 0x1002, 157 => 'ApertureValue' },
+	'applicationrecordversion' => { 54 => 0x0 },
+	'applyshootingmeteringmode' => { 25 => 0x10e },
+	'armidentifier' => { 55 => 0x78 },
+	'armversion' => { 55 => 0x7a },
+	'artist' => { 48 => 0x13b, 125 => 'Artist', 161 => 'Artist', 164 => 'artist' },
+	'asshoticcprofile' => { 48 => 0xc68f },
+	'asshotpreprofilematrix' => { 48 => 0xc690 },
+	'asshotwhitexy' => { 48 => 0xc629 },
+	'assistbuttonfunction' => { 23 => 0xd },
+	'audio' => { 126 => 0x20 },
+	'audiochanneltype' => { 164 => 'audioChannelType' },
+	'audiocompression' => { 69 => 'Compression' },
+	'audiocompressor' => { 164 => 'audioCompressor' },
+	'audioduration' => { 54 => 0x99 },
+	'audiomoddate' => { 164 => 'audioModDate' },
+	'audiooutcue' => { 54 => 0x9a },
+	'audiosamplerate' => { 164 => 'audioSampleRate' },
+	'audiosampletype' => { 164 => 'audioSampleType' },
+	'audiosamplingrate' => { 54 => 0x97 },
+	'audiosamplingresolution' => { 54 => 0x98 },
+	'audiotype' => { 54 => 0x96 },
+	'author' => { 71 => 'Author', 125 => 'Author', 142 => 'Author', 159 => 'Author' },
+	'authorsposition' => { 160 => 'AuthorsPosition' },
+	'autobracket' => { 146 => 0x19 },
+	'autobracketing' => { 52 => 0x1100, 134 => 0x18 },
+	'autobracketrelease' => { 98 => 0x8a },
+	'autobrightness' => { 154 => 'AutoBrightness' },
+	'autocontrast' => { 154 => 'AutoContrast' },
+	'autoexposure' => { 154 => 'AutoExposure' },
+	'autoexposurebracketing' => { 22 => 0x10 },
+	'autofocus' => { 118 => 0x209 },
+	'autoiso' => { 22 => 0x1, 45 => 0x3008 },
+	'autorotate' => { 22 => 0x1b },
+	'autoshadows' => { 154 => 'AutoShadows' },
+	'auxiliarylens' => { 98 => 0x82 },
+	'avsettingwithoutlens' => { 25 => 0x707 },
+	'avvalue' => { 34 => 0x2 },
+	'azimuth' => { 79 => 'Azimuth' },
+	'babyage' => { 126 => [0x8010,0x33] },
+	'baseiso' => { 22 => 0x2, 38 => 0x101c },
+	'baselineexposure' => { 48 => 0xc62a },
+	'baselinenoise' => { 48 => 0xc62b },
+	'baselinesharpness' => { 48 => 0xc62c },
+	'baseurl' => { 162 => 'BaseURL' },
+	'batteryadbodyload' => { 130 => 0x3 },
+	'batteryadbodynoload' => { 130 => 0x2 },
+	'batteryadgripload' => { 130 => 0x5 },
+	'batteryadgripnoload' => { 130 => 0x4 },
+	'batterybodygripstates' => { 130 => 0x1 },
+	'batterytype' => { 130 => 0x0 },
+	'bayergreensplit' => { 48 => 0xc62d },
+	'bayerpattern' => { 58 => 0xf902, 89 => 0x17 },
+	'beatspliceparamsriseindecibel' => { 164 => 'beatSpliceParamsRiseInDecibel' },
+	'beatspliceparamsriseintimeduration' => { 164 => 'beatSpliceParamsRiseInTimeDuration' },
+	'beatspliceparamsusefilebeatsmarker' => { 164 => 'beatSpliceParamsUseFileBeatsMarker' },
+	'bestqualityscale' => { 48 => 0xc65c },
+	'bestshotmode' => { 45 => 0x3007 },
+	'bitdepth' => { 76 => 'BitDepth', 89 => 0x11, 102 => 0x41 },
+	'bitspercomponent' => { 56 => 0x87 },
+	'bitspersample' => { 48 => 0x102, 161 => 'BitsPerSample' },
+	'blacklevel' => { 120 => 0x1012 },
+	'blacklevel2' => { 123 => 0x600 },
+	'blackpoint' => { 134 => 0x200 },
+	'bluebalance' => { 120 => 0x1018, 127 => 0x12, 134 => 0x1b },
+	'bluecurvelimits' => { 42 => 0x1fe },
+	'bluecurvepoints' => { 42 => 0x1d4 },
+	'bluehue' => { 154 => 'BlueHue' },
+	'bluesaturation' => { 154 => 'BlueSaturation' },
+	'blurwarning' => { 52 => 0x1300 },
+	'bodyfirmwareversion' => { 116 => 0x104, 117 => 0x100, 120 => 0x104 },
+	'bracketmode' => { 13 => 0x3 },
+	'bracketsequence' => { 45 => 0x301d },
+	'bracketshotnumber' => { 13 => 0x5 },
+	'bracketstep' => { 84 => 0xe },
+	'bracketvalue' => { 13 => 0x4 },
+	'brightness' => { 48 => 0xfe53, 70 => 'Brightness', 84 => 0x2c, 154 => 'Brightness' },
+	'brightnessadj' => { 42 => 0x114, 104 => 0x0 },
+	'brightnessvalue' => { 48 => 0x9203, 120 => 0x1003, 157 => 'BrightnessValue' },
+	'bulbduration' => { 22 => 0x18 },
+	'burstmode' => { 59 => 0xa, 126 => 0x2a },
+	'burstmode2' => { 59 => 0x18 },
+	'buttonfunctioncontroloff' => { 25 => 0x70a },
+	'bwfilter' => { 84 => 0x2a, 90 => 0x39 },
+	'bwmode' => { 120 => 0x203 },
+	'by-line' => { 54 => 0x50 },
+	'by-linetitle' => { 54 => 0x55 },
+	'calibrationilluminant1' => { 48 => 0xc65a },
+	'calibrationilluminant2' => { 48 => 0xc65b },
+	'cameraid' => { 120 => 0x209, 145 => 0x209 },
+	'cameraiso' => { 4 => 0x10 },
+	'cameraparameters' => { 120 => 0x2050 },
+	'cameraprofile' => { 154 => 'CameraProfile' },
+	'cameraprofiledigest' => { 154 => 'CameraProfileDigest' },
+	'cameraserialnumber' => { 48 => 0xc62f, 151 => 'CameraSerialNumber' },
+	'camerasettingsversion' => { 115 => 0x0 },
+	'cameratemperature' => { 134 => 0x47 },
+	'cameratype' => { 22 => 0x1a, 120 => 0x207 },
+	'cameratype2' => { 116 => 0x100 },
+	'canonexposuremode' => { 4 => 0x14 },
+	'canonfiledescription' => { 38 => 0x805 },
+	'canonfilelength' => { 16 => 0xe },
+	'canonfirmwareversion' => { 16 => 0x7, 38 => 0x80b },
+	'canonflashinfo' => { 38 => 0x1028 },
+	'canonflashmode' => { 4 => 0x4 },
+	'canonimagesize' => { 4 => 0xa },
+	'canonimagetype' => { 16 => 0x6, 38 => 0x815 },
+	'canonmodelid' => { 16 => 0x10, 38 => 0x1834 },
+	'canonvrd' => { 49 => 'CanonVRD' },
+	'caption-abstract' => { 54 => 0x78 },
+	'captionwriter' => { 160 => 'CaptionWriter' },
+	'casioimagesize' => { 45 => 0x9 },
+	'catalogsets' => { 150 => 'CatalogSets' },
+	'category' => { 54 => 0xf, 160 => 'Category' },
+	'ccdscanmode' => { 120 => 0x1039 },
+	'ccdsensitivity' => { 103 => 0x6 },
+	'celllength' => { 48 => 0x109 },
+	'cellwidth' => { 48 => 0x108 },
+	'certificate' => { 167 => 'Certificate' },
+	'cfapattern' => { 48 => 0xa302 },
+	'cfapatterncolumns' => { 157 => 'CFAPatternColumns' },
+	'cfapatternrows' => { 157 => 'CFAPatternRows' },
+	'cfapatternvalues' => { 157 => 'CFAPatternValues' },
+	'channels' => { 69 => 'Channels' },
+	'checkmark' => { 42 => 0x26a },
+	'chromablurradius' => { 48 => 0xc631 },
+	'chromaticaberrationb' => { 154 => 'ChromaticAberrationB' },
+	'chromaticaberrationr' => { 154 => 'ChromaticAberrationR' },
+	'chrominancenoisereduction' => { 43 => 0xbc },
+	'chrominancenr_tiff_jpeg' => { 43 => 0xc0 },
+	'city' => { 54 => 0x5a, 75 => 'City', 160 => 'City' },
+	'clarity' => { 154 => 'Clarity' },
+	'classifystate' => { 54 => 0xe1 },
+	'cmcontrast' => { 123 => 0x2022 },
+	'cmexposurecompensation' => { 123 => 0x2000 },
+	'cmhue' => { 123 => 0x2021 },
+	'cmsaturation' => { 123 => 0x2020 },
+	'cmsharpness' => { 123 => 0x2023 },
+	'cmwhitebalance' => { 123 => 0x2001 },
+	'cmwhitebalancecomp' => { 123 => 0x2002 },
+	'cmwhitebalancegraypoint' => { 123 => 0x2010 },
+	'codec' => { 83 => 'Codec' },
+	'codedcharacterset' => { 55 => 0x5a },
+	'coloraberrationcontrol' => { 109 => 0xc89224b },
+	'coloradjustment' => { 146 => 0x14 },
+	'coloradjustmentmode' => { 145 => 0x210 },
+	'colorantsa' => { 168 => 'ColorantsA' },
+	'colorantsb' => { 168 => 'ColorantsB' },
+	'colorantsblack' => { 168 => 'ColorantsBlack' },
+	'colorantsblue' => { 168 => 'ColorantsBlue' },
+	'colorantscyan' => { 168 => 'ColorantsCyan' },
+	'colorantsgreen' => { 168 => 'ColorantsGreen' },
+	'colorantsl' => { 168 => 'ColorantsL' },
+	'colorantsmagenta' => { 168 => 'ColorantsMagenta' },
+	'colorantsmode' => { 168 => 'ColorantsMode' },
+	'colorantsred' => { 168 => 'ColorantsRed' },
+	'colorantsswatchname' => { 168 => 'ColorantsSwatchName' },
+	'colorantstype' => { 168 => 'ColorantsType' },
+	'colorantsyellow' => { 168 => 'ColorantsYellow' },
+	'colorbalance' => { 70 => 'ColorBalance' },
+	'colorbalance1' => { 98 => 0xc },
+	'colorbalanceadj' => { 109 => 0x76a43202 },
+	'colorbalanceblue' => { 84 => 0x1e },
+	'colorbalancegreen' => { 84 => 0x1d },
+	'colorbalancered' => { 84 => 0x1c },
+	'colorbooster' => { 109 => 0x5f0e7d23 },
+	'colorboostlevel' => { 105 => 0x1 },
+	'colorboosttype' => { 105 => 0x0 },
+	'colorclass' => { 137 => 0xde },
+	'colorcontrol' => { 120 => 0x102b },
+	'coloreffect' => { 126 => 0x28 },
+	'colorfilter' => { 44 => 0x17, 84 => 0x29, 90 => 0x38, 136 => 0x17 },
+	'colorgain' => { 102 => 0x51 },
+	'colorhue' => { 9 => 0x2, 98 => 0x8d },
+	'colormatrix' => { 10 => 0xa, 119 => 0x200, 120 => 0x1011 },
+	'colormatrix2' => { 123 => 0x200 },
+	'colormatrixnumber' => { 120 => 0x1019 },
+	'colormode' => { 45 => 0x3015, 52 => 0x1210, 59 => 0x66, 84 => 0x28, 87 => 0x16, 88 => 0x101, 90 => 0x7, 98 => 0x3, 103 => 0x4, 120 => 0x101, 126 => 0x32, 147 => 0xb029, 160 => 'ColorMode' },
+	'colormoirereduction' => { 110 => 0x5 },
+	'colornoisereduction' => { 154 => 'ColorNoiseReduction' },
+	'colorplanes' => { 51 => 0x2 },
+	'colorprofile' => { 84 => 0x33 },
+	'colorrepresentation' => { 56 => 0x3c },
+	'colorsequence' => { 56 => 0x41 },
+	'colorspace' => { 9 => 0x3, 16 => 0xb4, 38 => 0x10b4, 48 => 0xa001, 76 => 'ColorSpace', 86 => 0x25, 98 => 0x1e, 115 => 0x507, 134 => 0x37, 146 => 0xb, 157 => 'ColorSpace' },
+	'colortempasshot' => { 6 => 0x1d, 7 => 0x1c, 8 => 0x43 },
+	'colortempauto' => { 6 => 0x22, 7 => 0x26, 8 => 0x48 },
+	'colortempcloudy' => { 6 => 0x31, 7 => 0x35, 8 => 0x5c },
+	'colortempcustom' => { 8 => 0x84 },
+	'colortempcustom1' => { 6 => 0x45 },
+	'colortempcustom2' => { 6 => 0x4a },
+	'colortempdaylight' => { 6 => 0x27, 7 => 0x2b, 8 => 0x52 },
+	'colortemperature' => { 10 => 0x9, 16 => 0xae, 21 => 0x9, 38 => 0x10ae, 52 => 0x1005, 70 => 'ColorTemperature', 85 => [0x6e,0x49], 86 => 0x3f, 87 => 0x5e, 88 => 0x10b, 90 => 0x3c },
+	'colortemperaturebg' => { 120 => 0x1013 },
+	'colortemperaturerg' => { 120 => 0x1014 },
+	'colortempflash' => { 6 => 0x40, 7 => 0x49, 8 => 0x70 },
+	'colortempfluorescent' => { 6 => 0x3b, 7 => 0x3f, 8 => 0x66 },
+	'colortempkelvin' => { 7 => 0x44, 8 => 0x6b },
+	'colortempmeasured' => { 8 => 0x4d },
+	'colortemppc1' => { 7 => 0x94, 8 => 0x75 },
+	'colortemppc2' => { 7 => 0x99, 8 => 0x7a },
+	'colortemppc3' => { 7 => 0x9e, 8 => 0x7f },
+	'colortempshade' => { 6 => 0x2c, 7 => 0x30, 8 => 0x57 },
+	'colortemptungsten' => { 6 => 0x36, 7 => 0x3a, 8 => 0x61 },
+	'colortempunknown' => { 7 => 0x21 },
+	'colortempunknown10' => { 7 => 0x76 },
+	'colortempunknown11' => { 7 => 0x7b },
+	'colortempunknown12' => { 7 => 0x80 },
+	'colortempunknown13' => { 7 => 0x85 },
+	'colortempunknown14' => { 7 => 0x8a },
+	'colortempunknown15' => { 7 => 0x8f },
+	'colortempunknown16' => { 7 => 0xa3 },
+	'colortempunknown2' => { 7 => 0x4e },
+	'colortempunknown3' => { 7 => 0x53 },
+	'colortempunknown4' => { 7 => 0x58 },
+	'colortempunknown5' => { 7 => 0x5d },
+	'colortempunknown6' => { 7 => 0x62 },
+	'colortempunknown7' => { 7 => 0x67 },
+	'colortempunknown8' => { 7 => 0x6c },
+	'colortempunknown9' => { 7 => 0x71 },
+	'colortone' => { 4 => 0x2a },
+	'colortoneadj' => { 42 => 0x11e },
+	'colortonefaithful' => { 1 => 0x107 },
+	'colortonelandscape' => { 1 => 0x105 },
+	'colortoneneutral' => { 1 => 0x106 },
+	'colortoneportrait' => { 1 => 0x104 },
+	'colortonestandard' => { 1 => 0x103 },
+	'colortoneuserdef1' => { 1 => 0x109 },
+	'colortoneuserdef2' => { 1 => 0x10a },
+	'colortoneuserdef3' => { 1 => 0x10b },
+	'comment' => { 0 => 0x2, 49 => 'Comment', 71 => 'Comment', 125 => 'Comment' },
+	'componentsconfiguration' => { 48 => 0x9101, 76 => 'Components', 157 => 'ComponentsConfiguration' },
+	'componentversion' => { 38 => 0x80c },
+	'composer' => { 164 => 'composer' },
+	'compressedbitsperpixel' => { 48 => 0x9102, 157 => 'CompressedBitsPerPixel' },
+	'compressedimagesize' => { 88 => 0x40, 120 => 0x40 },
+	'compression' => { 48 => 0x103, 161 => 'Compression' },
+	'compressionfactor' => { 115 => 0x50d },
+	'compressionratio' => { 76 => 'Compression', 120 => 0x1034 },
+	'constrainedcropheight' => { 42 => 0x266, 137 => 0xd6 },
+	'constrainedcropwidth' => { 42 => 0x262, 137 => 0xd5 },
+	'contact' => { 54 => 0x76 },
+	'contentlocationcode' => { 54 => 0x1a },
+	'contentlocationname' => { 54 => 0x1b },
+	'continuousdrive' => { 4 => 0x5 },
+	'continuousshootingspeed' => { 25 => 0x610 },
+	'continuousshotlimit' => { 25 => 0x611 },
+	'contrast' => { 4 => 0xd, 44 => 0xc, 45 => [0x3012,0x20], 48 => [0xa408,0xfe54], 52 => 0x1004, 70 => 'Contrast', 84 => 0x20, 85 => 0x31, 86 => 0x27, 87 => 0x19, 90 => 0x2, 120 => 0x1029, 126 => 0x2c, 134 => 0x20, 136 => 0xc, 146 => 0xd, 154 => 'Contrast', 157 => 'Contrast' },
+	'contrastadj' => { 42 => 0x115 },
+	'contrastfaithful' => { 1 => 0xec },
+	'contrastlandscape' => { 1 => 0xea },
+	'contrastmonochrome' => { 1 => 0xed },
+	'contrastneutral' => { 1 => 0xeb },
+	'contrastportrait' => { 1 => 0xe9 },
+	'contrastsetting' => { 115 => 0x505, 123 => 0x1012 },
+	'contraststandard' => { 1 => 0xe8 },
+	'contrastuserdef1' => { 1 => 0xee },
+	'contrastuserdef2' => { 1 => 0xef },
+	'contrastuserdef3' => { 1 => 0xf0 },
+	'contributedmediaduration' => { 164 => 'contributedMediaDuration' },
+	'contributedmediamanaged' => { 164 => 'contributedMediaManaged' },
+	'contributedmediapath' => { 164 => 'contributedMediaPath' },
+	'contributedmediastarttime' => { 164 => 'contributedMediaStartTime' },
+	'contributedmediatrack' => { 164 => 'contributedMediaTrack' },
+	'contributedmediawebstatement' => { 164 => 'contributedMediaWebStatement' },
+	'contributor' => { 155 => 'contributor' },
+	'contributors' => { 71 => 'Contributors' },
+	'conversionlens' => { 126 => 0x35 },
+	'converter' => { 48 => 0xfe4d, 103 => 0xb },
+	'converttograyscale' => { 154 => 'ConvertToGrayscale' },
+	'copyright' => { 0 => 0x3, 48 => 0x8298, 71 => 'Copyright', 125 => 'Copyright', 142 => 'Copyright', 161 => 'Copyright', 164 => 'copyright' },
+	'copyrightflag' => { 140 => 0x40a },
+	'copyrightnotice' => { 54 => 0x74 },
+	'coringfilter' => { 120 => 0x102d, 123 => 0x310 },
+	'coringvalues' => { 123 => 0x311 },
+	'country' => { 75 => 'Country', 160 => 'Country' },
+	'country-primarylocationcode' => { 54 => 0x64 },
+	'country-primarylocationname' => { 54 => 0x65 },
+	'countrycode' => { 138 => 'CountryCode', 158 => 'CountryCode' },
+	'coverage' => { 155 => 'coverage' },
+	'crc32' => { 156 => 'crc32' },
+	'createdate' => { 48 => 0x9004, 71 => 'CreateDate', 142 => 'CreationDate', 162 => 'CreateDate' },
+	'creationdate' => { 159 => 'CreationDate' },
+	'creationtime' => { 125 => 'Creation Time' },
+	'creator' => { 142 => 'Creator', 155 => 'creator', 159 => 'Creator' },
+	'creatorcontactinfociadrcity' => { 158 => 'CreatorContactInfoCiAdrCity' },
+	'creatorcontactinfociadrctry' => { 158 => 'CreatorContactInfoCiAdrCtry' },
+	'creatorcontactinfociadrextadr' => { 158 => 'CreatorContactInfoCiAdrExtadr' },
+	'creatorcontactinfociadrpcode' => { 158 => 'CreatorContactInfoCiAdrPcode' },
+	'creatorcontactinfociadrregion' => { 158 => 'CreatorContactInfoCiAdrRegion' },
+	'creatorcontactinfociemailwork' => { 158 => 'CreatorContactInfoCiEmailWork' },
+	'creatorcontactinfocitelwork' => { 158 => 'CreatorContactInfoCiTelWork' },
+	'creatorcontactinfociurlwork' => { 158 => 'CreatorContactInfoCiUrlWork' },
+	'creatortool' => { 162 => 'CreatorTool' },
+	'credit' => { 54 => 0x6e, 160 => 'Credit' },
+	'creditlinereq' => { 166 => 'CreditLineReq' },
+	'cropactive' => { 42 => 0x244 },
+	'cropangle' => { 154 => 'CropAngle' },
+	'cropaspectratio' => { 42 => 0x260 },
+	'cropbottom' => { 51 => 0x9, 106 => 0x36, 137 => 0xdc, 154 => 'CropBottom' },
+	'cropheight' => { 42 => 0x24c, 154 => 'CropHeight' },
+	'crophispeed' => { 98 => 0x1b },
+	'cropleft' => { 42 => 0x246, 51 => 0x6, 106 => 0x1e, 137 => 0xd9, 154 => 'CropLeft' },
+	'cropoutputscale' => { 106 => 0xbe },
+	'cropright' => { 51 => 0x8, 106 => 0x2e, 137 => 0xdb, 154 => 'CropRight' },
+	'croprotation' => { 51 => 0xb },
+	'croptop' => { 42 => 0x248, 51 => 0x7, 106 => 0x26, 137 => 0xda, 154 => 'CropTop' },
+	'cropunits' => { 154 => 'CropUnits' },
+	'cropwidth' => { 42 => 0x24a, 154 => 'CropWidth' },
+	'currenticcprofile' => { 48 => 0xc691 },
+	'currentpreprofilematrix' => { 48 => 0xc692 },
+	'curves' => { 109 => 0x76a43201 },
+	'customrendered' => { 48 => 0xa401, 157 => 'CustomRendered' },
+	'customsaturation' => { 115 => 0x503 },
+	'd-lightinghq' => { 109 => 0x2175eb78 },
+	'd-lightinghqselected' => { 109 => 0x6a6e36b6 },
+	'd-lightinghs' => { 109 => 0xce5554aa },
+	'd-lightinghsadjustment' => { 108 => 0x0 },
+	'd-lightinghscolorboost' => { 107 => 0x2, 108 => 0x1 },
+	'd-lightinghshighlight' => { 107 => 0x1 },
+	'd-lightinghsshadow' => { 107 => 0x0 },
+	'datacompressionmethod' => { 56 => 0x6e },
+	'dataimprint' => { 84 => 0x34 },
+	'date' => { 134 => 0x6, 155 => 'date' },
+	'dateacquired' => { 151 => 'DateAcquired' },
+	'datecreated' => { 54 => 0x37, 160 => 'DateCreated' },
+	'datesent' => { 55 => 0x46 },
+	'datestampmode' => { 16 => 0x1c },
+	'datetime' => { 161 => 'DateTime' },
+	'datetimedigitized' => { 157 => 'DateTimeDigitized' },
+	'datetimeoriginal' => { 41 => 0x0, 48 => 0x9003, 71 => 'OriginalDate', 157 => 'DateTimeOriginal' },
+	'datetimestamp' => { 59 => 0x64 },
+	'declination' => { 79 => 'Declination' },
+	'decposition' => { 84 => 0x32 },
+	'defaultcroporigin' => { 48 => 0xc61f },
+	'defaultcropsize' => { 48 => 0xc620 },
+	'defaultscale' => { 48 => 0xc61e },
+	'defringe' => { 154 => 'Defringe' },
+	'deletedimagecount' => { 98 => 0xa6 },
+	'derivedfromdocumentid' => { 165 => 'DerivedFromDocumentID' },
+	'derivedfrominstanceid' => { 165 => 'DerivedFromInstanceID' },
+	'derivedfrommanager' => { 165 => 'DerivedFromManager' },
+	'derivedfrommanagervariant' => { 165 => 'DerivedFromManagerVariant' },
+	'derivedfrommanageto' => { 165 => 'DerivedFromManageTo' },
+	'derivedfrommanageui' => { 165 => 'DerivedFromManageUI' },
+	'derivedfromrenditionclass' => { 165 => 'DerivedFromRenditionClass' },
+	'derivedfromrenditionparams' => { 165 => 'DerivedFromRenditionParams' },
+	'derivedfromversionid' => { 165 => 'DerivedFromVersionID' },
+	'description' => { 125 => 'Description', 155 => 'description' },
+	'destination' => { 55 => 0x5 },
+	'destinationcity' => { 134 => 0x24 },
+	'destinationcitycode' => { 136 => 0x1001 },
+	'destinationdst' => { 134 => 0x26 },
+	'developmentdynamicrange' => { 52 => 0x1403 },
+	'devicesettingdescriptioncolumns' => { 157 => 'DeviceSettingDescriptionColumns' },
+	'devicesettingdescriptionrows' => { 157 => 'DeviceSettingDescriptionRows' },
+	'devicesettingdescriptionsettings' => { 157 => 'DeviceSettingDescriptionSettings' },
+	'dialdirectiontvav' => { 25 => 0x706 },
+	'digitalcreationdate' => { 54 => 0x3e },
+	'digitalcreationtime' => { 54 => 0x3f },
+	'digitalgain' => { 21 => 0xb },
+	'digitalgem' => { 96 => 0x0 },
+	'digitalice' => { 102 => 0x100 },
+	'digitalroc' => { 101 => 0x0 },
+	'digitalzoom' => { 4 => 0xc, 44 => 0xa, 59 => 0x68, 70 => 'DigitalZoom', 84 => 0xc, 98 => 0x86, 103 => 0xa, 120 => 0x204, 134 => 0x1e, 136 => 0xa, 145 => 0x204 },
+	'digitalzoomon' => { 145 => 0x21b },
+	'digitalzoomratio' => { 48 => 0xa404, 157 => 'DigitalZoomRatio' },
+	'directory' => { 49 => 'Directory' },
+	'disclaimer' => { 125 => 'Disclaimer' },
+	'displayaperture' => { 4 => 0x23 },
+	'displayedunitsx' => { 141 => 0x2 },
+	'displayedunitsy' => { 141 => 0x6 },
+	'distance1' => { 59 => 0x28 },
+	'distance2' => { 59 => 0x2c },
+	'distance3' => { 59 => 0x30 },
+	'distance4' => { 59 => 0x34 },
+	'distortioncorrection' => { 115 => 0x50b },
+	'distortioncorrection2' => { 119 => 0x1011 },
+	'dngbackwardversion' => { 48 => 0xc613 },
+	'dnglensinfo' => { 48 => 0xc630 },
+	'dngversion' => { 48 => 0xc612 },
+	'document' => { 125 => 'Document' },
+	'documenthistory' => { 54 => 0xe7 },
+	'documentid' => { 165 => 'DocumentID' },
+	'documentname' => { 48 => 0x10d },
+	'documentnotes' => { 54 => 0xe6 },
+	'dotrange' => { 48 => 0x150 },
+	'drivemode' => { 84 => 0x6, 87 => 0x1e, 115 => 0x600, 134 => 0x34, 146 => 0x3 },
+	'duration' => { 69 => 'Duration', 83 => 'Duration', 164 => 'duration' },
+	'dynamicrange' => { 52 => 0x1400 },
+	'dynamicrangemax' => { 42 => 0x7c },
+	'dynamicrangemin' => { 42 => 0x7a },
+	'dynamicrangesetting' => { 52 => 0x1402 },
+	'easymode' => { 4 => 0xb },
+	'edgenoisereduction' => { 110 => 0x4 },
+	'editorialupdate' => { 54 => 0x8 },
+	'editstatus' => { 54 => 0x7, 138 => 'EditStatus' },
+	'elevation' => { 79 => 'Elevation' },
+	'email' => { 71 => 'EMail' },
+	'engineer' => { 164 => 'engineer' },
+	'enhancedarktones' => { 104 => 0x8 },
+	'enhancement' => { 44 => 0x16, 45 => 0x3016 },
+	'envelopenumber' => { 55 => 0x28 },
+	'envelopepriority' => { 55 => 0x3c },
+	'enveloperecordversion' => { 55 => 0x0 },
+	'epsonimageheight' => { 120 => 0x20c },
+	'epsonimagewidth' => { 120 => 0x20b },
+	'epsonsoftware' => { 120 => 0x20d },
+	'equipmentinstitution' => { 148 => 'EquipmentInstitution' },
+	'equipmentmanufacturer' => { 148 => 'EquipmentManufacturer' },
+	'equipmentversion' => { 116 => 0x0 },
+	'ettlii' => { 25 => 0x304, 26 => 0xd, 27 => 0xe, 28 => 0x7, 29 => 0x7, 30 => 0xe },
+	'event' => { 150 => 'Event' },
+	'excursiontolerance' => { 56 => 0x82 },
+	'exifbyteorder' => { 49 => 'ExifByteOrder' },
+	'exifcamerainfo' => { 54 => 0xe8 },
+	'exifimagelength' => { 48 => 0xa003, 157 => 'PixelYDimension' },
+	'exifimagewidth' => { 48 => 0xa002, 157 => 'PixelXDimension' },
+	'exifversion' => { 48 => 0x9000, 157 => 'ExifVersion' },
+	'expirationdate' => { 54 => 0x25 },
+	'expirationtime' => { 54 => 0x26 },
+	'exposure' => { 48 => 0xfe51, 154 => 'Exposure' },
+	'exposurebracketvalue' => { 98 => 0x19 },
+	'exposurecompensation' => { 22 => 0x6, 34 => 0x0, 48 => 0x9204, 59 => 0x24, 70 => 'ExposureComp', 84 => 0xd, 85 => 0x53, 86 => 0x1e, 87 => 0x1, 120 => 0x1006, 134 => 0x16, 146 => [0xc,0x35], 157 => 'ExposureBiasValue' },
+	'exposuredifference' => { 98 => 0xe },
+	'exposureindex' => { 48 => 0xa215, 157 => 'ExposureIndex' },
+	'exposurelevelincrements' => { 23 => 0x6, 24 => 0x6, 25 => 0x101, 26 => 0x5, 27 => 0x6, 28 => 0x5, 29 => 0x5, 30 => 0x6, 31 => 0x4 },
+	'exposuremode' => { 48 => 0xa402, 70 => 'ExposureMode', 84 => 0x1, 85 => 0xa, 86 => 0x0, 87 => 0x0, 115 => 0x200, 146 => 0x8, 157 => 'ExposureMode' },
+	'exposuremodeinmanual' => { 25 => 0x10b },
+	'exposureprogram' => { 48 => 0x8822, 157 => 'ExposureProgram' },
+	'exposuretime' => { 1 => 0x4, 2 => 0x4, 3 => 0x6, 22 => 0x16, 48 => 0x829a, 58 => 0xfd05, 59 => 0x20, 60 => 0xfa24, 62 => 0xf104, 66 => 0x38, 70 => 'ExposureTime', 84 => 0x9, 85 => 0x35, 86 => 0x48, 134 => 0x12, 146 => 0x32, 157 => 'ExposureTime' },
+	'exposurewarning' => { 52 => 0x1302 },
+	'extender' => { 116 => 0x301 },
+	'extenderfirmwareversion' => { 116 => 0x304 },
+	'extendermagnification' => { 72 => 'Magnification' },
+	'extendermake' => { 72 => 'Make' },
+	'extendermodel' => { 72 => 'Model', 116 => 0x303 },
+	'extenderserialnumber' => { 72 => 'SerialNumber', 116 => 0x302 },
+	'externalflash' => { 118 => 0x1201 },
+	'externalflashae1' => { 120 => 0x101f },
+	'externalflashae1_0' => { 120 => 0x101b },
+	'externalflashae2' => { 120 => 0x1020 },
+	'externalflashae2_0' => { 120 => 0x101c },
+	'externalflashbounce' => { 118 => 0x1204, 120 => 0x1026 },
+	'externalflashguidenumber' => { 118 => 0x1203 },
+	'externalflashgvalue' => { 120 => 0x1025 },
+	'externalflashmode' => { 120 => 0x1028, 132 => 0x2 },
+	'externalflashzoom' => { 118 => 0x1205, 120 => 0x1027 },
+	'face0position' => { 11 => 0x8 },
+	'face1position' => { 11 => 0xa },
+	'face2position' => { 11 => 0xc },
+	'face3position' => { 11 => 0xe },
+	'face4position' => { 11 => 0x10 },
+	'face5position' => { 11 => 0x12 },
+	'face6position' => { 11 => 0x14 },
+	'face7position' => { 11 => 0x16 },
+	'face8position' => { 11 => 0x18 },
+	'facedetectframeheight' => { 11 => 0x4 },
+	'facedetectframewidth' => { 11 => 0x3 },
+	'facesdetected' => { 11 => 0x2, 12 => 0x2 },
+	'ffid' => { 156 => 'ffid' },
+	'fieldcount' => { 120 => 0x103f },
+	'filedatarate' => { 164 => 'fileDataRate' },
+	'fileformat' => { 36 => 0x0, 55 => 0x14 },
+	'filemodifydate' => { 49 => 'FileModifyDate' },
+	'filename' => { 49 => 'FileName' },
+	'filenumber' => { 2 => 0x172, 13 => 0x1, 16 => 0x8, 38 => 0x1817 },
+	'filenumbermemory' => { 84 => 0x1a },
+	'filesource' => { 48 => 0xa300, 52 => 0x8000, 157 => 'FileSource' },
+	'fileversion' => { 55 => 0x16 },
+	'fillflashautoreduction' => { 23 => 0xe, 24 => 0xe, 31 => 0xa },
+	'filllight' => { 154 => 'FillLight' },
+	'fillorder' => { 48 => 0x10a },
+	'filmmode' => { 52 => 0x1401, 126 => 0x42 },
+	'filmtype' => { 102 => 0x2 },
+	'filter' => { 45 => 0x3017 },
+	'filtereffect' => { 13 => 0xe },
+	'filtereffectmonochrome' => { 1 => 0xff },
+	'finalimageheight' => { 123 => 0x615 },
+	'finalimagewidth' => { 123 => 0x614 },
+	'finderdisplayduringexposure' => { 24 => 0x1 },
+	'firmware' => { 120 => 0x405, 146 => [0x17,0x3b], 152 => 'Firmware' },
+	'firmwaredate' => { 45 => 0x2001 },
+	'firmwareid' => { 89 => 0x0 },
+	'firmwarerevision' => { 1 => 0xa4, 16 => 0x1e },
+	'firmwareversion' => { 70 => 'FirmwareVersion', 126 => 0x2 },
+	'fixtureidentifier' => { 54 => 0x16 },
+	'flash' => { 48 => 0x9209, 85 => 0x1f, 86 => 0x15 },
+	'flashactivity' => { 4 => 0x1c },
+	'flashadump' => { 134 => 0x20a },
+	'flashbdump' => { 134 => 0x20b },
+	'flashbias' => { 126 => 0x24 },
+	'flashbits' => { 4 => 0x1d },
+	'flashchargelevel' => { 120 => 0x1010 },
+	'flashcompensation' => { 152 => 'FlashCompensation' },
+	'flashdevice' => { 120 => 0x1005 },
+	'flashdistance' => { 45 => 0x2034 },
+	'flashenergy' => { 48 => 0xa20b, 157 => 'FlashEnergy' },
+	'flashexposurebracketvalue' => { 98 => 0x18 },
+	'flashexposurecomp' => { 22 => 0xf, 73 => 'ExposureComp', 84 => 0x23, 88 => 0x104, 98 => 0x12, 120 => 0x1023, 134 => 0x4d, 146 => 0x3a },
+	'flashexposurecompensation' => { 115 => 0x401 },
+	'flashfired' => { 59 => 0x5d, 73 => 'Fired', 84 => 0x14, 157 => 'FlashFired' },
+	'flashfiring' => { 25 => 0x306, 26 => 0x6, 27 => 0x7, 30 => 0x7 },
+	'flashfirmwareversion' => { 116 => 0x1002 },
+	'flashfunction' => { 157 => 'FlashFunction' },
+	'flashguidenumber' => { 22 => 0xd, 35 => 0x0, 73 => 'GuideNumber' },
+	'flashintensity' => { 44 => [0x19,0x5] },
+	'flashmake' => { 73 => 'Make' },
+	'flashmanufacturer' => { 151 => 'FlashManufacturer' },
+	'flashmetering' => { 84 => 0x3f },
+	'flashmode' => { 44 => 0x4, 59 => 0x5c, 73 => 'Mode', 84 => 0x2, 98 => 0x87, 115 => 0x400, 120 => 0x1004, 134 => 0xc, 136 => 0x4, 143 => 0x20, 145 => 0x225, 157 => 'FlashMode' },
+	'flashmodecode' => { 132 => 0x1 },
+	'flashmodel' => { 73 => 'Model', 116 => 0x1001, 151 => 'FlashModel' },
+	'flashoutput' => { 22 => 0x21 },
+	'flashpixversion' => { 48 => 0xa000, 157 => 'FlashpixVersion' },
+	'flashredeyemode' => { 157 => 'FlashRedEyeMode' },
+	'flashreturn' => { 157 => 'FlashReturn' },
+	'flashserialnumber' => { 73 => 'SerialNumber', 116 => 0x1003 },
+	'flashsetting' => { 98 => 0x8 },
+	'flashstatus' => { 132 => 0x0 },
+	'flashstrength' => { 52 => 0x1011 },
+	'flashsyncspeedav' => { 23 => 0x3, 25 => 0x10f, 26 => 0x2, 27 => 0x3, 28 => 0x2, 29 => 0x2, 30 => 0x3, 31 => 0x6 },
+	'flashthreshold' => { 35 => 0x1 },
+	'flashtype' => { 73 => 'Type', 98 => 0x9, 116 => 0x1000 },
+	'flickerreduce' => { 145 => 0x218 },
+	'fliphorizontal' => { 109 => 0x76a43206 },
+	'fnumber' => { 1 => 0x3, 2 => 0x3, 3 => 0x5, 22 => 0x15, 48 => 0x829d, 58 => 0xfd04, 59 => 0x1e, 60 => 0xfa23, 62 => 0xf103, 66 => 0x3c, 77 => 'FNumber', 84 => 0xa, 85 => 0x36, 86 => 0x47, 134 => 0x13, 146 => 0x31, 157 => 'FNumber' },
+	'focallength' => { 1 => 0x9, 2 => 0x1d, 15 => 0x1, 45 => 0x1d, 48 => 0x920a, 77 => 'FocalLength', 84 => 0x12, 134 => 0x1d, 157 => 'FocalLength' },
+	'focallengthin35mmformat' => { 48 => 0xa405, 157 => 'FocalLengthIn35mmFilm' },
+	'focalplanediagonal' => { 116 => 0x103, 120 => 0x205 },
+	'focalplaneresolutionunit' => { 48 => 0xa210, 157 => 'FocalPlaneResolutionUnit' },
+	'focalplanexresolution' => { 48 => 0xa20e, 157 => 'FocalPlaneXResolution' },
+	'focalplanexsize' => { 15 => 0x2 },
+	'focalplaneyresolution' => { 48 => 0xa20f, 157 => 'FocalPlaneYResolution' },
+	'focalplaneysize' => { 15 => 0x3 },
+	'focaltype' => { 1 => 0x2d, 15 => 0x0 },
+	'focalunits' => { 4 => 0x19 },
+	'focus' => { 103 => 0x8 },
+	'focusarea' => { 84 => 0x31 },
+	'focuscontinuous' => { 4 => 0x20 },
+	'focusdistance' => { 77 => 'FocusDistance', 84 => 0x13, 118 => 0x305 },
+	'focusdistancelower' => { 22 => 0x14 },
+	'focusdistanceupper' => { 22 => 0x13 },
+	'focusinfoversion' => { 118 => 0x0 },
+	'focusingscreen' => { 24 => 0x0, 25 => 0x80b, 30 => 0x0 },
+	'focusmode' => { 4 => 0x7, 44 => 0x3, 45 => [0x3003,0xd], 52 => 0x1021, 59 => 0x38, 70 => 'FocusMode', 84 => 0x30, 86 => 0xe, 98 => 0x7, 115 => 0x301, 120 => 0x100b, 126 => 0x7, 134 => 0xd, 136 => 0x3 },
+	'focuspixel' => { 52 => 0x1023 },
+	'focusposition' => { 134 => 0x10 },
+	'focusprocess' => { 115 => 0x302 },
+	'focusrange' => { 4 => 0x12, 120 => 0x100a },
+	'focussetting' => { 146 => 0x6 },
+	'focusstepcount' => { 118 => 0x301, 120 => 0x100e },
+	'focuswarning' => { 52 => 0x1301 },
+	'foldername' => { 84 => 0x27 },
+	'fontschildfontfiles' => { 168 => 'FontsChildFontFiles' },
+	'fontscomposite' => { 168 => 'FontsComposite' },
+	'fontsfontface' => { 168 => 'FontsFontFace' },
+	'fontsfontfamily' => { 168 => 'FontsFontFamily' },
+	'fontsfontfilename' => { 168 => 'FontsFontFileName' },
+	'fontsfontname' => { 168 => 'FontsFontName' },
+	'fontsfonttype' => { 168 => 'FontsFontType' },
+	'fontsversionstring' => { 168 => 'FontsVersionString' },
+	'for' => { 142 => 'For' },
+	'format' => { 155 => 'format' },
+	'framenum' => { 137 => 0xd7 },
+	'framenumber' => { 52 => 0x8003, 134 => 0x29 },
+	'freebytes' => { 38 => 0x1 },
+	'freememorycardimages' => { 85 => [0x37,0x54], 86 => [0x2d,0x4a] },
+	'fujiflashmode' => { 52 => 0x1010 },
+	'fullsizeimage' => { 76 => 'data' },
+	'fullsizeimagename' => { 76 => '1Name' },
+	'fullsizeimagetype' => { 76 => '0Type' },
+	'gaincontrol' => { 48 => 0xa407, 157 => 'GainControl' },
+	'gamma' => { 48 => 0xa500 },
+	'gammacompensatedvalue' => { 56 => 0x91 },
+	'genre' => { 164 => 'genre' },
+	'globalaltitude' => { 140 => 0x419 },
+	'globalangle' => { 140 => 0x40d },
+	'gpsaltitude' => { 53 => 0x6, 74 => 'Altitude', 157 => 'GPSAltitude' },
+	'gpsaltituderef' => { 53 => 0x5, 157 => 'GPSAltitudeRef' },
+	'gpsareainformation' => { 53 => 0x1c, 157 => 'GPSAreaInformation' },
+	'gpsdatestamp' => { 53 => 0x1d },
+	'gpsdatetime' => { 74 => 'DateTime' },
+	'gpsdestbearing' => { 53 => 0x18, 74 => 'Bearing', 157 => 'GPSDestBearing' },
+	'gpsdestbearingref' => { 53 => 0x17, 157 => 'GPSDestBearingRef' },
+	'gpsdestdistance' => { 53 => 0x1a, 74 => 'Distance', 157 => 'GPSDestDistance' },
+	'gpsdestdistanceref' => { 53 => 0x19, 157 => 'GPSDestDistanceRef' },
+	'gpsdestlatitude' => { 53 => 0x14, 157 => 'GPSDestLatitude' },
+	'gpsdestlatituderef' => { 53 => 0x13 },
+	'gpsdestlongitude' => { 53 => 0x16, 157 => 'GPSDestLongitude' },
+	'gpsdestlongituderef' => { 53 => 0x15 },
+	'gpsdifferential' => { 53 => 0x1e, 74 => 'Differential', 157 => 'GPSDifferential' },
+	'gpsdop' => { 53 => 0xb, 157 => 'GPSDOP' },
+	'gpsimgdirection' => { 53 => 0x11, 157 => 'GPSImgDirection' },
+	'gpsimgdirectionref' => { 53 => 0x10, 157 => 'GPSImgDirectionRef' },
+	'gpslatitude' => { 53 => 0x2, 74 => 'Latitude', 157 => 'GPSLatitude' },
+	'gpslatituderef' => { 53 => 0x1 },
+	'gpslongitude' => { 53 => 0x4, 74 => 'Longitude', 157 => 'GPSLongitude' },
+	'gpslongituderef' => { 53 => 0x3 },
+	'gpsmapdatum' => { 53 => 0x12, 74 => 'Datum', 157 => 'GPSMapDatum' },
+	'gpsmeasuremode' => { 53 => 0xa, 74 => 'MeasureMode', 157 => 'GPSMeasureMode' },
+	'gpsprocessingmethod' => { 53 => 0x1b, 157 => 'GPSProcessingMethod' },
+	'gpssatellites' => { 53 => 0x8, 74 => 'Satellites', 157 => 'GPSSatellites' },
+	'gpsspeed' => { 53 => 0xd, 74 => 'Speed', 157 => 'GPSSpeed' },
+	'gpsspeedref' => { 53 => 0xc, 157 => 'GPSSpeedRef' },
+	'gpsstatus' => { 53 => 0x9, 157 => 'GPSStatus' },
+	'gpstimestamp' => { 53 => 0x7, 157 => 'GPSTimeStamp' },
+	'gpstrack' => { 53 => 0xf, 74 => 'Heading', 157 => 'GPSTrack' },
+	'gpstrackref' => { 53 => 0xe, 157 => 'GPSTrackRef' },
+	'gpsversionid' => { 53 => 0x0, 157 => 'GPSVersionID' },
+	'gradation' => { 115 => 0x50f },
+	'grayresponseunit' => { 48 => 0x122 },
+	'greencurvelimits' => { 42 => 0x1c4 },
+	'greencurvepoints' => { 42 => 0x19a },
+	'greenhue' => { 154 => 'GreenHue' },
+	'greensaturation' => { 154 => 'GreenSaturation' },
+	'halftonehints' => { 48 => 0x141 },
+	'hascrop' => { 154 => 'HasCrop' },
+	'hassettings' => { 154 => 'HasSettings' },
+	'headline' => { 54 => 0x69, 160 => 'Headline' },
+	'hierarchicalsubject' => { 149 => 'hierarchicalSubject' },
+	'highisonoisereduction' => { 25 => 0x202, 98 => 0xb1 },
+	'highlight' => { 146 => 0xf },
+	'highlightrecovery' => { 154 => 'HighlightRecovery' },
+	'highlighttonepriority' => { 25 => 0x203 },
+	'history' => { 160 => 'History' },
+	'historyaction' => { 165 => 'HistoryAction' },
+	'historyinstanceid' => { 165 => 'HistoryInstanceID' },
+	'historyparameters' => { 165 => 'HistoryParameters' },
+	'historysoftwareagent' => { 165 => 'HistorySoftwareAgent' },
+	'historywhen' => { 165 => 'HistoryWhen' },
+	'hometowncity' => { 45 => 0x3006, 134 => 0x23 },
+	'hometowncitycode' => { 136 => 0x1000 },
+	'hometowndst' => { 134 => 0x25 },
+	'hostcomputer' => { 48 => 0x13c },
+	'hue' => { 90 => 0x3b },
+	'hueadjustment' => { 98 => 0x92 },
+	'hueadjustmentaqua' => { 154 => 'HueAdjustmentAqua' },
+	'hueadjustmentblue' => { 154 => 'HueAdjustmentBlue' },
+	'hueadjustmentgreen' => { 154 => 'HueAdjustmentGreen' },
+	'hueadjustmentmagenta' => { 154 => 'HueAdjustmentMagenta' },
+	'hueadjustmentorange' => { 154 => 'HueAdjustmentOrange' },
+	'hueadjustmentpurple' => { 154 => 'HueAdjustmentPurple' },
+	'hueadjustmentred' => { 154 => 'HueAdjustmentRed' },
+	'hueadjustmentyellow' => { 154 => 'HueAdjustmentYellow' },
+	'huesetting' => { 123 => 0x1011 },
+	'icc_profile' => { 49 => 'ICC_Profile' },
+	'iccprofilename' => { 160 => 'ICCProfile' },
+	'identifier' => { 155 => 'identifier', 162 => 'Identifier' },
+	'imageadjustment' => { 98 => 0x80, 103 => 0x5 },
+	'imageareaoffset' => { 134 => 0x38 },
+	'imageauthentication' => { 98 => 0x20 },
+	'imageboundary' => { 98 => 0x16 },
+	'imagecount' => { 98 => 0xa5 },
+	'imagedatasize' => { 98 => 0xa2 },
+	'imagedescription' => { 48 => 0x10e, 161 => 'ImageDescription' },
+	'imagedustoff' => { 109 => 0xfe443a45 },
+	'imageheight' => { 48 => 0x101, 89 => 0xc, 161 => 'ImageLength' },
+	'imagehistory' => { 48 => 0x9213 },
+	'imagenumber' => { 1 => 0xd0, 48 => 0x9211, 70 => 'ImageNumber', 85 => 0xae, 86 => 0x5e, 152 => 'ImageNumber' },
+	'imagenumber2' => { 86 => 0x62 },
+	'imageoptimization' => { 98 => 0xa9 },
+	'imageorientation' => { 54 => 0x83 },
+	'imageprocessing' => { 98 => 0x1a, 134 => 0x32 },
+	'imageprocessingversion' => { 119 => 0x0 },
+	'imagequality' => { 70 => 'ImageQuality', 126 => 0x1 },
+	'imagequality2' => { 115 => 0x603 },
+	'imagesize' => { 76 => 'ImageSize' },
+	'imagesourcedata' => { 48 => 0x935c },
+	'imagestabilization' => { 4 => 0x22, 45 => 0x3020, 70 => 'ImageStabilization', 85 => 0xbd, 86 => 0x71, 87 => 0x87, 88 => [0x18,0x107,0x113], 98 => 0xac, 118 => 0x1600, 126 => 0x1a },
+	'imagetone' => { 134 => 0x4f },
+	'imagetype' => { 54 => 0x82 },
+	'imageuniqueid' => { 48 => 0xa420, 157 => 'ImageUniqueID' },
+	'imagewidth' => { 48 => 0x100, 89 => 0xe, 161 => 'ImageWidth' },
+	'incrementaltemperature' => { 154 => 'IncrementalTemperature' },
+	'incrementaltint' => { 154 => 'IncrementalTint' },
+	'infinitylensstep' => { 120 => 0x103b },
+	'infobuttonwhenshooting' => { 25 => 0x409 },
+	'inkset' => { 48 => 0x14c },
+	'instanceid' => { 165 => 'InstanceID' },
+	'instructions' => { 160 => 'Instructions' },
+	'instrument' => { 164 => 'instrument' },
+	'intellectualgenre' => { 158 => 'IntellectualGenre' },
+	'interchangecolorspace' => { 56 => 0x40 },
+	'internalflash' => { 84 => 0x2b, 118 => 0x1208 },
+	'internalflashae1' => { 120 => 0x1021 },
+	'internalflashae1_0' => { 120 => 0x101d },
+	'internalflashae2' => { 120 => 0x1022 },
+	'internalflashae2_0' => { 120 => 0x101e },
+	'internalflashmagni' => { 132 => 0x3 },
+	'internalflashtable' => { 120 => 0x1024 },
+	'internalserialnumber' => { 16 => 0x96, 52 => 0x10, 116 => 0x102, 126 => 0x25, 131 => 0x4 },
+	'interopindex' => { 48 => 0x1 },
+	'interopversion' => { 48 => 0x2 },
+	'intervallength' => { 84 => 0x10 },
+	'intervalmode' => { 84 => 0x26 },
+	'intervalnumber' => { 84 => 0x11 },
+	'introtime' => { 164 => 'introTime' },
+	'iptc-naa' => { 48 => 0x83bb },
+	'iptcbitspersample' => { 56 => 0x56 },
+	'iptcimageheight' => { 56 => 0x1e },
+	'iptcimagerotation' => { 56 => 0x66 },
+	'iptcimagewidth' => { 56 => 0x14 },
+	'iptcpicturenumber' => { 56 => 0xa },
+	'iptcpixelheight' => { 56 => 0x32 },
+	'iptcpixelwidth' => { 56 => 0x28 },
+	'iso' => { 1 => 0x6, 2 => 0x6, 3 => 0x0, 44 => 0x14, 45 => [0x3014,0x14], 48 => 0x8827, 58 => 0xfd06, 59 => 0x60, 60 => [0xfa2e,0xfa46], 61 => [0x27,0x28], 62 => 0xf105, 66 => 0x4e, 70 => 'ISO', 84 => 0x8, 98 => 0x2, 127 => 0x17, 134 => 0x14, 136 => 0x14, 157 => 'ISOSpeedRatings' },
+	'isoexpansion' => { 25 => 0x103, 26 => 0x7, 27 => 0x8, 30 => 0x8 },
+	'isoselection' => { 98 => 0xf },
+	'isosetting' => { 59 => 0x5e, 70 => 'ISOSetting', 84 => 0x24, 85 => 0x26, 86 => 0x1c, 90 => 0x6, 98 => 0x13, 143 => 0x27 },
+	'isospeedexpansion' => { 24 => 0x3 },
+	'isospeedincrements' => { 25 => 0x102 },
+	'isospeedrange' => { 25 => 0x103 },
+	'isovalue' => { 120 => 0x1001 },
+	'jobid' => { 54 => 0xb8 },
+	'jobrefid' => { 163 => 'JobRefId' },
+	'jobrefname' => { 163 => 'JobRefName' },
+	'jobrefurl' => { 163 => 'JobRefUrl' },
+	'jpgfromraw' => { 38 => 0x2007, 46 => 'JpgFromRaw' },
+	'jpgfromrawlength' => { 48 => [0x117,0x202] },
+	'jpgfromrawstart' => { 48 => [0x111,0x201] },
+	'key' => { 164 => 'key' },
+	'keywords' => { 54 => 0x19, 71 => 'Keywords', 142 => 'Keywords', 159 => 'Keywords' },
+	'kodakimageheight' => { 58 => 0xf908, 59 => 0xe, 60 => [0xfa1e,0xfa52], 65 => 0x70 },
+	'kodakimagewidth' => { 58 => 0xf907, 59 => 0xc, 60 => [0xfa1d,0xfa51], 65 => 0x6c },
+	'kodakinfotype' => { 58 => 0xfa00 },
+	'kodakmaker' => { 65 => 0x8 },
+	'kodakmodel' => { 59 => 0x0, 65 => 0x28 },
+	'label' => { 125 => 'Label', 162 => 'Label' },
+	'language' => { 155 => 'language' },
+	'languageidentifier' => { 54 => 0x87 },
+	'lastfilenumber' => { 84 => 0x1b },
+	'lastkeywordiptc' => { 151 => 'LastKeywordIPTC' },
+	'lastkeywordxmp' => { 151 => 'LastKeywordXMP' },
+	'lasturl' => { 165 => 'LastURL' },
+	'lcddisplayatpoweron' => { 29 => 0xa },
+	'lcddisplayreturntoshoot' => { 30 => 0x12 },
+	'lcdilluminationduringbulb' => { 25 => 0x408 },
+	'lcdpanels' => { 24 => 0x8 },
+	'lcheditor' => { 109 => 0x8ae85e },
+	'legacyiptcdigest' => { 160 => 'LegacyIPTCDigest' },
+	'lens' => { 48 => 0xfdea, 98 => 0x84, 146 => 0xa, 152 => 'Lens' },
+	'lensafstopbutton' => { 23 => 0x11, 24 => 0x13, 25 => 0x506, 26 => 0x10, 27 => 0x12, 30 => 0x13, 31 => 0x9 },
+	'lensaperturerange' => { 146 => 0x30 },
+	'lenscodes' => { 133 => [0x4,0x5] },
+	'lensdataversion' => { 97 => 0x0 },
+	'lensdistortionparams' => { 120 => 0x206 },
+	'lensdrivenoaf' => { 25 => 0x505 },
+	'lensfirmwareversion' => { 116 => 0x204 },
+	'lensfstops' => { 97 => 0x7, 98 => 0x8b },
+	'lensid' => { 88 => 0x10c },
+	'lensidnumber' => { 97 => 0x6 },
+	'lensinfo' => { 152 => 'LensInfo' },
+	'lensmake' => { 77 => 'Make' },
+	'lensmanufacturer' => { 151 => 'LensManufacturer' },
+	'lensmodel' => { 77 => 'Model', 151 => 'LensModel' },
+	'lensproperties' => { 116 => 0x20b },
+	'lensserialnumber' => { 77 => 'SerialNumber', 116 => 0x202, 126 => 0x52 },
+	'lenstemperature' => { 120 => 0x1008 },
+	'lenstype' => { 1 => 0xd, 4 => 0x16, 16 => 0x95, 98 => 0x83, 116 => 0x201, 126 => 0x51, 133 => 0x0, 134 => 0x3f },
+	'license' => { 153 => 'license' },
+	'licensetype' => { 156 => 'licensetype' },
+	'lightcondition' => { 120 => 0x1009 },
+	'lightsource' => { 48 => 0x9208, 98 => 0x90, 123 => 0x1000, 157 => 'LightSource' },
+	'lightsourcespecial' => { 145 => 0x21d },
+	'lightvaluecenter' => { 120 => 0x103d },
+	'lightvalueperiphery' => { 120 => 0x103e },
+	'linearresponselimit' => { 48 => 0xc62e },
+	'liveviewexposuresimulation' => { 25 => 0x810 },
+	'localcaption' => { 54 => 0x79 },
+	'localizedcameramodel' => { 48 => 0xc615 },
+	'location' => { 150 => 'Location', 158 => 'Location' },
+	'lockmicrophonebutton' => { 25 => 0x709 },
+	'logcomment' => { 164 => 'logComment' },
+	'longexposurenoisereduction' => { 25 => 0x201, 26 => 0x1, 27 => 0x2, 28 => 0x1, 29 => 0x1, 30 => 0x2, 31 => 0x1 },
+	'longfocal' => { 1 => 0x13, 2 => 0x115, 4 => 0x17 },
+	'loop' => { 164 => 'loop' },
+	'luminanceadjustmentaqua' => { 154 => 'LuminanceAdjustmentAqua' },
+	'luminanceadjustmentblue' => { 154 => 'LuminanceAdjustmentBlue' },
+	'luminanceadjustmentgreen' => { 154 => 'LuminanceAdjustmentGreen' },
+	'luminanceadjustmentmagenta' => { 154 => 'LuminanceAdjustmentMagenta' },
+	'luminanceadjustmentorange' => { 154 => 'LuminanceAdjustmentOrange' },
+	'luminanceadjustmentpurple' => { 154 => 'LuminanceAdjustmentPurple' },
+	'luminanceadjustmentred' => { 154 => 'LuminanceAdjustmentRed' },
+	'luminanceadjustmentyellow' => { 154 => 'LuminanceAdjustmentYellow' },
+	'luminancenoisereduction' => { 43 => 0xbe },
+	'luminancesmoothing' => { 154 => 'LuminanceSmoothing' },
+	'macro' => { 52 => 0x1020, 120 => 0x202, 143 => 0x21, 145 => 0x202 },
+	'macromode' => { 4 => 0x1, 84 => 0xb, 115 => 0x300, 126 => 0x1c },
+	'magnifiedview' => { 27 => 0x11, 29 => 0x9 },
+	'make' => { 39 => 0x0, 48 => 0x10f, 70 => 'Make', 125 => 'Make', 127 => 0x10f, 161 => 'Make' },
+	'makernote' => { 157 => 'MakerNote' },
+	'makernotecanon' => { 47 => 'MakN', 48 => 0x927c },
+	'makernotecasio' => { 47 => 'MakN', 48 => 0x927c },
+	'makernotecasio2' => { 47 => 'MakN', 48 => 0x927c },
+	'makernotefujifilm' => { 47 => 'MakN', 48 => 0x927c },
+	'makernotehp' => { 47 => 'MakN', 48 => 0x927c },
+	'makernotehp2' => { 47 => 'MakN', 48 => 0x927c },
+	'makernotehp4' => { 47 => 'MakN', 48 => 0x927c },
+	'makernotehp6' => { 47 => 'MakN', 48 => 0x927c },
+	'makernotejvc' => { 47 => 'MakN', 48 => 0x927c },
+	'makernotejvctext' => { 47 => 'MakN', 48 => 0x927c },
+	'makernotekodak1a' => { 47 => 'MakN', 48 => 0x927c },
+	'makernotekodak1b' => { 47 => 'MakN', 48 => 0x927c },
+	'makernotekodak2' => { 47 => 'MakN', 48 => 0x927c },
+	'makernotekodak3' => { 47 => 'MakN', 48 => 0x927c },
+	'makernotekodak4' => { 47 => 'MakN', 48 => 0x927c },
+	'makernotekodak5' => { 47 => 'MakN', 48 => 0x927c },
+	'makernotekodak6a' => { 47 => 'MakN', 48 => 0x927c },
+	'makernotekodak6b' => { 47 => 'MakN', 48 => 0x927c },
+	'makernotekodak7' => { 47 => 'MakN', 48 => 0x927c },
+	'makernotekodakifd' => { 47 => 'MakN', 48 => 0x927c },
+	'makernotekodakunknown' => { 47 => 'MakN', 48 => 0x927c },
+	'makernotekyocera' => { 47 => 'MakN', 48 => 0x927c },
+	'makernoteleica' => { 47 => 'MakN', 48 => 0x927c },
+	'makernoteminolta' => { 47 => 'MakN', 48 => 0x927c },
+	'makernoteminolta2' => { 47 => 'MakN', 48 => 0x927c },
+	'makernoteminolta3' => { 47 => 'MakN', 48 => 0x927c },
+	'makernotenikon' => { 47 => 'MakN', 48 => 0x927c },
+	'makernotenikon2' => { 47 => 'MakN', 48 => 0x927c },
+	'makernotenikon3' => { 47 => 'MakN', 48 => 0x927c },
+	'makernoteoffset' => { 145 => 0xff },
+	'makernoteolympus' => { 47 => 'MakN', 48 => 0x927c },
+	'makernoteolympus2' => { 47 => 'MakN', 48 => 0x927c },
+	'makernotepanasonic' => { 47 => 'MakN', 48 => 0x927c },
+	'makernotepanasonic2' => { 47 => 'MakN', 48 => 0x927c },
+	'makernotepentax' => { 47 => 'MakN', 48 => 0x927c },
+	'makernotepentax2' => { 47 => 'MakN', 48 => 0x927c },
+	'makernotepentax3' => { 47 => 'MakN', 48 => 0x927c },
+	'makernotepentax4' => { 47 => 'MakN', 48 => 0x927c },
+	'makernotericoh' => { 47 => 'MakN', 48 => 0x927c },
+	'makernotericohtext' => { 47 => 'MakN', 48 => 0x927c },
+	'makernotesafety' => { 48 => 0xc635 },
+	'makernotesanyo' => { 47 => 'MakN', 48 => 0x927c },
+	'makernotesanyoc4' => { 47 => 'MakN', 48 => 0x927c },
+	'makernotesanyopatch' => { 47 => 'MakN', 48 => 0x927c },
+	'makernotesigma' => { 47 => 'MakN', 48 => 0x927c },
+	'makernotesony' => { 47 => 'MakN', 48 => 0x927c },
+	'makernotesonysr2' => { 47 => 'MakN', 48 => 0x927c },
+	'makernotesonysrf' => { 47 => 'MakN', 48 => 0x927c },
+	'makernotetype' => { 144 => 0x1 },
+	'makernoteunknown' => { 47 => 'MakN', 48 => 0x927c },
+	'makernoteversion' => { 88 => 0x0, 98 => 0x1, 120 => 0x0, 126 => 0x8000, 144 => 0x2, 146 => 0x1d },
+	'managedfromdocumentid' => { 165 => 'ManagedFromDocumentID' },
+	'managedfrominstanceid' => { 165 => 'ManagedFromInstanceID' },
+	'managedfrommanager' => { 165 => 'ManagedFromManager' },
+	'managedfrommanagervariant' => { 165 => 'ManagedFromManagerVariant' },
+	'managedfrommanageto' => { 165 => 'ManagedFromManageTo' },
+	'managedfrommanageui' => { 165 => 'ManagedFromManageUI' },
+	'managedfromrenditionclass' => { 165 => 'ManagedFromRenditionClass' },
+	'managedfromrenditionparams' => { 165 => 'ManagedFromRenditionParams' },
+	'managedfromversionid' => { 165 => 'ManagedFromVersionID' },
+	'manager' => { 165 => 'Manager' },
+	'managervariant' => { 165 => 'ManagerVariant' },
+	'manageto' => { 165 => 'ManageTo' },
+	'manageui' => { 165 => 'ManageUI' },
+	'manometerreading' => { 115 => 0x901 },
+	'manualflash' => { 118 => 0x1209 },
+	'manualflashoutput' => { 4 => 0x29 },
+	'manualfocusdistance' => { 98 => 0x85, 120 => 0x100c, 145 => 0x223 },
+	'manualtv' => { 24 => 0x5, 25 => 0x705 },
+	'manufacturedate' => { 131 => 0x1 },
+	'marked' => { 167 => 'Marked' },
+	'markerscomment' => { 164 => 'markersComment' },
+	'markersduration' => { 164 => 'markersDuration' },
+	'markerslocation' => { 164 => 'markersLocation' },
+	'markersname' => { 164 => 'markersName' },
+	'markersstarttime' => { 164 => 'markersStartTime' },
+	'markerstarget' => { 164 => 'markersTarget' },
+	'markerstype' => { 164 => 'markersType' },
+	'maskedareas' => { 48 => 0xc68e },
+	'masterdocumentid' => { 54 => 0xb9 },
+	'mastergain' => { 102 => 0x50 },
+	'maxaperture' => { 4 => 0x1a, 62 => 0x6103, 77 => 'MaxAperture', 84 => 0x17 },
+	'maxapertureatcurrentfocal' => { 116 => 0x20a },
+	'maxapertureatmaxfocal' => { 52 => 0x1407, 77 => 'MaxApertureAtMaxFocal', 97 => 0xb, 116 => 0x206 },
+	'maxapertureatminfocal' => { 52 => 0x1406, 97 => 0xa, 116 => 0x205 },
+	'maxaperturevalue' => { 48 => 0x9205, 157 => 'MaxApertureValue' },
+	'maxfocallength' => { 52 => 0x1405, 77 => 'MaxFocalLength', 97 => 0x9, 116 => 0x208 },
+	'maximumdensityrange' => { 56 => 0x8c },
+	'maxpagesizeh' => { 168 => 'MaxPageSizeH' },
+	'maxpagesizeunit' => { 168 => 'MaxPageSizeUnit' },
+	'maxpagesizew' => { 168 => 'MaxPageSizeW' },
+	'maxsamplevalue' => { 48 => 0x119 },
+	'mcuversion' => { 97 => 0xc },
+	'md5digest' => { 78 => 'zmd5' },
+	'measuredev' => { 22 => 0x3, 38 => 0x1814, 70 => 'MeasuredEV' },
+	'menubuttondisplayposition' => { 23 => 0xb, 26 => 0xa, 27 => 0xb, 30 => 0xb },
+	'menubuttonreturn' => { 31 => 0xb },
+	'metadatadate' => { 162 => 'MetadataDate' },
+	'metadatamoddate' => { 164 => 'metadataModDate' },
+	'meteringmode' => { 4 => 0x11, 48 => 0x9207, 59 => 0x1c, 84 => 0x7, 85 => 0x25, 115 => 0x202, 134 => 0x17, 146 => 0x9, 157 => 'MeteringMode' },
+	'mieversion' => { 78 => '0Vers' },
+	'minaperture' => { 4 => 0x1b, 77 => 'MinAperture' },
+	'minfocallength' => { 52 => 0x1404, 77 => 'MinFocalLength', 97 => 0x8, 116 => 0x207 },
+	'minoltadate' => { 84 => 0x15 },
+	'minoltaimagesize' => { 84 => 0x4, 85 => 0xc, 86 => 0x2, 88 => 0x103 },
+	'minoltamodelid' => { 84 => 0x25 },
+	'minoltaquality' => { 84 => 0x5, 85 => 0xd, 86 => 0x3, 88 => [0x102,0x103], 120 => [0x102,0x103] },
+	'minoltatime' => { 84 => 0x16 },
+	'minsamplevalue' => { 48 => 0x118 },
+	'mirrorlockup' => { 23 => 0xc, 24 => 0xc, 25 => 0x60f, 26 => 0xb, 27 => 0xc, 28 => 0x6, 29 => 0x6, 30 => 0xc, 31 => 0x3 },
+	'moddate' => { 159 => 'ModDate' },
+	'model' => { 39 => 0x6, 48 => 0x110, 70 => 'Model', 125 => 'Model', 127 => 0x110, 161 => 'Model' },
+	'modelrevision' => { 131 => 0x2 },
+	'modifiedcolortemp' => { 17 => 0x9 },
+	'modifieddigitalgain' => { 17 => 0xb },
+	'modifiedparamflag' => { 14 => 0x1 },
+	'modifiedpicturestyle' => { 17 => 0xa },
+	'modifiedsaturation' => { 115 => 0x504 },
+	'modifiedsensorbluelevel' => { 17 => 0x5 },
+	'modifiedsensorredlevel' => { 17 => 0x4 },
+	'modifiedsharpness' => { 17 => 0x2 },
+	'modifiedsharpnessfreq' => { 17 => 0x3 },
+	'modifiedtonecurve' => { 17 => 0x1 },
+	'modifiedwhitebalance' => { 17 => 0x8 },
+	'modifiedwhitebalanceblue' => { 17 => 0x7 },
+	'modifiedwhitebalancered' => { 17 => 0x6 },
+	'modifydate' => { 48 => 0x132, 71 => 'ModifyDate', 124 => 'tIME', 142 => 'ModDate', 162 => 'ModifyDate' },
+	'moirefilter' => { 48 => 0xfe58 },
+	'monochromecontrast' => { 43 => 0x78 },
+	'monochromefiltereffect' => { 43 => 0x74 },
+	'monochromelinear' => { 43 => 0x7a },
+	'monochromesharpness' => { 43 => 0x7c },
+	'monochrometoningeffect' => { 43 => 0x76 },
+	'monthdaycreated' => { 59 => 0x12, 66 => 0xe },
+	'multiexposureautogain' => { 99 => 0x3 },
+	'multiexposuremode' => { 99 => 0x1 },
+	'multiexposureshots' => { 99 => 0x2 },
+	'multiexposureversion' => { 99 => 0x0 },
+	'multisample' => { 102 => 0x40 },
+	'mycolormode' => { 18 => 0x2 },
+	'nativedigest' => { 157 => 'NativeDigest', 161 => 'NativeDigest' },
+	'ndfilter' => { 22 => 0x1c },
+	'nearlensstep' => { 120 => 0x103c },
+	'newsphotoversion' => { 56 => 0x0 },
+	'nickname' => { 162 => 'Nickname' },
+	'nikoncaptureversion' => { 98 => 0xe09 },
+	'noisefilter' => { 115 => 0x527 },
+	'noisereduction' => { 13 => 0x8, 85 => 0xb0, 86 => 0x60, 87 => 0x3f, 98 => 0x95, 109 => 0x753dcbc0, 115 => 0x50a, 120 => 0x103a, 126 => 0x2d, 134 => 0x49 },
+	'noisereduction2' => { 119 => 0x1010 },
+	'noisereductionintensity' => { 110 => 0x9 },
+	'noisereductionmethod' => { 110 => 0x11 },
+	'noisereductionsharpness' => { 110 => 0xd },
+	'npages' => { 168 => 'NPages' },
+	'nullrecord' => { 38 => 0x0 },
+	'numberofbeats' => { 164 => 'numberOfBeats' },
+	'numindexentries' => { 56 => 0x54 },
+	'objectattributereference' => { 54 => 0x4 },
+	'objectcycle' => { 54 => 0x4b },
+	'objectdistance' => { 44 => 0x6, 45 => 0x2022 },
+	'objectname' => { 54 => 0x5 },
+	'objectpreviewdata' => { 54 => 0xca },
+	'objectpreviewfileformat' => { 54 => 0xc8 },
+	'objectpreviewfileversion' => { 54 => 0xc9 },
+	'objecttypereference' => { 54 => 0x3 },
+	'oecfcolumns' => { 157 => 'OECFColumns' },
+	'oecfnames' => { 157 => 'OECFNames' },
+	'oecfrows' => { 157 => 'OECFRows' },
+	'oecfvalues' => { 157 => 'OECFValues' },
+	'offsetschema' => { 48 => 0xea1d },
+	'oldsubfiletype' => { 48 => 0xff },
+	'olympusimageheight' => { 120 => 0x102f },
+	'olympusimageheight2' => { 119 => 0x615 },
+	'olympusimagewidth' => { 120 => 0x102e },
+	'olympusimagewidth2' => { 119 => 0x614 },
+	'onetouchwb' => { 120 => 0x302 },
+	'opticalzoom' => { 60 => 0xfa3d, 62 => [0x6006,0xf006], 63 => 0x1000, 64 => 0xf, 66 => 0x1e, 77 => 'OpticalZoom' },
+	'opticalzoomcode' => { 22 => 0xa },
+	'opticalzoommode' => { 126 => 0x34 },
+	'opticalzoomon' => { 145 => 0x219 },
+	'ordernumber' => { 52 => 0x8002 },
+	'orientation' => { 48 => 0x112, 127 => 0x112, 161 => 'Orientation' },
+	'originaldecisiondata' => { 16 => 0x83 },
+	'originalfilename' => { 38 => 0x816, 67 => 0x20 },
+	'originalimageheight' => { 51 => 0x1 },
+	'originalimagewidth' => { 51 => 0x0 },
+	'originalrawfiledata' => { 48 => 0xc68c },
+	'originalrawfilename' => { 48 => 0xc68b },
+	'originaltransmissionreference' => { 54 => 0x67 },
+	'originatingprogram' => { 54 => 0x41 },
+	'os' => { 156 => 'os' },
+	'outcue' => { 164 => 'outCue' },
+	'outputheight' => { 106 => 0xce },
+	'outputheightinches' => { 106 => 0x96 },
+	'outputpixels' => { 106 => 0xd6 },
+	'outputresolution' => { 106 => 0xb6 },
+	'outputwidth' => { 106 => 0xc6 },
+	'outputwidthinches' => { 106 => 0x8e },
+	'owner' => { 167 => 'Owner' },
+	'ownerid' => { 54 => 0xbc },
+	'ownername' => { 16 => 0x9, 38 => 0x810, 48 => 0xfde8, 70 => 'OwnerName', 152 => 'OwnerName' },
+	'pagename' => { 48 => 0x11d },
+	'pagenumber' => { 48 => 0x129 },
+	'panasonicexifversion' => { 126 => 0x26 },
+	'panasonicrawversion' => { 127 => 0x1 },
+	'panoramadirection' => { 19 => 0x5 },
+	'panoramaframe' => { 19 => 0x2 },
+	'panoramamode' => { 59 => 0x3c, 115 => 0x601 },
+	'parametricdarks' => { 154 => 'ParametricDarks' },
+	'parametrichighlights' => { 154 => 'ParametricHighlights' },
+	'parametrichighlightsplit' => { 154 => 'ParametricHighlightSplit' },
+	'parametriclights' => { 154 => 'ParametricLights' },
+	'parametricmidtonesplit' => { 154 => 'ParametricMidtoneSplit' },
+	'parametricshadows' => { 154 => 'ParametricShadows' },
+	'parametricshadowsplit' => { 154 => 'ParametricShadowSplit' },
+	'patientid' => { 148 => 'PatientID' },
+	'patientsbirthdate' => { 148 => 'PatientDOB' },
+	'patientsname' => { 148 => 'PatientName' },
+	'patientssex' => { 148 => 'PatientSex' },
+	'pdfversion' => { 159 => 'PDFVersion' },
+	'pentaximagesize' => { 134 => 0x9 },
+	'pentaxmodelid' => { 131 => 0x0, 134 => 0x5 },
+	'pentaxmodeltype' => { 134 => 0x1 },
+	'pentaxversion' => { 134 => 0x0 },
+	'people' => { 150 => 'People' },
+	'pf0customfuncregistration' => { 33 => 0x1 },
+	'pf10retainprogramshift' => { 33 => 0xb },
+	'pf13drivepriority' => { 33 => 0xe },
+	'pf14disablefocussearch' => { 33 => 0xf },
+	'pf15disableafassistbeam' => { 33 => 0x10 },
+	'pf16autofocuspointshoot' => { 33 => 0x11 },
+	'pf17disableafpointsel' => { 33 => 0x12 },
+	'pf18enableautoafpointsel' => { 33 => 0x13 },
+	'pf19continuousshootspeed' => { 33 => 0x14 },
+	'pf19shootingspeedhigh' => { 32 => 0xa },
+	'pf19shootingspeedlow' => { 32 => 0x9 },
+	'pf1disableshootingmodes' => { 33 => 0x2 },
+	'pf1value' => { 32 => 0x1 },
+	'pf20limitcontinousshots' => { 33 => 0x15 },
+	'pf20maxcontinousshots' => { 32 => 0xb },
+	'pf21enablequietoperation' => { 33 => 0x16 },
+	'pf23felocktime' => { 32 => 0xd },
+	'pf23postreleasetime' => { 32 => 0xe },
+	'pf23settimerlengths' => { 33 => 0x18 },
+	'pf23shutterbuttontime' => { 32 => 0xc },
+	'pf24lightlcdduringbulb' => { 33 => 0x19 },
+	'pf25aemode' => { 32 => 0xf },
+	'pf25afmode' => { 32 => 0x12 },
+	'pf25afpointsel' => { 32 => 0x13 },
+	'pf25colormatrix' => { 32 => 0x17 },
+	'pf25defaultclearsettings' => { 33 => 0x1a },
+	'pf25drivemode' => { 32 => 0x11 },
+	'pf25imagesize' => { 32 => 0x14 },
+	'pf25meteringmode' => { 32 => 0x10 },
+	'pf25parameters' => { 32 => 0x16 },
+	'pf25wbmode' => { 32 => 0x15 },
+	'pf26shortenreleaselag' => { 33 => 0x1b },
+	'pf27reversedialrotation' => { 33 => 0x1c },
+	'pf27value' => { 32 => 0x18 },
+	'pf28noquickdialexpcomp' => { 33 => 0x1d },
+	'pf29quickdialswitchoff' => { 33 => 0x1e },
+	'pf2disablemeteringmodes' => { 33 => 0x3 },
+	'pf2value' => { 32 => 0x2 },
+	'pf30enlargementmode' => { 33 => 0x1f },
+	'pf31originaldecisiondata' => { 33 => 0x20 },
+	'pf3manualexposuremetering' => { 33 => 0x4 },
+	'pf3value' => { 32 => 0x3 },
+	'pf4exposuretimelimits' => { 33 => 0x5 },
+	'pf4exposuretimemax' => { 32 => 0x5 },
+	'pf4exposuretimemin' => { 32 => 0x4 },
+	'pf5aperturelimits' => { 33 => 0x6 },
+	'pf5aperturemax' => { 32 => 0x7 },
+	'pf5aperturemin' => { 32 => 0x6 },
+	'pf6presetshootingmodes' => { 33 => 0x7 },
+	'pf7bracketcontinuousshoot' => { 33 => 0x8 },
+	'pf8bracketshots' => { 32 => 0x8 },
+	'pf8setbracketshots' => { 33 => 0x9 },
+	'pf9changebracketsequence' => { 33 => 0xa },
+	'phonenumber' => { 71 => 'Phone' },
+	'photoeffect' => { 4 => 0x28 },
+	'photoeffects' => { 109 => 0xab5eca5e },
+	'photoeffectsblue' => { 111 => 0x8 },
+	'photoeffectsgreen' => { 111 => 0x6 },
+	'photoeffectsred' => { 111 => 0x4 },
+	'photoeffectstype' => { 111 => 0x0 },
+	'photometricinterpretation' => { 48 => 0x106, 161 => 'PhotometricInterpretation' },
+	'photoshopquality' => { 139 => 0x0 },
+	'pictinfo' => { 145 => 0x208 },
+	'picturemode' => { 52 => 0x1031, 115 => 0x520, 134 => [0xb,0x33] },
+	'picturemodebwfilter' => { 115 => 0x525 },
+	'picturemodecontrast' => { 115 => 0x523 },
+	'picturemodehue' => { 115 => 0x522 },
+	'picturemodesaturation' => { 115 => 0x521 },
+	'picturemodesharpness' => { 115 => 0x524 },
+	'picturemodetone' => { 115 => 0x526 },
+	'picturestyle' => { 1 => 0x6c, 2 => 0x86, 21 => 0xa, 43 => 0x4 },
+	'planarconfiguration' => { 48 => 0x11c, 161 => 'PlanarConfiguration' },
+	'platenames' => { 168 => 'PlateNames' },
+	'pngwarning' => { 125 => 'Warning' },
+	'postalcode' => { 75 => 'PostalCode' },
+	'precaptureframes' => { 120 => 0x300 },
+	'predictor' => { 48 => 0x13d },
+	'prefs' => { 54 => 0xdd, 138 => 'Prefs' },
+	'preservedfilename' => { 165 => 'PreservedFileName' },
+	'previewfocalplanexresolution' => { 20 => 0x6 },
+	'previewfocalplaneyresolution' => { 20 => 0x8 },
+	'previewheight' => { 60 => 0xfa58 },
+	'previewimage' => { 45 => 0x2000, 46 => 'PreviewImage', 47 => 'MakN', 48 => 0x927c, 50 => 0x4, 80 => 'data', 88 => 0x81, 120 => 0x280 },
+	'previewimageheight' => { 20 => 0x4 },
+	'previewimagelength' => { 20 => 0x2, 45 => 0x3, 48 => [0x117,0x202], 88 => 0x89, 100 => 0x202, 115 => 0x102, 120 => 0x1037, 134 => 0x3, 143 => 0x1e },
+	'previewimagename' => { 80 => '1Name' },
+	'previewimagesize' => { 45 => 0x2, 80 => 'ImageSize', 134 => 0x2 },
+	'previewimagestart' => { 20 => 0x5, 45 => 0x4, 48 => [0x111,0x201], 88 => 0x88, 100 => 0x201, 115 => 0x101, 120 => 0x1036, 134 => 0x4, 143 => 0x1c },
+	'previewimagetype' => { 80 => '0Type' },
+	'previewimagevalid' => { 115 => 0x100, 120 => 0x1035 },
+	'previewimagewidth' => { 20 => 0x3 },
+	'previewquality' => { 20 => 0x1 },
+	'previewwidth' => { 60 => 0xfa57 },
+	'primarychromaticities' => { 48 => 0x13f, 161 => 'PrimaryChromaticities' },
+	'printim' => { 48 => 0xc4a5 },
+	'privatertkinfo' => { 149 => 'privateRTKInfo' },
+	'processingsoftware' => { 48 => 0xb },
+	'producer' => { 159 => 'Producer' },
+	'productid' => { 55 => 0x32 },
+	'programiso' => { 126 => 0x3c },
+	'programmode' => { 90 => 0x5 },
+	'programshift' => { 98 => 0xd },
+	'programversion' => { 54 => 0x46 },
+	'projectrefpath' => { 164 => 'projectRefPath' },
+	'projectreftype' => { 164 => 'projectRefType' },
+	'province-state' => { 54 => 0x5f },
+	'publisher' => { 155 => 'publisher' },
+	'pulldown' => { 164 => 'pullDown' },
+	'quality' => { 0 => 0x1, 4 => 0x3, 44 => 0x2, 45 => 0x3002, 52 => 0x1000, 59 => 0x9, 98 => 0x4, 103 => 0x3, 120 => 0x201, 134 => 0x8, 136 => 0x2, 146 => 0x16 },
+	'qualitymode' => { 45 => 0x8 },
+	'quantizationmethod' => { 56 => 0x78 },
+	'quickcontroldialinmeter' => { 25 => 0x703 },
+	'quickshot' => { 145 => 0x213 },
+	'rasterizedcaption' => { 54 => 0x7d },
+	'rating' => { 48 => 0x4746, 137 => 0xdf, 156 => 'rating', 162 => 'Rating' },
+	'ratingpercent' => { 48 => 0x4749, 151 => 'Rating' },
+	'rawandjpgrecording' => { 23 => 0x8 },
+	'rawbrightnessadj' => { 42 => 0x38 },
+	'rawcoloradj' => { 42 => 0x2e },
+	'rawcolortoneadj' => { 43 => 0x1a },
+	'rawcontrastadj' => { 43 => 0x1e },
+	'rawcropbottom' => { 137 => 0xd4 },
+	'rawcropleft' => { 137 => 0xd1 },
+	'rawcropright' => { 137 => 0xd3 },
+	'rawcroptop' => { 137 => 0xd2 },
+	'rawcustomsaturation' => { 42 => 0x30 },
+	'rawcustomtone' => { 42 => 0x34 },
+	'rawdatauniqueid' => { 48 => 0xc65d },
+	'rawdepth' => { 89 => 0x10 },
+	'rawdevcolorspace' => { 121 => 0x108, 122 => 0x109 },
+	'rawdevcontrastvalue' => { 121 => 0x106, 122 => 0x105 },
+	'rawdeveditstatus' => { 121 => 0x10b },
+	'rawdevengine' => { 121 => 0x109, 122 => 0x10b },
+	'rawdevexposurebiasvalue' => { 121 => 0x100, 122 => 0x100 },
+	'rawdevgradation' => { 122 => 0x112 },
+	'rawdevgraypoint' => { 121 => 0x103, 122 => 0x104 },
+	'rawdevmemorycoloremphasis' => { 121 => 0x105, 122 => 0x108 },
+	'rawdevnoisereduction' => { 121 => 0x10a, 122 => 0x10a },
+	'rawdevpicturemode' => { 122 => 0x10c },
+	'rawdevpm_bwfilter' => { 122 => 0x110 },
+	'rawdevpmcontrast' => { 122 => 0x10e },
+	'rawdevpmpicturetone' => { 122 => 0x111 },
+	'rawdevpmsaturation' => { 122 => 0x10d },
+	'rawdevpmsharpness' => { 122 => 0x10f },
+	'rawdevsaturationemphasis' => { 121 => 0x104, 122 => 0x107 },
+	'rawdevsettings' => { 121 => 0x10c },
+	'rawdevsharpnessvalue' => { 121 => 0x107, 122 => 0x106 },
+	'rawdevversion' => { 121 => 0x0, 122 => 0x0 },
+	'rawdevwbfineadjustment' => { 121 => 0x102, 122 => 0x103 },
+	'rawdevwhitebalance' => { 122 => 0x101 },
+	'rawdevwhitebalancevalue' => { 121 => 0x101, 122 => 0x102 },
+	'rawfile' => { 48 => 0xfe4c },
+	'rawfilename' => { 154 => 'RawFileName' },
+	'rawhighlightpoint' => { 43 => 0x24 },
+	'rawimagecenter' => { 98 => 0x99 },
+	'rawimagesize' => { 134 => 0x39 },
+	'rawinfoversion' => { 123 => 0x0 },
+	'rawjpgheight' => { 40 => 0x4 },
+	'rawjpgquality' => { 13 => 0x6, 40 => 0x1 },
+	'rawjpgsize' => { 13 => 0x7, 40 => 0x2 },
+	'rawjpgwidth' => { 40 => 0x3 },
+	'rawlinear' => { 43 => 0x20 },
+	'rawsaturationadj' => { 43 => 0x1c },
+	'rawshadowpoint' => { 43 => 0x26 },
+	'rawsharpnessadj' => { 43 => 0x22 },
+	'recordid' => { 38 => 0x1804 },
+	'recordingmode' => { 44 => 0x1, 136 => 0x1 },
+	'recordmode' => { 4 => 0x9, 45 => 0x3000 },
+	'recordshutterrelease' => { 145 => 0x217 },
+	'redbalance' => { 120 => 0x1017, 127 => 0x11, 134 => 0x1c },
+	'redcurvelimits' => { 42 => 0x18a },
+	'redcurvepoints' => { 42 => 0x160 },
+	'redeyecorrection' => { 112 => 0x0 },
+	'redhue' => { 154 => 'RedHue' },
+	'redsaturation' => { 154 => 'RedSaturation' },
+	'referenceblackwhite' => { 48 => 0x214, 161 => 'ReferenceBlackWhite' },
+	'referencedate' => { 54 => 0x2f },
+	'referencenumber' => { 54 => 0x32 },
+	'references' => { 71 => 'References' },
+	'referenceservice' => { 54 => 0x2d },
+	'relatedaudiofile' => { 69 => 'data' },
+	'relatedaudiofilename' => { 69 => '1Name' },
+	'relatedaudiofiletype' => { 69 => '0Type' },
+	'relatedimagefileformat' => { 48 => 0x1000 },
+	'relatedimagelength' => { 48 => 0x1002 },
+	'relatedimagewidth' => { 48 => 0x1001 },
+	'relatedsoundfile' => { 48 => 0xa004, 157 => 'RelatedSoundFile' },
+	'relatedvideofile' => { 83 => 'data' },
+	'relatedvideofilename' => { 83 => '1Name' },
+	'relatedvideofiletype' => { 83 => '0Type' },
+	'relation' => { 155 => 'relation' },
+	'relativepeakaudiofilepath' => { 164 => 'relativePeakAudioFilePath' },
+	'relativetimestamp' => { 164 => 'relativeTimestamp' },
+	'releasedate' => { 54 => 0x1e, 164 => 'releaseDate' },
+	'releasesetting' => { 38 => 0x1016 },
+	'releasetime' => { 54 => 0x23 },
+	'renditionclass' => { 165 => 'RenditionClass' },
+	'renditionofdocumentid' => { 165 => 'RenditionOfDocumentID' },
+	'renditionofinstanceid' => { 165 => 'RenditionOfInstanceID' },
+	'renditionofmanager' => { 165 => 'RenditionOfManager' },
+	'renditionofmanagervariant' => { 165 => 'RenditionOfManagerVariant' },
+	'renditionofmanageto' => { 165 => 'RenditionOfManageTo' },
+	'renditionofmanageui' => { 165 => 'RenditionOfManageUI' },
+	'renditionofrenditionclass' => { 165 => 'RenditionOfRenditionClass' },
+	'renditionofrenditionparams' => { 165 => 'RenditionOfRenditionParams' },
+	'renditionofversionid' => { 165 => 'RenditionOfVersionID' },
+	'renditionparams' => { 165 => 'RenditionParams' },
+	'resampleparamsquality' => { 164 => 'resampleParamsQuality' },
+	'resaved' => { 145 => 0x21e },
+	'resolution' => { 76 => 'Resolution' },
+	'resolutionmode' => { 146 => 0x4 },
+	'resolutionunit' => { 48 => 0x128, 57 => 0x2, 161 => 'ResolutionUnit' },
+	'reuseallowed' => { 166 => 'ReuseAllowed' },
+	'revision' => { 156 => 'revision' },
+	'rgbcurvelimits' => { 42 => 0x238 },
+	'rgbcurvepoints' => { 42 => 0x20e },
+	'ricohdate' => { 143 => 0x6 },
+	'ricohimageheight' => { 143 => 0x2 },
+	'ricohimagewidth' => { 143 => 0x0 },
+	'rightascension' => { 79 => 'RightAscension' },
+	'rights' => { 155 => 'rights' },
+	'romoperationmode' => { 38 => 0x80d },
+	'rotation' => { 3 => 0x17, 37 => 0x3, 42 => 0x26e, 51 => 0x4, 79 => 'Rotation', 85 => 0x50, 86 => 0x46, 87 => 0x5a, 109 => 0x76a43207, 126 => 0x30, 137 => 0xd8 },
+	'routing' => { 142 => 'Routing' },
+	'rowsperstrip' => { 48 => 0x116 },
+	'safetyshift' => { 25 => 0x108 },
+	'safetyshiftinavortv' => { 23 => 0x10, 24 => 0x10, 26 => 0xf, 27 => 0x10, 30 => 0x10 },
+	'samplebits' => { 69 => 'SampleBits' },
+	'samplerate' => { 69 => 'SampleRate' },
+	'samplesperpixel' => { 48 => 0x115, 161 => 'SamplesPerPixel' },
+	'samplestructure' => { 56 => 0x5a },
+	'sanyoquality' => { 145 => 0x201 },
+	'sanyothumbnail' => { 145 => 0x100 },
+	'saturation' => { 4 => 0xe, 9 => 0x1, 44 => 0xd, 45 => [0x3013,0x1f], 48 => [0xa409,0xfe55], 52 => 0x1003, 70 => 'Saturation', 84 => 0x1f, 85 => 0x32, 86 => 0x28, 87 => 0x1a, 90 => 0x1, 98 => [0x94,0xaa], 134 => 0x1f, 136 => 0xd, 143 => 0x28, 146 => 0x10, 154 => 'Saturation', 157 => 'Saturation' },
+	'saturationadj' => { 42 => 0x116 },
+	'saturationadjustmentaqua' => { 154 => 'SaturationAdjustmentAqua' },
+	'saturationadjustmentblue' => { 154 => 'SaturationAdjustmentBlue' },
+	'saturationadjustmentgreen' => { 154 => 'SaturationAdjustmentGreen' },
+	'saturationadjustmentmagenta' => { 154 => 'SaturationAdjustmentMagenta' },
+	'saturationadjustmentorange' => { 154 => 'SaturationAdjustmentOrange' },
+	'saturationadjustmentpurple' => { 154 => 'SaturationAdjustmentPurple' },
+	'saturationadjustmentred' => { 154 => 'SaturationAdjustmentRed' },
+	'saturationadjustmentyellow' => { 154 => 'SaturationAdjustmentYellow' },
+	'saturationfaithful' => { 1 => 0xfe },
+	'saturationlandscape' => { 1 => 0xfc },
+	'saturationneutral' => { 1 => 0xfd },
+	'saturationportrait' => { 1 => 0xfb },
+	'saturationsetting' => { 123 => 0x1010 },
+	'saturationstandard' => { 1 => 0xfa },
+	'saturationuserdef1' => { 1 => 0x100 },
+	'saturationuserdef2' => { 1 => 0x101 },
+	'saturationuserdef3' => { 1 => 0x102 },
+	'saveid' => { 165 => 'SaveID' },
+	'scaledresolution' => { 106 => 0x9e },
+	'scaletype' => { 164 => 'scaleType' },
+	'scanningdirection' => { 56 => 0x64 },
+	'scene' => { 158 => 'Scene', 164 => 'scene' },
+	'scenearea' => { 120 => 0x1031 },
+	'scenecapturetype' => { 48 => 0xa406, 157 => 'SceneCaptureType' },
+	'scenedetect' => { 120 => 0x1030 },
+	'scenedetectarea' => { 120 => 0x1033 },
+	'scenemode' => { 60 => 0xfa02, 88 => 0x100, 98 => 0x8f, 115 => 0x509, 120 => 0x403, 126 => 0x8001 },
+	'scenemodeused' => { 62 => [0x6002,0xf002] },
+	'sceneselect' => { 145 => 0x21f },
+	'scenetype' => { 48 => 0xa301, 157 => 'SceneType' },
+	'securityclassification' => { 48 => 0x9212 },
+	'selectableafpoint' => { 25 => 0x509 },
+	'self-timer' => { 4 => 0x2 },
+	'self-timer2' => { 22 => 0x1d },
+	'selftimer' => { 45 => 0x3001, 126 => 0x2e, 145 => 0x214 },
+	'selftimermode' => { 48 => 0x882b },
+	'selftimertime' => { 38 => 0x1806 },
+	'seminfo' => { 48 => 0x8546 },
+	'sensingmethod' => { 48 => 0xa217, 157 => 'SensingMethod' },
+	'sensorbluelevel' => { 21 => 0x5 },
+	'sensorcleaning' => { 31 => 0xd },
+	'sensorfullheight' => { 58 => 0xf904 },
+	'sensorfullwidth' => { 58 => 0xf903 },
+	'sensorheight' => { 58 => 0xf901, 60 => 0xfa21, 89 => 0x8 },
+	'sensorpixelsize' => { 98 => 0x9a },
+	'sensorredlevel' => { 21 => 0x4 },
+	'sensorsize' => { 70 => 'SensorSize' },
+	'sensortemperature' => { 118 => 0x1500, 120 => 0x1007 },
+	'sensorwidth' => { 58 => 0xf900, 60 => 0xfa20, 89 => 0xa },
+	'sequencenumber' => { 22 => 0x9, 45 => 0x301c, 52 => 0x1101, 59 => 0x1d, 126 => 0x2b },
+	'sequenceshotinterval' => { 145 => 0x224 },
+	'sequentialshot' => { 145 => 0x20e },
+	'serialnumber' => { 16 => 0xc, 38 => 0x180b, 48 => 0xfde9, 58 => 0xfa04, 60 => 0xfa19, 68 => 0x0, 70 => 'SerialNumber', 98 => 0xa0, 116 => 0x101, 120 => [0x404,0x101a], 146 => 0x2, 152 => 'SerialNumber' },
+	'serialnumberformat' => { 16 => 0x15, 38 => 0x183b },
+	'seriesdatetime' => { 148 => 'SeriesDateTime' },
+	'seriesdescription' => { 148 => 'SeriesDescription' },
+	'seriesmodality' => { 148 => 'SeriesModality' },
+	'seriesnumber' => { 148 => 'SeriesNumber' },
+	'serviceidentifier' => { 55 => 0x1e },
+	'setbuttoncrosskeysfunc' => { 28 => 0x0, 29 => 0x0 },
+	'setbuttonfunction' => { 23 => 0x1, 31 => 0xc },
+	'setbuttonwhenshooting' => { 25 => 0x704 },
+	'setfunctionwhenshooting' => { 26 => 0x0, 27 => 0x1, 30 => 0x1 },
+	'shadingcompensation' => { 115 => 0x50c },
+	'shadingcompensation2' => { 119 => 0x1012 },
+	'shadow' => { 146 => 0xe },
+	'shadows' => { 48 => 0xfe52, 154 => 'Shadows' },
+	'shadowscale' => { 48 => 0xc633 },
+	'shadowtint' => { 154 => 'ShadowTint' },
+	'shakereduction' => { 135 => 0x1 },
+	'sharpendetail' => { 154 => 'SharpenDetail' },
+	'sharpenedgemasking' => { 154 => 'SharpenEdgeMasking' },
+	'sharpenradius' => { 154 => 'SharpenRadius' },
+	'sharpness' => { 4 => 0xf, 21 => 0x2, 44 => 0xb, 45 => [0x3011,0x21], 48 => [0xa40a,0xfe56], 52 => 0x1001, 59 => 0x6b, 66 => 0x37, 70 => 'Sharpness', 84 => 0x21, 85 => 0x30, 86 => 0x26, 87 => 0x18, 90 => 0x3, 98 => 0x6, 120 => 0x100f, 134 => 0x21, 136 => 0xb, 143 => 0x22, 144 => 0x1003, 146 => 0x11, 154 => 'Sharpness', 157 => 'Sharpness' },
+	'sharpnessfactor' => { 120 => 0x102a },
+	'sharpnessfaithful' => { 1 => 0xf5 },
+	'sharpnessfrequency' => { 21 => 0x3 },
+	'sharpnesslandscape' => { 1 => 0xf3 },
+	'sharpnessmonochrome' => { 1 => 0xf6 },
+	'sharpnessneutral' => { 1 => 0xf4 },
+	'sharpnessportrait' => { 1 => 0xf2 },
+	'sharpnesssetting' => { 115 => 0x506, 123 => 0x1013 },
+	'sharpnessstandard' => { 1 => 0xf1 },
+	'sharpnessuserdef1' => { 1 => 0xf7 },
+	'sharpnessuserdef2' => { 1 => 0xf8 },
+	'sharpnessuserdef3' => { 1 => 0xf9 },
+	'shootingmode' => { 70 => 'ShootingMode', 98 => 0x89, 126 => 0x1f },
+	'shortdescription' => { 156 => 'shortdescription' },
+	'shortdocumentid' => { 54 => 0xba },
+	'shortfocal' => { 1 => 0x11, 2 => 0x113, 4 => 0x18 },
+	'shortownername' => { 1 => 0xac },
+	'shortreleasetimelag' => { 25 => 0x80d },
+	'shotdate' => { 164 => 'shotDate' },
+	'shotlocation' => { 164 => 'shotLocation' },
+	'shotname' => { 164 => 'shotName' },
+	'shutter-aelock' => { 23 => 0x4, 26 => 0x3, 27 => 0x4, 28 => 0x3, 29 => 0x3, 30 => 0x4, 31 => 0x2 },
+	'shutteraelbutton' => { 24 => 0x4 },
+	'shutterbuttonafonbutton' => { 25 => 0x701 },
+	'shuttercount' => { 2 => 0x176, 13 => 0x1, 134 => 0x5d },
+	'shuttercurtainsync' => { 23 => 0xf, 24 => 0xf, 25 => 0x305, 26 => 0xe, 27 => 0xf, 28 => 0x8, 29 => 0x8, 30 => 0xf, 31 => 0x8 },
+	'shuttermode' => { 59 => 0x1b },
+	'shutterreleasemethod' => { 38 => 0x1010 },
+	'shutterreleasenocfcard' => { 23 => 0x2, 24 => 0x2, 31 => 0xf },
+	'shutterreleasetiming' => { 38 => 0x1011 },
+	'shutterspeedrange' => { 25 => 0x10c },
+	'shutterspeedvalue' => { 48 => 0x9201, 120 => 0x1000, 157 => 'ShutterSpeedValue' },
+	'sidecarforextension' => { 160 => 'SidecarForExtension' },
+	'similarityindex' => { 54 => 0xe4 },
+	'slowshutter' => { 22 => 0x8 },
+	'slowsync' => { 52 => 0x1030 },
+	'smoothingparameter1' => { 119 => 0x300 },
+	'smoothingparameter2' => { 119 => 0x310 },
+	'smoothingthreshold2' => { 119 => 0x610 },
+	'smoothingthresholds' => { 119 => 0x600 },
+	'smoothness' => { 48 => 0xfe57 },
+	'software' => { 48 => 0x131, 71 => 'Software', 125 => 'Software', 146 => 0x18, 161 => 'Software' },
+	'softwareversion' => { 145 => 0x207 },
+	'source' => { 54 => 0x73, 125 => 'Source', 155 => 'source', 156 => 'source', 160 => 'Source' },
+	'sourceresolution' => { 106 => 0xae },
+	'spatialfrequencyresponsecolumns' => { 157 => 'SpatialFrequencyResponseColumns' },
+	'spatialfrequencyresponsenames' => { 157 => 'SpatialFrequencyResponseNames' },
+	'spatialfrequencyresponserows' => { 157 => 'SpatialFrequencyResponseRows' },
+	'spatialfrequencyresponsevalues' => { 157 => 'SpatialFrequencyResponseValues' },
+	'speakerplacement' => { 164 => 'speakerPlacement' },
+	'specialinstructions' => { 54 => 0x28 },
+	'specialmode' => { 120 => 0x200, 145 => 0x200 },
+	'spectralsensitivity' => { 48 => 0x8824, 157 => 'SpectralSensitivity' },
+	'splittoningbalance' => { 154 => 'SplitToningBalance' },
+	'splittoninghighlighthue' => { 154 => 'SplitToningHighlightHue' },
+	'splittoninghighlightsaturation' => { 154 => 'SplitToningHighlightSaturation' },
+	'splittoningshadowhue' => { 154 => 'SplitToningShadowHue' },
+	'splittoningshadowsaturation' => { 154 => 'SplitToningShadowSaturation' },
+	'spotfocuspointx' => { 84 => 0x2d },
+	'spotfocuspointy' => { 84 => 0x2e },
+	'spotmeterlinktoafpoint' => { 25 => 0x107 },
+	'sr_swstoswrtime' => { 135 => 0x2 },
+	'srresult' => { 135 => 0x0 },
+	'startoffsetx' => { 123 => 0x612 },
+	'startoffsety' => { 123 => 0x613 },
+	'starttimecodetimeformat' => { 164 => 'startTimecodeTimeFormat' },
+	'starttimecodetimevalue' => { 164 => 'startTimecodeTimeValue' },
+	'state' => { 75 => 'State', 160 => 'State' },
+	'status' => { 150 => 'Status' },
+	'storagemethod' => { 89 => 0x12 },
+	'straightenangle' => { 109 => 0x2fc08431 },
+	'stretchmode' => { 164 => 'stretchMode' },
+	'studydatetime' => { 148 => 'StudyDateTime' },
+	'studydescription' => { 148 => 'StudyDescription' },
+	'studyid' => { 148 => 'StudyID' },
+	'studyphysician' => { 148 => 'StudyPhysician' },
+	'sub-location' => { 54 => 0x5c },
+	'subfiledata' => { 78 => 'data' },
+	'subfiledirectory' => { 78 => '1Directory' },
+	'subfilemimetype' => { 78 => '2MIME' },
+	'subfilename' => { 78 => '1Name' },
+	'subfileresource' => { 78 => 'rsrc' },
+	'subfiletype' => { 48 => 0xfe, 78 => '0Type' },
+	'subject' => { 142 => 'Subject', 155 => 'subject', 159 => 'Subject' },
+	'subjectarea' => { 157 => 'SubjectArea' },
+	'subjectcode' => { 158 => 'SubjectCode' },
+	'subjectdistance' => { 48 => 0x9206, 59 => 0x3e, 157 => 'SubjectDistance' },
+	'subjectdistancerange' => { 48 => 0xa40c, 157 => 'SubjectDistanceRange' },
+	'subjectlocation' => { 48 => [0x9214,0xa214], 157 => 'SubjectLocation' },
+	'subjectprogram' => { 84 => 0x22 },
+	'subjectreference' => { 54 => 0xc },
+	'subsectime' => { 48 => 0x9290 },
+	'subsectimedigitized' => { 48 => 0x9292 },
+	'subsectimeoriginal' => { 48 => 0x9291 },
+	'superimposeddisplay' => { 23 => 0xa, 25 => 0x510, 26 => 0x9, 27 => 0xa, 30 => 0xa, 31 => 0xe },
+	'supplementalcategories' => { 54 => 0x14, 160 => 'SupplementalCategories' },
+	'supplementaltype' => { 56 => 0x37 },
+	'switchtoregisteredafpoint' => { 24 => 0x12, 25 => 0x50a },
+	'tagged' => { 137 => 0xdd },
+	'tapename' => { 164 => 'tapeName' },
+	'targetaperture' => { 22 => 0x4 },
+	'targetcompressionratio' => { 36 => 0x1 },
+	'targetdistancesetting' => { 38 => 0x1807 },
+	'targetexposuretime' => { 22 => 0x5 },
+	'targetimagetype' => { 38 => 0x100a },
+	'temperature' => { 154 => 'Temperature' },
+	'tempo' => { 164 => 'tempo' },
+	'thresholding' => { 48 => 0x107 },
+	'thumbnailfilename' => { 38 => 0x817 },
+	'thumbnailheight' => { 60 => 0xfa55 },
+	'thumbnailimage' => { 38 => 0x2008, 46 => 'ThumbnailImage', 50 => 0x3, 81 => 'data', 120 => 0x100 },
+	'thumbnailimagename' => { 81 => '1Name' },
+	'thumbnailimagesize' => { 81 => 'ImageSize' },
+	'thumbnailimagetype' => { 81 => '0Type' },
+	'thumbnaillength' => { 48 => 0x202 },
+	'thumbnailoffset' => { 48 => 0x201 },
+	'thumbnailsformat' => { 162 => 'ThumbnailsFormat' },
+	'thumbnailsheight' => { 162 => 'ThumbnailsHeight' },
+	'thumbnailsimage' => { 162 => 'ThumbnailsImage' },
+	'thumbnailswidth' => { 162 => 'ThumbnailsWidth' },
+	'thumbnailwidth' => { 60 => 0xfa54 },
+	'tilelength' => { 48 => 0x143 },
+	'tilewidth' => { 48 => 0x142 },
+	'time' => { 134 => 0x7 },
+	'timecreated' => { 54 => 0x3c, 59 => 0x14, 66 => 0x10, 138 => 'TimeCreated' },
+	'timerlength' => { 25 => 0x80c },
+	'timescaleparamsframeoverlappingpercentage' => { 164 => 'timeScaleParamsFrameOverlappingPercentage' },
+	'timescaleparamsframesize' => { 164 => 'timeScaleParamsFrameSize' },
+	'timescaleparamsquality' => { 164 => 'timeScaleParamsQuality' },
+	'timesent' => { 55 => 0x50 },
+	'timesignature' => { 164 => 'timeSignature' },
+	'timesincepoweron' => { 126 => 0x29 },
+	'timestamp' => { 1 => 0x11c, 2 => 0x45e, 125 => 'TimeStamp' },
+	'timezonecode' => { 41 => 0x1 },
+	'timezoneinfo' => { 41 => 0x2 },
+	'timezoneoffset' => { 48 => 0x882a },
+	'tint' => { 154 => 'Tint' },
+	'title' => { 71 => 'Title', 125 => 'Title', 142 => 'Title', 155 => 'title', 159 => 'Title' },
+	'tonecomp' => { 98 => 0x81 },
+	'tonecurve' => { 21 => 0x1, 134 => 0x402, 154 => 'ToneCurve' },
+	'tonecurveactive' => { 42 => 0x110 },
+	'tonecurvename' => { 154 => 'ToneCurveName' },
+	'tonecurveproperty' => { 42 => 0x3c },
+	'tonecurves' => { 134 => 0x403 },
+	'toningeffect' => { 13 => 0xf },
+	'toningeffectmonochrome' => { 1 => 0x108 },
+	'totalzoom' => { 59 => 0x62 },
+	'tracknumber' => { 164 => 'trackNumber' },
+	'trailersignature' => { 78 => 'zmie' },
+	'transferfunction' => { 161 => 'TransferFunction' },
+	'transmissionreference' => { 160 => 'TransmissionReference' },
+	'travelday' => { 126 => 0x36 },
+	'ttl_da_adown' => { 132 => 0x5 },
+	'ttl_da_aup' => { 132 => 0x4 },
+	'ttl_da_bdown' => { 132 => 0x7 },
+	'ttl_da_bup' => { 132 => 0x6 },
+	'tvvalue' => { 34 => 0x1 },
+	'type' => { 155 => 'type' },
+	'uniquecameramodel' => { 48 => 0xc614 },
+	'uniquedocumentid' => { 54 => 0xbb },
+	'uniqueobjectname' => { 55 => 0x64 },
+	'unknownblock' => { 119 => 0x1103 },
+	'unknowncompensation' => { 146 => 0xc },
+	'unsharp1color' => { 113 => 0x13 },
+	'unsharp1halowidth' => { 113 => 0x19 },
+	'unsharp1intensity' => { 113 => 0x17 },
+	'unsharp1threshold' => { 113 => 0x1b },
+	'unsharp2color' => { 113 => 0x2e },
+	'unsharp2halowidth' => { 113 => 0x34 },
+	'unsharp2intensity' => { 113 => 0x32 },
+	'unsharp2threshold' => { 113 => 0x36 },
+	'unsharp3color' => { 113 => 0x49 },
+	'unsharp3halowidth' => { 113 => 0x4f },
+	'unsharp3intensity' => { 113 => 0x4d },
+	'unsharp3threshold' => { 113 => 0x51 },
+	'unsharp4color' => { 113 => 0x64 },
+	'unsharp4halowidth' => { 113 => 0x6a },
+	'unsharp4intensity' => { 113 => 0x68 },
+	'unsharp4threshold' => { 113 => 0x6c },
+	'unsharpcount' => { 113 => 0x0 },
+	'unsharpmask' => { 109 => 0x76a43200 },
+	'urgency' => { 54 => 0xa, 160 => 'Urgency' },
+	'url' => { 71 => 'URL', 125 => 'URL', 140 => 0x40b },
+	'usablemeteringmodes' => { 25 => 0x10a },
+	'usableshootingmodes' => { 25 => 0x109 },
+	'usageterms' => { 167 => 'UsageTerms' },
+	'usercomment' => { 38 => 0x805, 48 => 0x9286, 157 => 'UserComment' },
+	'userdef1picturestyle' => { 1 => 0x10c },
+	'userdef2picturestyle' => { 1 => 0x10e },
+	'userdef3picturestyle' => { 1 => 0x110 },
+	'userfields' => { 150 => 'UserFields' },
+	'usmlenselectronicmf' => { 24 => 0x7, 25 => 0x501 },
+	'utmeasting' => { 82 => 'Easting' },
+	'utmmapdatum' => { 82 => 'Datum' },
+	'utmnorthing' => { 82 => 'Northing' },
+	'utmzone' => { 82 => 'Zone' },
+	'validbits' => { 119 => 0x611, 120 => 0x102c },
+	'validpixeldepth' => { 123 => 0x611 },
+	'variousmodes' => { 59 => 0x26 },
+	'variousmodes2' => { 59 => 0x3a },
+	'variprogram' => { 98 => 0xab },
+	'version' => { 52 => 0x0, 142 => 'Version', 154 => 'Version' },
+	'versionid' => { 165 => 'VersionID' },
+	'versionscomments' => { 165 => 'VersionsComments' },
+	'versionseventaction' => { 165 => 'VersionsEventAction' },
+	'versionseventinstanceid' => { 165 => 'VersionsEventInstanceID' },
+	'versionseventparameters' => { 165 => 'VersionsEventParameters' },
+	'versionseventsoftwareagent' => { 165 => 'VersionsEventSoftwareAgent' },
+	'versionseventwhen' => { 165 => 'VersionsEventWhen' },
+	'versionsmodifier' => { 165 => 'VersionsModifier' },
+	'versionsmodifydate' => { 165 => 'VersionsModifyDate' },
+	'versionsversion' => { 165 => 'VersionsVersion' },
+	'vibrance' => { 154 => 'Vibrance' },
+	'videoalphamode' => { 164 => 'videoAlphaMode' },
+	'videoalphapremultiplecolora' => { 164 => 'videoAlphaPremultipleColorA' },
+	'videoalphapremultiplecolorb' => { 164 => 'videoAlphaPremultipleColorB' },
+	'videoalphapremultiplecolorblack' => { 164 => 'videoAlphaPremultipleColorBlack' },
+	'videoalphapremultiplecolorblue' => { 164 => 'videoAlphaPremultipleColorBlue' },
+	'videoalphapremultiplecolorcyan' => { 164 => 'videoAlphaPremultipleColorCyan' },
+	'videoalphapremultiplecolorgreen' => { 164 => 'videoAlphaPremultipleColorGreen' },
+	'videoalphapremultiplecolorl' => { 164 => 'videoAlphaPremultipleColorL' },
+	'videoalphapremultiplecolormagenta' => { 164 => 'videoAlphaPremultipleColorMagenta' },
+	'videoalphapremultiplecolormode' => { 164 => 'videoAlphaPremultipleColorMode' },
+	'videoalphapremultiplecolorred' => { 164 => 'videoAlphaPremultipleColorRed' },
+	'videoalphapremultiplecolorswatchname' => { 164 => 'videoAlphaPremultipleColorSwatchName' },
+	'videoalphapremultiplecolortype' => { 164 => 'videoAlphaPremultipleColorType' },
+	'videoalphapremultiplecoloryellow' => { 164 => 'videoAlphaPremultipleColorYellow' },
+	'videoalphaunityistransparent' => { 164 => 'videoAlphaUnityIsTransparent' },
+	'videocolorspace' => { 164 => 'videoColorSpace' },
+	'videocompressor' => { 164 => 'videoCompressor' },
+	'videofieldorder' => { 164 => 'videoFieldOrder' },
+	'videoframerate' => { 164 => 'videoFrameRate' },
+	'videoframesizeh' => { 164 => 'videoFrameSizeH' },
+	'videoframesizeunit' => { 164 => 'videoFrameSizeUnit' },
+	'videoframesizew' => { 164 => 'videoFrameSizeW' },
+	'videomoddate' => { 164 => 'videoModDate' },
+	'videopixelaspectratio' => { 164 => 'videoPixelAspectRatio' },
+	'videopixeldepth' => { 164 => 'videoPixelDepth' },
+	'viewinfoduringexposure' => { 25 => 0x407 },
+	'vignetteamount' => { 154 => 'VignetteAmount' },
+	'vignettecontrol' => { 109 => 0x76a43205 },
+	'vignettecontrolintensity' => { 109 => 0xac6bd5c0 },
+	'vignettemidpoint' => { 154 => 'VignetteMidpoint' },
+	'voicememo' => { 145 => 0x216 },
+	'vrdoffset' => { 16 => 0xd0 },
+	'wb_rbgglevels' => { 93 => 0x0 },
+	'wb_rblevels' => { 119 => 0x100 },
+	'wb_rblevelsauto' => { 123 => 0x110 },
+	'wb_rblevelscloudy' => { 123 => 0x121 },
+	'wb_rblevelscoolwhitefluor' => { 123 => 0x132 },
+	'wb_rblevelsdaylightfluor' => { 123 => 0x130 },
+	'wb_rblevelsdaywhitefluor' => { 123 => 0x131 },
+	'wb_rblevelseveningsunlight' => { 123 => 0x124 },
+	'wb_rblevelsfineweather' => { 123 => 0x122 },
+	'wb_rblevelsshade' => { 123 => 0x120 },
+	'wb_rblevelstungsten' => { 123 => 0x123 },
+	'wb_rblevelsused' => { 123 => 0x100 },
+	'wb_rblevelswhitefluorescent' => { 123 => 0x133 },
+	'wb_rgbglevels' => { 95 => 0x0 },
+	'wb_rggblevels' => { 94 => 0x0 },
+	'wb_rggblevelsasshot' => { 6 => 0x19, 7 => 0x18, 8 => 0x3f },
+	'wb_rggblevelsauto' => { 5 => 0x0, 6 => 0x1e, 7 => 0x22, 8 => 0x44 },
+	'wb_rggblevelscloudy' => { 5 => 0xc, 6 => 0x2d, 7 => 0x31, 8 => 0x58, 134 => 0x20f },
+	'wb_rggblevelscustom' => { 5 => 0x1c, 8 => 0x80 },
+	'wb_rggblevelscustom1' => { 6 => 0x41 },
+	'wb_rggblevelscustom2' => { 6 => 0x46 },
+	'wb_rggblevelsdaylight' => { 5 => 0x4, 6 => 0x23, 7 => 0x27, 8 => 0x4e, 134 => 0x20d },
+	'wb_rggblevelsflash' => { 5 => 0x18, 6 => 0x3c, 7 => 0x45, 8 => 0x6c, 134 => 0x214 },
+	'wb_rggblevelsfluorescent' => { 5 => 0x14, 6 => 0x37, 7 => 0x3b, 8 => 0x62 },
+	'wb_rggblevelsfluorescentd' => { 134 => 0x211 },
+	'wb_rggblevelsfluorescentn' => { 134 => 0x212 },
+	'wb_rggblevelsfluorescentw' => { 134 => 0x213 },
+	'wb_rggblevelskelvin' => { 5 => 0x20, 7 => 0x40, 8 => 0x67 },
+	'wb_rggblevelsmeasured' => { 8 => 0x49 },
+	'wb_rggblevelspc1' => { 7 => 0x90, 8 => 0x71 },
+	'wb_rggblevelspc2' => { 7 => 0x95, 8 => 0x76 },
+	'wb_rggblevelspc3' => { 7 => 0x9a, 8 => 0x7b },
+	'wb_rggblevelsshade' => { 5 => 0x8, 6 => 0x28, 7 => 0x2c, 8 => 0x53, 134 => 0x20e },
+	'wb_rggblevelstungsten' => { 5 => 0x10, 6 => 0x32, 7 => 0x36, 8 => 0x5d, 134 => 0x210 },
+	'wb_rggblevelsunknown' => { 7 => 0x1d },
+	'wb_rggblevelsunknown10' => { 7 => 0x72 },
+	'wb_rggblevelsunknown11' => { 7 => 0x77 },
+	'wb_rggblevelsunknown12' => { 7 => 0x7c },
+	'wb_rggblevelsunknown13' => { 7 => 0x81 },
+	'wb_rggblevelsunknown14' => { 7 => 0x86 },
+	'wb_rggblevelsunknown15' => { 7 => 0x8b },
+	'wb_rggblevelsunknown16' => { 7 => 0x9f },
+	'wb_rggblevelsunknown2' => { 7 => 0x4a },
+	'wb_rggblevelsunknown3' => { 7 => 0x4f },
+	'wb_rggblevelsunknown4' => { 7 => 0x54 },
+	'wb_rggblevelsunknown5' => { 7 => 0x59 },
+	'wb_rggblevelsunknown6' => { 7 => 0x5e },
+	'wb_rggblevelsunknown7' => { 7 => 0x63 },
+	'wb_rggblevelsunknown8' => { 7 => 0x68 },
+	'wb_rggblevelsunknown9' => { 7 => 0x6d },
+	'wbadjbluebalance' => { 114 => 0x8 },
+	'wbadjcolortemp' => { 42 => 0x1a },
+	'wbadjlighting' => { 114 => 0x15 },
+	'wbadjmode' => { 114 => 0x10 },
+	'wbadjredbalance' => { 114 => 0x0 },
+	'wbadjrgblevels' => { 42 => 0x8 },
+	'wbadjtemperature' => { 114 => 0x18 },
+	'wbadjustab' => { 126 => 0x46 },
+	'wbadjustgm' => { 126 => 0x47 },
+	'wbbluelevel' => { 126 => 0x8006, 127 => 0x26 },
+	'wbbracketmode' => { 13 => 0x9 },
+	'wbbracketvalueab' => { 13 => 0xc },
+	'wbbracketvaluegm' => { 13 => 0xd },
+	'wbfinetuneactive' => { 42 => 0x24 },
+	'wbfinetunesaturation' => { 42 => 0x28 },
+	'wbfinetunetone' => { 42 => 0x2c },
+	'wbgreenlevel' => { 126 => 0x8005, 127 => 0x25 },
+	'wblevels' => { 91 => 0x4 },
+	'wbmediaimagesizesetting' => { 25 => 0x708 },
+	'wbmode' => { 90 => 0x4, 120 => 0x1015 },
+	'wbredlevel' => { 126 => 0x8004, 127 => 0x24 },
+	'wbscale' => { 91 => 0x0 },
+	'wbshiftab' => { 21 => 0xc },
+	'wbshiftgm' => { 21 => 0xd },
+	'webstatement' => { 167 => 'WebStatement' },
+	'whitebalance' => { 21 => 0x8, 22 => 0x7, 44 => 0x7, 45 => [0x19,0x2012], 48 => [0xa403,0xfe4e], 52 => 0x1002, 59 => 0x40, 84 => 0x3, 85 => 0xe, 86 => 0x4, 87 => 0xb, 98 => 0x5, 103 => 0x7, 126 => 0x3, 134 => 0x19, 136 => 0x7, 143 => 0x26, 146 => [0x3c,0x7], 154 => 'WhiteBalance', 157 => 'WhiteBalance' },
+	'whitebalance2' => { 115 => 0x500 },
+	'whitebalanceadj' => { 42 => 0x18, 109 => 0x76a43204 },
+	'whitebalancebias' => { 45 => 0x2011, 120 => 0x304, 126 => 0x23 },
+	'whitebalanceblue' => { 21 => 0x7 },
+	'whitebalancebracket' => { 115 => 0x502, 120 => 0x303 },
+	'whitebalancecomp' => { 123 => 0x1001 },
+	'whitebalancefinetune' => { 98 => 0xb },
+	'whitebalancemode' => { 134 => 0x1a },
+	'whitebalancered' => { 21 => 0x6 },
+	'whitebalancetemperature' => { 115 => 0x501 },
+	'whiteboard' => { 120 => 0x301 },
+	'whitepoint' => { 48 => 0x13e, 134 => 0x201, 161 => 'WhitePoint' },
+	'widefocuszone' => { 84 => 0x2f },
+	'widerange' => { 145 => 0x20f },
+	'workcolorspace' => { 42 => 0x270 },
+	'worldtimelocation' => { 126 => 0x3a, 134 => 0x22 },
+	'writer-editor' => { 54 => 0x7a },
+	'x3filllight' => { 146 => 0x12 },
+	'xmp' => { 49 => 'XMP' },
+	'xpauthor' => { 48 => 0x9c9d },
+	'xpcomment' => { 48 => 0x9c9c },
+	'xpkeywords' => { 48 => 0x9c9e },
+	'xposition' => { 48 => 0x11e },
+	'xpsubject' => { 48 => 0x9c9f },
+	'xptitle' => { 48 => 0x9c9b },
+	'xresolution' => { 48 => 0x11a, 57 => 0x3, 141 => 0x0, 161 => 'XResolution' },
+	'xyresolution' => { 51 => 0x3 },
+	'ycbcrcoefficients' => { 48 => 0x211, 161 => 'YCbCrCoefficients' },
+	'ycbcrpositioning' => { 48 => 0x213, 161 => 'YCbCrPositioning' },
+	'ycbcrsubsampling' => { 48 => 0x212, 161 => 'YCbCrSubSampling' },
+	'yearcreated' => { 59 => 0x10, 66 => 0xc },
+	'yposition' => { 48 => 0x11f },
+	'yresolution' => { 48 => 0x11b, 57 => 0x5, 141 => 0x4, 161 => 'YResolution' },
+	'zonematching' => { 88 => 0x10a, 90 => 0x3a },
+	'zonematchingon' => { 86 => 0x75 },
+	'zoomsourcewidth' => { 4 => 0x24 },
+	'zoomstepcount' => { 118 => 0x300, 120 => 0x100d },
+	'zoomtargetwidth' => { 4 => 0x25 },
+);
+
+# lookup for non-writable tags to check if the name exists
+my %tagExists = (
+	'abstract' => 1,
+	'adobe' => 1,
+	'adobe_cm' => 1,
+	'adobecmtype' => 1,
+	'adobecrw' => 1,
+	'adobemrw' => 1,
+	'adobephotoshop' => 1,
+	'adoberaf' => 1,
+	'adobesr2' => 1,
+	'advancedcontentencryption' => 1,
+	'advancedmutualexcl' => 1,
+	'aeinfo' => 1,
+	'afaperture' => 1,
+	'afarea' => 1,
+	'afareaheight' => 1,
+	'afareaheights' => 1,
+	'afareawidth' => 1,
+	'afareawidths' => 1,
+	'afareaxpositions' => 1,
+	'afareaypositions' => 1,
+	'afcp' => 1,
+	'afcp_iptc' => 1,
+	'afimageheight' => 1,
+	'afimagewidth' => 1,
+	'afinfo' => 1,
+	'afinfocus' => 1,
+	'afinfosize' => 1,
+	'afpointsinfocus1d' => 1,
+	'afpointsselected' => 1,
+	'albumartist' => 1,
+	'albumcoverurl' => 1,
+	'albumname' => 1,
+	'albumsortorder' => 1,
+	'albumtitle' => 1,
+	'aliaslayermetadata' => 1,
+	'alphabitdepth' => 1,
+	'alphabytecount' => 1,
+	'alphachannelsnames' => 1,
+	'alphacompression' => 1,
+	'alphadatadiscard' => 1,
+	'alphafilter' => 1,
+	'alphaidentifiers' => 1,
+	'alphainterlace' => 1,
+	'alphaoffset' => 1,
+	'alphasample' => 1,
+	'analogbalance' => 1,
+	'annotation' => 1,
+	'annotations' => 1,
+	'aperture' => 1,
+	'aperturedisplayed' => 1,
+	'apeversion' => 1,
+	'app14flags0' => 1,
+	'app14flags1' => 1,
+	'app1_profile' => 1,
+	'applestoreid' => 1,
+	'applicationnotes' => 1,
+	'appversion' => 1,
+	'archivallocation' => 1,
+	'artistlen' => 1,
+	'artisturl' => 1,
+	'asfleakybucketpairs' => 1,
+	'asfpacketcount' => 1,
+	'asfsecurityobjectssize' => 1,
+	'aspectratio' => 1,
+	'aspectratiox' => 1,
+	'aspectratioy' => 1,
+	'asshotneutral' => 1,
+	'association' => 1,
+	'atob0' => 1,
+	'atob1' => 1,
+	'atob2' => 1,
+	'audiences' => 1,
+	'audiobitrate' => 1,
+	'audiobytes' => 1,
+	'audiochannels' => 1,
+	'audiocodecdescription' => 1,
+	'audiocodecid' => 1,
+	'audiocodecname' => 1,
+	'audiodelay' => 1,
+	'audioencoding' => 1,
+	'audiofilesize' => 1,
+	'audiofileurl' => 1,
+	'audioformat' => 1,
+	'audioframesize' => 1,
+	'audioinfo' => 1,
+	'audiolayer' => 1,
+	'audiomode' => 1,
+	'audiosamplebits' => 1,
+	'audiosamplesize' => 1,
+	'audiosize' => 1,
+	'audiosourceurl' => 1,
+	'audiostream' => 1,
+	'authorlen' => 1,
+	'authorship' => 1,
+	'authorurl' => 1,
+	'aux' => 1,
+	'averagelevel' => 1,
+	'avgbitrate' => 1,
+	'avgbytespersec' => 1,
+	'avgpacketsize' => 1,
+	'avif' => 1,
+	'aviheader' => 1,
+	'background' => 1,
+	'backgroundcolor' => 1,
+	'backgroundcolorindicator' => 1,
+	'backgroundcolorvalue' => 1,
+	'backgroundimageid' => 1,
+	'backgroundtiling' => 1,
+	'backlight' => 1,
+	'backserial' => 1,
+	'badfaxlines' => 1,
+	'band' => 1,
+	'bandwidthsharing' => 1,
+	'bannerimage' => 1,
+	'bannerimagedata' => 1,
+	'bannerimagetype' => 1,
+	'bannerimageurl' => 1,
+	'basisobject' => 1,
+	'batteryinfo' => 1,
+	'batterylevel' => 1,
+	'beatsperminute' => 1,
+	'binaryfilter' => 1,
+	'bitrate' => 1,
+	'bitratemutualexclusion' => 1,
+	'bitsperextendedrunlength' => 1,
+	'bitsperrunlength' => 1,
+	'blackleveldeltah' => 1,
+	'blackleveldeltav' => 1,
+	'blacklevelrepeatdim' => 1,
+	'blocklocation' => 1,
+	'blocksize' => 1,
+	'blocksizemax' => 1,
+	'blocksizemin' => 1,
+	'blocksperframe' => 1,
+	'blueadjust' => 1,
+	'bluegain' => 1,
+	'bluematrixcolumn' => 1,
+	'blueprimary' => 1,
+	'bluesample' => 1,
+	'bluetrc' => 1,
+	'bluex' => 1,
+	'bluey' => 1,
+	'bordercolor' => 1,
+	'borderid' => 1,
+	'borderinformation' => 1,
+	'borderlocation' => 1,
+	'bordername' => 1,
+	'bordersversion' => 1,
+	'bordertype' => 1,
+	'bottommag' => 1,
+	'boundaryorigin' => 1,
+	'boundingbox' => 1,
+	'bracketshot' => 1,
+	'brightnessdata' => 1,
+	'broadcast' => 1,
+	'btoa0' => 1,
+	'btoa1' => 1,
+	'btoa2' => 1,
+	'bufferaverage' => 1,
+	'burstshot' => 1,
+	'bw_halftoninginfo' => 1,
+	'bw_transferfunc' => 1,
+	'bytes' => 1,
+	'bytesperminute' => 1,
+	'cachecontrol' => 1,
+	'cachedimageheight' => 1,
+	'cachedimagewidth' => 1,
+	'calibrationdatetime' => 1,
+	'camera' => 1,
+	'camerabacktype' => 1,
+	'cameracalibration1' => 1,
+	'cameracalibration2' => 1,
+	'cameradatetime' => 1,
+	'camerainfo' => 1,
+	'cameraname' => 1,
+	'cameraobjbacktype' => 1,
+	'cameraobject' => 1,
+	'cameraobjname' => 1,
+	'cameraobjtype' => 1,
+	'cameraobjversion' => 1,
+	'cameraowner' => 1,
+	'cameraprofileversion' => 1,
+	'camerasettings' => 1,
+	'camerasettings2' => 1,
+	'camerasetup' => 1,
+	'cameraspecification' => 1,
+	'can_skip_backward' => 1,
+	'can_skip_forward' => 1,
+	'canon' => 1,
+	'canon_afinfo2_0x000d' => 1,
+	'canon_afinfo_0x000b' => 1,
+	'canonafinfo' => 1,
+	'canonafinfo2' => 1,
+	'canoncamerainfo' => 1,
+	'canoncamerainfo1dmkiii' => 1,
+	'canoncamerainfo2' => 1,
+	'canoncamerainfounknown16' => 1,
+	'canoncamerainfounknown32' => 1,
+	'canoncamerainfounknown8' => 1,
+	'canoncamerasettings' => 1,
+	'canoncolorinfo1' => 1,
+	'canoncolorinfo2' => 1,
+	'canonfileinfo' => 1,
+	'canonflags' => 1,
+	'canonfocallength' => 1,
+	'canonimageheight' => 1,
+	'canonimagewidth' => 1,
+	'canonpanorama' => 1,
+	'canonrawmakemodel' => 1,
+	'canonshotinfo' => 1,
+	'canseektoend' => 1,
+	'captiontext' => 1,
+	'captprofbacktype' => 1,
+	'captprofname' => 1,
+	'captproftype' => 1,
+	'captprofversion' => 1,
+	'captureconditionspar' => 1,
+	'capturedevicefid' => 1,
+	'captureobjbacktype' => 1,
+	'captureobjname' => 1,
+	'captureobjtype' => 1,
+	'captureobjversion' => 1,
+	'captureprofile' => 1,
+	'captureresolution' => 1,
+	'captureserial' => 1,
+	'capturesetup' => 1,
+	'capturexresolution' => 1,
+	'capturexresolutionunit' => 1,
+	'captureyresolution' => 1,
+	'captureyresolutionunit' => 1,
+	'casio' => 1,
+	'cc' => 1,
+	'ccdrect' => 1,
+	'ccdvalidrect' => 1,
+	'ccdvideorect' => 1,
+	'centerdarkrect' => 1,
+	'cfalayout' => 1,
+	'cfapattern2' => 1,
+	'cfaplanecolor' => 1,
+	'cfarepeatpatterndim' => 1,
+	'channelmode' => 1,
+	'charcount' => 1,
+	'charcountwithspaces' => 1,
+	'charset' => 1,
+	'chartarget' => 1,
+	'chromaticadaptation' => 1,
+	'chromaticity' => 1,
+	'chromaticitychannel1' => 1,
+	'chromaticitychannel2' => 1,
+	'chromaticitychannel3' => 1,
+	'chromaticitychannel4' => 1,
+	'chromaticitychannels' => 1,
+	'chromaticitycolorant' => 1,
+	'ciff' => 1,
+	'circleofconfusion' => 1,
+	'class' => 1,
+	'classification' => 1,
+	'cleanfaxdata' => 1,
+	'clipboundary' => 1,
+	'clipobjects' => 1,
+	'clippath' => 1,
+	'clippingboundary' => 1,
+	'clippingpathname' => 1,
+	'cloneid' => 1,
+	'cloneobject' => 1,
+	'clonetype' => 1,
+	'cmmflags' => 1,
+	'codecflavorid' => 1,
+	'codeclist' => 1,
+	'codedframesize' => 1,
+	'codepage' => 1,
+	'codestreamheader' => 1,
+	'codestreamregistration' => 1,
+	'codingmethods' => 1,
+	'colorant1coordinates' => 1,
+	'colorant1name' => 1,
+	'colorant2coordinates' => 1,
+	'colorant2name' => 1,
+	'colorant3coordinates' => 1,
+	'colorant3name' => 1,
+	'colorantcount' => 1,
+	'colorantorder' => 1,
+	'coloranttable' => 1,
+	'coloraverages' => 1,
+	'colorbalance0100' => 1,
+	'colorbalance0102' => 1,
+	'colorbalance0103' => 1,
+	'colorbalance02' => 1,
+	'colorbalance0205' => 1,
+	'colorbalance2' => 1,
+	'colorbalance3' => 1,
+	'colorbalanceunknown' => 1,
+	'colorbitdepth' => 1,
+	'colorboostdata' => 1,
+	'colorbw' => 1,
+	'colorcalibrationmatrix' => 1,
+	'colorcasts' => 1,
+	'colorcharacterization' => 1,
+	'colorcomponents' => 1,
+	'colorgroup' => 1,
+	'colorhalftoninginfo' => 1,
+	'colorinfo' => 1,
+	'colorinfod30' => 1,
+	'colormap' => 1,
+	'colormatrix1' => 1,
+	'colorobjbacktype' => 1,
+	'colorobjname' => 1,
+	'colorobjtype' => 1,
+	'colorobjversion' => 1,
+	'colorpalette' => 1,
+	'colorresponseunit' => 1,
+	'colors' => 1,
+	'colorsamplersresource' => 1,
+	'colorsetup' => 1,
+	'colorspacedata' => 1,
+	'colorspecification' => 1,
+	'colortable' => 1,
+	'colortransferfuncs' => 1,
+	'colortransform' => 1,
+	'colortwistmatrix' => 1,
+	'colortype' => 1,
+	'commentlen' => 1,
+	'comments' => 1,
+	'commenttime' => 1,
+	'commercialurl' => 1,
+	'commissioned' => 1,
+	'common' => 1,
+	'company' => 1,
+	'compatibility' => 1,
+	'compilation' => 1,
+	'compobj' => 1,
+	'compobjusertype' => 1,
+	'compobjusertypelen' => 1,
+	'componentbitdepth' => 1,
+	'componentdefinition' => 1,
+	'componentmapping' => 1,
+	'compositinglayerheader' => 1,
+	'composition' => 1,
+	'compositionmode' => 1,
+	'compositionoptions' => 1,
+	'compressed' => 1,
+	'compressedtext' => 1,
+	'compressionlevel' => 1,
+	'compressiontype' => 1,
+	'compressor' => 1,
+	'compressorid' => 1,
+	'concreteflag' => 1,
+	'condition' => 1,
+	'conditionalfec' => 1,
+	'conductor' => 1,
+	'confirmedobjectsize' => 1,
+	'connectionspaceilluminant' => 1,
+	'consecutivebadfaxlines' => 1,
+	'cont' => 1,
+	'containerformat' => 1,
+	'contake' => 1,
+	'contentbranding' => 1,
+	'contentdescription' => 1,
+	'contentdescriptionnotes' => 1,
+	'contentdisposition' => 1,
+	'contentdistributor' => 1,
+	'contentencryption' => 1,
+	'contentgroupdescription' => 1,
+	'contentlanguage' => 1,
+	'contentrating' => 1,
+	'contentscripttype' => 1,
+	'contentstyletype' => 1,
+	'contenttype' => 1,
+	'contiguouscodestream' => 1,
+	'contrastadjustment' => 1,
+	'copyrightlen' => 1,
+	'copyrighturl' => 1,
+	'coverart' => 1,
+	'coverartmimetype' => 1,
+	'cpuversions' => 1,
+	'crdinfo' => 1,
+	'creatingapplication' => 1,
+	'creatingtransform' => 1,
+	'creationpathvector' => 1,
+	'cropdata' => 1,
+	'cropped' => 1,
+	'cross-reference' => 1,
+	'crs' => 1,
+	'cuepoint' => 1,
+	'currentbitrate' => 1,
+	'currenttime' => 1,
+	'currentuser' => 1,
+	'customfunctions10d' => 1,
+	'customfunctions1d' => 1,
+	'customfunctions2' => 1,
+	'customfunctions20d' => 1,
+	'customfunctions30d' => 1,
+	'customfunctions350d' => 1,
+	'customfunctions400d' => 1,
+	'customfunctions5d' => 1,
+	'customfunctionsd30' => 1,
+	'customfunctionsd60' => 1,
+	'customfunctionsunknown' => 1,
+	'd-lightinghqdata' => 1,
+	'd-lightinghsdata' => 1,
+	'darkcorrectiontype' => 1,
+	'data' => 1,
+	'datacreatedate' => 1,
+	'datadump' => 1,
+	'datadump2' => 1,
+	'datakey' => 1,
+	'datalen' => 1,
+	'datamodifydate' => 1,
+	'dataobject' => 1,
+	'dataobjectid' => 1,
+	'dataobjectstatus' => 1,
+	'dataobjecttitle' => 1,
+	'dataoffset' => 1,
+	'dataoffsets' => 1,
+	'datapackets' => 1,
+	'datareference' => 1,
+	'datasize' => 1,
+	'datatype' => 1,
+	'datetimecreated' => 1,
+	'dc' => 1,
+	'dctencodeversion' => 1,
+	'dealeridnumber' => 1,
+	'decimationmethod' => 1,
+	'decimationprefilterwidth' => 1,
+	'decode' => 1,
+	'decodertable' => 1,
+	'defaultdisplayheight' => 1,
+	'defaultdisplaywidth' => 1,
+	'defaultimagecolor' => 1,
+	'defaultrgb' => 1,
+	'defaultstyle' => 1,
+	'defineobject' => 1,
+	'delay' => 1,
+	'deltapngheader' => 1,
+	'deltatype' => 1,
+	'deltaxy' => 1,
+	'depth' => 1,
+	'desiredreproductions' => 1,
+	'destinationid' => 1,
+	'deviceattributes' => 1,
+	'devicemanufacturer' => 1,
+	'devicemfgdesc' => 1,
+	'devicemodel' => 1,
+	'devicemodeldesc' => 1,
+	'devicesettingdescription' => 1,
+	'devicesettings' => 1,
+	'dex' => 1,
+	'dicom' => 1,
+	'dictionary' => 1,
+	'digitaleffectsname' => 1,
+	'digitaleffectstype' => 1,
+	'digitaleffectsversion' => 1,
+	'digitalimagebroker' => 1,
+	'digitalsignature' => 1,
+	'dimensions' => 1,
+	'dinf' => 1,
+	'director' => 1,
+	'discardobjects' => 1,
+	'disknumber' => 1,
+	'displayinfo' => 1,
+	'displayresolution' => 1,
+	'displaysize' => 1,
+	'displayunits' => 1,
+	'displayxresolution' => 1,
+	'displayxresolutionunit' => 1,
+	'displayyresolution' => 1,
+	'displayyresolutionunit' => 1,
+	'dispose' => 1,
+	'distribution' => 1,
+	'dngadobedata' => 1,
+	'dngpentaxdata' => 1,
+	'dngprivatedata' => 1,
+	'docclass' => 1,
+	'docrights' => 1,
+	'doctype' => 1,
+	'documentinfo' => 1,
+	'dof' => 1,
+	'donotshow' => 1,
+	'dotsperinch' => 1,
+	'drm' => 1,
+	'drm_contentid' => 1,
+	'drm_drmheader' => 1,
+	'drm_drmheader_contentdistributor' => 1,
+	'drm_drmheader_contentid' => 1,
+	'drm_drmheader_individualizedversion' => 1,
+	'drm_drmheader_keyid' => 1,
+	'drm_drmheader_licenseacqurl' => 1,
+	'drm_drmheader_subscriptioncontentid' => 1,
+	'drm_individualizedversion' => 1,
+	'drm_keyid' => 1,
+	'drm_lasignaturecert' => 1,
+	'drm_lasignaturelicsrvcert' => 1,
+	'drm_lasignatureprivkey' => 1,
+	'drm_lasignaturerootcert' => 1,
+	'drm_licenseacqurl' => 1,
+	'drm_v1licenseacqurl' => 1,
+	'dropbykeyword' => 1,
+	'dropchunks' => 1,
+	'ducky' => 1,
+	'duotonehalftoninginfo' => 1,
+	'duotoneimageinfo' => 1,
+	'duotonetransferfuncs' => 1,
+	'dustremovaldata' => 1,
+	'dvdid' => 1,
+	'dynamicrangeoptimizer' => 1,
+	'edit1' => 1,
+	'edit2' => 1,
+	'edit3' => 1,
+	'edit4' => 1,
+	'edit5' => 1,
+	'edit6' => 1,
+	'edit7' => 1,
+	'edit8' => 1,
+	'edit9' => 1,
+	'edittagarray' => 1,
+	'effectivebw' => 1,
+	'effectivemaxaperture' => 1,
+	'effectsvisible' => 1,
+	'emphasis' => 1,
+	'encodedby' => 1,
+	'encodedusing' => 1,
+	'encoder' => 1,
+	'encodersettings' => 1,
+	'encoderversion' => 1,
+	'encoding' => 1,
+	'encodingprocess' => 1,
+	'encodingsettings' => 1,
+	'encodingtime' => 1,
+	'encryption' => 1,
+	'endpoints' => 1,
+	'eppim' => 1,
+	'epsoptions' => 1,
+	'equipment' => 1,
+	'equipment2' => 1,
+	'error' => 1,
+	'errorcorrection' => 1,
+	'errorcorrectiontype' => 1,
+	'evalstate' => 1,
+	'events' => 1,
+	'exif' => 1,
+	'exif_profile' => 1,
+	'exifinfo' => 1,
+	'exifinformation' => 1,
+	'exifoffset' => 1,
+	'exiftoolversion' => 1,
+	'exitpupilposition' => 1,
+	'expires' => 1,
+	'exportimage' => 1,
+	'exposureadjust' => 1,
+	'exposureinfo' => 1,
+	'extcache' => 1,
+	'extendedcontentdescr' => 1,
+	'extendedcontentencryption' => 1,
+	'extendedstreamprops' => 1,
+	'extenderstatus' => 1,
+	'extensionclassid' => 1,
+	'extensioncreatedate' => 1,
+	'extensiondescription' => 1,
+	'extensionmodifydate' => 1,
+	'extensionname' => 1,
+	'extensionpersistence' => 1,
+	'extensions' => 1,
+	'extrasamples' => 1,
+	'facedetect1' => 1,
+	'facedetect2' => 1,
+	'fastseek' => 1,
+	'faxprofile' => 1,
+	'faxrecvparams' => 1,
+	'faxrecvtime' => 1,
+	'faxsubaddress' => 1,
+	'fileid' => 1,
+	'fileinfolen' => 1,
+	'fileinfolen2' => 1,
+	'fileinfoproperties' => 1,
+	'fileinfoversion' => 1,
+	'fileowner' => 1,
+	'fileproperties' => 1,
+	'filerul' => 1,
+	'files' => 1,
+	'filesize' => 1,
+	'filesizebytes' => 1,
+	'filetype' => 1,
+	'fillmethod' => 1,
+	'filmbrand' => 1,
+	'filmcategory' => 1,
+	'filmframenumber' => 1,
+	'filmgencode' => 1,
+	'filmproductcode' => 1,
+	'filmrollnumber' => 1,
+	'filmsize' => 1,
+	'filtering' => 1,
+	'finalframeblocks' => 1,
+	'firstobject' => 1,
+	'firstobjectid' => 1,
+	'flags' => 1,
+	'flashexpcomp' => 1,
+	'flashinfo' => 1,
+	'flashon' => 1,
+	'flashpixstreamfieldoffset' => 1,
+	'flashpixstreampathname' => 1,
+	'flashpower' => 1,
+	'flashttlmode' => 1,
+	'flashversion' => 1,
+	'focallength35efl' => 1,
+	'focusinfo' => 1,
+	'focusinfo2' => 1,
+	'focuspos' => 1,
+	'footnotes' => 1,
+	'formatter' => 1,
+	'formatversion' => 1,
+	'formatversiontime' => 1,
+	'fotostation' => 1,
+	'fourcc1' => 1,
+	'fourcc2' => 1,
+	'fourcc2len' => 1,
+	'fourcc3' => 1,
+	'fourcc3len' => 1,
+	'fpxr' => 1,
+	'fractalparameters' => 1,
+	'fragmentlist' => 1,
+	'fragmenttable' => 1,
+	'frame' => 1,
+	'framecount' => 1,
+	'framepriority' => 1,
+	'framerate' => 1,
+	'framesize' => 1,
+	'framesizemax' => 1,
+	'framesizemin' => 1,
+	'frametype' => 1,
+	'free' => 1,
+	'freebytecounts' => 1,
+	'freeoffsets' => 1,
+	'fujifilm' => 1,
+	'fujilayout' => 1,
+	'gain' => 1,
+	'gamut' => 1,
+	'gapless' => 1,
+	'gdalmetadata' => 1,
+	'gdalnodata' => 1,
+	'geminfo' => 1,
+	'generator' => 1,
+	'genr' => 1,
+	'genreid' => 1,
+	'geo' => 1,
+	'geogangularunits' => 1,
+	'geogangularunitsize' => 1,
+	'geogazimuthunits' => 1,
+	'geogcitation' => 1,
+	'geogellipsoid' => 1,
+	'geoggeodeticdatum' => 1,
+	'geoginvflattening' => 1,
+	'geoglinearunits' => 1,
+	'geoglinearunitsize' => 1,
+	'geogprimemeridian' => 1,
+	'geogprimemeridianlong' => 1,
+	'geographictype' => 1,
+	'geogsemimajoraxis' => 1,
+	'geogsemiminoraxis' => 1,
+	'geotiffasciiparams' => 1,
+	'geotiffdirectory' => 1,
+	'geotiffdoubleparams' => 1,
+	'geotiffversion' => 1,
+	'gifapplicationextension' => 1,
+	'gifgraphiccontrolextension' => 1,
+	'gifplaintextextension' => 1,
+	'globalinfo' => 1,
+	'globalparametersifd' => 1,
+	'globalpixelsize' => 1,
+	'googlebot' => 1,
+	'gps' => 1,
+	'gpsinfo' => 1,
+	'gpsposition' => 1,
+	'graphicconverter' => 1,
+	'graphicstechnologystandardoutput' => 1,
+	'grayresponsecurve' => 1,
+	'graytrc' => 1,
+	'greenadjust' => 1,
+	'greengain' => 1,
+	'greenmatrixcolumn' => 1,
+	'greenprimary' => 1,
+	'greensample' => 1,
+	'greentrc' => 1,
+	'greenx' => 1,
+	'greeny' => 1,
+	'gridguidesinfo' => 1,
+	'groupcaption' => 1,
+	'grouping' => 1,
+	'groupmutualexclusion' => 1,
+	'gtcitation' => 1,
+	'gtmodeltype' => 1,
+	'gtrastertype' => 1,
+	'hasarbitrarydatastream' => 1,
+	'hasattachedimages' => 1,
+	'hasaudio' => 1,
+	'hascuepoints' => 1,
+	'hasfiletransferstream' => 1,
+	'hasicc' => 1,
+	'hasimage' => 1,
+	'haskeyframes' => 1,
+	'hasmetadata' => 1,
+	'hasscript' => 1,
+	'hasvideo' => 1,
+	'hcusage' => 1,
+	'hdrl' => 1,
+	'header' => 1,
+	'headerext' => 1,
+	'headerextension' => 1,
+	'headersize' => 1,
+	'headingpairs' => 1,
+	'heightresolution' => 1,
+	'hiddenslides' => 1,
+	'highlightendpoints' => 1,
+	'http-equiv' => 1,
+	'hyperfocaldistance' => 1,
+	'hyperlinkbase' => 1,
+	'hyperlinks' => 1,
+	'hyperlinkschanged' => 1,
+	'icc_untagged' => 1,
+	'iccbased' => 1,
+	'id' => 1,
+	'id3' => 1,
+	'id3v1' => 1,
+	'id3v2_2' => 1,
+	'id3v2_3' => 1,
+	'id3v2_4' => 1,
+	'identification' => 1,
+	'idsbasevalue' => 1,
+	'ifd0_offset' => 1,
+	'image' => 1,
+	'imagebounds' => 1,
+	'imagebytecount' => 1,
+	'imagecolorindicator' => 1,
+	'imagecolorvalue' => 1,
+	'imagedata' => 1,
+	'imagedatadiscard' => 1,
+	'imagedepth' => 1,
+	'imagefields' => 1,
+	'imageformat' => 1,
+	'imageheader' => 1,
+	'imageid' => 1,
+	'imageinfo' => 1,
+	'imagelayer' => 1,
+	'imagelength' => 1,
+	'imageoffset' => 1,
+	'imageprintstatus' => 1,
+	'imageprocessing2' => 1,
+	'imageprofile' => 1,
+	'imageprops' => 1,
+	'imagerboardid' => 1,
+	'imageresources' => 1,
+	'imagerotated' => 1,
+	'imagerotationstatus' => 1,
+	'imagesourceek' => 1,
+	'imagestatus' => 1,
+	'imagetoolbar' => 1,
+	'imgprofbacktype' => 1,
+	'imgprofname' => 1,
+	'imgproftype' => 1,
+	'imgprofversion' => 1,
+	'index' => 1,
+	'indexable' => 1,
+	'indexed' => 1,
+	'indexedcolourtablecount' => 1,
+	'indexoffset' => 1,
+	'indexparameters' => 1,
+	'info' => 1,
+	'infolist' => 1,
+	'information' => 1,
+	'initialkey' => 1,
+	'inknames' => 1,
+	'inputdataobjectlist' => 1,
+	'inputprofile' => 1,
+	'instructionset' => 1,
+	'integrationtime' => 1,
+	'intellectualproperty' => 1,
+	'intellectualpropertynotes' => 1,
+	'intensitystereo' => 1,
+	'intergraphflagregisters' => 1,
+	'intergraphmatrix' => 1,
+	'intergraphpacketdata' => 1,
+	'interlace' => 1,
+	'internationaltext' => 1,
+	'internetradiostationname' => 1,
+	'internetradiostationowner' => 1,
+	'internetradiostationurl' => 1,
+	'interopoffset' => 1,
+	'interpretedby' => 1,
+	'involvedpeople' => 1,
+	'iptc' => 1,
+	'iptc_profile' => 1,
+	'iptcapplication' => 1,
+	'iptccore' => 1,
+	'iptcdata' => 1,
+	'iptcenvelope' => 1,
+	'iptcnewsphoto' => 1,
+	'iptcobjectdata' => 1,
+	'iptcpostobjectdata' => 1,
+	'iptcpreobjectdata' => 1,
+	'is_protected' => 1,
+	'is_trusted' => 1,
+	'isospeed' => 1,
+	'isrc' => 1,
+	'isrcnumber' => 1,
+	'isvbr' => 1,
+	'it8header' => 1,
+	'iterationcount' => 1,
+	'iterationendaction' => 1,
+	'iterationmax' => 1,
+	'iterationminmax' => 1,
+	'iterations' => 1,
+	'itunesinfo' => 1,
+	'jfif' => 1,
+	'jfifversion' => 1,
+	'jfxx' => 1,
+	'jngheader' => 1,
+	'jp2header' => 1,
+	'jp2signature' => 1,
+	'jpeg_quality' => 1,
+	'jpegactables' => 1,
+	'jpegdctables' => 1,
+	'jpeglosslesspredictors' => 1,
+	'jpegpointtransforms' => 1,
+	'jpegproc' => 1,
+	'jpegqtables' => 1,
+	'jpegrestartinterval' => 1,
+	'jpegtables' => 1,
+	'jumptoxpep' => 1,
+	'kbytesize' => 1,
+	'keyframepositions' => 1,
+	'keyframestimes' => 1,
+	'kids' => 1,
+	'kodak' => 1,
+	'kodakbordersifd' => 1,
+	'kodakeffectsifd' => 1,
+	'konicaminolta' => 1,
+	'languagelist' => 1,
+	'lastkeyframetime' => 1,
+	'lastmodifier' => 1,
+	'lastobject' => 1,
+	'lastobjectid' => 1,
+	'lastprinted' => 1,
+	'lastsavedby' => 1,
+	'lasttimestamp' => 1,
+	'layersgroupinfo' => 1,
+	'layerstateinfo' => 1,
+	'leafautoactive' => 1,
+	'leafautobasename' => 1,
+	'leafdata' => 1,
+	'leafhotfolder' => 1,
+	'leafopenprochdr' => 1,
+	'leafoutputfiletype' => 1,
+	'leafsaveselection' => 1,
+	'leafsubifd' => 1,
+	'leftdarkrect' => 1,
+	'leftmag' => 1,
+	'length' => 1,
+	'lens35efl' => 1,
+	'lensdata0100' => 1,
+	'lensdata0101' => 1,
+	'lensdata0201' => 1,
+	'lensdataunknown' => 1,
+	'lensfocalrange' => 1,
+	'lensshading' => 1,
+	'lensspec' => 1,
+	'lightness' => 1,
+	'lights' => 1,
+	'lightvalue' => 1,
+	'linearizationtable' => 1,
+	'lines' => 1,
+	'linksuptodate' => 1,
+	'lit' => 1,
+	'localdeltatype' => 1,
+	'localeindicator' => 1,
+	'lockedpropertylist' => 1,
+	'locks' => 1,
+	'lookheadbacktype' => 1,
+	'lookheader' => 1,
+	'lookheadname' => 1,
+	'lookheadtype' => 1,
+	'lookheadversion' => 1,
+	'lookuptable' => 1,
+	'loopstyle' => 1,
+	'lotus' => 1,
+	'lr' => 1,
+	'luminance' => 1,
+	'luminanceconsts' => 1,
+	'lyricist' => 1,
+	'lyrics' => 1,
+	'lyrics_synchronised' => 1,
+	'macintoshprintinfo' => 1,
+	'magnification' => 1,
+	'magnifyobject' => 1,
+	'makeandmodel' => 1,
+	'makernotes' => 1,
+	'mandatorybackground' => 1,
+	'marker' => 1,
+	'markerid' => 1,
+	'mattcolor' => 1,
+	'matte' => 1,
+	'matteing' => 1,
+	'maxband' => 1,
+	'maxbitrate' => 1,
+	'maxdatarate' => 1,
+	'maximumbitrate' => 1,
+	'maximumimageindex' => 1,
+	'maximumobjectsize' => 1,
+	'maximumoperationindex' => 1,
+	'maximumtransformindex' => 1,
+	'maxjpegtableindex' => 1,
+	'maxpacketsize' => 1,
+	'maxpagenormal' => 1,
+	'maxsubfilesize' => 1,
+	'maxval' => 1,
+	'mcdi' => 1,
+	'mdat' => 1,
+	'mdcolortable' => 1,
+	'mdfiletag' => 1,
+	'mdfileunits' => 1,
+	'mdlabname' => 1,
+	'mdpr' => 1,
+	'mdprepdate' => 1,
+	'mdpreptime' => 1,
+	'mdsampleinfo' => 1,
+	'mdscalepixel' => 1,
+	'measuredinfo' => 1,
+	'measurement' => 1,
+	'measurementbacking' => 1,
+	'measurementflare' => 1,
+	'measurementgeometry' => 1,
+	'measurementilluminant' => 1,
+	'measurementobserver' => 1,
+	'media' => 1,
+	'mediablackpoint' => 1,
+	'mediaclassprimaryid' => 1,
+	'mediaclasssecondaryid' => 1,
+	'mediacredits' => 1,
+	'mediadata' => 1,
+	'mediaindex' => 1,
+	'mediaisdelay' => 1,
+	'mediaisfinale' => 1,
+	'mediaislive' => 1,
+	'mediaispremiere' => 1,
+	'mediaisrepeat' => 1,
+	'mediaissap' => 1,
+	'mediaisstereo' => 1,
+	'mediaissubtitled' => 1,
+	'mediaistape' => 1,
+	'medianetworkaffiliation' => 1,
+	'mediaoriginalbroadcastdatetime' => 1,
+	'mediaoriginalchannel' => 1,
+	'mediapro' => 1,
+	'mediastationcallsign' => 1,
+	'mediastationname' => 1,
+	'mediawhitepoint' => 1,
+	'medium' => 1,
+	'meta' => 1,
+	'metadata' => 1,
+	'metadatacreator' => 1,
+	'metadatalibrary' => 1,
+	'metadatanumber' => 1,
+	'microsoft' => 1,
+	'mie' => 1,
+	'mimetype' => 1,
+	'minf' => 1,
+	'minimumbitrate' => 1,
+	'minoltacamerasettings' => 1,
+	'minoltacamerasettings2' => 1,
+	'minoltacamerasettings5d' => 1,
+	'minoltacamerasettings7d' => 1,
+	'minoltacamerasettingsa100' => 1,
+	'minoltacamerasettingsold' => 1,
+	'minoltamakernote' => 1,
+	'minoltaprd' => 1,
+	'minoltarif' => 1,
+	'minoltattw' => 1,
+	'minoltawbg' => 1,
+	'minpacketsize' => 1,
+	'mmclips' => 1,
+	'mngheader' => 1,
+	'modeextension' => 1,
+	'model2' => 1,
+	'modelandversion' => 1,
+	'modeltiepoint' => 1,
+	'modeltransform' => 1,
+	'modenumber' => 1,
+	'modifiedby' => 1,
+	'modifiedinfo' => 1,
+	'montage' => 1,
+	'mood' => 1,
+	'mosaicpattern' => 1,
+	'moveobjects' => 1,
+	'movie' => 1,
+	'movieheader' => 1,
+	'mpeg7binary' => 1,
+	'mpegaudioversion' => 1,
+	'msstereo' => 1,
+	'multiexposure' => 1,
+	'multimediatype' => 1,
+	'multiquality' => 1,
+	'musiccdidentifier' => 1,
+	'musiciancredits' => 1,
+	'mycolors' => 1,
+	'name' => 1,
+	'namedcolor' => 1,
+	'namedcolor2' => 1,
+	'namelength' => 1,
+	'narrator' => 1,
+	'nativedisplayinfo' => 1,
+	'nativeresolutionunit' => 1,
+	'nativexresolution' => 1,
+	'nativeyresolution' => 1,
+	'ncc' => 1,
+	'nefcurve1' => 1,
+	'nefcurve2' => 1,
+	'nestlevel' => 1,
+	'netexposurecompensation' => 1,
+	'neutals' => 1,
+	'neutobjbacktype' => 1,
+	'neutobjname' => 1,
+	'neutobjtype' => 1,
+	'neutobjversion' => 1,
+	'neutrals' => 1,
+	'newbitdepth' => 1,
+	'newcolortype' => 1,
+	'nexttrackid' => 1,
+	'nikon' => 1,
+	'nikoncapturedata' => 1,
+	'nikoncaptureoffsets' => 1,
+	'nikonpreview' => 1,
+	'nikonscanifd' => 1,
+	'nikontags' => 1,
+	'noise' => 1,
+	'noisereductiondata' => 1,
+	'nominalbitrate' => 1,
+	'nominalframecount' => 1,
+	'nominallayercount' => 1,
+	'nominalplaytime' => 1,
+	'nomssmarttags' => 1,
+	'notes' => 1,
+	'npts' => 1,
+	'nsc_address' => 1,
+	'nsc_description' => 1,
+	'nsc_email' => 1,
+	'nsc_name' => 1,
+	'nsc_phone' => 1,
+	'numafpoints' => 1,
+	'numberlist' => 1,
+	'numberofcomponents' => 1,
+	'numberofframes' => 1,
+	'numberofinks' => 1,
+	'numberofplanes' => 1,
+	'numberofresolutions' => 1,
+	'numchannels' => 1,
+	'numcolors' => 1,
+	'numimportantcolors' => 1,
+	'numpackets' => 1,
+	'numproperties' => 1,
+	'numrules' => 1,
+	'numsampleframes' => 1,
+	'numstreams' => 1,
+	'objectid' => 1,
+	'objectsizeannounced' => 1,
+	'objecttype' => 1,
+	'obsoletephotoshoptag1' => 1,
+	'obsoletephotoshoptag2' => 1,
+	'obsoletephotoshoptag3' => 1,
+	'oceapplicationselector' => 1,
+	'oceidnumber' => 1,
+	'oceimagelogic' => 1,
+	'ocescanjobdesc' => 1,
+	'offsetorigin' => 1,
+	'offsetxy' => 1,
+	'olympus' => 1,
+	'olympus2100' => 1,
+	'olympus2200' => 1,
+	'olympus2300' => 1,
+	'olympus2400' => 1,
+	'olympus2500' => 1,
+	'olympus2600' => 1,
+	'olympus2700' => 1,
+	'olympus2800' => 1,
+	'olympus2900' => 1,
+	'opacity' => 1,
+	'operation' => 1,
+	'operationclassid' => 1,
+	'operationid' => 1,
+	'operationnumber' => 1,
+	'opiproxy' => 1,
+	'optimalbitrate' => 1,
+	'opto-electricconvfactor' => 1,
+	'orderingrestrictions' => 1,
+	'organization' => 1,
+	'originalalbum' => 1,
+	'originalalbumtitle' => 1,
+	'originalartist' => 1,
+	'originaldocumentsize' => 1,
+	'originalimagebroker' => 1,
+	'originallyricist' => 1,
+	'originalmedia' => 1,
+	'originalmedium' => 1,
+	'originalrawcreator' => 1,
+	'originalrawfiletype' => 1,
+	'originalrawimage' => 1,
+	'originalrawresource' => 1,
+	'originalreleasetime' => 1,
+	'originalreleaseyear' => 1,
+	'originalscannedimagesize' => 1,
+	'originalthmcreator' => 1,
+	'originalthmfiletype' => 1,
+	'originalthmimage' => 1,
+	'originalthmresource' => 1,
+	'othercodecdescription' => 1,
+	'othercodecname' => 1,
+	'otherimage' => 1,
+	'otherimagelength' => 1,
+	'otherimagestart' => 1,
+	'outputdataobjectlist' => 1,
+	'outputprofile' => 1,
+	'outputresponse' => 1,
+	'packets' => 1,
+	'padding' => 1,
+	'page' => 1,
+	'pagecount' => 1,
+	'pageenter' => 1,
+	'pageexit' => 1,
+	'pagefront' => 1,
+	'pagenormal' => 1,
+	'pages' => 1,
+	'pagespecial' => 1,
+	'palette' => 1,
+	'palettehistogram' => 1,
+	'panasonic' => 1,
+	'paragraphs' => 1,
+	'parameter' => 1,
+	'parentalrating' => 1,
+	'parentalratingreason' => 1,
+	'partialpalette' => 1,
+	'partofset' => 1,
+	'pastclippingboundary' => 1,
+	'pasteimage' => 1,
+	'patternangle' => 1,
+	'paymenturl' => 1,
+	'pcscitation' => 1,
+	'pdahistogram' => 1,
+	'pdf' => 1,
+	'peakbitrate' => 1,
+	'peakvalue' => 1,
+	'pentax' => 1,
+	'pentaxtags' => 1,
+	'performer' => 1,
+	'performers' => 1,
+	'performersortorder' => 1,
+	'period' => 1,
+	'perpicturenotes' => 1,
+	'personalfunctions' => 1,
+	'personalfunctionvalues' => 1,
+	'photoeffectsdata' => 1,
+	'photomech' => 1,
+	'photomechanic' => 1,
+	'photoshop' => 1,
+	'photoshop2colortable' => 1,
+	'photoshop2info' => 1,
+	'photoshopbgrthumbnail' => 1,
+	'photoshopdata' => 1,
+	'photoshopformat' => 1,
+	'photoshopsettings' => 1,
+	'photoshopthumbnail' => 1,
+	'physicalpixel' => 1,
+	'physicalstreamnumbermap' => 1,
+	'physicalstreamnumbers' => 1,
+	'physicalstreams' => 1,
+	'picslabel' => 1,
+	'picture' => 1,
+	'pictureinfo' => 1,
+	'pieceinfo' => 1,
+	'pixelaspectratio' => 1,
+	'pixelcalibration' => 1,
+	'pixelformat' => 1,
+	'pixelintensityrange' => 1,
+	'pixellive' => 1,
+	'pixelscale' => 1,
+	'pixelspermeterx' => 1,
+	'pixelspermetery' => 1,
+	'pixelsperunitx' => 1,
+	'pixelsperunity' => 1,
+	'pixelunits' => 1,
+	'places' => 1,
+	'planes' => 1,
+	'playallframes' => 1,
+	'playcounter' => 1,
+	'playduration' => 1,
+	'playgap' => 1,
+	'playlistdelay' => 1,
+	'playselection' => 1,
+	'postertime' => 1,
+	'postscript2crd0' => 1,
+	'postscript2crd1' => 1,
+	'postscript2crd2' => 1,
+	'postscript2csa' => 1,
+	'pragma' => 1,
+	'preferredrate' => 1,
+	'preferredvolume' => 1,
+	'preroll' => 1,
+	'presentationtarget' => 1,
+	'preview' => 1,
+	'preview0' => 1,
+	'preview1' => 1,
+	'preview2' => 1,
+	'previewatomindex' => 1,
+	'previewatomtype' => 1,
+	'previewdate' => 1,
+	'previewdescription' => 1,
+	'previewduration' => 1,
+	'previewifd_offset' => 1,
+	'previewimagedata' => 1,
+	'previewimageinfo' => 1,
+	'previewinfo' => 1,
+	'previewmimetype' => 1,
+	'previewpict' => 1,
+	'previewtime' => 1,
+	'previewtype' => 1,
+	'previewversion' => 1,
+	'primaryafpoint' => 1,
+	'primaryplatform' => 1,
+	'printflags' => 1,
+	'printflagsinfo' => 1,
+	'printquality' => 1,
+	'printtovideo' => 1,
+	'priority' => 1,
+	'private' => 1,
+	'proccessinginfo' => 1,
+	'prod' => 1,
+	'prodnotes' => 1,
+	'produceddate' => 1,
+	'producednotice' => 1,
+	'product' => 1,
+	'profile' => 1,
+	'profileclass' => 1,
+	'profilecmmtype' => 1,
+	'profileconnectionspace' => 1,
+	'profilecopyright' => 1,
+	'profilecreator' => 1,
+	'profiledatetime' => 1,
+	'profiledescription' => 1,
+	'profiledescriptionml' => 1,
+	'profilefilesignature' => 1,
+	'profileheader' => 1,
+	'profileid' => 1,
+	'profilesequencedesc' => 1,
+	'profiletype' => 1,
+	'profileversion' => 1,
+	'progid' => 1,
+	'progressivescans' => 1,
+	'projazimuthangle' => 1,
+	'projcentereasting' => 1,
+	'projcenterlat' => 1,
+	'projcenterlong' => 1,
+	'projcenternorthing' => 1,
+	'projcoordtrans' => 1,
+	'projectedcstype' => 1,
+	'projection' => 1,
+	'projfalseeasting' => 1,
+	'projfalsenorthing' => 1,
+	'projfalseorigineasting' => 1,
+	'projfalseoriginlat' => 1,
+	'projfalseoriginlong' => 1,
+	'projfalseoriginnorthing' => 1,
+	'projlinearunits' => 1,
+	'projlinearunitsize' => 1,
+	'projnatoriginlat' => 1,
+	'projnatoriginlong' => 1,
+	'projrectifiedgridangle' => 1,
+	'projscaleatcenter' => 1,
+	'projscaleatnatorigin' => 1,
+	'projstdparallel1' => 1,
+	'projstdparallel2' => 1,
+	'projstraightvertpolelong' => 1,
+	'promoteparent' => 1,
+	'promotionurl' => 1,
+	'prop' => 1,
+	'properties' => 1,
+	'propertysetidcodes' => 1,
+	'propertysetpathname' => 1,
+	'propertyvectorelements' => 1,
+	'protect' => 1,
+	'protectiontype' => 1,
+	'provider' => 1,
+	'providercopyright' => 1,
+	'providerrating' => 1,
+	'providerstyle' => 1,
+	'ps2crdvmsize' => 1,
+	'ps2renteringintent' => 1,
+	'pstringcaption' => 1,
+	'publisherurl' => 1,
+	'quickmaskinfo' => 1,
+	'ra3' => 1,
+	'ra4' => 1,
+	'ra5' => 1,
+	'radiostationname' => 1,
+	'radiostationowner' => 1,
+	'rasterpadding' => 1,
+	'rawdata' => 1,
+	'rawdatarotation' => 1,
+	'rawdev2' => 1,
+	'rawdev2_2' => 1,
+	'rawdevelopment' => 1,
+	'rawdevelopment2' => 1,
+	'rawimagefullsize' => 1,
+	'rawimagemode' => 1,
+	'rawinfo' => 1,
+	'rawinfo2' => 1,
+	'rawjpginfo' => 1,
+	'readerrequirements' => 1,
+	'recengineer' => 1,
+	'reclocation' => 1,
+	'reconstructiontype' => 1,
+	'recordingdates' => 1,
+	'recordingtime' => 1,
+	'rect' => 1,
+	'rectangleofinterest' => 1,
+	'redadjust' => 1,
+	'redeyedata' => 1,
+	'redeyereduction' => 1,
+	'redgain' => 1,
+	'redmatrixcolumn' => 1,
+	'redprimary' => 1,
+	'redsample' => 1,
+	'redtrc' => 1,
+	'reductionmatrix1' => 1,
+	'reductionmatrix2' => 1,
+	'redx' => 1,
+	'redy' => 1,
+	'refresh' => 1,
+	'relatedimagefile' => 1,
+	'renderingintent' => 1,
+	'replaygainalbumgain' => 1,
+	'replaygainalbumpeak' => 1,
+	'replaygaintrackgain' => 1,
+	'replaygaintrackpeak' => 1,
+	'replyto' => 1,
+	'requirements' => 1,
+	'reserved1' => 1,
+	'resolutioninfo' => 1,
+	'resources' => 1,
+	'resourcesneeded' => 1,
+	'resourcetype' => 1,
+	'resultaspectratio' => 1,
+	'revisiondate' => 1,
+	'revisionnumber' => 1,
+	'revisitafter' => 1,
+	'rgb_profile' => 1,
+	'ricoh' => 1,
+	'ricohdatetime1' => 1,
+	'ricohdatetime2' => 1,
+	'ricohrr1subdir' => 1,
+	'ricohsubdir' => 1,
+	'rightdarkrect' => 1,
+	'rightmag' => 1,
+	'rjmd' => 1,
+	'rmeta' => 1,
+	'robots' => 1,
+	'rocinfo' => 1,
+	'roidescription' => 1,
+	'rollguidelements' => 1,
+	'root' => 1,
+	'rotationangle' => 1,
+	'sampleformat' => 1,
+	'samplerate2' => 1,
+	'samplesize' => 1,
+	'sanyomov' => 1,
+	'sanyomp4' => 1,
+	'saveobjbacktype' => 1,
+	'saveobjects' => 1,
+	'saveobjname' => 1,
+	'saveobjtype' => 1,
+	'saveobjversion' => 1,
+	'savesetup' => 1,
+	'sba_rgbshifts' => 1,
+	'sbaexposurerecord' => 1,
+	'sbainputimagebitdepth' => 1,
+	'sbainputimagecolorspace' => 1,
+	'scale' => 1,
+	'scalecrop' => 1,
+	'scalefactor35efl' => 1,
+	'scandate' => 1,
+	'scannermake' => 1,
+	'scannermodel' => 1,
+	'scannerpixelsize' => 1,
+	'scannerserialnumber' => 1,
+	'scanoperatorid' => 1,
+	'scansoftware' => 1,
+	'scansoftwarerevisiondate' => 1,
+	'screening' => 1,
+	'screeningdesc' => 1,
+	'screennail' => 1,
+	'scriptcommand' => 1,
+	'security' => 1,
+	'seekable' => 1,
+	'seekpoint' => 1,
+	'selection' => 1,
+	'selectionduration' => 1,
+	'selectiontime' => 1,
+	'selobjbacktype' => 1,
+	'selobjname' => 1,
+	'selobjtype' => 1,
+	'selobjversion' => 1,
+	'sendduration' => 1,
+	'sensorbottomborder' => 1,
+	'sensorid' => 1,
+	'sensorinfo' => 1,
+	'sensorleftborder' => 1,
+	'sensorrightborder' => 1,
+	'sensortopborder' => 1,
+	'serviceorganizationname' => 1,
+	'setcookie' => 1,
+	'setinfo' => 1,
+	'setsubtitle' => 1,
+	'shadowendpoints' => 1,
+	'shakereductioninfo' => 1,
+	'shareddoc' => 1,
+	'shareduserrating' => 1,
+	'sharpinfo' => 1,
+	'sharpmethod' => 1,
+	'sharpnessapproximation' => 1,
+	'sharpnessfreqtable' => 1,
+	'sharpnesstable' => 1,
+	'sharpobjbacktype' => 1,
+	'sharpobjname' => 1,
+	'sharpobjtype' => 1,
+	'sharpobjversion' => 1,
+	'shootingrecord' => 1,
+	'shootobjbacktype' => 1,
+	'shootobjname' => 1,
+	'shootobjtype' => 1,
+	'shootobjversion' => 1,
+	'shootsetup' => 1,
+	'shotinfo' => 1,
+	'shotinfoversion' => 1,
+	'showmode' => 1,
+	'showobjects' => 1,
+	'shuttercurtainhack' => 1,
+	'shutterspeed' => 1,
+	'shutterspeeddisplayed' => 1,
+	'sidebars' => 1,
+	'sigma' => 1,
+	'signalnumber' => 1,
+	'signature' => 1,
+	'signature_name' => 1,
+	'significantbits' => 1,
+	'signtype' => 1,
+	'simpleindex' => 1,
+	'simplicityprofile' => 1,
+	'singlequality' => 1,
+	'site' => 1,
+	'siteenter' => 1,
+	'siteexit' => 1,
+	'size' => 1,
+	'sizemode' => 1,
+	'skip' => 1,
+	'slices' => 1,
+	'slides' => 1,
+	'slideshow' => 1,
+	'smaxsamplevalue' => 1,
+	'sminsamplevalue' => 1,
+	'snapshotid' => 1,
+	'snapshotname' => 1,
+	'softedit' => 1,
+	'softwarerelease' => 1,
+	'sony' => 1,
+	'sourcedate' => 1,
+	'sourceedition' => 1,
+	'sourceform' => 1,
+	'sourceid' => 1,
+	'sourceimagedirectory' => 1,
+	'sourceimagefilename' => 1,
+	'sourceimagevolumename' => 1,
+	'sourcepublisher' => 1,
+	'sourcerights' => 1,
+	'sourcetitle' => 1,
+	'sourceurl' => 1,
+	'spatialfrequencyresponse' => 1,
+	'spatialorientation' => 1,
+	'specialeffectsopticalfilter' => 1,
+	'spiff' => 1,
+	'spiffversion' => 1,
+	'spothalftone' => 1,
+	'sr2dataifd' => 1,
+	'sr2private' => 1,
+	'sr2subifdkey' => 1,
+	'sr2subifdlength' => 1,
+	'sr2subifdoffset' => 1,
+	'srf2_key' => 1,
+	'srgbrendering' => 1,
+	'stamptoolcount' => 1,
+	'startofframe' => 1,
+	'stbl' => 1,
+	'stdautoactive' => 1,
+	'stdbasename' => 1,
+	'stdhotfolder' => 1,
+	'stdopeninphotoshop' => 1,
+	'stdoutputbitdepth' => 1,
+	'stdoutputcolormode' => 1,
+	'stdoutputfiletype' => 1,
+	'stdoxygen' => 1,
+	'stdsaveselection' => 1,
+	'stdscaledoutput' => 1,
+	'stdsharpenoutput' => 1,
+	'stereo' => 1,
+	'stonits' => 1,
+	'storage-streampathname' => 1,
+	'stream' => 1,
+	'streamavgbitrate' => 1,
+	'streamavgpacketsize' => 1,
+	'streambitrateprops' => 1,
+	'streamcount' => 1,
+	'streamdata' => 1,
+	'streamduration' => 1,
+	'streamheader' => 1,
+	'streaminfo' => 1,
+	'streammaxbitrate' => 1,
+	'streammaxpacketsize' => 1,
+	'streammimelen' => 1,
+	'streammimetype' => 1,
+	'streamname' => 1,
+	'streamnamelen' => 1,
+	'streamnumber' => 1,
+	'streampreroll' => 1,
+	'streamprioritization' => 1,
+	'streamproperties' => 1,
+	'streamstarttime' => 1,
+	'streamtype' => 1,
+	'streamtypeinfo' => 1,
+	'stridable' => 1,
+	'stripbytecounts' => 1,
+	'stripoffsets' => 1,
+	'striprowcounts' => 1,
+	'strobe' => 1,
+	'strobetime' => 1,
+	'subfile' => 1,
+	'subifd' => 1,
+	'subifd0' => 1,
+	'subifd1' => 1,
+	'subifd2' => 1,
+	'subifd3' => 1,
+	'subifd4' => 1,
+	'subifd5' => 1,
+	'subifd_offset' => 1,
+	'subimagecolor' => 1,
+	'subimagehdr' => 1,
+	'subimageheight' => 1,
+	'subimageicc_profile' => 1,
+	'subimagenumericalformat' => 1,
+	'subimageresolutions' => 1,
+	'subimagetilecount' => 1,
+	'subimagetileheight' => 1,
+	'subimagetilewidth' => 1,
+	'subimagewidth' => 1,
+	'subpacketh' => 1,
+	'subpacketsize' => 1,
+	'subscriptioncontentid' => 1,
+	'subsecdatetimeoriginal' => 1,
+	'subtitle' => 1,
+	'subtitledescription' => 1,
+	'suggestedpalette' => 1,
+	'summaryinfo' => 1,
+	't4options' => 1,
+	't6options' => 1,
+	'taggingtime' => 1,
+	'targetaudiences' => 1,
+	'targetdeltatype' => 1,
+	'targetprinter' => 1,
+	'targetxy' => 1,
+	'technician' => 1,
+	'technology' => 1,
+	'template' => 1,
+	'terminationaction' => 1,
+	'terminationcondition' => 1,
+	'termsofuse' => 1,
+	'testtarget' => 1,
+	'text' => 1,
+	'textinfo' => 1,
+	'textualdata' => 1,
+	'things' => 1,
+	'thumbnail' => 1,
+	'thumbnailclip' => 1,
+	'tickspersecond' => 1,
+	'tiff' => 1,
+	'tiff-epstandardid' => 1,
+	'tiffpreview' => 1,
+	'tilebytecounts' => 1,
+	'tiledepth' => 1,
+	'tileoffsets' => 1,
+	'timecode' => 1,
+	'timecodeindex' => 1,
+	'timecodeindexparms' => 1,
+	'timeoffset' => 1,
+	'timescale' => 1,
+	'titlelen' => 1,
+	'titleofparts' => 1,
+	'titlesortorder' => 1,
+	'tocitems' => 1,
+	'tonecurvematching' => 1,
+	'tonecurvetable' => 1,
+	'tonematrix' => 1,
+	'toneobjbacktype' => 1,
+	'toneobjname' => 1,
+	'toneobjtype' => 1,
+	'toneobjversion' => 1,
+	'tones' => 1,
+	'tonespaceflow' => 1,
+	'toolname' => 1,
+	'toolversion' => 1,
+	'topmag' => 1,
+	'totaledittime' => 1,
+	'totalframes' => 1,
+	'totalsamples' => 1,
+	'track' => 1,
+	'trackcategory' => 1,
+	'trackcomments' => 1,
+	'trackcreatedate' => 1,
+	'trackduration' => 1,
+	'trackheader' => 1,
+	'trackid' => 1,
+	'tracklayer' => 1,
+	'tracklyrics' => 1,
+	'trackmodifydate' => 1,
+	'trackversion' => 1,
+	'trackvolume' => 1,
+	'transferrange' => 1,
+	'transfomation' => 1,
+	'transform' => 1,
+	'transformcreatedate' => 1,
+	'transformedimagetitle' => 1,
+	'transformmodifydate' => 1,
+	'transformnodeid' => 1,
+	'transformtitle' => 1,
+	'transparency' => 1,
+	'transparencyindicator' => 1,
+	'transparentindex' => 1,
+	'trapped' => 1,
+	'typeoforiginal' => 1,
+	'ucrbg' => 1,
+	'uncompressed' => 1,
+	'unicodealphanames' => 1,
+	'uniquefileidentifier' => 1,
+	'units' => 1,
+	'unknown' => 1,
+	'unknownblock1' => 1,
+	'unknownblock2' => 1,
+	'unknowntags' => 1,
+	'unsharpdata' => 1,
+	'unused' => 1,
+	'url_list' => 1,
+	'usedextensionnumbers' => 1,
+	'useradjsba_rgbshifts' => 1,
+	'userdata' => 1,
+	'userdefinedid' => 1,
+	'userdefinedtext' => 1,
+	'userdefinedurl' => 1,
+	'userselectgrouptitle' => 1,
+	'userweburl' => 1,
+	'usingtransforms' => 1,
+	'utm' => 1,
+	'uuid-exif' => 1,
+	'uuid-geojp2' => 1,
+	'uuid-iptc' => 1,
+	'uuid-unknown' => 1,
+	'uuid-xmp' => 1,
+	'uuidinfo' => 1,
+	'uuidlist' => 1,
+	'validafpoints' => 1,
+	'vary' => 1,
+	'vbrpeak' => 1,
+	'vendor' => 1,
+	'vendorid' => 1,
+	'version2' => 1,
+	'versionbf' => 1,
+	'versioninfo' => 1,
+	'versionyear' => 1,
+	'verticalcitation' => 1,
+	'verticalcstype' => 1,
+	'verticaldatum' => 1,
+	'verticalunits' => 1,
+	'vibrationreduction' => 1,
+	'video' => 1,
+	'videobitrate' => 1,
+	'videocardgamma' => 1,
+	'videoclosedcaptioning' => 1,
+	'videocodecdescription' => 1,
+	'videocodecid' => 1,
+	'videocodecname' => 1,
+	'videoencoding' => 1,
+	'videoformat' => 1,
+	'videoheight' => 1,
+	'videomode' => 1,
+	'videoquality' => 1,
+	'videosize' => 1,
+	'videowidth' => 1,
+	'viewable' => 1,
+	'viewingconddesc' => 1,
+	'viewingcondilluminant' => 1,
+	'viewingcondilluminanttype' => 1,
+	'viewingconditions' => 1,
+	'viewingcondsurround' => 1,
+	'visibleoutputs' => 1,
+	'vorbiscomment' => 1,
+	'vorbisversion' => 1,
+	'vr_0x66' => 1,
+	'vrd1' => 1,
+	'vrd2' => 1,
+	'vrdstamptool' => 1,
+	'vrdversion' => 1,
+	'vw96' => 1,
+	'wangannotation' => 1,
+	'warning' => 1,
+	'watermark' => 1,
+	'watermarktype' => 1,
+	'wb_grbglevels' => 1,
+	'wb_grgblevels' => 1,
+	'wbadjdata' => 1,
+	'wcsprofiles' => 1,
+	'whitebalancematching' => 1,
+	'whitebalancetable' => 1,
+	'whitelevel' => 1,
+	'whitepointx' => 1,
+	'whitepointy' => 1,
+	'wide' => 1,
+	'widthresolution' => 1,
+	'windowlocation' => 1,
+	'windowtarget' => 1,
+	'wmadrcaveragereference' => 1,
+	'wmadrcaveragetarget' => 1,
+	'wmadrcpeakreference' => 1,
+	'wmadrcpeaktarget' => 1,
+	'wmcollectiongroupid' => 1,
+	'wmcollectionid' => 1,
+	'wmcontentid' => 1,
+	'wordcount' => 1,
+	'workflowurl' => 1,
+	'workingpath' => 1,
+	'writer' => 1,
+	'xclippathunits' => 1,
+	'xmag' => 1,
+	'xmethod' => 1,
+	'xml' => 1,
+	'xmldata' => 1,
+	'xmp_profile' => 1,
+	'xmpbj' => 1,
+	'xmpdm' => 1,
+	'xmpmm' => 1,
+	'xmpplus' => 1,
+	'xmprights' => 1,
+	'xmptpg' => 1,
+	'xylocation' => 1,
+	'xyoffsetinfo' => 1,
+	'yclippathunits' => 1,
+	'year' => 1,
+	'ylevel' => 1,
+	'ymag' => 1,
+	'ymethod' => 1,
+	'ytarget' => 1,
+	'zoom' => 1,
+	'zoompos' => 1,
+);
+
+# module names for writable Composite tags
+my %compositeModules = (
+	'jpgfromraw' => 'Image::ExifTool::Exif',
+	'previewimage' => 'Image::ExifTool::Exif',
+	'thumbnailimage' => 'Image::ExifTool::Exif',
+);
+
+#++++ End automatically generated code ++++
+
+# insert any user-defined tags into our tag lookup
+if (defined %Image::ExifTool::UserDefined) {
+    my ($table, %tableNum, $tagID);
+    my $tnum = 0; # make hash lookup to get table numbers
+    foreach $table (@tableList) {
+        $tableNum{$table} = $tnum++;
+    }
+    my @userTables = sort keys %Image::ExifTool::UserDefined;
+    while (@userTables) {
+        my $table = shift @userTables;
+        $tnum = $tableNum{$table};
+        unless ($tnum) {
+            $tnum = scalar @tableList;
+            push @tableList, $table;
+        }
+        my $userTable = $Image::ExifTool::UserDefined{$table};
+        foreach $tagID (Image::ExifTool::TagTableKeys($userTable)) {
+            my $newInfo = $$userTable{$tagID};
+            if (ref $newInfo eq 'HASH') {
+                $$newInfo{Name} or $$newInfo{Name} = ucfirst($tagID);
+            } else {
+                $newInfo = $$userTable{$tagID} = { Name => $newInfo };
+            }
+            my $lcTag = lc($$newInfo{Name});
+            # add to lookup even though we don't know it is writable
+            # (to save speed by not having to load the module)
+            my $lookup = $tagLookup{$lcTag};
+            $lookup or $lookup = $tagLookup{$lcTag} = { };
+            if ($$lookup{$tnum}) {
+                my $le = $$lookup{$tnum};
+                my @ids = (ref $le eq 'ARRAY') ? @{$le} : ( $le );
+                # only add new tag ID if it didn't already exist
+                next if grep /^$tagID$/, @ids;
+                push @ids, $tagID;
+                $$lookup{$tnum} = \@ids;
+            } else {
+                $$lookup{$tnum} = $tagID;
+            }
+            # add any new subdirectory tables
+            next unless $$newInfo{SubDirectory};
+            my $subTable = $newInfo->{SubDirectory}->{TagTable};
+            next unless $subTable and not defined $tableNum{$subTable};
+            next if $Image::ExifTool::UserDefined{$subTable};
+            no strict 'refs';
+            next unless defined %$subTable;
+            $Image::ExifTool::UserDefined{$subTable} = \%$subTable;
+            push @userTables, $subTable;
+            # save parent table name so we can load it when necessary
+            $$subTable{PARENT} = $table;
+        }
+    }
+}
+
+#------------------------------------------------------------------------------
+# Return true if the tag exists
+# Inputs: 0) tag name (case insensitive)
+# Returns: true if tag exists
+sub TagExists($)
+{
+    my $tag = lc($_[0]);
+    return 1 if $tagExists{$tag} or $tagLookup{$tag};
+    return 0;
+}
+
+#------------------------------------------------------------------------------
+# Find information for writable tags (case insensitive)
+# Inputs: 0) tag name (case insensitive)
+# Returns: Scalar context: tag info or false if none found
+#          List context: list of all matching tagInfo hashes
+sub FindTagInfo($)
+{
+    local $_;
+    my $tag = shift;
+    my $lcTag = lc($tag);
+    my ($tableNum, @tagInfoList);
+    my $lookup = $tagLookup{$lcTag};
+    if ($lookup) {
+        foreach $tableNum (keys %$lookup) {
+            my $table = GetTagTable($tableList[$tableNum]);
+            my $tagID = $$lookup{$tableNum};
+            my @tagIDs;
+            if (ref $tagID eq 'ARRAY') {
+                @tagIDs = @$tagID;
+            } else {
+                push @tagIDs, $tagID;
+            }
+            foreach $tagID (@tagIDs) {
+                my @infoList = GetTagInfoList($table,$tagID);
+                unless (@infoList) {
+                    my $reload;
+                    # call write proc if it exists in case it adds tags to the table
+                    my $writeProc = $table->{WRITE_PROC};
+                    $reload = 1 if $writeProc and &$writeProc();
+                    # load module with composite tag if necessary
+                    my $compMod = $compositeModules{$lcTag};
+                    $reload = 1 if $compMod and eval "require $compMod";
+                    @infoList = GetTagInfoList($table,$tagID) if $reload;
+                }
+                if (@infoList == 1) {
+                    push @tagInfoList, @infoList;
+                } elsif (@infoList > 1) {
+                    my $tagInfo;
+                    # must check tag list in case tags have different names
+                    foreach $tagInfo (@infoList) {
+                        next unless $lcTag eq lc($$tagInfo{Name});
+                        push @tagInfoList, $tagInfo;
+                    }
+                }
+            }
+        }
+    }
+    if (wantarray) {
+        return @tagInfoList;
+    } else {
+        return $tagInfoList[0];
+    }
+}
+
+
+1;  # end
+
+__END__
+
+=head1 NAME
+
+Image::ExifTool::TagLookup - Fast lookup for ExifTool tags
+
+=head1 SYNOPSIS
+
+This module is required by Image::ExifTool for writing tags.
+
+=head1 DESCRIPTION
+
+Provides a fast, case insensitive lookup for tag names.
+
+=head1 AUTHOR
+
+Copyright 2003-2007, Phil Harvey (phil at owl.phy.queensu.ca)
+
+This library is free software; you can redistribute it and/or modify it
+under the same terms as Perl itself.
+
+=head1 SEE ALSO
+
+L<Image::ExifTool(3pm)|Image::ExifTool>,
+L<Image::ExifTool::BuildTagLookup(3pm)|Image::ExifTool::BuildTagLookup>,
+L<Image::ExifTool::TagNames(3pm)|Image::ExifTool::TagNames>
+
+=cut
Index: /gsdl/trunk/perllib/cpan/Image/ExifTool/TagNames.pod
===================================================================
--- /gsdl/trunk/perllib/cpan/Image/ExifTool/TagNames.pod	(revision 16842)
+++ /gsdl/trunk/perllib/cpan/Image/ExifTool/TagNames.pod	(revision 16842)
@@ -0,0 +1,10834 @@
+
+=head1 NAME
+
+Image::ExifTool::TagNames - ExifTool tag name documentation
+
+=head1 DESCRIPTION
+
+This document contains a complete list of ExifTool tag names, organized into
+tables based on information type.  Tag names are used to indicate the
+specific meta information that is extracted or written in an image.
+
+=head1 TAG TABLES
+
+The tables listed below give the names of all tags recognized by ExifTool.
+
+B<Tag ID>, B<Index> or B<Sequence> is given in the first column of each
+table.  A B<Tag ID> is the computer-readable equivalent of a tag name, and
+is the identifier that is actually stored in the file.  An B<Index> refers
+to the location of a value when found at a fixed position within a data
+block, and B<Sequence> gives the order of values for a serial data stream.
+
+A B<Tag Name> is the handle by which the information is accessed in
+ExifTool.  In some instances, more than one name may correspond to a single
+tag ID.  In these cases, the actual name used depends on the context in
+which the information is found.  Case is not significant for tag names.  A
+question mark after a tag name indicates that the information is either not
+understood, not verified, or not very useful -- these tags are not extracted
+by ExifTool unless the Unknown (-u) option is enabled.  Be aware that some
+tag names are different than the descriptions printed out by default when
+extracting information with exiftool.  To see the tag names instead of the
+descriptions, use C<exiftool -s>.
+
+The B<Writable> column indicates whether the tag is writable by ExifTool.
+Anything but an C<N> in this column means the tag is writable.  A C<Y>
+indicates writable information that is either unformatted or written using
+the existing format.  Other expressions give details about the information
+format, and vary depending on the general type of information.  The format
+name may be followed by a number in square brackets to indicate the number
+of values written, or the number of characters in a fixed-length string
+(including a null terminator which is added if required).
+
+An asterisk (C<*>) after an entry in the B<Writable> column indicates a
+"protected" tag which is not writable directly, but is set via a Composite
+tag.  A tilde (C<~>) indicates a tag this is only writable when print
+conversion is disabled (by setting PrintConv to 0, or using the -n option).
+A slash (C</>) indicates an "avoided" tag that is not created unless the
+group is specified (due to name conflicts with other tags).  An exclamation
+point (C<!>) indicates a tag that is considered unsafe to write under normal
+circumstances.  These "unsafe" tags are not set when calling
+SetNewValuesFromFile() or when using the exiftool -TagsFromFile option
+unless specified explicitly, and care should be taken when editing them
+manually since they may affect the way an image is rendered.  A plus sign
+(C<+>) indicates a "list" tag which supports multiple instances.
+
+The HTML version of these tables also list possible B<Values> for
+discrete-valued tags, as well as B<Notes> for some tags.
+
+B<Note>: If you are familiar with common meta-information tag names, you may
+find that some ExifTool tag names are different than expected.  The usual
+reason for this is to make the tag names more consistent across different
+types of meta information.  To determine a tag name, either consult this
+documentation or run C<exiftool -s> on a file containing the information in
+question.
+
+=head2 JPEG Tags
+
+This table lists information extracted by ExifTool from JPEG images.
+
+  Tag ID       Tag Name                         Writable
+  ------       --------                         --------
+  'APP0'       JFIF                             JFIF
+               JFXX                             JFIF Extension
+               CIFF                             CanonRaw
+  'APP1'       EXIF                             EXIF
+               XMP                              XMP
+  'APP2'       ICC_Profile                      ICC_Profile
+               FPXR                             FlashPix
+  'APP3'       Meta                             Kodak Meta
+  'APP5'       RMETA                            Ricoh RMETA
+  'APP6'       EPPIM                            JPEG EPPIM
+  'APP8'       SPIFF                            JPEG SPIFF
+  'APP10'      Comment                          N
+  'APP12'      PictureInfo                      APP12 PictureInfo
+               Ducky                            APP12 Ducky
+  'APP13'      Photoshop                        Photoshop
+               Adobe_CM                         JPEG AdobeCM
+  'APP14'      Adobe                            JPEG Adobe
+  'APP15'      GraphicConverter                 JPEG GraphConv
+  'COM'        Comment                          Y
+  'SOF'        StartOfFrame                     JPEG SOF
+  'Trailer'    AFCP                             AFCP
+               CanonVRD                         CanonVRD
+               FotoStation                      FotoStation
+               PhotoMechanic                    PhotoMechanic
+               MIE                              MIE
+               PreviewImage                     Y
+
+=head3 JPEG EPPIM Tags
+
+APP6 is used in by the Toshiba PDR-M700 to store a TIFF structure containing
+PrintIM information.
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+  0xc4a5   PrintIM                              PrintIM
+
+=head3 JPEG SPIFF Tags
+
+This information is found in APP8 of SPIFF-style JPEG images (the "official"
+yet rarely used JPEG file format standard: Still Picture Interchange File
+Format).
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      0    SPIFFVersion                         N
+      2    ProfileID                            N
+      3    ColorComponents                      N
+      6    ImageHeight                          N
+     10    ImageWidth                           N
+     14    ColorSpace                           N
+     15    BitsPerSample                        N
+     16    Compression                          N
+     17    ResolutionUnit                       N
+     18    YResolution                          N
+     22    XResolution                          N
+
+=head3 JPEG AdobeCM Tags
+
+The "Adobe_CM" APP13 segment presumably contains color management
+information, but the meaning of the data is currently unknown.  If anyone
+has an idea about what this means, please let me know.
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      0    AdobeCMType                          N
+
+=head3 JPEG Adobe Tags
+
+The "Adobe" APP14 segment stores image encoding information for DCT filters.
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      0    DCTEncodeVersion                     N
+      1    APP14Flags0                          N
+      2    APP14Flags1                          N
+      3    ColorTransform                       N
+
+=head3 JPEG GraphConv Tags
+
+APP15 is used by GraphicConverter to store JPEG quality.
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+  'Q'      Quality                              N
+
+=head3 JPEG SOF Tags
+
+This information is extracted from the JPEG Start Of Frame segment.
+
+  Tag Name                                      Writable
+  --------                                      --------
+  BitsPerSample                                 N
+  ColorComponents                               N
+  EncodingProcess                               N
+  ImageHeight                                   N
+  ImageWidth                                    N
+  YCbCrSubSampling                              N
+
+=head2 EXIF Tags
+
+EXIF stands for "Exchangeable Image File Format".  This type of information
+is formatted according to the TIFF specification, and may be found in JPG,
+TIFF, PNG, MIFF and WDP images, as well as many TIFF-based RAW images.
+
+The EXIF meta information is organized into different Image File Directories
+(IFD's) within an image.  The names of these IFD's correspond to the
+ExifTool family 1 group names.  When writing EXIF information, the default
+B<Group> listed below is used unless another group is specified.
+
+Also listed in the table below are TIFF, DNG, WDP and other tags which are
+not part of the EXIF specification, but may co-exist with EXIF tags in some
+images.
+
+  Tag ID   Tag Name                  Group      Writable
+  ------   --------                  -----      --------
+  0x0001   InteropIndex              InteropIFD string!
+  0x0002   InteropVersion            InteropIFD undef!
+  0x000b   ProcessingSoftware        IFD0       string
+  0x00fe   SubfileType               IFD0       int32u!
+  0x00ff   OldSubfileType            IFD0       int16u!
+  0x0100   ImageWidth                IFD0       int32u!
+  0x0101   ImageHeight               IFD0       int32u!
+  0x0102   BitsPerSample             IFD0       int16u[n]!
+  0x0103   Compression               IFD0       int16u!
+  0x0106   PhotometricInterpretation IFD0       int16u!
+  0x0107   Thresholding              IFD0       int16u!
+  0x0108   CellWidth                 IFD0       int16u!
+  0x0109   CellLength                IFD0       int16u!
+  0x010a   FillOrder                 IFD0       int16u!
+  0x010d   DocumentName              IFD0       string
+  0x010e   ImageDescription          IFD0       string
+  0x010f   Make                      IFD0       string
+  0x0110   Model                     IFD0       string
+  0x0111   StripOffsets              -          N
+           PreviewImageStart         IFD0       int32u*
+           PreviewImageStart         SubIFD1    int32u*
+           JpgFromRawStart           SubIFD2    int32u*
+  0x0112   Orientation               IFD0       int16u
+  0x0115   SamplesPerPixel           IFD0       int16u!
+  0x0116   RowsPerStrip              IFD0       int32u!
+  0x0117   StripByteCounts           -          N
+           PreviewImageLength        IFD0       int32u*
+           PreviewImageLength        SubIFD1    int32u*
+           JpgFromRawLength          SubIFD2    int32u*
+  0x0118   MinSampleValue            IFD0       int16u
+  0x0119   MaxSampleValue            IFD0       int16u
+  0x011a   XResolution               IFD0       rational64u
+  0x011b   YResolution               IFD0       rational64u
+  0x011c   PlanarConfiguration       IFD0       int16u!
+  0x011d   PageName                  IFD0       string
+  0x011e   XPosition                 IFD0       rational64u
+  0x011f   YPosition                 IFD0       rational64u
+  0x0120   FreeOffsets               -          N
+  0x0121   FreeByteCounts            -          N
+  0x0122   GrayResponseUnit          IFD0       int16u
+  0x0123   GrayResponseCurve         -          N
+  0x0124   T4Options                 -          N
+  0x0125   T6Options                 -          N
+  0x0128   ResolutionUnit            IFD0       int16u
+  0x0129   PageNumber                IFD0       int16u[2]
+  0x012c   ColorResponseUnit         -          N
+  0x012d   TransferFunction          -          N
+  0x0131   Software                  IFD0       string
+  0x0132   ModifyDate                IFD0       string
+  0x013b   Artist                    IFD0       string
+  0x013c   HostComputer              IFD0       string
+  0x013d   Predictor                 IFD0       int16u!
+  0x013e   WhitePoint                IFD0       rational64u[2]
+  0x013f   PrimaryChromaticities     IFD0       rational64u[6]
+  0x0140   ColorMap                  -          N
+  0x0141   HalftoneHints             IFD0       int16u[2]
+  0x0142   TileWidth                 IFD0       int32u!
+  0x0143   TileLength                IFD0       int32u!
+  0x0144   TileOffsets               -          N
+  0x0145   TileByteCounts            -          N
+  0x0146   BadFaxLines               -          N
+  0x0147   CleanFaxData              -          N
+  0x0148   ConsecutiveBadFaxLines    -          N
+  0x014a   SubIFD                    -          EXIF
+  0x014c   InkSet                    IFD0       int16u
+  0x014d   InkNames                  -          N
+  0x014e   NumberofInks              -          N
+  0x0150   DotRange                  IFD0       string
+  0x0151   TargetPrinter             -          N
+  0x0152   ExtraSamples              -          N
+  0x0153   SampleFormat              -          N
+  0x0154   SMinSampleValue           -          N
+  0x0155   SMaxSampleValue           -          N
+  0x0156   TransferRange             -          N
+  0x0157   ClipPath                  -          N
+  0x0158   XClipPathUnits            -          N
+  0x0159   YClipPathUnits            -          N
+  0x015a   Indexed                   -          N
+  0x015b   JPEGTables                -          N
+  0x015f   OPIProxy                  -          N
+  0x0190   GlobalParametersIFD       -          EXIF
+  0x0191   ProfileType               -          N
+  0x0192   FaxProfile                -          N
+  0x0193   CodingMethods             -          N
+  0x0194   VersionYear               -          N
+  0x0195   ModeNumber                -          N
+  0x01b1   Decode                    -          N
+  0x01b2   DefaultImageColor         -          N
+  0x0200   JPEGProc                  -          N
+  0x0201   ThumbnailOffset           IFD1       int32u*
+           PreviewImageStart         MakerNotes int32u*
+           JpgFromRawStart           SubIFD     int32u*
+           JpgFromRawStart           IFD2       int32u*
+           OtherImageStart           -          N
+  0x0202   ThumbnailLength           IFD1       int32u*
+           PreviewImageLength        MakerNotes int32u*
+           JpgFromRawLength          SubIFD     int32u*
+           JpgFromRawLength          IFD2       int32u*
+           OtherImageLength          -          N
+  0x0203   JPEGRestartInterval       -          N
+  0x0205   JPEGLosslessPredictors    -          N
+  0x0206   JPEGPointTransforms       -          N
+  0x0207   JPEGQTables               -          N
+  0x0208   JPEGDCTables              -          N
+  0x0209   JPEGACTables              -          N
+  0x0211   YCbCrCoefficients         IFD0       rational64u[3]!
+  0x0212   YCbCrSubSampling          IFD0       int16u[2]!
+  0x0213   YCbCrPositioning          IFD0       int16u!
+  0x0214   ReferenceBlackWhite       IFD0       rational64u[6]
+  0x022f   StripRowCounts            -          N
+  0x02bc   ApplicationNotes          -          XMP
+  0x1000   RelatedImageFileFormat    InteropIFD string!
+  0x1001   RelatedImageWidth         InteropIFD int16u!
+  0x1002   RelatedImageLength        InteropIFD int16u!
+  0x4746   Rating                    IFD0       int16u/
+  0x4749   RatingPercent             IFD0       int16u/
+  0x800d   ImageID                   -          N
+  0x80a4   WangAnnotation            -          N
+  0x80e3   Matteing                  -          N
+  0x80e4   DataType                  -          N
+  0x80e5   ImageDepth                -          N
+  0x80e6   TileDepth                 -          N
+  0x827d   Model2                    -          N
+  0x828d   CFARepeatPatternDim       -          N
+  0x828e   CFAPattern2               -          N
+  0x828f   BatteryLevel              -          N
+  0x8298   Copyright                 IFD0       string
+  0x829a   ExposureTime              ExifIFD    rational64u
+  0x829d   FNumber                   ExifIFD    rational64u
+  0x82a5   MDFileTag                 -          N
+  0x82a6   MDScalePixel              -          N
+  0x82a7   MDColorTable              -          N
+  0x82a8   MDLabName                 -          N
+  0x82a9   MDSampleInfo              -          N
+  0x82aa   MDPrepDate                -          N
+  0x82ab   MDPrepTime                -          N
+  0x82ac   MDFileUnits               -          N
+  0x830e   PixelScale                -          N
+  0x83bb   IPTC-NAA                  IFD0       IPTC
+  0x847e   IntergraphPacketData      -          N
+  0x847f   IntergraphFlagRegisters   -          N
+  0x8480   IntergraphMatrix          -          N
+  0x8482   ModelTiePoint             -          N
+  0x84e0   Site                      -          N
+  0x84e1   ColorSequence             -          N
+  0x84e2   IT8Header                 -          N
+  0x84e3   RasterPadding             -          N
+  0x84e4   BitsPerRunLength          -          N
+  0x84e5   BitsPerExtendedRunLength  -          N
+  0x84e6   ColorTable                -          N
+  0x84e7   ImageColorIndicator       -          N
+  0x84e8   BackgroundColorIndicator  -          N
+  0x84e9   ImageColorValue           -          N
+  0x84ea   BackgroundColorValue      -          N
+  0x84eb   PixelIntensityRange       -          N
+  0x84ec   TransparencyIndicator     -          N
+  0x84ed   ColorCharacterization     -          N
+  0x84ee   HCUsage                   -          N
+  0x8546   SEMInfo                   IFD0       string
+  0x8568   AFCP_IPTC                 -          IPTC
+  0x85d8   ModelTransform            -          N
+  0x8606   LeafData                  -          Leaf
+  0x8649   PhotoshopSettings         -          Photoshop
+  0x8769   ExifOffset                -          EXIF
+  0x8773   ICC_Profile               -          ICC_Profile
+  0x87ac   ImageLayer                -          N
+  0x87af   GeoTiffDirectory          -          N
+  0x87b0   GeoTiffDoubleParams       -          N
+  0x87b1   GeoTiffAsciiParams        -          N
+  0x8822   ExposureProgram           ExifIFD    int16u
+  0x8824   SpectralSensitivity       ExifIFD    string
+  0x8825   GPSInfo                   -          GPS
+  0x8827   ISO                       ExifIFD    int16u
+  0x8828   Opto-ElectricConvFactor   -          N
+  0x8829   Interlace                 -          N
+  0x882a   TimeZoneOffset            ExifIFD    int16s[n]
+  0x882b   SelfTimerMode             ExifIFD    int16u
+  0x885c   FaxRecvParams             -          N
+  0x885d   FaxSubAddress             -          N
+  0x885e   FaxRecvTime               -          N
+  0x888a   LeafSubIFD                -          Leaf SubIFD
+  0x9000   ExifVersion               ExifIFD    undef
+  0x9003   DateTimeOriginal          ExifIFD    string
+  0x9004   CreateDate                ExifIFD    string
+  0x9101   ComponentsConfiguration   ExifIFD    undef
+  0x9102   CompressedBitsPerPixel    ExifIFD    rational64u
+  0x9201   ShutterSpeedValue         ExifIFD    rational64s
+  0x9202   ApertureValue             ExifIFD    rational64u
+  0x9203   BrightnessValue           ExifIFD    rational64s
+  0x9204   ExposureCompensation      ExifIFD    rational64s
+  0x9205   MaxApertureValue          ExifIFD    rational64u
+  0x9206   SubjectDistance           ExifIFD    rational64u
+  0x9207   MeteringMode              ExifIFD    int16u
+  0x9208   LightSource               ExifIFD    int16u
+  0x9209   Flash                     ExifIFD    int16u
+  0x920a   FocalLength               ExifIFD    rational64u
+  0x920b   FlashEnergy               -          N
+  0x920c   SpatialFrequencyResponse  -          N
+  0x920d   Noise                     -          N
+  0x920e   FocalPlaneXResolution     -          N
+  0x920f   FocalPlaneYResolution     -          N
+  0x9210   FocalPlaneResolutionUnit  -          N
+  0x9211   ImageNumber               ExifIFD    int32u
+  0x9212   SecurityClassification    ExifIFD    string
+  0x9213   ImageHistory              ExifIFD    string
+  0x9214   SubjectLocation           ExifIFD    int16u[4]
+  0x9215   ExposureIndex             -          N
+  0x9216   TIFF-EPStandardID         -          N
+  0x9217   SensingMethod             -          N
+  0x923f   StoNits                   -          N
+  0x927c   MakerNoteCanon            ExifIFD    Canon
+           MakerNoteCasio            ExifIFD    Casio
+           MakerNoteCasio2           ExifIFD    Casio Type2
+           MakerNoteFujiFilm         ExifIFD    FujiFilm
+           MakerNoteHP               ExifIFD    HP
+           MakerNoteHP2              ExifIFD    HP Type2
+           MakerNoteHP4              ExifIFD    HP Type4
+           MakerNoteHP6              ExifIFD    HP Type6
+           MakerNoteJVC              ExifIFD    JVC
+           MakerNoteJVCText          ExifIFD    JVC Text
+           MakerNoteKodak1a          ExifIFD    Kodak
+           MakerNoteKodak1b          ExifIFD    Kodak
+           MakerNoteKodak2           ExifIFD    Kodak Type2
+           MakerNoteKodak3           ExifIFD    Kodak Type3
+           MakerNoteKodak4           ExifIFD    Kodak Type4
+           MakerNoteKodak5           ExifIFD    Kodak Type5
+           MakerNoteKodak6a          ExifIFD    Kodak Type6
+           MakerNoteKodak6b          ExifIFD    Kodak Type6
+           MakerNoteKodak7           ExifIFD    Kodak Type7
+           MakerNoteKodakIFD         ExifIFD    Kodak IFD
+           MakerNoteKodakUnknown     ExifIFD    Kodak Unknown
+           MakerNoteKyocera          ExifIFD    Unknown
+           MakerNoteMinolta          ExifIFD    Minolta
+           MakerNoteMinolta2         ExifIFD    Olympus
+           MakerNoteMinolta3         ExifIFD    undef
+           MakerNoteNikon            ExifIFD    Nikon
+           MakerNoteNikon2           ExifIFD    Nikon Type2
+           MakerNoteNikon3           ExifIFD    Nikon
+           MakerNoteOlympus          ExifIFD    Olympus
+           MakerNoteOlympus2         ExifIFD    Olympus
+           MakerNoteLeica            ExifIFD    Panasonic
+           MakerNotePanasonic        ExifIFD    Panasonic
+           MakerNotePanasonic2       ExifIFD    Panasonic Type2
+           MakerNotePentax           ExifIFD    Pentax
+           MakerNotePentax2          ExifIFD    Pentax Type2
+           MakerNotePentax3          ExifIFD    Casio Type2
+           MakerNotePentax4          ExifIFD    Pentax Type4
+           MakerNoteRicoh            ExifIFD    Ricoh
+           MakerNoteRicohText        ExifIFD    Ricoh Text
+           PreviewImage              ExifIFD    undef
+           MakerNoteSanyo            ExifIFD    Sanyo
+           MakerNoteSanyoC4          ExifIFD    Sanyo
+           MakerNoteSanyoPatch       ExifIFD    Sanyo
+           MakerNoteSigma            ExifIFD    Sigma
+           MakerNoteSony             ExifIFD    Sony
+           MakerNoteSonySRF          ExifIFD    Sony SRF
+           MakerNoteSonySR2          ExifIFD    Sony
+           MakerNoteUnknown          ExifIFD    Unknown
+  0x9286   UserComment               ExifIFD    undef
+  0x9290   SubSecTime                ExifIFD    string
+  0x9291   SubSecTimeOriginal        ExifIFD    string
+  0x9292   SubSecTimeDigitized       ExifIFD    string
+  0x935c   ImageSourceData           IFD0       undef
+  0x9c9b   XPTitle                   IFD0       int8u
+  0x9c9c   XPComment                 IFD0       int8u
+  0x9c9d   XPAuthor                  IFD0       int8u
+  0x9c9e   XPKeywords                IFD0       int8u
+  0x9c9f   XPSubject                 IFD0       int8u
+  0xa000   FlashpixVersion           ExifIFD    undef
+  0xa001   ColorSpace                ExifIFD    int16u
+  0xa002   ExifImageWidth            ExifIFD    int16u
+  0xa003   ExifImageLength           ExifIFD    int16u
+  0xa004   RelatedSoundFile          ExifIFD    string
+  0xa005   InteropOffset             -          EXIF
+  0xa20b   FlashEnergy               ExifIFD    rational64u[n]
+  0xa20c   SpatialFrequencyResponse  -          N
+  0xa20d   Noise                     -          N
+  0xa20e   FocalPlaneXResolution     ExifIFD    rational64u
+  0xa20f   FocalPlaneYResolution     ExifIFD    rational64u
+  0xa210   FocalPlaneResolutionUnit  ExifIFD    int16u
+  0xa211   ImageNumber               -          N
+  0xa212   SecurityClassification    -          N
+  0xa213   ImageHistory              -          N
+  0xa214   SubjectLocation           ExifIFD    int16u[2]
+  0xa215   ExposureIndex             ExifIFD    rational64u
+  0xa216   TIFF-EPStandardID         -          N
+  0xa217   SensingMethod             ExifIFD    int16u
+  0xa300   FileSource                ExifIFD    undef
+  0xa301   SceneType                 ExifIFD    undef
+  0xa302   CFAPattern                ExifIFD    undef
+  0xa401   CustomRendered            ExifIFD    int16u
+  0xa402   ExposureMode              ExifIFD    int16u
+  0xa403   WhiteBalance              ExifIFD    int16u
+  0xa404   DigitalZoomRatio          ExifIFD    rational64u
+  0xa405   FocalLengthIn35mmFormat   ExifIFD    int16u
+  0xa406   SceneCaptureType          ExifIFD    int16u
+  0xa407   GainControl               ExifIFD    int16u
+  0xa408   Contrast                  ExifIFD    int16u
+  0xa409   Saturation                ExifIFD    int16u
+  0xa40a   Sharpness                 ExifIFD    int16u
+  0xa40b   DeviceSettingDescription  -          N
+  0xa40c   SubjectDistanceRange      ExifIFD    int16u
+  0xa420   ImageUniqueID             ExifIFD    string
+  0xa480   GDALMetadata              -          N
+  0xa481   GDALNoData                -          N
+  0xa500   Gamma                     ExifIFD    rational64u
+  0xbc01   PixelFormat               -          N
+  0xbc02   Transfomation             -          N
+  0xbc03   Uncompressed              -          N
+  0xbc04   ImageType                 -          N
+  0xbc80   ImageWidth                -          N
+  0xbc81   ImageHeight               -          N
+  0xbc82   WidthResolution           -          N
+  0xbc83   HeightResolution          -          N
+  0xbcc0   ImageOffset               -          N
+  0xbcc1   ImageByteCount            -          N
+  0xbcc2   AlphaOffset               -          N
+  0xbcc3   AlphaByteCount            -          N
+  0xbcc4   ImageDataDiscard          -          N
+  0xbcc5   AlphaDataDiscard          -          N
+  0xc427   OceScanjobDesc            -          N
+  0xc428   OceApplicationSelector    -          N
+  0xc429   OceIDNumber               -          N
+  0xc42a   OceImageLogic             -          N
+  0xc44f   Annotations               -          N
+  0xc4a5   PrintIM                   IFD0       PrintIM
+  0xc612   DNGVersion                IFD0       int8u[4]
+  0xc613   DNGBackwardVersion        IFD0       int8u[4]
+  0xc614   UniqueCameraModel         IFD0       string
+  0xc615   LocalizedCameraModel      IFD0       string
+  0xc616   CFAPlaneColor             -          N
+  0xc617   CFALayout                 -          N
+  0xc618   LinearizationTable        -          N
+  0xc619   BlackLevelRepeatDim       -          N
+  0xc61a   BlackLevel                -          N
+  0xc61b   BlackLevelDeltaH          -          N
+  0xc61c   BlackLevelDeltaV          -          N
+  0xc61d   WhiteLevel                -          N
+  0xc61e   DefaultScale              SubIFD     rational64u[2]
+  0xc61f   DefaultCropOrigin         SubIFD     int32u[2]
+  0xc620   DefaultCropSize           SubIFD     int32u[2]
+  0xc621   ColorMatrix1              -          N
+  0xc622   ColorMatrix2              -          N
+  0xc623   CameraCalibration1        -          N
+  0xc624   CameraCalibration2        -          N
+  0xc625   ReductionMatrix1          -          N
+  0xc626   ReductionMatrix2          -          N
+  0xc627   AnalogBalance             -          N
+  0xc628   AsShotNeutral             -          N
+  0xc629   AsShotWhiteXY             IFD0       rational64u[2]
+  0xc62a   BaselineExposure          IFD0       rational64s
+  0xc62b   BaselineNoise             IFD0       rational64u
+  0xc62c   BaselineSharpness         IFD0       rational64u
+  0xc62d   BayerGreenSplit           SubIFD     int32u
+  0xc62e   LinearResponseLimit       IFD0       rational64u
+  0xc62f   CameraSerialNumber        IFD0       string
+  0xc630   DNGLensInfo               IFD0       rational64u[4]
+  0xc631   ChromaBlurRadius          SubIFD     rational64u
+  0xc632   AntiAliasStrength         SubIFD     rational64u
+  0xc633   ShadowScale               IFD0       rational64u
+  0xc634   SR2Private                -          Sony SR2Private
+           DNGAdobeData              -          DNG AdobeData
+           DNGPentaxData             -          Pentax
+           DNGPrivateData            -          N
+  0xc635   MakerNoteSafety           IFD0       int16u
+  0xc65a   CalibrationIlluminant1    IFD0       int16u
+  0xc65b   CalibrationIlluminant2    IFD0       int16u
+  0xc65c   BestQualityScale          SubIFD     rational64u
+  0xc65d   RawDataUniqueID           IFD0       int8u[16]
+  0xc660   AliasLayerMetadata        -          N
+  0xc68b   OriginalRawFileName       IFD0       string
+  0xc68c   OriginalRawFileData       IFD0       DNG OriginalRaw
+  0xc68d   ActiveArea                SubIFD     int32u[4]
+  0xc68e   MaskedAreas               SubIFD     int32u[4]
+  0xc68f   AsShotICCProfile          IFD0       ICC_Profile
+  0xc690   AsShotPreProfileMatrix    IFD0       rational64s[n]!
+  0xc691   CurrentICCProfile         IFD0       ICC_Profile
+  0xc692   CurrentPreProfileMatrix   IFD0       rational64s[n]!
+  0xea1d   OffsetSchema              ExifIFD    int32s
+  0xfde8   OwnerName                 ExifIFD    string/
+  0xfde9   SerialNumber              ExifIFD    string/
+  0xfdea   Lens                      ExifIFD    string/
+  0xfe4c   RawFile                   ExifIFD    string/
+  0xfe4d   Converter                 ExifIFD    string/
+  0xfe4e   WhiteBalance              ExifIFD    string/
+  0xfe51   Exposure                  ExifIFD    string/
+  0xfe52   Shadows                   ExifIFD    string/
+  0xfe53   Brightness                ExifIFD    string/
+  0xfe54   Contrast                  ExifIFD    string/
+  0xfe55   Saturation                ExifIFD    string/
+  0xfe56   Sharpness                 ExifIFD    string/
+  0xfe57   Smoothness                ExifIFD    string/
+  0xfe58   MoireFilter               ExifIFD    string/
+
+=head2 IPTC Tags
+
+IPTC stands for "International Press Telecommunications Council".  This is
+an older meta information format that is slowly being phased out in favor of
+XMP.  IPTC information may be embedded in JPG, TIFF, PNG, MIFF, PS, PDF, PSD
+and DNG images.
+
+The IPTC specification dictates a length for ASCII (C<string> or C<digits>)
+values.  These lengths are given in square brackets after the B<Writable>
+format name.  For tags where a range of lengths is allowed, the minimum and
+maximum lengths are separated by a comma within the brackets.  IPTC strings
+are not null terminated.
+
+IPTC information is separated into different records, each of which has its
+own set of tags.
+
+  Record   Tag Name                             Writable
+  ------   --------                             --------
+      1    IPTCEnvelope                         IPTC EnvelopeRecord
+      2    IPTCApplication                      IPTC ApplicationRecord
+      3    IPTCNewsPhoto                        IPTC NewsPhoto
+      7    IPTCPreObjectData                    IPTC PreObjectData
+      8    IPTCObjectData                       IPTC ObjectData
+      9    IPTCPostObjectData                   IPTC PostObjectData
+
+=head3 IPTC EnvelopeRecord Tags
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+      0    EnvelopeRecordVersion                int16u
+      5    Destination                          string[0,1024]+
+     20    FileFormat                           int16u
+     22    FileVersion                          int16u
+     30    ServiceIdentifier                    string[0,10]
+     40    EnvelopeNumber                       digits[8]
+     50    ProductID                            string[0,32]+
+     60    EnvelopePriority                     digits[1]
+     70    DateSent                             digits[8]
+     80    TimeSent                             string[11]
+     90    CodedCharacterSet                    string[0,32]
+    100    UniqueObjectName                     string[14,80]
+    120    ARMIdentifier                        int16u
+    122    ARMVersion                           int16u
+
+=head3 IPTC ApplicationRecord Tags
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+      0    ApplicationRecordVersion             int16u
+      3    ObjectTypeReference                  string[3,67]
+      4    ObjectAttributeReference             string[4,68]+
+      5    ObjectName                           string[0,64]
+      7    EditStatus                           string[0,64]
+      8    EditorialUpdate                      digits[2]
+     10    Urgency                              digits[1]
+     12    SubjectReference                     string[13,236]+
+     15    Category                             string[0,3]
+     20    SupplementalCategories               string[0,32]+
+     22    FixtureIdentifier                    string[0,32]
+     25    Keywords                             string[0,64]+
+     26    ContentLocationCode                  string[3]+
+     27    ContentLocationName                  string[0,64]+
+     30    ReleaseDate                          digits[8]
+     35    ReleaseTime                          string[11]
+     37    ExpirationDate                       digits[8]
+     38    ExpirationTime                       string[11]
+     40    SpecialInstructions                  string[0,256]
+     42    ActionAdvised                        digits[2]
+     45    ReferenceService                     string[0,10]+
+     47    ReferenceDate                        digits[8]+
+     50    ReferenceNumber                      digits[8]+
+     55    DateCreated                          digits[8]
+     60    TimeCreated                          string[11]
+     62    DigitalCreationDate                  digits[8]
+     63    DigitalCreationTime                  string[11]
+     65    OriginatingProgram                   string[0,32]
+     70    ProgramVersion                       string[0,10]
+     75    ObjectCycle                          string[1]
+     80    By-line                              string[0,32]+
+     85    By-lineTitle                         string[0,32]+
+     90    City                                 string[0,32]
+     92    Sub-location                         string[0,32]
+     95    Province-State                       string[0,32]
+    100    Country-PrimaryLocationCode          string[3]
+    101    Country-PrimaryLocationName          string[0,64]
+    103    OriginalTransmissionReference        string[0,32]
+    105    Headline                             string[0,256]
+    110    Credit                               string[0,32]
+    115    Source                               string[0,32]
+    116    CopyrightNotice                      string[0,128]
+    118    Contact                              string[0,128]+
+    120    Caption-Abstract                     string[0,2000]
+    121    LocalCaption                         string[0,256]
+    122    Writer-Editor                        string[0,32]+
+    125    RasterizedCaption                    string[7360]
+    130    ImageType                            string[2]
+    131    ImageOrientation                     string[1]
+    135    LanguageIdentifier                   string[2,3]
+    150    AudioType                            string[2]
+    151    AudioSamplingRate                    digits[6]
+    152    AudioSamplingResolution              digits[2]
+    153    AudioDuration                        digits[6]
+    154    AudioOutcue                          string[0,64]
+    184    JobID                                string[0,64]
+    185    MasterDocumentID                     string[0,256]
+    186    ShortDocumentID                      string[0,64]
+    187    UniqueDocumentID                     string[0,128]
+    188    OwnerID                              string[0,128]
+    200    ObjectPreviewFileFormat              int16u
+    201    ObjectPreviewFileVersion             int16u
+    202    ObjectPreviewData                    string[0,256000]
+    221    Prefs                                string[0,64]
+    225    ClassifyState                        string[0,64]
+    228    SimilarityIndex                      string[0,32]
+    230    DocumentNotes                        string[0,1024]
+    231    DocumentHistory                      string[0,256]
+    232    ExifCameraInfo                       string[0,4096]
+
+=head3 IPTC NewsPhoto Tags
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+      0    NewsPhotoVersion                     int16u
+     10    IPTCPictureNumber                    string[16]
+     20    IPTCImageWidth                       int16u
+     30    IPTCImageHeight                      int16u
+     40    IPTCPixelWidth                       int16u
+     50    IPTCPixelHeight                      int16u
+     55    SupplementalType                     int8u
+     60    ColorRepresentation                  int16u
+     64    InterchangeColorSpace                int8u
+     65    ColorSequence                        int8u
+     66    ICC_Profile                          N
+     70    ColorCalibrationMatrix               N
+     80    LookupTable                          N
+     84    NumIndexEntries                      int16u
+     85    ColorPalette                         N
+     86    IPTCBitsPerSample                    int8u
+     90    SampleStructure                      int8u
+    100    ScanningDirection                    int8u
+    102    IPTCImageRotation                    int8u
+    110    DataCompressionMethod                int32u
+    120    QuantizationMethod                   int8u
+    125    EndPoints                            N
+    130    ExcursionTolerance                   int8u
+    135    BitsPerComponent                     int8u
+    140    MaximumDensityRange                  int16u
+    145    GammaCompensatedValue                int16u
+
+=head3 IPTC PreObjectData Tags
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+     10    SizeMode                             N
+     20    MaxSubfileSize                       N
+     90    ObjectSizeAnnounced                  N
+     95    MaximumObjectSize                    N
+
+=head3 IPTC ObjectData Tags
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+     10    SubFile                              N+
+
+=head3 IPTC PostObjectData Tags
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+     10    ConfirmedObjectSize                  N
+
+=head2 XMP Tags
+
+XMP stands for "Extensible Metadata Platform", an XML/RDF-based metadata
+format which is being pushed by Adobe.  Information in this format can be
+embedded in many different image file types including JPG, JP2, TIFF, PNG,
+MIFF, PS, PDF, PSD and DNG, as well as audio file formats supporting ID3v2
+information.
+
+The XMP B<Tag ID>'s aren't listed because in most cases they are identical
+to the B<Tag Name>.
+
+All XMP information is stored as character strings.  The B<Writable> column
+specifies the information format:  C<integer> is a string of digits
+(possibly beginning with a '+' or '-'), C<real> is a floating point number,
+C<rational> is two C<integer> strings separated by a '/' character, C<date>
+is a date/time string in the format "YYYY:MM:DD HH:MM:SS[+/-HH:MM]",
+C<boolean> is either "True" or "False", and C<lang-alt> is a list of string
+alternatives in different languages.
+
+Individual languages for C<lang-alt> tags are accessed by suffixing the tag
+name with a '-', followed by an RFC 3066 language code (ie. "XMP:Title-fr",
+or "Rights-en-US").  A C<lang-alt> tag with no language code accesses the
+"x-default" language, but causes other languages to be deleted when writing.
+The "x-default" language code may be specified when writing a new value to
+write only the default language, but note that all languages are still
+deleted if "x-default" tag is deleted.  When reading, "x-default" is not
+specified.
+
+The XMP tags are organized according to schema B<Namespace> in the following
+tables.  Note that a few of the longer namespace prefixes given below have
+been shortened for convenience (since the family 1 group names are derived
+from these by adding a leading "XMP-").  In cases where a tag name exists in
+more than one namespace, less common namespaces are avoided when writing.
+However, any namespace may be written by specifying a family 1 group name
+for the tag, ie) XMP-exif:Contrast or XMP-crs:Contrast.
+
+ExifTool will extract XMP information even if it is not listed in these
+tables.  For example, the C<pdfx> namespace doesn't have a predefined set of
+tag names because it is used to store application-defined PDF information,
+but this information is extracted by ExifTool anyway.
+
+  Namespace                                     Writable
+  ---------                                     --------
+  aux                                           XMP aux
+  cc                                            XMP cc
+  crs                                           XMP crs
+  dc                                            XMP dc
+  dex                                           XMP dex
+  DICOM                                         XMP DICOM
+  exif                                          XMP exif
+  iptcCore                                      XMP iptcCore
+  lr                                            XMP Lightroom
+  mediapro                                      XMP MediaPro
+  microsoft                                     XMP Microsoft
+  pdf                                           XMP pdf
+  photomech                                     PhotoMechanic XMP
+  photoshop                                     XMP photoshop
+  PixelLive                                     XMP PixelLive
+  tiff                                          XMP tiff
+  xmp                                           XMP xmp
+  xmpBJ                                         XMP xmpBJ
+  xmpDM                                         XMP xmpDM
+  xmpMM                                         XMP xmpMM
+  xmpPLUS                                       XMP xmpPLUS
+  xmpRights                                     XMP xmpRights
+  xmpTPg                                        XMP xmpTPg
+
+=head3 XMP aux Tags
+
+Photoshop Auxiliary schema tags.
+
+  Tag Name                                      Writable
+  --------                                      --------
+  Firmware                                      string
+  FlashCompensation                             rational
+  ImageNumber                                   string
+  Lens                                          string
+  LensInfo                                      string
+  OwnerName                                     string
+  SerialNumber                                  string
+
+=head3 XMP cc Tags
+
+Creative Commons schema tags.
+
+  Tag Name                                      Writable
+  --------                                      --------
+  License                                       string
+
+=head3 XMP crs Tags
+
+Photoshop Camera Raw Schema tags.
+
+  Tag Name                                      Writable
+  --------                                      --------
+  AlreadyApplied                                boolean
+  AutoBrightness                                boolean
+  AutoContrast                                  boolean
+  AutoExposure                                  boolean
+  AutoShadows                                   boolean
+  BlueHue                                       integer
+  BlueSaturation                                integer
+  Brightness                                    integer
+  CameraProfile                                 string
+  CameraProfileDigest                           string
+  ChromaticAberrationB                          integer
+  ChromaticAberrationR                          integer
+  Clarity                                       integer
+  ColorNoiseReduction                           integer
+  Contrast                                      integer/
+  ConvertToGrayscale                            boolean
+  CropAngle                                     real
+  CropBottom                                    real
+  CropHeight                                    real
+  CropLeft                                      real
+  CropRight                                     real
+  CropTop                                       real
+  CropUnits                                     integer
+  CropWidth                                     real
+  Defringe                                      integer
+  Exposure                                      real
+  FillLight                                     integer
+  GreenHue                                      integer
+  GreenSaturation                               integer
+  HasCrop                                       boolean
+  HasSettings                                   boolean
+  HighlightRecovery                             integer
+  HueAdjustmentAqua                             integer
+  HueAdjustmentBlue                             integer
+  HueAdjustmentGreen                            integer
+  HueAdjustmentMagenta                          integer
+  HueAdjustmentOrange                           integer
+  HueAdjustmentPurple                           integer
+  HueAdjustmentRed                              integer
+  HueAdjustmentYellow                           integer
+  IncrementalTemperature                        integer
+  IncrementalTint                               integer
+  LuminanceAdjustmentAqua                       integer
+  LuminanceAdjustmentBlue                       integer
+  LuminanceAdjustmentGreen                      integer
+  LuminanceAdjustmentMagenta                    integer
+  LuminanceAdjustmentOrange                     integer
+  LuminanceAdjustmentPurple                     integer
+  LuminanceAdjustmentRed                        integer
+  LuminanceAdjustmentYellow                     integer
+  LuminanceSmoothing                            integer
+  ParametricDarks                               integer
+  ParametricHighlights                          integer
+  ParametricHighlightSplit                      integer
+  ParametricLights                              integer
+  ParametricMidtoneSplit                        integer
+  ParametricShadows                             integer
+  ParametricShadowSplit                         integer
+  RawFileName                                   string
+  RedHue                                        integer
+  RedSaturation                                 integer
+  Saturation                                    integer/
+  SaturationAdjustmentAqua                      integer
+  SaturationAdjustmentBlue                      integer
+  SaturationAdjustmentGreen                     integer
+  SaturationAdjustmentMagenta                   integer
+  SaturationAdjustmentOrange                    integer
+  SaturationAdjustmentPurple                    integer
+  SaturationAdjustmentRed                       integer
+  SaturationAdjustmentYellow                    integer
+  Shadows                                       integer
+  ShadowTint                                    integer
+  SharpenDetail                                 integer
+  SharpenEdgeMasking                            integer
+  SharpenRadius                                 real
+  Sharpness                                     integer/
+  SplitToningBalance                            integer
+  SplitToningHighlightHue                       integer
+  SplitToningHighlightSaturation                integer
+  SplitToningShadowHue                          integer
+  SplitToningShadowSaturation                   integer
+  Temperature                                   integer
+  Tint                                          integer
+  ToneCurve                                     string+
+  ToneCurveName                                 string
+  Version                                       string
+  Vibrance                                      integer
+  VignetteAmount                                integer
+  VignetteMidpoint                              integer
+  WhiteBalance                                  string/
+
+=head3 XMP dc Tags
+
+Dublin Core schema tags.
+
+  Tag Name                                      Writable
+  --------                                      --------
+  Contributor                                   string+
+  Coverage                                      string
+  Creator                                       string+
+  Date                                          date+
+  Description                                   lang-alt
+  Format                                        string
+  Identifier                                    string
+  Language                                      string+
+  Publisher                                     string+
+  Relation                                      string+
+  Rights                                        lang-alt
+  Source                                        string
+  Subject                                       string+
+  Title                                         lang-alt
+  Type                                          string+
+
+=head3 XMP dex Tags
+
+Description Explorer schema tags.  These tags are not very common.  The
+Source and Rating tags are avoided when writing due to name conflicts with
+other XMP tags.
+
+  Tag Name                                      Writable
+  --------                                      --------
+  CRC32                                         integer
+  FFID                                          string
+  LicenseType                                   string
+  OS                                            integer
+  Rating                                        string/
+  Revision                                      string
+  ShortDescription                              lang-alt
+  Source                                        string/
+
+=head3 XMP DICOM Tags
+
+DICOM schema tags.
+
+  Tag Name                                      Writable
+  --------                                      --------
+  EquipmentInstitution                          string
+  EquipmentManufacturer                         string
+  PatientsBirthDate                             date
+  PatientID                                     string
+  PatientsName                                  string
+  PatientsSex                                   string
+  SeriesDateTime                                date
+  SeriesDescription                             string
+  SeriesModality                                string
+  SeriesNumber                                  string
+  StudyDateTime                                 date
+  StudyDescription                              string
+  StudyID                                       string
+  StudyPhysician                                string
+
+=head3 XMP exif Tags
+
+EXIF schema for EXIF tags.
+
+  Tag Name                                      Writable
+  --------                                      --------
+  ApertureValue                                 rational
+  BrightnessValue                               rational
+  CFAPatternColumns                             integer
+  CFAPatternRows                                integer
+  CFAPatternValues                              integer+
+  ColorSpace                                    integer
+  ComponentsConfiguration                       integer+
+  CompressedBitsPerPixel                        rational
+  Contrast                                      integer
+  CustomRendered                                integer
+  DateTimeDigitized                             date
+  DateTimeOriginal                              date
+  DeviceSettingDescriptionColumns               integer
+  DeviceSettingDescriptionRows                  integer
+  DeviceSettingDescriptionSettings              string+
+  DigitalZoomRatio                              rational
+  ExifVersion                                   string
+  ExposureCompensation                          rational
+  ExposureIndex                                 rational
+  ExposureMode                                  integer
+  ExposureProgram                               integer
+  ExposureTime                                  rational
+  FileSource                                    integer
+  FlashEnergy                                   rational
+  FlashFired                                    boolean
+  FlashFunction                                 boolean
+  FlashMode                                     integer
+  FlashpixVersion                               string
+  FlashRedEyeMode                               boolean
+  FlashReturn                                   integer
+  FNumber                                       rational
+  FocalLength                                   rational
+  FocalLengthIn35mmFormat                       integer
+  FocalPlaneResolutionUnit                      integer
+  FocalPlaneXResolution                         rational
+  FocalPlaneYResolution                         rational
+  GainControl                                   integer
+  GPSAltitude                                   rational
+  GPSAltitudeRef                                integer
+  GPSAreaInformation                            string
+  GPSDestBearing                                rational
+  GPSDestBearingRef                             string
+  GPSDestDistance                               rational
+  GPSDestDistanceRef                            string
+  GPSDestLatitude                               string
+  GPSDestLongitude                              string
+  GPSDifferential                               integer
+  GPSDOP                                        rational
+  GPSImgDirection                               rational
+  GPSImgDirectionRef                            string
+  GPSLatitude                                   string
+  GPSLongitude                                  string
+  GPSMapDatum                                   string
+  GPSMeasureMode                                integer
+  GPSProcessingMethod                           string
+  GPSSatellites                                 string
+  GPSSpeed                                      rational
+  GPSSpeedRef                                   string
+  GPSStatus                                     string
+  GPSTimeStamp                                  date
+  GPSTrack                                      rational
+  GPSTrackRef                                   string
+  GPSVersionID                                  string
+  ImageUniqueID                                 string
+  ISO                                           integer+
+  LightSource                                   string
+  MakerNote                                     string
+  MaxApertureValue                              rational
+  MeteringMode                                  integer
+  NativeDigest                                  string
+  OECFColumns                                   integer
+  OECFNames                                     string+
+  OECFRows                                      integer
+  OECFValues                                    rational+
+  ExifImageWidth                                integer
+  ExifImageLength                               integer
+  RelatedSoundFile                              string
+  Saturation                                    integer
+  SceneCaptureType                              integer
+  SceneType                                     integer
+  SensingMethod                                 integer
+  Sharpness                                     integer
+  ShutterSpeedValue                             rational
+  SpatialFrequencyResponseColumns               integer
+  SpatialFrequencyResponseNames                 string+
+  SpatialFrequencyResponseRows                  integer
+  SpatialFrequencyResponseValues                rational+
+  SpectralSensitivity                           string
+  SubjectArea                                   integer+
+  SubjectDistance                               rational
+  SubjectDistanceRange                          integer
+  SubjectLocation                               integer+
+  UserComment                                   lang-alt
+  WhiteBalance                                  integer
+
+=head3 XMP iptcCore Tags
+
+IPTC Core schema tags.  The actual IPTC Core namespace schema prefix is
+"Iptc4xmpCore", which is the prefix recorded in the file, but ExifTool
+shortens this for the "XMP-iptcCore" family 1 group name.
+
+  Tag Name                                      Writable
+  --------                                      --------
+  CountryCode                                   string
+  CreatorContactInfoCiAdrCity                   string
+  CreatorContactInfoCiAdrCtry                   string
+  CreatorContactInfoCiAdrExtadr                 string
+  CreatorContactInfoCiAdrPcode                  string
+  CreatorContactInfoCiAdrRegion                 string
+  CreatorContactInfoCiEmailWork                 string
+  CreatorContactInfoCiTelWork                   string
+  CreatorContactInfoCiUrlWork                   string
+  IntellectualGenre                             string
+  Location                                      string
+  Scene                                         string+
+  SubjectCode                                   string+
+
+=head3 XMP Lightroom Tags
+
+Adobe Lightroom "lr" schema tags.
+
+  Tag Name                                      Writable
+  --------                                      --------
+  HierarchicalSubject                           string+
+  PrivateRTKInfo                                string
+
+=head3 XMP MediaPro Tags
+
+IView MediaPro schema tags.
+
+  Tag Name                                      Writable
+  --------                                      --------
+  CatalogSets                                   string+
+  Event                                         string
+  Location                                      string/
+  People                                        string+
+  Status                                        string
+  UserFields                                    string+
+
+=head3 XMP Microsoft Tags
+
+Microsoft Photo schema tags.  This is likely not a complete list, but
+represents tags which have been observed in sample images.  The actual
+namespace prefix is "MicrosoftPhoto", but ExifTool shortens this to
+"XMP-microsoft" in the family 1 group name.
+
+  Tag Name                                      Writable
+  --------                                      --------
+  CameraSerialNumber                            string
+  DateAcquired                                  date
+  FlashManufacturer                             string
+  FlashModel                                    string
+  LastKeywordIPTC                               string+
+  LastKeywordXMP                                string+
+  LensManufacturer                              string
+  LensModel                                     string
+  RatingPercent                                 string
+
+=head3 XMP pdf Tags
+
+Adobe PDF schema tags.  The official XMP specification defines only
+Keywords, PDFVersion and Producer.  The other tags are included because they
+have been observed in PDF files, but Creator, Subject and Title are avoided
+when writing due to name conflicts with XMP-dc tags.
+
+  Tag Name                                      Writable
+  --------                                      --------
+  Author                                        string
+  CreationDate                                  date
+  Creator                                       string/
+  Keywords                                      string
+  ModDate                                       date
+  PDFVersion                                    string
+  Producer                                      string
+  Subject                                       string/
+  Title                                         string/
+
+=head3 XMP photoshop Tags
+
+Adobe Photoshop schema tags.
+
+  Tag Name                                      Writable
+  --------                                      --------
+  AuthorsPosition                               string
+  CaptionWriter                                 string
+  Category                                      string
+  City                                          string
+  ColorMode                                     string
+  Country                                       string
+  Credit                                        string
+  DateCreated                                   date
+  Headline                                      string
+  History                                       string
+  ICCProfileName                                string
+  Instructions                                  string
+  LegacyIPTCDigest                              string
+  SidecarForExtension                           string
+  Source                                        string/
+  State                                         string
+  SupplementalCategories                        string+
+  TransmissionReference                         string
+  Urgency                                       integer
+
+=head3 XMP PixelLive Tags
+
+PixelLive schema tags.  These tags are not writable becase they are very
+uncommon and I haven't been able to locate a reference which gives the
+namespace URI.
+
+  Tag Name                                      Writable
+  --------                                      --------
+  Author                                        N
+  Comments                                      N
+  Copyright                                     N
+  Date                                          N
+  Genre                                         N
+  Title                                         N
+
+=head3 XMP tiff Tags
+
+EXIF schema for TIFF tags.
+
+  Tag Name                                      Writable
+  --------                                      --------
+  Artist                                        string
+  BitsPerSample                                 integer+
+  Compression                                   integer
+  Copyright                                     lang-alt
+  DateTime                                      date
+  ImageDescription                              lang-alt
+  ImageHeight                                   integer
+  ImageWidth                                    integer
+  Make                                          string
+  Model                                         string
+  NativeDigest                                  string
+  Orientation                                   integer
+  PhotometricInterpretation                     integer
+  PlanarConfiguration                           integer
+  PrimaryChromaticities                         rational+
+  ReferenceBlackWhite                           rational+
+  ResolutionUnit                                integer
+  SamplesPerPixel                               integer
+  Software                                      string
+  TransferFunction                              integer+
+  WhitePoint                                    rational+
+  XResolution                                   rational
+  YCbCrCoefficients                             rational+
+  YCbCrPositioning                              integer
+  YCbCrSubSampling                              string
+  YResolution                                   rational
+
+=head3 XMP xmp Tags
+
+XMP Basic schema tags.  If the older "xap", "xapBJ", "xapMM" or "xapRights"
+namespace prefixes are found, they are translated to the newer "xmp",
+"xmpBJ", "xmpMM" and "xmpRights" prefixes for use in family 1 group names.
+
+  Tag Name                                      Writable
+  --------                                      --------
+  Advisory                                      string+
+  BaseURL                                       string
+  CreateDate                                    date
+  CreatorTool                                   string
+  Identifier                                    string/+
+  Label                                         string
+  MetadataDate                                  date
+  ModifyDate                                    date
+  Nickname                                      string
+  Rating                                        integer
+  ThumbnailsFormat                              string+
+  ThumbnailsHeight                              string+
+  ThumbnailsImage                               N+
+  ThumbnailsWidth                               string+
+
+=head3 XMP xmpBJ Tags
+
+XMP Basic Job Ticket schema tags.
+
+  Tag Name                                      Writable
+  --------                                      --------
+  JobRefId                                      string+
+  JobRefName                                    string+
+  JobRefUrl                                     string+
+
+=head3 XMP xmpDM Tags
+
+XMP Dynamic Media schema tags.
+
+  Tag Name                                      Writable
+  --------                                      --------
+  AbsPeakAudioFilePath                          string
+  Album                                         string
+  AltTapeName                                   string
+  AltTimecodeTimeFormat                         string
+  AltTimecodeTimeValue                          string
+  Artist                                        string/
+  AudioChannelType                              string
+  AudioCompressor                               string
+  AudioModDate                                  date
+  AudioSampleRate                               integer
+  AudioSampleType                               string
+  BeatSpliceParamsRiseInDecibel                 real
+  BeatSpliceParamsRiseInTimeDuration            string
+  BeatSpliceParamsUseFileBeatsMarker            boolean
+  Composer                                      string
+  ContributedMediaDuration                      string+
+  ContributedMediaManaged                       boolean+
+  ContributedMediaPath                          string+
+  ContributedMediaStartTime                     string+
+  ContributedMediaTrack                         string+
+  ContributedMediaWebStatement                  string+
+  Copyright                                     string/
+  Duration                                      string
+  Engineer                                      string
+  FileDataRate                                  rational
+  Genre                                         string
+  Instrument                                    string
+  IntroTime                                     string
+  Key                                           string
+  LogComment                                    string
+  Loop                                          boolean
+  MarkersComment                                string+
+  MarkersDuration                               string+
+  MarkersLocation                               string+
+  MarkersName                                   string+
+  MarkersStartTime                              string+
+  MarkersTarget                                 string+
+  MarkersType                                   string+
+  MetadataModDate                               date
+  NumberOfBeats                                 real
+  OutCue                                        string
+  ProjectRefPath                                string
+  ProjectRefType                                string
+  PullDown                                      string
+  RelativePeakAudioFilePath                     string
+  RelativeTimestamp                             string
+  ReleaseDate                                   date
+  ResampleParamsQuality                         string
+  ScaleType                                     string
+  Scene                                         string/
+  ShotDate                                      date
+  ShotLocation                                  string
+  ShotName                                      string
+  SpeakerPlacement                              string
+  StartTimecodeTimeFormat                       string
+  StartTimecodeTimeValue                        string
+  StretchMode                                   string
+  TapeName                                      string
+  Tempo                                         real
+  TimeScaleParamsFrameOverlappingPercentage     real
+  TimeScaleParamsFrameSize                      real
+  TimeScaleParamsQuality                        string
+  TimeSignature                                 string
+  TrackNumber                                   integer
+  VideoAlphaMode                                string
+  VideoAlphaPremultipleColorA                   integer
+  VideoAlphaPremultipleColorB                   integer
+  VideoAlphaPremultipleColorBlack               real
+  VideoAlphaPremultipleColorBlue                integer
+  VideoAlphaPremultipleColorCyan                real
+  VideoAlphaPremultipleColorGreen               integer
+  VideoAlphaPremultipleColorL                   real
+  VideoAlphaPremultipleColorMagenta             real
+  VideoAlphaPremultipleColorMode                string
+  VideoAlphaPremultipleColorRed                 integer
+  VideoAlphaPremultipleColorSwatchName          string
+  VideoAlphaPremultipleColorType                string
+  VideoAlphaPremultipleColorYellow              real
+  VideoAlphaUnityIsTransparent                  boolean
+  VideoColorSpace                               string
+  VideoCompressor                               string
+  VideoFieldOrder                               string
+  VideoFrameRate                                string
+  VideoFrameSizeH                               real
+  VideoFrameSizeUnit                            string
+  VideoFrameSizeW                               real
+  VideoModDate                                  date
+  VideoPixelAspectRatio                         rational
+  VideoPixelDepth                               string
+
+=head3 XMP xmpMM Tags
+
+XMP Media Management schema tags.
+
+  Tag Name                                      Writable
+  --------                                      --------
+  DerivedFromDocumentID                         string
+  DerivedFromInstanceID                         string
+  DerivedFromManager                            string
+  DerivedFromManagerVariant                     string
+  DerivedFromManageTo                           string
+  DerivedFromManageUI                           string
+  DerivedFromRenditionClass                     string
+  DerivedFromRenditionParams                    string
+  DerivedFromVersionID                          string
+  DocumentID                                    string
+  HistoryAction                                 string+
+  HistoryInstanceID                             string+
+  HistoryParameters                             string+
+  HistorySoftwareAgent                          string+
+  HistoryWhen                                   date+
+  InstanceID                                    string
+  LastURL                                       string
+  ManagedFromDocumentID                         string
+  ManagedFromInstanceID                         string
+  ManagedFromManager                            string
+  ManagedFromManagerVariant                     string
+  ManagedFromManageTo                           string
+  ManagedFromManageUI                           string
+  ManagedFromRenditionClass                     string
+  ManagedFromRenditionParams                    string
+  ManagedFromVersionID                          string
+  Manager                                       string
+  ManagerVariant                                string
+  ManageTo                                      string
+  ManageUI                                      string
+  PreservedFileName                             string
+  RenditionClass                                string
+  RenditionOfDocumentID                         string
+  RenditionOfInstanceID                         string
+  RenditionOfManager                            string
+  RenditionOfManagerVariant                     string
+  RenditionOfManageTo                           string
+  RenditionOfManageUI                           string
+  RenditionOfRenditionClass                     string
+  RenditionOfRenditionParams                    string
+  RenditionOfVersionID                          string
+  RenditionParams                               string
+  SaveID                                        integer
+  VersionID                                     string
+  VersionsComments                              string+
+  VersionsEventAction                           string+
+  VersionsEventInstanceID                       string+
+  VersionsEventParameters                       string+
+  VersionsEventSoftwareAgent                    string+
+  VersionsEventWhen                             date+
+  VersionsModifier                              string+
+  VersionsModifyDate                            date+
+  VersionsVersion                               string+
+
+=head3 XMP xmpPLUS Tags
+
+XMP Picture Licensing Universal System (PLUS) schema tags.
+
+  Tag Name                                      Writable
+  --------                                      --------
+  CreditLineReq                                 boolean
+  ReuseAllowed                                  boolean
+
+=head3 XMP xmpRights Tags
+
+XMP Rights Management schema tags.
+
+  Tag Name                                      Writable
+  --------                                      --------
+  Certificate                                   string
+  Marked                                        boolean
+  Owner                                         string+
+  UsageTerms                                    lang-alt
+  WebStatement                                  string
+
+=head3 XMP xmpTPg Tags
+
+XMP Paged-Text schema tags.
+
+  Tag Name                                      Writable
+  --------                                      --------
+  ColorantsA                                    integer+
+  ColorantsB                                    integer+
+  ColorantsBlack                                real+
+  ColorantsBlue                                 integer+
+  ColorantsCyan                                 real+
+  ColorantsGreen                                integer+
+  ColorantsL                                    real+
+  ColorantsMagenta                              real+
+  ColorantsMode                                 string+
+  ColorantsRed                                  integer+
+  ColorantsSwatchName                           string+
+  ColorantsType                                 string+
+  ColorantsYellow                               real+
+  FontsChildFontFiles                           string+
+  FontsComposite                                boolean+
+  FontsFontFace                                 string+
+  FontsFontFamily                               string+
+  FontsFontFileName                             string+
+  FontsFontName                                 string+
+  FontsFontType                                 string+
+  FontsVersionString                            string+
+  MaxPageSizeH                                  real
+  MaxPageSizeUnit                               string
+  MaxPageSizeW                                  real
+  NPages                                        integer
+  PlateNames                                    string+
+
+=head2 GPS Tags
+
+These GPS tags are part of the EXIF standard, and are stored in a separate
+IFD within the EXIF information.
+
+ExifTool is very flexible about the input format when writing lat/long
+coordinates, and will accept from 1 to 3 floating point numbers (for decimal
+degrees, degrees and minutes, or degrees, minutes and seconds) separated by
+just about anything, and will format them properly according to the EXIF
+specification.
+
+Some GPS tags have values which are fixed-length strings. For these, the
+indicated string lengths include a null terminator which is added
+automatically by ExifTool.  Remember that the descriptive values are used
+when writing (ie. 'Above Sea Level', not '0') unless the print conversion is
+disabled (with '-n' on the command line, or the PrintConv option in the
+API).
+
+When adding GPS information to an image, it is important to set all of the
+following tags: GPSLatitude, GPSLatitudeRef, GPSLongitude, GPSLongitudeRef,
+GPSAltitude and GPSAltitudeRef.  ExifTool will write the required
+GPSVersionID tag automatically if new a GPS IFD is added to an image.
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+  0x0000   GPSVersionID                         int8u[4]
+  0x0001   GPSLatitudeRef                       string[2]
+  0x0002   GPSLatitude                          rational64u[3]
+  0x0003   GPSLongitudeRef                      string[2]
+  0x0004   GPSLongitude                         rational64u[3]
+  0x0005   GPSAltitudeRef                       int8u
+  0x0006   GPSAltitude                          rational64u
+  0x0007   GPSTimeStamp                         rational64u[3]
+  0x0008   GPSSatellites                        string
+  0x0009   GPSStatus                            string[2]
+  0x000a   GPSMeasureMode                       string[2]
+  0x000b   GPSDOP                               rational64u
+  0x000c   GPSSpeedRef                          string[2]
+  0x000d   GPSSpeed                             rational64u
+  0x000e   GPSTrackRef                          string[2]
+  0x000f   GPSTrack                             rational64u
+  0x0010   GPSImgDirectionRef                   string[2]
+  0x0011   GPSImgDirection                      rational64u
+  0x0012   GPSMapDatum                          string
+  0x0013   GPSDestLatitudeRef                   string[2]
+  0x0014   GPSDestLatitude                      rational64u[3]
+  0x0015   GPSDestLongitudeRef                  string[2]
+  0x0016   GPSDestLongitude                     rational64u[3]
+  0x0017   GPSDestBearingRef                    string[2]
+  0x0018   GPSDestBearing                       rational64u
+  0x0019   GPSDestDistanceRef                   string[2]
+  0x001a   GPSDestDistance                      rational64u
+  0x001b   GPSProcessingMethod                  undef
+  0x001c   GPSAreaInformation                   undef
+  0x001d   GPSDateStamp                         string[11]
+  0x001e   GPSDifferential                      int16u
+
+=head2 GeoTiff Tags
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+  0x0001   GeoTiffVersion                       N
+  0x0400   GTModelType                          N
+  0x0401   GTRasterType                         N
+  0x0402   GTCitation                           N
+  0x0800   GeographicType                       N
+  0x0801   GeogCitation                         N
+  0x0802   GeogGeodeticDatum                    N
+  0x0803   GeogPrimeMeridian                    N
+  0x0804   GeogLinearUnits                      N
+  0x0805   GeogLinearUnitSize                   N
+  0x0806   GeogAngularUnits                     N
+  0x0807   GeogAngularUnitSize                  N
+  0x0808   GeogEllipsoid                        N
+  0x0809   GeogSemiMajorAxis                    N
+  0x080a   GeogSemiMinorAxis                    N
+  0x080b   GeogInvFlattening                    N
+  0x080c   GeogAzimuthUnits                     N
+  0x080d   GeogPrimeMeridianLong                N
+  0x0c00   ProjectedCSType                      N
+  0x0c01   PCSCitation                          N
+  0x0c02   Projection                           N
+  0x0c03   ProjCoordTrans                       N
+  0x0c04   ProjLinearUnits                      N
+  0x0c05   ProjLinearUnitSize                   N
+  0x0c06   ProjStdParallel1                     N
+  0x0c07   ProjStdParallel2                     N
+  0x0c08   ProjNatOriginLong                    N
+  0x0c09   ProjNatOriginLat                     N
+  0x0c0a   ProjFalseEasting                     N
+  0x0c0b   ProjFalseNorthing                    N
+  0x0c0c   ProjFalseOriginLong                  N
+  0x0c0d   ProjFalseOriginLat                   N
+  0x0c0e   ProjFalseOriginEasting               N
+  0x0c0f   ProjFalseOriginNorthing              N
+  0x0c10   ProjCenterLong                       N
+  0x0c11   ProjCenterLat                        N
+  0x0c12   ProjCenterEasting                    N
+  0x0c13   ProjCenterNorthing                   N
+  0x0c14   ProjScaleAtNatOrigin                 N
+  0x0c15   ProjScaleAtCenter                    N
+  0x0c16   ProjAzimuthAngle                     N
+  0x0c17   ProjStraightVertPoleLong             N
+  0x0c18   ProjRectifiedGridAngle               N
+  0x1000   VerticalCSType                       N
+  0x1001   VerticalCitation                     N
+  0x1002   VerticalDatum                        N
+  0x1003   VerticalUnits                        N
+
+=head2 ICC_Profile Tags
+
+ICC profile information is used in many different file types including JPEG,
+TIFF, PDF, PostScript, Photoshop, PNG, MIFF, PICT, QuickTime and some RAW
+formats.  While the tags listed below are not individually writable, the
+entire profile itself can be accessed via the extra 'ICC_Profile' tag, but
+this tag is neither extracted nor written unless specified explicitly.
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+  'A2B0'   AToB0                                N
+  'A2B1'   AToB1                                N
+  'A2B2'   AToB2                                N
+  'B2A0'   BToA0                                N
+  'B2A1'   BToA1                                N
+  'B2A2'   BToA2                                N
+  'Header' ProfileHeader                        ICC_Profile Header
+  'MS00'   WCSProfiles                          N
+  'bTRC'   BlueTRC                              N
+  'bXYZ'   BlueMatrixColumn                     N
+  'bfd '   UCRBG                                N
+  'bkpt'   MediaBlackPoint                      N
+  'calt'   CalibrationDateTime                  N
+  'chad'   ChromaticAdaptation                  N
+  'chrm'   Chromaticity                         ICC_Profile Chromaticity
+  'clro'   ColorantOrder                        N
+  'clrt'   ColorantTable                        ICC_Profile ColorantTable
+  'cprt'   ProfileCopyright                     N
+  'crdi'   CRDInfo                              N
+  'desc'   ProfileDescription                   N
+  'devs'   DeviceSettings                       N
+  'dmdd'   DeviceModelDesc                      N
+  'dmnd'   DeviceMfgDesc                        N
+  'dscm'   ProfileDescriptionML                 N
+  'gTRC'   GreenTRC                             N
+  'gXYZ'   GreenMatrixColumn                    N
+  'gamt'   Gamut                                N
+  'kTRC'   GrayTRC                              N
+  'lumi'   Luminance                            N
+  'meas'   Measurement                          ICC_Profile Measurement
+  'mmod'   MakeAndModel                         N
+  'ncl2'   NamedColor2                          N
+  'ncol'   NamedColor                           N
+  'ndin'   NativeDisplayInfo                    N
+  'pre0'   Preview0                             N
+  'pre1'   Preview1                             N
+  'pre2'   Preview2                             N
+  'ps2i'   PS2RenteringIntent                   N
+  'ps2s'   PostScript2CSA                       N
+  'psd0'   PostScript2CRD0                      N
+  'psd1'   PostScript2CRD1                      N
+  'psd2'   PostScript2CRD2                      N
+  'pseq'   ProfileSequenceDesc                  N
+  'psvm'   PS2CRDVMSize                         N
+  'rTRC'   RedTRC                               N
+  'rXYZ'   RedMatrixColumn                      N
+  'resp'   OutputResponse                       N
+  'scrd'   ScreeningDesc                        N
+  'scrn'   Screening                            N
+  'targ'   CharTarget                           N
+  'tech'   Technology                           N
+  'vcgt'   VideoCardGamma                       N
+  'view'   ViewingConditions                    ICC_Profile ViewingConditions
+  'vued'   ViewingCondDesc                      N
+  'wtpt'   MediaWhitePoint                      N
+
+=head3 ICC_Profile Header Tags
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      4    ProfileCMMType                       N
+      8    ProfileVersion                       N
+     12    ProfileClass                         N
+     16    ColorSpaceData                       N
+     20    ProfileConnectionSpace               N
+     24    ProfileDateTime                      N
+     36    ProfileFileSignature                 N
+     40    PrimaryPlatform                      N
+     44    CMMFlags                             N
+     48    DeviceManufacturer                   N
+     52    DeviceModel                          N
+     56    DeviceAttributes                     N
+     64    RenderingIntent                      N
+     68    ConnectionSpaceIlluminant            N
+     80    ProfileCreator                       N
+     84    ProfileID                            N
+
+=head3 ICC_Profile Chromaticity Tags
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      8    ChromaticityChannels                 N
+     10    ChromaticityColorant                 N
+     12    ChromaticityChannel1                 N
+     20    ChromaticityChannel2                 N
+     28    ChromaticityChannel3                 N
+     36    ChromaticityChannel4                 N
+
+=head3 ICC_Profile ColorantTable Tags
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      8    ColorantCount                        N
+     12    Colorant1Name                        N
+     44    Colorant1Coordinates                 N
+     50    Colorant2Name                        N
+     82    Colorant2Coordinates                 N
+     88    Colorant3Name                        N
+    120    Colorant3Coordinates                 N
+
+=head3 ICC_Profile Measurement Tags
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      8    MeasurementObserver                  N
+     12    MeasurementBacking                   N
+     24    MeasurementGeometry                  N
+     28    MeasurementFlare                     N
+     32    MeasurementIlluminant                N
+
+=head3 ICC_Profile ViewingConditions Tags
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      8    ViewingCondIlluminant                N
+     20    ViewingCondSurround                  N
+     32    ViewingCondIlluminantType            N
+
+=head2 PrintIM Tags
+
+The format of the PrintIM information is known, however no PrintIM tags have
+been decoded.  Use the Unknown (-u) option to extract PrintIM information.
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+  [no tags known]
+
+=head2 Photoshop Tags
+
+Photoshop tags are found in PSD files, as well as inside embedded Photoshop
+information in many other file types (JPEG, TIFF, PDF, PNG to name a few).
+
+Many Photoshop tags are marked as Unknown (indicated by a question mark
+after the tag name) because the information they provide is not very useful
+under normal circumstances (and because Adobe denied my application for
+their file format documentation -- apparently open source software is too
+big a concept for them).  These unknown tags are not extracted unless the
+Unknown (-u) option is used.
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+  0x03e8   Photoshop2Info?                      N
+  0x03e9   MacintoshPrintInfo?                  N
+  0x03ea   XMLData?                             N
+  0x03eb   Photoshop2ColorTable?                N
+  0x03ed   ResolutionInfo                       Photoshop Resolution
+  0x03ee   AlphaChannelsNames                   N
+  0x03ef   DisplayInfo?                         N
+  0x03f0   PStringCaption?                      N
+  0x03f1   BorderInformation?                   N
+  0x03f2   BackgroundColor?                     N
+  0x03f3   PrintFlags?                          N
+  0x03f4   BW_HalftoningInfo?                   N
+  0x03f5   ColorHalftoningInfo?                 N
+  0x03f6   DuotoneHalftoningInfo?               N
+  0x03f7   BW_TransferFunc?                     N
+  0x03f8   ColorTransferFuncs?                  N
+  0x03f9   DuotoneTransferFuncs?                N
+  0x03fa   DuotoneImageInfo?                    N
+  0x03fb   EffectiveBW?                         N
+  0x03fc   ObsoletePhotoshopTag1?               N
+  0x03fd   EPSOptions?                          N
+  0x03fe   QuickMaskInfo?                       N
+  0x03ff   ObsoletePhotoshopTag2?               N
+  0x0400   LayerStateInfo?                      N
+  0x0401   WorkingPath?                         N
+  0x0402   LayersGroupInfo?                     N
+  0x0403   ObsoletePhotoshopTag3?               N
+  0x0404   IPTCData                             IPTC
+  0x0405   RawImageMode?                        N
+  0x0406   JPEG_Quality                         Photoshop JPEG_Quality
+  0x0408   GridGuidesInfo?                      N
+  0x0409   PhotoshopBGRThumbnail                N
+  0x040a   CopyrightFlag                        int8u
+  0x040b   URL                                  string
+  0x040c   PhotoshopThumbnail                   N
+  0x040d   GlobalAngle                          int32u
+  0x040e   ColorSamplersResource?               N
+  0x040f   ICC_Profile                          ICC_Profile
+  0x0410   Watermark?                           N
+  0x0411   ICC_Untagged?                        N
+  0x0412   EffectsVisible?                      N
+  0x0413   SpotHalftone?                        N
+  0x0414   IDsBaseValue?                        N
+  0x0415   UnicodeAlphaNames?                   N
+  0x0416   IndexedColourTableCount?             N
+  0x0417   TransparentIndex?                    N
+  0x0419   GlobalAltitude                       int32u
+  0x041a   Slices?                              N
+  0x041b   WorkflowURL?                         N
+  0x041c   JumpToXPEP?                          N
+  0x041d   AlphaIdentifiers?                    N
+  0x041e   URL_List?                            N
+  0x0421   VersionInfo?                         N
+  0x0422   EXIFInfo                             EXIF
+  0x0424   XMP                                  XMP
+  0x0bb7   ClippingPathName?                    N
+  0x2710   PrintFlagsInfo?                      N
+
+=head3 Photoshop Resolution Tags
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      0    XResolution                          int32u
+      2    DisplayedUnitsX                      int16u
+      4    YResolution                          int32u
+      6    DisplayedUnitsY                      int16u
+
+=head3 Photoshop JPEG_Quality Tags
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      0    PhotoshopQuality                     int16s
+      1    PhotoshopFormat                      N
+      2    ProgressiveScans                     N
+
+=head3 Photoshop Header Tags
+
+This information is found in the PSD file header.
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      6    NumChannels                          N
+      7    ImageHeight                          N
+      9    ImageWidth                           N
+     11    BitDepth                             N
+     12    ColorMode                            N
+
+=head2 Canon Tags
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+  0x0001   CanonCameraSettings                  Canon CameraSettings
+  0x0002   CanonFocalLength                     Canon FocalLength
+  0x0003   CanonFlashInfo?                      N
+  0x0004   CanonShotInfo                        Canon ShotInfo
+  0x0005   CanonPanorama                        Canon Panorama
+  0x0006   CanonImageType                       string
+  0x0007   CanonFirmwareVersion                 string
+  0x0008   FileNumber                           int32u
+  0x0009   OwnerName                            string
+  0x000a   ColorInfoD30                         Canon ColorInfoD30
+  0x000c   SerialNumber                         int32u
+  0x000d   CanonCameraInfo                      Canon CameraInfo
+           CanonCameraInfo1DmkIII               Canon CameraInfo1DmkIII
+           CanonCameraInfo2                     Canon CameraInfo2
+           CanonCameraInfoUnknown32             Canon CameraInfoUnknown32
+           CanonCameraInfoUnknown16             Canon CameraInfoUnknown16
+           CanonCameraInfoUnknown8              Canon CameraInfoUnknown8
+  0x000e   CanonFileLength                      int32u
+  0x000f   CustomFunctions1D                    CanonCustom Functions1D
+           CustomFunctions5D                    CanonCustom Functions5D
+           CustomFunctions10D                   CanonCustom Functions10D
+           CustomFunctions20D                   CanonCustom Functions20D
+           CustomFunctions30D                   CanonCustom Functions30D
+           CustomFunctions350D                  CanonCustom Functions350D
+           CustomFunctions400D                  CanonCustom Functions400D
+           CustomFunctionsD30                   CanonCustom FunctionsD30
+           CustomFunctionsD60                   CanonCustom FunctionsD30
+           CustomFunctionsUnknown               CanonCustom FuncsUnknown
+  0x0010   CanonModelID                         int32u
+  0x0012   CanonAFInfo                          Canon AFInfo
+  0x0015   SerialNumberFormat                   int32u
+  0x001c   DateStampMode                        int16u
+  0x001d   MyColors                             Canon MyColors
+  0x001e   FirmwareRevision                     int32u
+  0x0024   FaceDetect1                          Canon FaceDetect1
+  0x0025   FaceDetect2                          Canon FaceDetect2
+  0x0026   CanonAFInfo2                         Canon AFInfo2
+  0x0083   OriginalDecisionData                 int32u
+  0x0090   CustomFunctions1D                    CanonCustom Functions1D
+  0x0091   PersonalFunctions                    CanonCustom PersonalFuncs
+  0x0092   PersonalFunctionValues               CanonCustom PersonalFuncValues
+  0x0093   CanonFileInfo                        Canon FileInfo
+  0x0094   AFPointsInFocus1D                    N
+  0x0095   LensType                             string
+  0x0096   InternalSerialNumber                 string
+  0x0097   DustRemovalData                      N
+  0x0099   CustomFunctions2                     CanonCustom Functions2
+  0x00a0   ProccessingInfo                      Canon Processing
+  0x00a1   ToneCurveTable                       N
+  0x00a2   SharpnessTable                       N
+  0x00a3   SharpnessFreqTable                   N
+  0x00a4   WhiteBalanceTable                    N
+  0x00a9   ColorBalance                         Canon ColorBalance
+  0x00ae   ColorTemperature                     int16u
+  0x00b0   CanonFlags                           Canon Flags
+  0x00b1   ModifiedInfo                         Canon ModifiedInfo
+  0x00b2   ToneCurveMatching                    N
+  0x00b3   WhiteBalanceMatching                 N
+  0x00b4   ColorSpace                           int16u
+  0x00b6   PreviewImageInfo                     Canon PreviewImageInfo
+  0x00d0   VRDOffset                            int32u*
+  0x00e0   SensorInfo                           Canon SensorInfo
+  0x4001   ColorBalance1                        Canon ColorBalance1
+           ColorBalance2                        Canon ColorBalance2
+           ColorBalance3                        Canon ColorBalance3
+           ColorBalanceUnknown                  N
+  0x4002   UnknownBlock1?                       N
+  0x4003   ColorInfo                            Canon ColorInfo
+  0x4005   UnknownBlock2?                       N
+  0x4008   BlackLevel?                          N
+
+=head3 Canon CameraSettings Tags
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      1    MacroMode                            int16s
+      2    Self-timer                           int16s
+      3    Quality                              int16s
+      4    CanonFlashMode                       int16s
+      5    ContinuousDrive                      int16s
+      7    FocusMode                            int16s
+      9    RecordMode                           int16s
+     10    CanonImageSize                       int16s
+     11    EasyMode                             int16s
+     12    DigitalZoom                          int16s
+     13    Contrast                             int16s
+     14    Saturation                           int16s
+     15    Sharpness                            int16s
+     16    CameraISO                            int16s
+     17    MeteringMode                         int16s
+     18    FocusRange                           int16s
+     19    AFPoint                              int16s
+     20    CanonExposureMode                    int16s
+     22    LensType                             int16s
+     23    LongFocal                            int16u
+     24    ShortFocal                           int16u
+     25    FocalUnits                           int16s
+     26    MaxAperture                          int16s
+     27    MinAperture                          int16s
+     28    FlashActivity                        int16s
+     29    FlashBits                            int16s
+     32    FocusContinuous                      int16s
+     33    AESetting                            int16s
+     34    ImageStabilization                   int16s
+     35    DisplayAperture                      int16s
+     36    ZoomSourceWidth                      int16s
+     37    ZoomTargetWidth                      int16s
+     40    PhotoEffect                          int16s
+     41    ManualFlashOutput                    int16s
+     42    ColorTone                            int16s
+
+=head3 Canon FocalLength Tags
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      0    FocalType                            int16u
+      1    FocalLength                          int16u
+      2    FocalPlaneXSize                      int16u
+      3    FocalPlaneYSize                      int16u
+
+=head3 Canon ShotInfo Tags
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      1    AutoISO                              int16s
+      2    BaseISO                              int16s
+      3    MeasuredEV                           int16s
+      4    TargetAperture                       int16s
+      5    TargetExposureTime                   int16s
+      6    ExposureCompensation                 int16s
+      7    WhiteBalance                         int16s
+      8    SlowShutter                          int16s
+      9    SequenceNumber                       int16s
+     10    OpticalZoomCode                      int16s
+     13    FlashGuideNumber                     int16s
+     14    AFPointsInFocus                      int16s
+     15    FlashExposureComp                    int16s
+     16    AutoExposureBracketing               int16s
+     17    AEBBracketValue                      int16s
+     19    FocusDistanceUpper                   int16s
+     20    FocusDistanceLower                   int16s
+     21    FNumber                              int16s
+     22    ExposureTime                         int16s
+     24    BulbDuration                         int16s
+     26    CameraType                           int16s
+     27    AutoRotate                           int16s
+     28    NDFilter                             int16s
+     29    Self-timer2                          int16s
+     33    FlashOutput                          int16s
+
+=head3 Canon Panorama Tags
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      2    PanoramaFrame                        int16s
+      5    PanoramaDirection                    int16s
+
+=head3 Canon ColorInfoD30 Tags
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      9    ColorTemperature                     int16s
+     10    ColorMatrix                          int16s
+
+=head3 Canon CameraInfo Tags
+
+Information in the "CameraInfo" records is tricky to decode because the
+encodings are very different than in other Canon records (even sometimes
+switching endianness between values within a single camera), plus there is
+considerable variation in format from model to model. The table below is
+common to the 1D, 1DS, 1DmkII, 1DSmkII and 5D, but individual tags may not
+be valid for all these models.
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      3    FNumber                              int8u
+      4    ExposureTime                         int8u
+      6    ISO                                  int8u
+      9    FocalLength                          int16u
+     13    LensType                             int8u
+     17    ShortFocal                           int16u
+     19    LongFocal                            int16u
+     45    FocalType                            int8u
+     56    AFPointsInFocus5D                    undef[2]
+    108    PictureStyle                         int16u
+    164    FirmwareRevision                     string[8]
+    172    ShortOwnerName                       string[16]
+    208    ImageNumber                          int16u
+    232    ContrastStandard                     int8s
+    233    ContrastPortrait                     int8s
+    234    ContrastLandscape                    int8s
+    235    ContrastNeutral                      int8s
+    236    ContrastFaithful                     int8s
+    237    ContrastMonochrome                   int8s
+    238    ContrastUserDef1                     int8s
+    239    ContrastUserDef2                     int8s
+    240    ContrastUserDef3                     int8s
+    241    SharpnessStandard                    int8s
+    242    SharpnessPortrait                    int8s
+    243    SharpnessLandscape                   int8s
+    244    SharpnessNeutral                     int8s
+    245    SharpnessFaithful                    int8s
+    246    SharpnessMonochrome                  int8s
+    247    SharpnessUserDef1                    int8s
+    248    SharpnessUserDef2                    int8s
+    249    SharpnessUserDef3                    int8s
+    250    SaturationStandard                   int8s
+    251    SaturationPortrait                   int8s
+    252    SaturationLandscape                  int8s
+    253    SaturationNeutral                    int8s
+    254    SaturationFaithful                   int8s
+    255    FilterEffectMonochrome               int8s
+    256    SaturationUserDef1                   int8s
+    257    SaturationUserDef2                   int8s
+    258    SaturationUserDef3                   int8s
+    259    ColorToneStandard                    int8s
+    260    ColorTonePortrait                    int8s
+    261    ColorToneLandscape                   int8s
+    262    ColorToneNeutral                     int8s
+    263    ColorToneFaithful                    int8s
+    264    ToningEffectMonochrome               int8s
+    265    ColorToneUserDef1                    int8s
+    266    ColorToneUserDef2                    int8s
+    267    ColorToneUserDef3                    int8s
+    268    UserDef1PictureStyle                 int16u
+    270    UserDef2PictureStyle                 int16u
+    272    UserDef3PictureStyle                 int16u
+    284    TimeStamp                            int32u
+
+=head3 Canon CameraInfo1DmkIII Tags
+
+CameraInfo tags valid for the EOS 1D Mark III.
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      3    FNumber                              int8u
+      4    ExposureTime                         int8u
+      6    ISO                                  int8u
+     29    FocalLength                          int16u
+    134    PictureStyle                         int8u
+    275    ShortFocal                           int16u
+    277    LongFocal                            int16u
+    370    FileNumber                           int32u
+    374    ShutterCount                         int32u
+   1118    TimeStamp                            int32u
+
+=head3 Canon CameraInfo2 Tags
+
+These tags used by a number of newer PowerShot models.
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      0    ISO                                  int32s
+      5    FNumber                              int32s
+      6    ExposureTime                         int32s
+     23    Rotation                             int32s
+
+=head3 Canon CameraInfoUnknown32 Tags
+
+Unknown CameraInfo tags are divided into 3 tables based on format size.
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+  [no tags known]
+
+=head3 Canon CameraInfoUnknown16 Tags
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+  [no tags known]
+
+=head3 Canon CameraInfoUnknown8 Tags
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+  [no tags known]
+
+=head3 Canon AFInfo Tags
+
+Auto-focus information used by many older Canon models.  The values in this
+record are sequential, and some have variable sizes based on the value of
+NumAFPoints (which may be 1,5,7,9,15,45 or 53).  The AFArea coordinates are
+given in a system where the image has dimensions given by AFImageWidth and
+AFImageHeight, and 0,0 is the image center. The direction of the Y axis
+depends on the camera model, with positive Y upwards for EOS models, but
+apparently downwards for PowerShot models.
+
+  Sequence Tag Name                             Writable
+  -------- --------                             --------
+      0    NumAFPoints                          N
+      1    ValidAFPoints                        N
+      2    CanonImageWidth                      N
+      3    CanonImageHeight                     N
+      4    AFImageWidth                         N
+      5    AFImageHeight                        N
+      6    AFAreaWidth                          N
+      7    AFAreaHeight                         N
+      8    AFAreaXPositions                     N
+      9    AFAreaYPositions                     N
+     10    AFPointsInFocus                      N
+     11    PrimaryAFPoint                       N
+           Canon_AFInfo_0x000b?                 N
+     12    PrimaryAFPoint                       N
+
+=head3 Canon MyColors Tags
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      2    MyColorMode                          int16u
+
+=head3 Canon FaceDetect1 Tags
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      2    FacesDetected                        int16u
+      3    FaceDetectFrameWidth                 int16u
+      4    FaceDetectFrameHeight                int16u
+      8    Face0Position                        int16s[2]
+     10    Face1Position                        int16s[2]
+     12    Face2Position                        int16s[2]
+     14    Face3Position                        int16s[2]
+     16    Face4Position                        int16s[2]
+     18    Face5Position                        int16s[2]
+     20    Face6Position                        int16s[2]
+     22    Face7Position                        int16s[2]
+     24    Face8Position                        int16s[2]
+
+=head3 Canon FaceDetect2 Tags
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      2    FacesDetected                        int8u
+
+=head3 Canon AFInfo2 Tags
+
+Newer version of the AFInfo record containing much of the same information
+(and coordinate confusion) as the older version.  In this record, values of
+9 and 45 have been observed for NumAFPoints.
+
+  Sequence Tag Name                             Writable
+  -------- --------                             --------
+      0    AFInfoSize?                          N
+      1    AFMode                               N
+      2    NumAFPoints                          N
+      3    ValidAFPoints                        N
+      4    CanonImageWidth                      N
+      5    CanonImageWidth                      N
+      6    AFImageWidth                         N
+      7    AFImageHeight                        N
+      8    AFAreaWidths                         N
+      9    AFAreaHeights                        N
+     10    AFAreaXPositions                     N
+     11    AFAreaYPositions                     N
+     12    AFPointsInFocus                      N
+     13    AFPointsSelected                     N
+           Canon_AFInfo2_0x000d?                N
+     14    PrimaryAFPoint                       N
+
+=head3 Canon FileInfo Tags
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      1    FileNumber                           int32u
+           ShutterCount                         int32u
+      3    BracketMode                          int16s
+      4    BracketValue                         int16s
+      5    BracketShotNumber                    int16s
+      6    RawJpgQuality                        int16s
+      7    RawJpgSize                           int16s
+      8    NoiseReduction                       int16s
+      9    WBBracketMode                        int16s
+     12    WBBracketValueAB                     int16s
+     13    WBBracketValueGM                     int16s
+     14    FilterEffect                         int16s
+     15    ToningEffect                         int16s
+
+=head3 Canon Processing Tags
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      1    ToneCurve                            int16s
+      2    Sharpness                            int16s
+      3    SharpnessFrequency                   int16s
+      4    SensorRedLevel                       int16s
+      5    SensorBlueLevel                      int16s
+      6    WhiteBalanceRed                      int16s
+      7    WhiteBalanceBlue                     int16s
+      8    WhiteBalance                         int16s
+      9    ColorTemperature                     int16s
+     10    PictureStyle                         int16s
+     11    DigitalGain                          int16s
+     12    WBShiftAB                            int16s
+     13    WBShiftGM                            int16s
+
+=head3 Canon ColorBalance Tags
+
+This table is used by the 10D and 300D.
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      0    WB_RGGBLevelsAuto                    int16u[4]
+      4    WB_RGGBLevelsDaylight                int16u[4]
+      8    WB_RGGBLevelsShade                   int16u[4]
+     12    WB_RGGBLevelsCloudy                  int16u[4]
+     16    WB_RGGBLevelsTungsten                int16u[4]
+     20    WB_RGGBLevelsFluorescent             int16u[4]
+     24    WB_RGGBLevelsFlash                   int16u[4]
+     28    WB_RGGBLevelsCustom                  int16u[4]
+     32    WB_RGGBLevelsKelvin                  int16u[4]
+
+=head3 Canon Flags Tags
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      1    ModifiedParamFlag                    int16s
+
+=head3 Canon ModifiedInfo Tags
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      1    ModifiedToneCurve                    int16s
+      2    ModifiedSharpness                    int16s
+      3    ModifiedSharpnessFreq                int16s
+      4    ModifiedSensorRedLevel               int16s
+      5    ModifiedSensorBlueLevel              int16s
+      6    ModifiedWhiteBalanceRed              int16s
+      7    ModifiedWhiteBalanceBlue             int16s
+      8    ModifiedWhiteBalance                 int16s
+      9    ModifiedColorTemp                    int16s
+     10    ModifiedPictureStyle                 int16s
+     11    ModifiedDigitalGain                  int16s
+
+=head3 Canon PreviewImageInfo Tags
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      1    PreviewQuality                       int32u
+      2    PreviewImageLength                   int32u*
+      3    PreviewImageWidth                    int32u
+      4    PreviewImageHeight                   int32u
+      5    PreviewImageStart                    int32u*
+      6    PreviewFocalPlaneXResolution         rational64s
+      8    PreviewFocalPlaneYResolution         rational64s
+
+=head3 Canon SensorInfo Tags
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      1    SensorWidth                          N
+      2    SensorHeight                         N
+      5    SensorLeftBorder                     N
+      6    SensorTopBorder                      N
+      7    SensorRightBorder                    N
+      8    SensorBottomBorder                   N
+
+=head3 Canon ColorBalance1 Tags
+
+This table is used by the 20D and 350D.
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+     25    WB_RGGBLevelsAsShot                  int16u[4]
+     29    ColorTempAsShot                      int16u
+     30    WB_RGGBLevelsAuto                    int16u[4]
+     34    ColorTempAuto                        int16u
+     35    WB_RGGBLevelsDaylight                int16u[4]
+     39    ColorTempDaylight                    int16u
+     40    WB_RGGBLevelsShade                   int16u[4]
+     44    ColorTempShade                       int16u
+     45    WB_RGGBLevelsCloudy                  int16u[4]
+     49    ColorTempCloudy                      int16u
+     50    WB_RGGBLevelsTungsten                int16u[4]
+     54    ColorTempTungsten                    int16u
+     55    WB_RGGBLevelsFluorescent             int16u[4]
+     59    ColorTempFluorescent                 int16u
+     60    WB_RGGBLevelsFlash                   int16u[4]
+     64    ColorTempFlash                       int16u
+     65    WB_RGGBLevelsCustom1                 int16u[4]
+     69    ColorTempCustom1                     int16u
+     70    WB_RGGBLevelsCustom2                 int16u[4]
+     74    ColorTempCustom2                     int16u
+
+=head3 Canon ColorBalance2 Tags
+
+This table is used by the 1DmkII and 1DSmkII.
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+     24    WB_RGGBLevelsAsShot                  int16u[4]
+     28    ColorTempAsShot                      int16u
+     29    WB_RGGBLevelsUnknown?                int16u[4]
+     33    ColorTempUnknown?                    int16u
+     34    WB_RGGBLevelsAuto                    int16u[4]
+     38    ColorTempAuto                        int16u
+     39    WB_RGGBLevelsDaylight                int16u[4]
+     43    ColorTempDaylight                    int16u
+     44    WB_RGGBLevelsShade                   int16u[4]
+     48    ColorTempShade                       int16u
+     49    WB_RGGBLevelsCloudy                  int16u[4]
+     53    ColorTempCloudy                      int16u
+     54    WB_RGGBLevelsTungsten                int16u[4]
+     58    ColorTempTungsten                    int16u
+     59    WB_RGGBLevelsFluorescent             int16u[4]
+     63    ColorTempFluorescent                 int16u
+     64    WB_RGGBLevelsKelvin                  int16u[4]
+     68    ColorTempKelvin                      int16u
+     69    WB_RGGBLevelsFlash                   int16u[4]
+     73    ColorTempFlash                       int16u
+     74    WB_RGGBLevelsUnknown2?               int16u[4]
+     78    ColorTempUnknown2?                   int16u
+     79    WB_RGGBLevelsUnknown3?               int16u[4]
+     83    ColorTempUnknown3?                   int16u
+     84    WB_RGGBLevelsUnknown4?               int16u[4]
+     88    ColorTempUnknown4?                   int16u
+     89    WB_RGGBLevelsUnknown5?               int16u[4]
+     93    ColorTempUnknown5?                   int16u
+     94    WB_RGGBLevelsUnknown6?               int16u[4]
+     98    ColorTempUnknown6?                   int16u
+     99    WB_RGGBLevelsUnknown7?               int16u[4]
+    103    ColorTempUnknown7?                   int16u
+    104    WB_RGGBLevelsUnknown8?               int16u[4]
+    108    ColorTempUnknown8?                   int16u
+    109    WB_RGGBLevelsUnknown9?               int16u[4]
+    113    ColorTempUnknown9?                   int16u
+    114    WB_RGGBLevelsUnknown10?              int16u[4]
+    118    ColorTempUnknown10?                  int16u
+    119    WB_RGGBLevelsUnknown11?              int16u[4]
+    123    ColorTempUnknown11?                  int16u
+    124    WB_RGGBLevelsUnknown12?              int16u[4]
+    128    ColorTempUnknown12?                  int16u
+    129    WB_RGGBLevelsUnknown13?              int16u[4]
+    133    ColorTempUnknown13?                  int16u
+    134    WB_RGGBLevelsUnknown14?              int16u[4]
+    138    ColorTempUnknown14?                  int16u
+    139    WB_RGGBLevelsUnknown15?              int16u[4]
+    143    ColorTempUnknown15?                  int16u
+    144    WB_RGGBLevelsPC1                     int16u[4]
+    148    ColorTempPC1                         int16u
+    149    WB_RGGBLevelsPC2                     int16u[4]
+    153    ColorTempPC2                         int16u
+    154    WB_RGGBLevelsPC3                     int16u[4]
+    158    ColorTempPC3                         int16u
+    159    WB_RGGBLevelsUnknown16?              int16u[4]
+    163    ColorTempUnknown16?                  int16u
+
+=head3 Canon ColorBalance3 Tags
+
+This table is used by the 1DmkIIN, 5D, 30D and 400D.
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+     63    WB_RGGBLevelsAsShot                  int16u[4]
+     67    ColorTempAsShot                      int16u
+     68    WB_RGGBLevelsAuto                    int16u[4]
+     72    ColorTempAuto                        int16u
+     73    WB_RGGBLevelsMeasured                int16u[4]
+     77    ColorTempMeasured                    int16u
+     78    WB_RGGBLevelsDaylight                int16u[4]
+     82    ColorTempDaylight                    int16u
+     83    WB_RGGBLevelsShade                   int16u[4]
+     87    ColorTempShade                       int16u
+     88    WB_RGGBLevelsCloudy                  int16u[4]
+     92    ColorTempCloudy                      int16u
+     93    WB_RGGBLevelsTungsten                int16u[4]
+     97    ColorTempTungsten                    int16u
+     98    WB_RGGBLevelsFluorescent             int16u[4]
+    102    ColorTempFluorescent                 int16u
+    103    WB_RGGBLevelsKelvin                  int16u[4]
+    107    ColorTempKelvin                      int16u
+    108    WB_RGGBLevelsFlash                   int16u[4]
+    112    ColorTempFlash                       int16u
+    113    WB_RGGBLevelsPC1                     int16u[4]
+    117    ColorTempPC1                         int16u
+    118    WB_RGGBLevelsPC2                     int16u[4]
+    122    ColorTempPC2                         int16u
+    123    WB_RGGBLevelsPC3                     int16u[4]
+    127    ColorTempPC3                         int16u
+    128    WB_RGGBLevelsCustom                  int16u[4]
+    132    ColorTempCustom                      int16u
+
+=head3 Canon ColorInfo Tags
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      1    Saturation                           int16s
+      2    ColorHue                             int16s
+      3    ColorSpace                           int16s
+
+=head2 CanonCustom Tags
+
+=head3 CanonCustom Functions1D Tags
+
+These custom functions are used by all 1D models up to but not including the
+Mark III.
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+      0    FocusingScreen                       int8u
+      1    FinderDisplayDuringExposure          int8u
+      2    ShutterReleaseNoCFCard               int8u
+      3    ISOSpeedExpansion                    int8u
+      4    ShutterAELButton                     int8u
+      5    ManualTv                             int8u
+      6    ExposureLevelIncrements              int8u
+      7    USMLensElectronicMF                  int8u
+      8    LCDPanels                            int8u
+      9    AEBSequence                          int8u
+     10    AFPointIllumination                  int8u
+     11    AFPointSelection                     int8u
+     12    MirrorLockup                         int8u
+     13    AFPointSpotMetering                  int8u
+     14    FillFlashAutoReduction               int8u
+     15    ShutterCurtainSync                   int8u
+     16    SafetyShiftInAvOrTv                  int8u
+     17    AFPointActivationArea                int8u
+     18    SwitchToRegisteredAFPoint            int8u
+     19    LensAFStopButton                     int8u
+     20    AIServoTrackingSensitivity           int8u
+
+=head3 CanonCustom Functions5D Tags
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+      0    FocusingScreen                       int8u
+      1    SetFunctionWhenShooting              int8u
+      2    LongExposureNoiseReduction           int8u
+      3    FlashSyncSpeedAv                     int8u
+      4    Shutter-AELock                       int8u
+      5    AFAssistBeam                         int8u
+      6    ExposureLevelIncrements              int8u
+      7    FlashFiring                          int8u
+      8    ISOExpansion                         int8u
+      9    AEBSequence                          int8u
+     10    SuperimposedDisplay                  int8u
+     11    MenuButtonDisplayPosition            int8u
+     12    MirrorLockup                         int8u
+     13    AFPointSelectionMethod               int8u
+     14    ETTLII                               int8u
+     15    ShutterCurtainSync                   int8u
+     16    SafetyShiftInAvOrTv                  int8u
+     17    AFPointActivationArea                int8u
+     18    LCDDisplayReturnToShoot              int8u
+     19    LensAFStopButton                     int8u
+     20    AddOriginalDecisionData              int8u
+
+=head3 CanonCustom Functions10D Tags
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+      1    SetButtonFunction                    int8u
+      2    ShutterReleaseNoCFCard               int8u
+      3    FlashSyncSpeedAv                     int8u
+      4    Shutter-AELock                       int8u
+      5    AFAssist                             int8u
+      6    ExposureLevelIncrements              int8u
+      7    AFPointRegistration                  int8u
+      8    RawAndJpgRecording                   int8u
+      9    AEBSequence                          int8u
+     10    SuperimposedDisplay                  int8u
+     11    MenuButtonDisplayPosition            int8u
+     12    MirrorLockup                         int8u
+     13    AssistButtonFunction                 int8u
+     14    FillFlashAutoReduction               int8u
+     15    ShutterCurtainSync                   int8u
+     16    SafetyShiftInAvOrTv                  int8u
+     17    LensAFStopButton                     int8u
+
+=head3 CanonCustom Functions20D Tags
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+      0    SetFunctionWhenShooting              int8u
+      1    LongExposureNoiseReduction           int8u
+      2    FlashSyncSpeedAv                     int8u
+      3    Shutter-AELock                       int8u
+      4    AFAssistBeam                         int8u
+      5    ExposureLevelIncrements              int8u
+      6    FlashFiring                          int8u
+      7    ISOExpansion                         int8u
+      8    AEBSequence                          int8u
+      9    SuperimposedDisplay                  int8u
+     10    MenuButtonDisplayPosition            int8u
+     11    MirrorLockup                         int8u
+     12    AFPointSelectionMethod               int8u
+     13    ETTLII                               int8u
+     14    ShutterCurtainSync                   int8u
+     15    SafetyShiftInAvOrTv                  int8u
+     16    LensAFStopButton                     int8u
+     17    AddOriginalDecisionData              int8u
+
+=head3 CanonCustom Functions30D Tags
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+      1    SetFunctionWhenShooting              int8u
+      2    LongExposureNoiseReduction           int8u
+      3    FlashSyncSpeedAv                     int8u
+      4    Shutter-AELock                       int8u
+      5    AFAssistBeam                         int8u
+      6    ExposureLevelIncrements              int8u
+      7    FlashFiring                          int8u
+      8    ISOExpansion                         int8u
+      9    AEBSequence                          int8u
+     10    SuperimposedDisplay                  int8u
+     11    MenuButtonDisplayPosition            int8u
+     12    MirrorLockup                         int8u
+     13    AFPointSelectionMethod               int8u
+     14    ETTLII                               int8u
+     15    ShutterCurtainSync                   int8u
+     16    SafetyShiftInAvOrTv                  int8u
+     17    MagnifiedView                        int8u
+     18    LensAFStopButton                     int8u
+     19    AddOriginalDecisionData              int8u
+
+=head3 CanonCustom Functions350D Tags
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+      0    SetButtonCrossKeysFunc               int8u
+      1    LongExposureNoiseReduction           int8u
+      2    FlashSyncSpeedAv                     int8u
+      3    Shutter-AELock                       int8u
+      4    AFAssistBeam                         int8u
+      5    ExposureLevelIncrements              int8u
+      6    MirrorLockup                         int8u
+      7    ETTLII                               int8u
+      8    ShutterCurtainSync                   int8u
+
+=head3 CanonCustom Functions400D Tags
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+      0    SetButtonCrossKeysFunc               int8u
+      1    LongExposureNoiseReduction           int8u
+      2    FlashSyncSpeedAv                     int8u
+      3    Shutter-AELock                       int8u
+      4    AFAssistBeam                         int8u
+      5    ExposureLevelIncrements              int8u
+      6    MirrorLockup                         int8u
+      7    ETTLII                               int8u
+      8    ShutterCurtainSync                   int8u
+      9    MagnifiedView                        int8u
+     10    LCDDisplayAtPowerOn                  int8u
+
+=head3 CanonCustom FunctionsD30 Tags
+
+Custom functions for the EOS-D30 and D60.
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+      1    LongExposureNoiseReduction           int8u
+      2    Shutter-AELock                       int8u
+      3    MirrorLockup                         int8u
+      4    ExposureLevelIncrements              int8u
+      5    AFAssist                             int8u
+      6    FlashSyncSpeedAv                     int8u
+      7    AEBSequence                          int8u
+      8    ShutterCurtainSync                   int8u
+      9    LensAFStopButton                     int8u
+     10    FillFlashAutoReduction               int8u
+     11    MenuButtonReturn                     int8u
+     12    SetButtonFunction                    int8u
+     13    SensorCleaning                       int8u
+     14    SuperimposedDisplay                  int8u
+     15    ShutterReleaseNoCFCard               int8u
+
+=head3 CanonCustom FuncsUnknown Tags
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+  [no tags known]
+
+=head3 CanonCustom PersonalFuncs Tags
+
+Personal function settings for the EOS-1D.
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      1    PF0CustomFuncRegistration            int16u
+      2    PF1DisableShootingModes              int16u
+      3    PF2DisableMeteringModes              int16u
+      4    PF3ManualExposureMetering            int16u
+      5    PF4ExposureTimeLimits                int16u
+      6    PF5ApertureLimits                    int16u
+      7    PF6PresetShootingModes               int16u
+      8    PF7BracketContinuousShoot            int16u
+      9    PF8SetBracketShots                   int16u
+     10    PF9ChangeBracketSequence             int16u
+     11    PF10RetainProgramShift               int16u
+     14    PF13DrivePriority                    int16u
+     15    PF14DisableFocusSearch               int16u
+     16    PF15DisableAFAssistBeam              int16u
+     17    PF16AutoFocusPointShoot              int16u
+     18    PF17DisableAFPointSel                int16u
+     19    PF18EnableAutoAFPointSel             int16u
+     20    PF19ContinuousShootSpeed             int16u
+     21    PF20LimitContinousShots              int16u
+     22    PF21EnableQuietOperation             int16u
+     24    PF23SetTimerLengths                  int16u
+     25    PF24LightLCDDuringBulb               int16u
+     26    PF25DefaultClearSettings             int16u
+     27    PF26ShortenReleaseLag                int16u
+     28    PF27ReverseDialRotation              int16u
+     29    PF28NoQuickDialExpComp               int16u
+     30    PF29QuickDialSwitchOff               int16u
+     31    PF30EnlargementMode                  int16u
+     32    PF31OriginalDecisionData             int16u
+
+=head3 CanonCustom PersonalFuncValues Tags
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      1    PF1Value                             int16u
+      2    PF2Value                             int16u
+      3    PF3Value                             int16u
+      4    PF4ExposureTimeMin                   int16u
+      5    PF4ExposureTimeMax                   int16u
+      6    PF5ApertureMin                       int16u
+      7    PF5ApertureMax                       int16u
+      8    PF8BracketShots                      int16u
+      9    PF19ShootingSpeedLow                 int16u
+     10    PF19ShootingSpeedHigh                int16u
+     11    PF20MaxContinousShots                int16u
+     12    PF23ShutterButtonTime                int16u
+     13    PF23FELockTime                       int16u
+     14    PF23PostReleaseTime                  int16u
+     15    PF25AEMode                           int16u
+     16    PF25MeteringMode                     int16u
+     17    PF25DriveMode                        int16u
+     18    PF25AFMode                           int16u
+     19    PF25AFPointSel                       int16u
+     20    PF25ImageSize                        int16u
+     21    PF25WBMode                           int16u
+     22    PF25Parameters                       int16u
+     23    PF25ColorMatrix                      int16u
+     24    PF27Value                            int16u
+
+=head3 CanonCustom Functions2 Tags
+
+Beginning with the EOS 1D Mark III, Canon finally created a set of custom
+function tags which are consistent across models.  The EOS 1D Mark III has
+57 custom function tags divided into four main groups: 1. Exposure
+(0x0101-0x010f), 2. Image (0x0201-0x0203), Flash Exposure (0x0304-0x0306)
+and Display (0x0407-0x0409), 3. Auto Focus (0x0501-0x050e) and Drive
+(0x060f-0x0611), and 4. Operation (0x0701-0x070a) and Others
+(0x080b-0x0810).  The table below lists tags used by the EOS 1D Mark III, as
+well as newer values and tags used by the EOS 40D.  It is expected that
+future models will re-use some of these tags (possibly with minor value
+changes), as well as defining additional new tags of their own.
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+  0x0101   ExposureLevelIncrements              int32s
+  0x0102   ISOSpeedIncrements                   int32s
+  0x0103   ISOExpansion                         int32s
+           ISOSpeedRange                        int32s[3]
+  0x0104   AEBAutoCancel                        int32s
+  0x0105   AEBSequence                          int32s
+  0x0106   AEBShotCount                         int32s
+  0x0107   SpotMeterLinkToAFPoint               int32s
+  0x0108   SafetyShift                          int32s
+  0x0109   UsableShootingModes                  int32s[2]
+  0x010a   UsableMeteringModes                  int32s[2]
+  0x010b   ExposureModeInManual                 int32s
+  0x010c   ShutterSpeedRange                    int32s[3]
+  0x010d   ApertureRange                        int32s[3]
+  0x010e   ApplyShootingMeteringMode            int32s[8]
+  0x010f   FlashSyncSpeedAv                     int32s
+  0x0201   LongExposureNoiseReduction           int32s
+  0x0202   HighISONoiseReduction                int32s
+  0x0203   HighlightTonePriority                int32s
+  0x0304   ETTLII                               int32s
+  0x0305   ShutterCurtainSync                   int32s
+  0x0306   FlashFiring                          int32s
+  0x0407   ViewInfoDuringExposure               int32s
+  0x0408   LCDIlluminationDuringBulb            int32s
+  0x0409   InfoButtonWhenShooting               int32s
+  0x0501   USMLensElectronicMF                  int32s
+  0x0502   AIServoTrackingSensitivity           int32s
+  0x0503   AIServoImagePriority                 int32s
+  0x0504   AIServoTrackingMethod                int32s
+  0x0505   LensDriveNoAF                        int32s
+  0x0506   LensAFStopButton                     int32s
+  0x0507   AFMicroadjustment                    int32s[5]
+  0x0508   AFExpansionWithSelectedPoint         int32s
+  0x0509   SelectableAFPoint                    int32s
+  0x050a   SwitchToRegisteredAFPoint            int32s
+  0x050b   AFPointAutoSelection                 int32s
+  0x050c   AFPointDisplayDuringFocus            int32s
+  0x050d   AFPointBrightness                    int32s
+  0x050e   AFAssistBeam                         int32s
+  0x050f   AFPointSelectionMethod               int32s
+  0x0510   SuperimposedDisplay                  int32s
+  0x0511   AFDuringLiveView                     int32s
+  0x060f   MirrorLockup                         int32s
+  0x0610   ContinuousShootingSpeed              int32s[3]
+  0x0611   ContinuousShotLimit                  int32s[2]
+  0x0701   ShutterButtonAFOnButton              int32s
+  0x0702   AFOnAELockButtonSwitch               int32s
+  0x0703   QuickControlDialInMeter              int32s
+  0x0704   SetButtonWhenShooting                int32s
+  0x0705   ManualTv                             int32s
+  0x0706   DialDirectionTvAv                    int32s
+  0x0707   AvSettingWithoutLens                 int32s
+  0x0708   WBMediaImageSizeSetting              int32s
+  0x0709   LockMicrophoneButton                 int32s
+  0x070a   ButtonFunctionControlOff             int32s
+  0x080b   FocusingScreen                       int32s
+  0x080c   TimerLength                          int32s[4]
+  0x080d   ShortReleaseTimeLag                  int32s
+  0x080e   AddAspectRatioInfo                   int32s
+  0x080f   AddOriginalDecisionData              int32s
+  0x0810   LiveViewExposureSimulation           int32s
+
+=head2 Casio Tags
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+  0x0001   RecordingMode                        int16u
+  0x0002   Quality                              int16u
+  0x0003   FocusMode                            int16u
+  0x0004   FlashMode                            int16u
+  0x0005   FlashIntensity                       int16u
+  0x0006   ObjectDistance                       int32u
+  0x0007   WhiteBalance                         int16u
+  0x000a   DigitalZoom                          int32u
+  0x000b   Sharpness                            int16u
+  0x000c   Contrast                             int16u
+  0x000d   Saturation                           int16u
+  0x0014   ISO                                  int16u
+  0x0016   Enhancement                          int16u
+  0x0017   ColorFilter                          int16u
+  0x0018   AFPoint                              int16u
+  0x0019   FlashIntensity                       int16u
+  0x0e00   PrintIM                              PrintIM
+
+=head3 Casio Type2 Tags
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+  0x0002   PreviewImageSize                     int16u[2]
+  0x0003   PreviewImageLength                   int32u*
+  0x0004   PreviewImageStart                    int32u*
+  0x0008   QualityMode                          int16u
+  0x0009   CasioImageSize                       int16u
+  0x000d   FocusMode                            int16u
+  0x0014   ISO                                  int16u
+  0x0019   WhiteBalance                         int16u
+  0x001d   FocalLength                          rational64u
+  0x001f   Saturation                           int16u
+  0x0020   Contrast                             int16u
+  0x0021   Sharpness                            int16u
+  0x0e00   PrintIM                              PrintIM
+  0x2000   PreviewImage                         undef
+  0x2001   FirmwareDate                         string[18]
+  0x2011   WhiteBalanceBias                     int16u[2]
+  0x2012   WhiteBalance                         int16u
+  0x2022   ObjectDistance                       int32u
+  0x2034   FlashDistance                        int16u
+  0x3000   RecordMode                           int16u
+  0x3001   SelfTimer                            int16u
+  0x3002   Quality                              int16u
+  0x3003   FocusMode                            int16u
+  0x3006   HometownCity                         string
+  0x3007   BestShotMode                         int16u
+  0x3008   AutoISO                              int16u
+  0x3011   Sharpness                            undef[2]
+  0x3012   Contrast                             undef[2]
+  0x3013   Saturation                           undef[2]
+  0x3014   ISO                                  int16u
+  0x3015   ColorMode                            int16u
+  0x3016   Enhancement                          int16u
+  0x3017   Filter                               int16u
+  0x301c   SequenceNumber                       int16u
+  0x301d   BracketSequence                      int16u[2]
+  0x3020   ImageStabilization                   int16u
+
+=head2 FujiFilm Tags
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+  0x0000   Version                              undef
+  0x0010   InternalSerialNumber                 string
+  0x1000   Quality                              string
+  0x1001   Sharpness                            int16u
+  0x1002   WhiteBalance                         int16u
+  0x1003   Saturation                           int16u
+  0x1004   Contrast                             int16u
+  0x1005   ColorTemperature                     int16u
+  0x1010   FujiFlashMode                        int16u
+  0x1011   FlashStrength                        rational64s
+  0x1020   Macro                                int16u
+  0x1021   FocusMode                            int16u
+  0x1023   FocusPixel                           int16u[2]
+  0x1030   SlowSync                             int16u
+  0x1031   PictureMode                          int16u
+  0x1100   AutoBracketing                       int16u
+  0x1101   SequenceNumber                       int16u
+  0x1210   ColorMode                            int16u
+  0x1300   BlurWarning                          int16u
+  0x1301   FocusWarning                         int16u
+  0x1302   ExposureWarning                      int16u
+  0x1400   DynamicRange                         int16u
+  0x1401   FilmMode                             int16u
+  0x1402   DynamicRangeSetting                  int16u
+  0x1403   DevelopmentDynamicRange              int16u
+  0x1404   MinFocalLength                       rational64s
+  0x1405   MaxFocalLength                       rational64s
+  0x1406   MaxApertureAtMinFocal                rational64s
+  0x1407   MaxApertureAtMaxFocal                rational64s
+  0x8000   FileSource                           string
+  0x8002   OrderNumber                          int32u
+  0x8003   FrameNumber                          int16u
+
+=head3 FujiFilm RAF Tags
+
+Tags extracted from FujiFilm RAF-format information.
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+  0x0100   RawImageFullSize                     N
+  0x0121   RawImageSize                         N
+  0x0130   FujiLayout                           N
+  0x2ff0   WB_GRGBLevels                        N
+
+=head2 HP Tags
+
+These tables list tags found in the maker notes of some Hewlett-Packard
+camera models.
+
+The first table lists tags found in the EXIF-format maker notes of the
+PhotoSmart 720 (also used by the Vivitar ViviCam 3705, 3705B and 3715).
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+  0x0e00   PrintIM                              PrintIM
+
+=head3 HP Type2 Tags
+
+These tags are used by the PhotoSmart E427.
+
+  Tag ID               Tag Name                 Writable
+  ------               --------                 --------
+  'Lens Shading'       LensShading              N
+  'PreviewImage'       PreviewImage             N
+  'Serial Number'      SerialNumber             N
+
+=head3 HP Type4 Tags
+
+These tags are used by the PhotoSmart M627.
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+     12    MaxAperture                          N
+     16    ExposureTime                         N
+     20    CameraDateTime                       N
+     52    ISO                                  N
+     92    SerialNumber                         N
+
+=head3 HP Type6 Tags
+
+These tags are used by the PhotoSmart M425, M525 and M527.
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+     12    FNumber                              N
+     16    ExposureTime                         N
+     20    CameraDateTime                       N
+     52    ISO                                  N
+     88    SerialNumber                         N
+
+=head2 JVC Tags
+
+JVC EXIF maker note tags.
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+  0x0002   CPUVersions                          N
+  0x0003   Quality                              N
+
+=head3 JVC Text Tags
+
+JVC/Victor text-based maker note tags.
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+  'QTY'    Quality                              N
+  'VER'    MakerNoteVersion                     N
+
+=head2 Kodak Tags
+
+The Kodak maker notes aren't in standard IFD format, and the format varies
+frequently with different models.  Some information has been decoded, but
+much of the Kodak information remains unknown.
+
+The table below contains the most common set of Kodak tags.  The following
+Kodak camera models have been tested and found to use these tags: C360,
+C663, C875, CX6330, CX6445, CX7330, CX7430, CX7525, CX7530, DC4800, DC4900,
+DX3500, DX3600, DX3900, DX4330, DX4530, DX4900, DX6340, DX6440, DX6490,
+DX7440, DX7590, DX7630, EasyShare-One, LS420, LS443, LS633, LS743, LS753,
+V530, V550, V570, V603, V610, V705, Z650, Z700, Z710, Z730, Z740, Z760 and
+Z7590.
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      0    KodakModel                           string[8]
+      9    Quality                              int8u
+     10    BurstMode                            int8u
+     12    KodakImageWidth                      int16u
+     14    KodakImageHeight                     int16u
+     16    YearCreated                          int16u
+     18    MonthDayCreated                      int8u[2]
+     20    TimeCreated                          int8u[4]
+     24    BurstMode2?                          int16u
+     27    ShutterMode                          int8u
+     28    MeteringMode                         int8u
+     29    SequenceNumber                       int8u
+     30    FNumber                              int16u
+     32    ExposureTime                         int32u
+     36    ExposureCompensation                 int16s
+     38    VariousModes?                        int16u
+     40    Distance1?                           int32u
+     44    Distance2?                           int32u
+     48    Distance3?                           int32u
+     52    Distance4?                           int32u
+     56    FocusMode                            int8u
+     58    VariousModes2?                       int16u
+     60    PanoramaMode?                        int16u
+     62    SubjectDistance?                     int16u
+     64    WhiteBalance                         int8u
+     92    FlashMode                            int8u
+     93    FlashFired                           int8u
+     94    ISOSetting                           int16u
+     96    ISO                                  int16u
+     98    TotalZoom                            int16u
+    100    DateTimeStamp                        int16u
+    102    ColorMode                            int16u
+    104    DigitalZoom                          int16u
+    107    Sharpness                            int8s
+
+=head3 Kodak Type2 Tags
+
+These tags are used by the Kodak DC220, DC260, DC265 and DC290,
+Hewlett-Packard PhotoSmart 618, C500 and C912, Pentax EI-200 and EI-2000,
+and Minolta EX1500Z.
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      8    KodakMaker                           string[32]
+     40    KodakModel                           string[32]
+    108    KodakImageWidth                      int32u
+    112    KodakImageHeight                     int32u
+
+=head3 Kodak Type3 Tags
+
+These tags are used by the DC240, DC280, DC3400 and DC5000.
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+     12    YearCreated                          int16u
+     14    MonthDayCreated                      int8u[2]
+     16    TimeCreated                          int8u[4]
+     30    OpticalZoom                          int16u
+     55    Sharpness                            int8s
+     56    ExposureTime                         int32u
+     60    FNumber                              int16u
+     78    ISO                                  int16u
+
+=head3 Kodak Type4 Tags
+
+These tags are used by the DC200 and DC215.
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+     32    OriginalFileName                     string[12]
+
+=head3 Kodak Type5 Tags
+
+These tags are used by the CX4200, CX4210, CX4230, CX4300, CX4310, CX6200
+and CX6230.
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+     20    ExposureTime                         int32u
+     26    WhiteBalance                         int8u
+     28    FNumber                              int16u
+     30    ISO                                  int16u
+     32    OpticalZoom                          int16u
+     34    DigitalZoom                          int16u
+     39    FlashMode                            int8u
+     42    ImageRotated                         int8u
+     43    Macro                                int8u
+
+=head3 Kodak Type6 Tags
+
+These tags are used by the DX3215 and DX3700.
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+     16    ExposureTime                         int32u
+     20    ISOSetting?                          int32u
+     24    FNumber                              int16u
+     26    ISO                                  int16u
+     28    OpticalZoom                          int16u
+     30    DigitalZoom                          int16u
+     34    Flash                                int16u
+
+=head3 Kodak Type7 Tags
+
+The maker notes of models such as the C340, C433, CC533, LS755, V803 and
+V1003 seem to start with the camera serial number.  The C310, C315, C330,
+C643, C743, CD33, CD43, CX7220 and CX7300 maker notes are also decoded using
+this table, although the strings for these cameras don't conform to the
+usual Kodak serial number format, and instead have the model name followed
+by 8 digits.
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      0    SerialNumber                         string[16]
+
+=head3 Kodak IFD Tags
+
+Newer Kodak models such as the P712, P850, P880, Z612 and Z712 use standard
+TIFF IFD format for the maker notes.  There is a large amount of information
+stored in these maker notes (with apparently much duplication), but
+relatively few tags have so far been decoded.
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+  0xfc00   SubIFD0                              Kodak SubIFD0
+  0xfc01   SubIFD1                              Kodak SubIFD1
+  0xfc02   SubIFD2                              Kodak SubIFD2
+  0xfc03   SubIFD3                              Kodak SubIFD3
+  0xfc04   SubIFD4                              Kodak SubIFD4
+  0xfc05   SubIFD5                              Kodak SubIFD5
+  0xff00   CameraInfo                           Kodak CameraInfo
+
+=head3 Kodak SubIFD0 Tags
+
+SubIFD0 through SubIFD5 tags are used by the Z612 and Z712.
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+  0xfa02   SceneMode                            int16u
+  0xfa19   SerialNumber                         string
+  0xfa1d   KodakImageWidth                      int16u
+  0xfa1e   KodakImageHeight                     int16u
+  0xfa20   SensorWidth                          int16u
+  0xfa21   SensorHeight                         int16u
+  0xfa23   FNumber                              int16u
+  0xfa24   ExposureTime                         int32u
+  0xfa2e   ISO                                  int16u
+  0xfa3d   OpticalZoom                          int16u
+  0xfa46   ISO                                  int16u
+  0xfa51   KodakImageWidth                      int16u
+  0xfa52   KodakImageHeight                     int16u
+  0xfa54   ThumbnailWidth                       int16u
+  0xfa55   ThumbnailHeight                      int16u
+  0xfa57   PreviewWidth                         int16u
+  0xfa58   PreviewHeight                        int16u
+
+=head3 Kodak SubIFD1 Tags
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+  0x0027   ISO                                  int16u
+  0x0028   ISO                                  int16u
+
+=head3 Kodak SubIFD2 Tags
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+  0x6002   SceneModeUsed                        int32u
+  0x6006   OpticalZoom                          int32u
+  0x6103   MaxAperture                          int32u
+  0xf002   SceneModeUsed                        int32u
+  0xf006   OpticalZoom                          int32u
+  0xf103   FNumber                              int32u
+  0xf104   ExposureTime                         int32u
+  0xf105   ISO                                  int32u
+
+=head3 Kodak SubIFD3 Tags
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+  0x1000   OpticalZoom                          int16u
+
+=head3 Kodak SubIFD4 Tags
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+  [no tags known]
+
+=head3 Kodak SubIFD5 Tags
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+  0x000f   OpticalZoom                          int16u
+
+=head3 Kodak CameraInfo Tags
+
+These tags are used by the  P712, P850 and P880.
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+  0xf900   SensorWidth                          int16u
+  0xf901   SensorHeight                         int16u
+  0xf902   BayerPattern                         string
+  0xf903   SensorFullWidth                      int16u
+  0xf904   SensorFullHeight                     int16u
+  0xf907   KodakImageWidth                      int16u
+  0xf908   KodakImageHeight                     int16u
+  0xfa00   KodakInfoType                        string
+  0xfa04   SerialNumber                         string
+  0xfd04   FNumber                              int16u
+  0xfd05   ExposureTime                         int32u
+  0xfd06   ISO                                  int16u
+
+=head3 Kodak Unknown Tags
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+  [no tags known]
+
+=head3 Kodak Meta Tags
+
+These tags are found in the APP3 "Meta" segment of JPEG images from Kodak
+cameras such as the DC280, DC3400, DC5000 and MC3.  The structure of this
+segment is similar to the APP1 "Exif" segment, but a different set of tags
+is used.
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+  0xc350   FilmProductCode                      N
+  0xc351   ImageSourceEK                        N
+  0xc352   CaptureConditionsPAR                 N
+  0xc353   CameraOwner                          N
+  0xc354   SerialNumber                         N
+  0xc355   UserSelectGroupTitle                 N
+  0xc356   DealerIDNumber                       N
+  0xc357   CaptureDeviceFID                     N
+  0xc358   EnvelopeNumber                       N
+  0xc359   FrameNumber                          N
+  0xc35a   FilmCategory                         N
+  0xc35b   FilmGencode                          N
+  0xc35c   ModelAndVersion                      N
+  0xc35d   FilmSize                             N
+  0xc35e   SBA_RGBShifts                        N
+  0xc35f   SBAInputImageColorspace              N
+  0xc360   SBAInputImageBitDepth                N
+  0xc361   SBAExposureRecord                    N
+  0xc362   UserAdjSBA_RGBShifts                 N
+  0xc363   ImageRotationStatus                  N
+  0xc364   RollGuidElements                     N
+  0xc365   MetadataNumber                       N
+  0xc366   EditTagArray                         N
+  0xc367   Magnification                        N
+  0xc36c   NativeXResolution                    N
+  0xc36d   NativeYResolution                    N
+  0xc36e   KodakEffectsIFD                      Kodak SpecialEffects
+  0xc36f   KodakBordersIFD                      Kodak Borders
+  0xc37a   NativeResolutionUnit                 N
+  0xc418   SourceImageDirectory                 N
+  0xc419   SourceImageFileName                  N
+  0xc41a   SourceImageVolumeName                N
+  0xc46c   PrintQuality                         N
+  0xc46e   ImagePrintStatus                     N
+
+=head3 Kodak SpecialEffects Tags
+
+The Kodak SpecialEffects and Borders tags are found in sub-IFD's within the
+Kodak JPEG APP3 "Meta" segment.
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+  0x0000   DigitalEffectsVersion                N
+  0x0001   DigitalEffectsName                   N
+  0x0002   DigitalEffectsType                   N
+
+=head3 Kodak Borders Tags
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+  0x0000   BordersVersion                       N
+  0x0001   BorderName                           N
+  0x0002   BorderID                             N
+  0x0003   BorderLocation                       N
+  0x0004   BorderType                           N
+  0x0008   WatermarkType                        N
+
+=head2 Leaf Tags
+
+These tags are found in .MOS images from Leaf digital camera backs as
+written by Creo Leaf Capture.  They exist within the Leaf-specific directory
+structure of EXIF tag 0x8606. The tables below list observed Leaf tags,
+however ExifTool will extract any tags found in the Leaf directories even if
+they don't appear in these tables.
+
+  Tag ID                          Tag Name      Writable
+  ------                          --------      --------
+  'JPEG_preview_data'             PreviewImage  undef
+  'JPEG_preview_info'             PreviewInfo   N
+  'PDA_histogram_data'            PDAHistogram  N
+  'back_serial_number'            BackSerial    N
+  'camera_profile'                CameraProfile Leaf CameraProfile
+  'icc_camera_profile'            ICC_Profile   ICC_Profile
+  'icc_camera_to_tone_matrix'     ToneMatrix    N
+  'icc_camera_to_tone_space_flow' ToneSpaceFlow N
+  'icc_rgb_ws_profile'            RGB_Profile   ICC_Profile
+  'image_offset'                  ImageOffset   N
+  'pattern_ratation_angle'        PatternAngle  N
+
+=head3 Leaf CameraProfile Tags
+
+All B<Tag ID>'s in the following table have a leading 'CamProf_' which
+has been removed.
+
+  Tag ID               Tag Name                 Writable
+  ------               --------                 --------
+  'back_type'          CameraBackType           N
+  'capture_profile'    CaptureProfile           Leaf CaptureProfile
+  'image_profile'      ImageProfile             Leaf ImageProfile
+  'name'               CameraName               N
+  'type'               CameraType               N
+  'version'            CameraProfileVersion     N
+
+=head3 Leaf CaptureProfile Tags
+
+All B<Tag ID>'s in the following table have a leading 'CaptProf_' which
+has been removed.
+
+  Tag ID                 Tag Name               Writable
+  ------                 --------               --------
+  'CCD_rect'             CCDRect                N
+  'CCD_valid_rect'       CCDValidRect           N
+  'CCD_video_rect'       CCDVideoRect           N
+  'back_type'            CaptProfBackType       N
+  'center_dark_rect'     CenterDarkRect         N
+  'color_averages'       ColorAverages          N
+  'color_matrix'         ColorMatrix            N
+  'dark_correction_type' DarkCorrectionType     N
+  'image_bounds'         ImageBounds            N
+  'image_fields'         ImageFields            N
+  'image_offset'         ImageOffset            N
+  'left_dark_rect'       LeftDarkRect           N
+  'luminance_consts'     LuminanceConsts        N
+  'mosaic_pattern'       MosaicPattern          N
+  'name'                 CaptProfName           N
+  'number_of_planes'     NumberOfPlanes         N
+  'raw_data_rotation'    RawDataRotation        N
+  'reconstruction_type'  ReconstructionType     N
+  'right_dark_rect'      RightDarkRect          N
+  'serial_number'        CaptureSerial          N
+  'type'                 CaptProfType           N
+  'version'              CaptProfVersion        N
+  'xy_offset_info'       XYOffsetInfo           N
+
+=head3 Leaf ImageProfile Tags
+
+All B<Tag ID>'s in the following table have a leading 'ImgProf_' which
+has been removed.
+
+  Tag ID               Tag Name                 Writable
+  ------               --------                 --------
+  'back_type'          ImgProfBackType          N
+  'image_status'       ImageStatus              N
+  'name'               ImgProfName              N
+  'rotation_angle'     RotationAngle            N
+  'shoot_setup'        ShootSetup               Leaf ShootSetup
+  'type'               ImgProfType              N
+  'version'            ImgProfVersion           N
+
+=head3 Leaf ShootSetup Tags
+
+All B<Tag ID>'s in the following table have a leading 'ShootObj_' which
+has been removed.
+
+  Tag ID               Tag Name                 Writable
+  ------               --------                 --------
+  'back_type'          ShootObjBackType         N
+  'camera_setup'       CameraSetup              Leaf CameraSetup
+  'capture_setup'      CaptureSetup             Leaf CaptureSetup
+  'color_setup'        ColorSetup               Leaf ColorSetup
+  'look_header'        LookHeader               Leaf LookHeader
+  'name'               ShootObjName             N
+  'save_setup'         SaveSetup                Leaf SaveSetup
+  'type'               ShootObjType             N
+  'version'            ShootObjVersion          N
+
+=head3 Leaf CameraSetup Tags
+
+All B<Tag ID>'s in the following table have a leading 'CameraObj_' which
+has been removed.
+
+  Tag ID               Tag Name                 Writable
+  ------               --------                 --------
+  'ISO_speed'          ISOSpeed                 N
+  'back_type'          CameraObjBackType        N
+  'camera_type'        CameraType               N
+  'lens_ID'            LensID                   N
+  'lens_type'          LensType                 N
+  'name'               CameraObjName            N
+  'strobe'             Strobe                   N
+  'type'               CameraObjType            N
+  'version'            CameraObjVersion         N
+
+=head3 Leaf CaptureSetup Tags
+
+All B<Tag ID>'s in the following table have a leading 'CaptureObj_' which
+has been removed.
+
+  Tag ID               Tag Name                 Writable
+  ------               --------                 --------
+  'Multi_quality'      MultiQuality             N
+  'back_type'          CaptureObjBackType       N
+  'name'               CaptureObjName           N
+  'neutals'            Neutals                  Leaf Neutrals
+  'selection'          Selection                Leaf Selection
+  'sharpness'          Sharpness                Leaf Sharpness
+  'single_quality'     SingleQuality            N
+  'tone_curve'         ToneCurve                Leaf ToneCurve
+  'type'               CaptureObjType           N
+  'version'            CaptureObjVersion        N
+
+=head3 Leaf Neutrals Tags
+
+All B<Tag ID>'s in the following table have a leading 'NeutObj_' which
+has been removed.
+
+  Tag ID                 Tag Name               Writable
+  ------                 --------               --------
+  'back_type'            NeutObjBackType        N
+  'color_casts'          ColorCasts             N
+  'highlight_end_points' HighlightEndPoints     N
+  'name'                 NeutObjName            N
+  'neutrals'             Neutrals               N
+  'shadow_end_points'    ShadowEndPoints        N
+  'type'                 NeutObjType            N
+  'version'              NeutObjVersion         N
+
+=head3 Leaf Selection Tags
+
+All B<Tag ID>'s in the following table have a leading 'SelObj_' which
+has been removed.
+
+  Tag ID               Tag Name                 Writable
+  ------               --------                 --------
+  'back_type'          SelObjBackType           N
+  'locks'              Locks                    N
+  'name'               SelObjName               N
+  'orientation'        Orientation              N
+  'rect'               Rect                     N
+  'resolution'         Resolution               N
+  'scale'              Scale                    N
+  'type'               SelObjType               N
+  'version'            SelObjVersion            N
+
+=head3 Leaf Sharpness Tags
+
+All B<Tag ID>'s in the following table have a leading 'SharpObj_' which
+has been removed.
+
+  Tag ID               Tag Name                 Writable
+  ------               --------                 --------
+  'back_type'          SharpObjBackType         N
+  'data_len'           DataLen                  N
+  'name'               SharpObjName             N
+  'sharp_info'         SharpInfo                N
+  'sharp_method'       SharpMethod              N
+  'type'               SharpObjType             N
+  'version'            SharpObjVersion          N
+
+=head3 Leaf ToneCurve Tags
+
+All B<Tag ID>'s in the following table have a leading 'ToneObj_' which
+has been removed.
+
+  Tag ID               Tag Name                 Writable
+  ------               --------                 --------
+  'back_type'          ToneObjBackType          N
+  'gamma'              Gamma                    N
+  'name'               ToneObjName              N
+  'npts'               Npts                     N
+  'tones'              Tones                    N
+  'type'               ToneObjType              N
+  'version'            ToneObjVersion           N
+
+=head3 Leaf ColorSetup Tags
+
+All B<Tag ID>'s in the following table have a leading 'ColorObj_' which
+has been removed.
+
+  Tag ID               Tag Name                 Writable
+  ------               --------                 --------
+  'back_type'          ColorObjBackType         N
+  'color_mode'         ColorMode                N
+  'color_type'         ColorType                N
+  'has_ICC'            HasICC                   N
+  'input_profile'      InputProfile             N
+  'name'               ColorObjName             N
+  'output_profile'     OutputProfile            N
+  'type'               ColorObjType             N
+  'version'            ColorObjVersion          N
+
+=head3 Leaf LookHeader Tags
+
+All B<Tag ID>'s in the following table have a leading 'LookHead_' which
+has been removed.
+
+  Tag ID               Tag Name                 Writable
+  ------               --------                 --------
+  'back_type'          LookHeadBackType         N
+  'name'               LookHeadName             N
+  'type'               LookHeadType             N
+  'version'            LookHeadVersion          N
+
+=head3 Leaf SaveSetup Tags
+
+All B<Tag ID>'s in the following table have a leading 'SaveObj_' which
+has been removed.
+
+  Tag ID                  Tag Name              Writable
+  ------                  --------              --------
+  'back_type'             SaveObjBackType       N
+  'leaf_auto_active'      LeafAutoActive        N
+  'leaf_auto_base_name'   LeafAutoBaseName      N
+  'leaf_hot_folder'       LeafHotFolder         N
+  'leaf_open_proc_HDR'    LeafOpenProcHDR       N
+  'leaf_output_file_type' LeafOutputFileType    N
+  'leaf_save_selection'   LeafSaveSelection     N
+  'name'                  SaveObjName           N
+  'std_auto_active'       StdAutoActive         N
+  'std_base_name'         StdBaseName           N
+  'std_hot_folder'        StdHotFolder          N
+  'std_open_in_photoshop' StdOpenInPhotoshop    N
+  'std_output_bit_depth'  StdOutputBitDepth     N
+  'std_output_color_mode' StdOutputColorMode    N
+  'std_output_file_type'  StdOutputFileType     N
+  'std_oxygen'            StdOxygen             N
+  'std_save_selection'    StdSaveSelection      N
+  'std_scaled_output'     StdScaledOutput       N
+  'std_sharpen_output'    StdSharpenOutput      N
+  'type'                  SaveObjType           N
+  'version'               SaveObjVersion        N
+
+=head3 Leaf SubIFD Tags
+
+Leaf also writes a TIFF-format sub-IFD inside IFD0 of a MOS image.  No tags
+in this sub-IFD are currently known, except for tag 0x8606 which really
+shouldn't be here anyway (so it doesn't appear in the table below) because
+it duplicates a reference to the same data of tag 0x8606 in IFD0.
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+  [no tags known]
+
+=head2 Minolta Tags
+
+These tags are used by Minolta and Konica/Minolta cameras.  Minolta doesn't
+make things easy for decoders because the meaning of some tags and the
+location where some information is stored is different for different camera
+models.  (Take MinoltaQuality for example, which may be located in 5
+different places.)
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+  0x0000   MakerNoteVersion                     undef[4]
+  0x0001   MinoltaCameraSettingsOld             Minolta CameraSettings
+  0x0003   MinoltaCameraSettings                Minolta CameraSettings
+  0x0004   MinoltaCameraSettings7D              Minolta CameraSettings7D
+  0x0018   ImageStabilization                   N
+  0x0040   CompressedImageSize                  int32u
+  0x0081   PreviewImage                         undef
+  0x0088   PreviewImageStart                    int32u*
+  0x0089   PreviewImageLength                   int32u*
+  0x0100   SceneMode                            Y
+  0x0101   ColorMode                            int32u
+  0x0102   MinoltaQuality                       int32u
+  0x0103   MinoltaQuality                       int32u
+           MinoltaImageSize                     int32u
+  0x0104   FlashExposureComp                    rational64s
+  0x0107   ImageStabilization                   int32u
+  0x010a   ZoneMatching                         int32u
+  0x010b   ColorTemperature                     int32u
+  0x010c   LensID                               int32u
+  0x0113   ImageStabilization                   int32u
+  0x0114   MinoltaCameraSettings5D              Minolta CameraSettings5D
+           MinoltaCameraSettingsA100            Minolta CameraSettingsA100
+  0x0e00   PrintIM                              PrintIM
+  0x0f00   MinoltaCameraSettings2               N
+
+=head3 Minolta CameraSettings Tags
+
+There is some variability in CameraSettings information between different
+models (and sometimes even between different firmware versions), so this
+information may not be as reliable as it should be.  Because of this, tags
+in the following tables are set to lower priority to prevent them from
+superceeding the values of same-named tags in other locations when duplicate
+tags are disabled.
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      1    ExposureMode                         int32u
+      2    FlashMode                            int32u
+      3    WhiteBalance                         int32u~
+      4    MinoltaImageSize                     int32u
+      5    MinoltaQuality                       int32u
+      6    DriveMode                            int32u
+      7    MeteringMode                         int32u
+      8    ISO                                  int32u
+      9    ExposureTime                         int32u
+     10    FNumber                              int32u
+     11    MacroMode                            int32u
+     12    DigitalZoom                          int32u
+     13    ExposureCompensation                 int32u
+     14    BracketStep                          int32u
+     16    IntervalLength                       int32u
+     17    IntervalNumber                       int32u
+     18    FocalLength                          int32u
+     19    FocusDistance                        int32u
+     20    FlashFired                           int32u
+     21    MinoltaDate                          int32u
+     22    MinoltaTime                          int32u
+     23    MaxAperture                          int32u
+     26    FileNumberMemory                     int32u
+     27    LastFileNumber                       int32u
+     28    ColorBalanceRed                      int32u
+     29    ColorBalanceGreen                    int32u
+     30    ColorBalanceBlue                     int32u
+     31    Saturation                           int32u
+     32    Contrast                             int32u
+     33    Sharpness                            int32u
+     34    SubjectProgram                       int32u
+     35    FlashExposureComp                    int32u
+     36    ISOSetting                           int32u
+     37    MinoltaModelID                       int32u
+     38    IntervalMode                         int32u
+     39    FolderName                           int32u
+     40    ColorMode                            int32u
+     41    ColorFilter                          int32u
+     42    BWFilter                             int32u
+     43    InternalFlash                        int32u
+     44    Brightness                           int32u
+     45    SpotFocusPointX                      int32u
+     46    SpotFocusPointY                      int32u
+     47    WideFocusZone                        int32u
+     48    FocusMode                            int32u
+     49    FocusArea                            int32u
+     50    DECPosition                          int32u
+     51    ColorProfile                         int32u
+     52    DataImprint                          int32u
+     63    FlashMetering                        int32u
+
+=head3 Minolta CameraSettings7D Tags
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      0    ExposureMode                         int16u
+      2    MinoltaImageSize                     int16u
+      3    MinoltaQuality                       int16u
+      4    WhiteBalance                         int16u
+     14    FocusMode                            int16u
+     16    AFPoints                             int16u
+     21    Flash                                int16u
+     28    ISOSetting                           int16u
+     30    ExposureCompensation                 int16s
+     37    ColorSpace                           int16u
+     38    Sharpness                            int16u
+     39    Contrast                             int16u
+     40    Saturation                           int16u
+     45    FreeMemoryCardImages                 int16u
+     63    ColorTemperature                     int16s
+     70    Rotation                             int16u
+     71    FNumber                              int16u
+     72    ExposureTime                         int16u
+     74    FreeMemoryCardImages                 int16u
+     94    ImageNumber                          int16u
+     96    NoiseReduction                       int16u
+     98    ImageNumber2                         int16u
+    113    ImageStabilization                   int16u
+    117    ZoneMatchingOn                       int16u
+
+=head3 Minolta CameraSettings5D Tags
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+     10    ExposureMode                         int16u
+     12    MinoltaImageSize                     int16u
+     13    MinoltaQuality                       int16u
+     14    WhiteBalance                         int16u
+     31    Flash                                int16u
+     37    MeteringMode                         int16u
+     38    ISOSetting                           int16u
+     48    Sharpness                            int16u
+     49    Contrast                             int16u
+     50    Saturation                           int16u
+     53    ExposureTime                         int16u
+     54    FNumber                              int16u
+     55    FreeMemoryCardImages                 int16u
+     73    ColorTemperature                     int16s
+     80    Rotation                             int16u
+     83    ExposureCompensation                 int16u
+     84    FreeMemoryCardImages                 int16u
+    110    ColorTemperature                     int16s
+    174    ImageNumber                          int16u
+    176    NoiseReduction                       int16u
+    189    ImageStabilization                   int16u
+
+=head3 Minolta CameraSettingsA100 Tags
+
+Camera settings information for the Sony DSLR-A100.
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      0    ExposureMode                         int16u
+      1    ExposureCompensation                 int16u
+     11    WhiteBalance                         int16u
+     22    ColorMode                            int16u
+     24    Sharpness                            int16u
+     25    Contrast                             int16u
+     26    Saturation                           int16u
+     30    DriveMode                            int16u
+     63    NoiseReduction                       int16u
+     90    Rotation                             int16u
+     94    ColorTemperature                     int16u
+    135    ImageStabilization                   int32u[0.5]
+
+=head2 Nikon Tags
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+  0x0001   MakerNoteVersion                     undef[4]
+  0x0002   ISO                                  int16u[2]
+  0x0003   ColorMode                            string
+  0x0004   Quality                              string
+  0x0005   WhiteBalance                         string
+  0x0006   Sharpness                            string
+  0x0007   FocusMode                            string
+  0x0008   FlashSetting                         string
+  0x0009   FlashType                            string[13]
+  0x000b   WhiteBalanceFineTune                 int16s
+  0x000c   ColorBalance1                        rational64u[4]
+  0x000d   ProgramShift                         undef[4]
+  0x000e   ExposureDifference                   undef[4]
+  0x000f   ISOSelection                         string
+  0x0010   DataDump                             N
+  0x0011   NikonPreview                         Nikon PreviewImage
+  0x0012   FlashExposureComp                    undef[4]
+  0x0013   ISOSetting                           int16u[2]
+  0x0016   ImageBoundary                        int16u[4]
+  0x0018   FlashExposureBracketValue            undef[4]
+  0x0019   ExposureBracketValue                 rational64s
+  0x001a   ImageProcessing                      string
+  0x001b   CropHiSpeed                          int16u[7]~
+  0x001d   SerialNumber                         N
+  0x001e   ColorSpace                           int16u
+  0x0020   ImageAuthentication                  int8u
+  0x0080   ImageAdjustment                      string
+  0x0081   ToneComp                             string
+  0x0082   AuxiliaryLens                        string
+  0x0083   LensType                             int8u
+  0x0084   Lens                                 rational64u[4]
+  0x0085   ManualFocusDistance                  rational64u
+  0x0086   DigitalZoom                          rational64u
+  0x0087   FlashMode                            int8u
+  0x0088   AFInfo                               Nikon AFInfo
+  0x0089   ShootingMode                         int16u~
+  0x008a   AutoBracketRelease                   int16u
+  0x008b   LensFStops                           undef[4]
+  0x008c   NEFCurve1                            N
+  0x008d   ColorHue                             string
+  0x008f   SceneMode                            string
+  0x0090   LightSource                          string
+  0x0091   ShotInfo                             Nikon ShotInfo
+           ShotInfo?                            Nikon ShotInfo
+  0x0092   HueAdjustment                        int16s
+  0x0094   Saturation                           int16s
+  0x0095   NoiseReduction                       string
+  0x0096   NEFCurve2                            N
+  0x0097   ColorBalance0100                     Nikon ColorBalance1
+           ColorBalance0102                     Nikon ColorBalance2
+           ColorBalance0103                     Nikon ColorBalance3
+           ColorBalance0205                     Nikon ColorBalance2
+           ColorBalance02                       Nikon ColorBalance2
+           ColorBalanceUnknown                  N
+  0x0098   LensData0100                         Nikon LensData00
+           LensData0101                         Nikon LensData01
+           LensData0201                         Nikon LensData01
+           LensDataUnknown                      N
+  0x0099   RawImageCenter                       int16u[2]
+  0x009a   SensorPixelSize                      rational64u[2]
+  0x00a0   SerialNumber                         string
+  0x00a2   ImageDataSize                        int32u
+  0x00a5   ImageCount                           int32u
+  0x00a6   DeletedImageCount                    int32u
+  0x00a7   ShutterCount                         N
+  0x00a9   ImageOptimization                    string[16]
+  0x00aa   Saturation                           string
+  0x00ab   VariProgram                          string
+  0x00ac   ImageStabilization                   string
+  0x00ad   AFResponse                           string
+  0x00b0   MultiExposure                        Nikon MultiExposure
+  0x00b1   HighISONoiseReduction                int16u
+  0x0e00   PrintIM                              PrintIM
+  0x0e01   NikonCaptureData                     NikonCapture
+  0x0e09   NikonCaptureVersion                  string
+  0x0e0e   NikonCaptureOffsets                  Nikon CaptureOffsets
+  0x0e10   NikonScanIFD                         Nikon Scan
+
+=head3 Nikon PreviewImage Tags
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+  0x0103   Compression                          N
+  0x011a   XResolution                          N
+  0x011b   YResolution                          N
+  0x0128   ResolutionUnit                       N
+  0x0201   PreviewImageStart                    int32u*
+  0x0202   PreviewImageLength                   int32u*
+  0x0213   YCbCrPositioning                     N
+
+=head3 Nikon AFInfo Tags
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      0    AFAreaMode                           int8u
+      1    AFPoint                              int8u
+      2    AFPointsInFocus                      int16u
+
+=head3 Nikon ShotInfo Tags
+
+This information is encrypted for ShotInfoVersion 02xx, and some tags are
+only valid for specific versions.
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      0    ShotInfoVersion                      N
+    102    VR_0x66?                             N
+    106    ShutterCount                         N
+    110    DeletedImageCount                    N
+    117    VibrationReduction                   N
+    130    VibrationReduction                   N
+    343    ShutterCount                         N
+    430    VibrationReduction                   N
+    586    ShutterCount                         N
+    590    VibrationReduction                   N
+
+=head3 Nikon ColorBalance1 Tags
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      0    WB_RBGGLevels                        int16u[4]!
+
+=head3 Nikon ColorBalance2 Tags
+
+This information is encrypted for most camera models, and if encrypted is
+not currently writable by exiftool.
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      0    WB_RGGBLevels                        int16u[4]!
+
+=head3 Nikon ColorBalance3 Tags
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      0    WB_RGBGLevels                        int16u[4]!
+
+=head3 Nikon LensData00 Tags
+
+This structure is used by the D100, and D1X with firmware version 1.1.
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      0    LensDataVersion                      undef[4]
+      6    LensIDNumber                         int8u
+      7    LensFStops                           int8u
+      8    MinFocalLength                       int8u
+      9    MaxFocalLength                       int8u
+     10    MaxApertureAtMinFocal                int8u
+     11    MaxApertureAtMaxFocal                int8u
+     12    MCUVersion                           int8u
+
+=head3 Nikon LensData01 Tags
+
+Nikon encrypts the LensData information below if LensDataVersion is 0201 or
+higher, but  the decryption algorithm is known so the information can be
+extracted.  It isn't yet writable, however, because the encryption adds
+complications which make writing more difficult.
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      0    LensDataVersion                      N
+      4    ExitPupilPosition                    N
+      5    AFAperture                           N
+      8    FocusPosition                        N
+      9    FocusDistance                        N
+     10    FocalLength                          N
+     11    LensIDNumber                         N
+     12    LensFStops                           N
+     13    MinFocalLength                       N
+     14    MaxFocalLength                       N
+     15    MaxApertureAtMinFocal                N
+     16    MaxApertureAtMaxFocal                N
+     17    MCUVersion                           N
+     18    EffectiveMaxAperture                 N
+
+=head3 Nikon MultiExposure Tags
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      0    MultiExposureVersion                 string[4]
+      1    MultiExposureMode                    int32u
+      2    MultiExposureShots                   int32u
+      3    MultiExposureAutoGain                int32u
+
+=head3 Nikon CaptureOffsets Tags
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+  0x0001   IFD0_Offset                          N
+  0x0002   PreviewIFD_Offset                    N
+  0x0003   SubIFD_Offset                        N
+
+=head3 Nikon Scan Tags
+
+This information is written by the Nikon Scan software.
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+  0x0002   FilmType                             string
+  0x0040   MultiSample                          string
+  0x0041   BitDepth                             int16u
+  0x0050   MasterGain                           rational64s
+  0x0051   ColorGain                            rational64s[3]
+  0x0100   DigitalICE                           string
+  0x0110   ROCInfo                              Nikon ROC
+  0x0120   GEMInfo                              Nikon GEM
+
+=head3 Nikon ROC Tags
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      0    DigitalROC                           int32u
+
+=head3 Nikon GEM Tags
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      0    DigitalGEM                           int32u
+
+=head3 Nikon Type2 Tags
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+  0x0003   Quality                              Y
+  0x0004   ColorMode                            Y
+  0x0005   ImageAdjustment                      Y
+  0x0006   CCDSensitivity                       Y
+  0x0007   WhiteBalance                         Y
+  0x0008   Focus                                Y
+  0x000a   DigitalZoom                          Y
+  0x000b   Converter                            Y
+
+=head3 Nikon MOV Tags
+
+This information is found in Nikon QT video images, and is very similar to
+information found in Pentax MOV videos.
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      0    Make                                 N
+     24    Model                                N
+     38    ExposureTime                         N
+     42    FNumber                              N
+     50    ExposureCompensation                 N
+     68    WhiteBalance                         N
+     72    FocalLength                          N
+    175    Software                             N
+    223    ISO                                  N
+
+=head2 NikonCapture Tags
+
+This information is written by the Nikon Capture software in tag 0x0e01 of
+the maker notes of NEF images.
+
+  Tag ID       Tag Name                         Writable
+  ------       --------                         --------
+  0x8ae85e     LCHEditor                        int8u
+  0xc89224b    ColorAberrationControl           int8u
+  0x2175eb78   D-LightingHQ                     int8u
+  0x2fc08431   StraightenAngle                  double
+  0x374233e0   CropData                         NikonCapture CropData
+  0x3cfc73c6   RedEyeData                       NikonCapture RedEyeData
+  0x5f0e7d23   ColorBooster                     int8u
+  0x6a6e36b6   D-LightingHQSelected             int8u
+  0x753dcbc0   NoiseReduction                   int8u
+  0x76a43200   UnsharpMask                      int8u
+  0x76a43201   Curves                           int8u
+  0x76a43202   ColorBalanceAdj                  int8u
+  0x76a43203   AdvancedRaw                      int8u
+  0x76a43204   WhiteBalanceAdj                  int8u
+  0x76a43205   VignetteControl                  int8u
+  0x76a43206   FlipHorizontal                   int8u
+  0x76a43207   Rotation                         int16u
+  0x84589434   BrightnessData                   NikonCapture Brightness
+  0x890ff591   D-LightingHQData                 NikonCapture DLightingHQ
+  0x926f13e0   NoiseReductionData               NikonCapture NoiseReduction
+  0x9ef5f6e0   IPTCData                         IPTC
+  0xab5eca5e   PhotoEffects                     int8u
+  0xac6bd5c0   VignetteControlIntensity         int16s
+  0xb0384e1e   PhotoEffectsData                 NikonCapture PhotoEffects
+  0xb999a36f   ColorBoostData                   NikonCapture ColorBoost
+  0xbf3c6c20   WBAdjData                        NikonCapture WBAdjData
+  0xce5554aa   D-LightingHS                     int8u
+  0xe37b4337   D-LightingHSData                 NikonCapture DLightingHS
+  0xe42b5161   UnsharpData                      NikonCapture UnsharpData
+  0xfe443a45   ImageDustOff                     int8u
+
+=head3 NikonCapture CropData Tags
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+     30    CropLeft                             double
+     38    CropTop                              double
+     46    CropRight                            double
+     54    CropBottom                           double
+    142    OutputWidthInches                    double
+    150    OutputHeightInches                   double
+    158    ScaledResolution                     double
+    174    SourceResolution                     double
+    182    OutputResolution                     double
+    190    CropOutputScale                      double
+    198    OutputWidth                          double
+    206    OutputHeight                         double
+    214    OutputPixels                         double
+
+=head3 NikonCapture RedEyeData Tags
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      0    RedEyeCorrection                     int8u
+
+=head3 NikonCapture Brightness Tags
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      0    BrightnessAdj                        double
+      8    EnhanceDarkTones                     int8u
+
+=head3 NikonCapture DLightingHQ Tags
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      0    D-LightingHSShadow                   int32u
+      1    D-LightingHSHighlight                int32u
+      2    D-LightingHSColorBoost               int32u
+
+=head3 NikonCapture NoiseReduction Tags
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      4    EdgeNoiseReduction                   int8u
+      5    ColorMoireReduction                  int8u
+      9    NoiseReductionIntensity              int32u
+     13    NoiseReductionSharpness              int32u
+     17    NoiseReductionMethod                 int16u
+
+=head3 NikonCapture PhotoEffects Tags
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      0    PhotoEffectsType                     int8u
+      4    PhotoEffectsRed                      int16s
+      6    PhotoEffectsGreen                    int16s
+      8    PhotoEffectsBlue                     int16s
+
+=head3 NikonCapture ColorBoost Tags
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      0    ColorBoostType                       int8u
+      1    ColorBoostLevel                      int32u
+
+=head3 NikonCapture WBAdjData Tags
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      0    WBAdjRedBalance                      double
+      8    WBAdjBlueBalance                     double
+     16    WBAdjMode                            int8u
+     21    WBAdjLighting                        int8u
+     24    WBAdjTemperature                     int16u
+
+=head3 NikonCapture DLightingHS Tags
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      0    D-LightingHSAdjustment               int32u
+      1    D-LightingHSColorBoost               int32u
+
+=head3 NikonCapture UnsharpData Tags
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      0    UnsharpCount                         int8u
+     19    Unsharp1Color                        int16u
+     23    Unsharp1Intensity                    int16u
+     25    Unsharp1HaloWidth                    int16u
+     27    Unsharp1Threshold                    int8u
+     46    Unsharp2Color                        int16u
+     50    Unsharp2Intensity                    int16u
+     52    Unsharp2HaloWidth                    int16u
+     54    Unsharp2Threshold                    int8u
+     73    Unsharp3Color                        int16u
+     77    Unsharp3Intensity                    int16u
+     79    Unsharp3HaloWidth                    int16u
+     81    Unsharp3Threshold                    int8u
+    100    Unsharp4Color                        int16u
+    104    Unsharp4Intensity                    int16u
+    106    Unsharp4HaloWidth                    int16u
+    108    Unsharp4Threshold                    int8u
+
+=head2 Olympus Tags
+
+Tags 0x0000 through 0x0103 are used by some older Olympus cameras, and are
+the same as Konica/Minolta tags.  The Olympus tags are also used for Epson
+and Agfa cameras.
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+  0x0000   MakerNoteVersion                     undef
+  0x0001   MinoltaCameraSettingsOld             Minolta CameraSettings
+  0x0003   MinoltaCameraSettings                Minolta CameraSettings
+  0x0040   CompressedImageSize                  int32u
+  0x0081   PreviewImageData                     N
+  0x0088   PreviewImageStart                    N
+  0x0089   PreviewImageLength                   N
+  0x0100   ThumbnailImage                       undef
+  0x0101   ColorMode                            int16u
+  0x0102   MinoltaQuality                       int16u
+  0x0103   MinoltaQuality                       int16u
+  0x0104   BodyFirmwareVersion                  string
+  0x0200   SpecialMode                          int32u[3]~
+  0x0201   Quality                              int16u~
+  0x0202   Macro                                int16u
+  0x0203   BWMode                               int16u
+  0x0204   DigitalZoom                          rational64u
+  0x0205   FocalPlaneDiagonal                   rational64u
+  0x0206   LensDistortionParams                 int16s[6]
+  0x0207   CameraType                           string
+  0x0208   TextInfo                             Olympus TextInfo
+  0x0209   CameraID                             string
+  0x020b   EpsonImageWidth                      int16u
+  0x020c   EpsonImageHeight                     int16u
+  0x020d   EpsonSoftware                        string
+  0x0280   PreviewImage                         int8u
+  0x0300   PreCaptureFrames                     int16u
+  0x0301   WhiteBoard                           int16u
+  0x0302   OneTouchWB                           int16u
+  0x0303   WhiteBalanceBracket                  int16u
+  0x0304   WhiteBalanceBias                     int16u
+  0x0403   SceneMode                            int16u
+  0x0404   SerialNumber                         string
+  0x0405   Firmware                             string
+  0x0e00   PrintIM                              PrintIM
+  0x0f00   DataDump                             N
+  0x0f01   DataDump2                            N
+  0x1000   ShutterSpeedValue                    rational64s
+  0x1001   ISOValue                             rational64s
+  0x1002   ApertureValue                        rational64s
+  0x1003   BrightnessValue                      rational64s
+  0x1004   FlashMode                            int16u
+  0x1005   FlashDevice                          int16u
+  0x1006   ExposureCompensation                 rational64s
+  0x1007   SensorTemperature                    int16s
+  0x1008   LensTemperature                      int16s
+  0x1009   LightCondition                       int16u
+  0x100a   FocusRange                           int16u
+  0x100b   FocusMode                            int16u
+  0x100c   ManualFocusDistance                  rational64u
+  0x100d   ZoomStepCount                        int16u
+  0x100e   FocusStepCount                       int16u
+  0x100f   Sharpness                            int16u
+  0x1010   FlashChargeLevel                     int16u
+  0x1011   ColorMatrix                          int16u[9]
+  0x1012   BlackLevel                           int16u[4]
+  0x1013   ColorTemperatureBG?                  int16u
+  0x1014   ColorTemperatureRG?                  int16u
+  0x1015   WBMode                               int16u[2]
+  0x1017   RedBalance                           int16u[2]
+  0x1018   BlueBalance                          int16u[2]
+  0x1019   ColorMatrixNumber                    int16u
+  0x101a   SerialNumber                         string
+  0x101b   ExternalFlashAE1_0?                  int32u
+  0x101c   ExternalFlashAE2_0?                  int32u
+  0x101d   InternalFlashAE1_0?                  int32u
+  0x101e   InternalFlashAE2_0?                  int32u
+  0x101f   ExternalFlashAE1?                    int32u
+  0x1020   ExternalFlashAE2?                    int32u
+  0x1021   InternalFlashAE1?                    int32u
+  0x1022   InternalFlashAE2?                    int32u
+  0x1023   FlashExposureComp                    rational64s
+  0x1024   InternalFlashTable                   int16u
+  0x1025   ExternalFlashGValue                  rational64s
+  0x1026   ExternalFlashBounce                  int16u
+  0x1027   ExternalFlashZoom                    int16u
+  0x1028   ExternalFlashMode                    int16u
+  0x1029   Contrast                             int16u
+  0x102a   SharpnessFactor                      int16u
+  0x102b   ColorControl                         int16u[6]
+  0x102c   ValidBits                            int16u[2]
+  0x102d   CoringFilter                         int16u
+  0x102e   OlympusImageWidth                    int32u
+  0x102f   OlympusImageHeight                   int32u
+  0x1030   SceneDetect                          int16u
+  0x1031   SceneArea?                           int32u[8]
+  0x1033   SceneDetectArea?                     int32u[720]
+  0x1034   CompressionRatio                     rational64u
+  0x1035   PreviewImageValid                    int32u
+  0x1036   PreviewImageStart                    int32u*
+  0x1037   PreviewImageLength                   int32u*
+  0x1038   AFResult                             int16u
+  0x1039   CCDScanMode                          int16u
+  0x103a   NoiseReduction                       int16u
+  0x103b   InfinityLensStep                     int16u
+  0x103c   NearLensStep                         int16u
+  0x103d   LightValueCenter                     rational64s
+  0x103e   LightValuePeriphery                  rational64s
+  0x103f   FieldCount?                          int16u
+  0x2010   Equipment                            Olympus Equipment
+           Equipment2                           Olympus Equipment
+  0x2020   CameraSettings                       Olympus CameraSettings
+           CameraSettings2                      Olympus CameraSettings
+  0x2030   RawDevelopment                       Olympus RawDevelopment
+           RawDevelopment2                      Olympus RawDevelopment
+  0x2031   RawDev2                              Olympus RawDevelopment2
+           RawDev2_2                            Olympus RawDevelopment2
+  0x2040   ImageProcessing                      Olympus ImageProcessing
+           ImageProcessing2                     Olympus ImageProcessing
+  0x2050   FocusInfo                            Olympus FocusInfo
+           FocusInfo2                           Olympus FocusInfo
+           CameraParameters                     undef
+  0x2100   Olympus2100                          Olympus FE
+  0x2200   Olympus2200                          Olympus FE
+  0x2300   Olympus2300                          Olympus FE
+  0x2400   Olympus2400                          Olympus FE
+  0x2500   Olympus2500                          Olympus FE
+  0x2600   Olympus2600                          Olympus FE
+  0x2700   Olympus2700                          Olympus FE
+  0x2800   Olympus2800                          Olympus FE
+  0x2900   Olympus2900                          Olympus FE
+  0x3000   RawInfo                              Olympus RawInfo
+           RawInfo2                             Olympus RawInfo
+
+=head3 Olympus TextInfo Tags
+
+This information is in text format (similar to APP12 information, but with
+spaces instead of linefeeds).  Below are tags which have been observed, but
+any information found here will be extracted, even if the tag is not listed.
+
+  Tag ID               Tag Name                 Writable
+  ------               --------                 --------
+  'Resolution'         Resolution               N
+  'Type'               CameraType               N
+
+=head3 Olympus Equipment Tags
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+  0x0000   EquipmentVersion                     undef[4]
+  0x0100   CameraType2                          string[6]
+  0x0101   SerialNumber                         string[32]
+  0x0102   InternalSerialNumber                 string[32]
+  0x0103   FocalPlaneDiagonal                   rational64u
+  0x0104   BodyFirmwareVersion                  int32u
+  0x0201   LensType                             int8u[6]~
+  0x0202   LensSerialNumber                     string[32]
+  0x0204   LensFirmwareVersion                  int32u
+  0x0205   MaxApertureAtMinFocal                int16u
+  0x0206   MaxApertureAtMaxFocal                int16u
+  0x0207   MinFocalLength                       int16u
+  0x0208   MaxFocalLength                       int16u
+  0x020a   MaxApertureAtCurrentFocal            int16u
+  0x020b   LensProperties                       int16u
+  0x0301   Extender                             int8u[6]~
+  0x0302   ExtenderSerialNumber                 string[32]
+  0x0303   ExtenderModel                        string
+  0x0304   ExtenderFirmwareVersion              int32u
+  0x1000   FlashType                            int16u
+  0x1001   FlashModel                           int16u
+  0x1002   FlashFirmwareVersion                 int32u
+  0x1003   FlashSerialNumber                    string[32]
+
+=head3 Olympus CameraSettings Tags
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+  0x0000   CameraSettingsVersion                undef[4]
+  0x0100   PreviewImageValid                    int32u
+  0x0101   PreviewImageStart                    int32u*
+  0x0102   PreviewImageLength                   int32u*
+  0x0200   ExposureMode                         int16u
+  0x0201   AELock                               int16u
+  0x0202   MeteringMode                         int16u
+  0x0300   MacroMode                            int16u
+  0x0301   FocusMode                            int16u
+  0x0302   FocusProcess                         int16u
+  0x0303   AFSearch                             int16u
+  0x0304   AFAreas                              int32u[64]~
+  0x0400   FlashMode                            int16u
+  0x0401   FlashExposureCompensation            rational64s
+  0x0500   WhiteBalance2                        int16u
+  0x0501   WhiteBalanceTemperature              int16u
+  0x0502   WhiteBalanceBracket                  int16s
+  0x0503   CustomSaturation                     int16s[3]~
+  0x0504   ModifiedSaturation                   int16u
+  0x0505   ContrastSetting                      int16s[3]
+  0x0506   SharpnessSetting                     int16s[3]
+  0x0507   ColorSpace                           int16u
+  0x0509   SceneMode                            int16u
+  0x050a   NoiseReduction                       int16u
+  0x050b   DistortionCorrection                 int16u
+  0x050c   ShadingCompensation                  int16u
+  0x050d   CompressionFactor                    rational64u
+  0x050f   Gradation                            int16s[3]
+  0x0520   PictureMode                          int16u
+  0x0521   PictureModeSaturation                int16s[3]
+  0x0522   PictureModeHue?                      int16s
+  0x0523   PictureModeContrast                  int16s[3]
+  0x0524   PictureModeSharpness                 int16s[3]
+  0x0525   PictureModeBWFilter                  int16s
+  0x0526   PictureModeTone                      int16s
+  0x0527   NoiseFilter                          int16s[3]
+  0x0600   DriveMode                            int16u[n]~
+  0x0601   PanoramaMode                         int16u~
+  0x0603   ImageQuality2                        int16u
+  0x0901   ManometerReading                     int32s[2]
+
+=head3 Olympus RawDevelopment Tags
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+  0x0000   RawDevVersion                        undef[4]
+  0x0100   RawDevExposureBiasValue              rational64s
+  0x0101   RawDevWhiteBalanceValue              int16u
+  0x0102   RawDevWBFineAdjustment               int16s
+  0x0103   RawDevGrayPoint                      int16u[3]
+  0x0104   RawDevSaturationEmphasis             int16s[3]
+  0x0105   RawDevMemoryColorEmphasis            int16u
+  0x0106   RawDevContrastValue                  int16s[3]
+  0x0107   RawDevSharpnessValue                 int16s[3]
+  0x0108   RawDevColorSpace                     int16u
+  0x0109   RawDevEngine                         int16u
+  0x010a   RawDevNoiseReduction                 int16u
+  0x010b   RawDevEditStatus                     int16u
+  0x010c   RawDevSettings                       int16u
+
+=head3 Olympus RawDevelopment2 Tags
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+  0x0000   RawDevVersion                        undef[4]
+  0x0100   RawDevExposureBiasValue              rational64s
+  0x0101   RawDevWhiteBalance                   int16u
+  0x0102   RawDevWhiteBalanceValue              int16u
+  0x0103   RawDevWBFineAdjustment               int16s
+  0x0104   RawDevGrayPoint                      int16u[3]
+  0x0105   RawDevContrastValue                  int16s[3]
+  0x0106   RawDevSharpnessValue                 int16s[3]
+  0x0107   RawDevSaturationEmphasis             int16s[3]
+  0x0108   RawDevMemoryColorEmphasis            int16u
+  0x0109   RawDevColorSpace                     int16u
+  0x010a   RawDevNoiseReduction                 int16u
+  0x010b   RawDevEngine                         int16u
+  0x010c   RawDevPictureMode                    int16u
+  0x010d   RawDevPMSaturation                   int16s[3]
+  0x010e   RawDevPMContrast                     int16s[3]
+  0x010f   RawDevPMSharpness                    int16s[3]
+  0x0110   RawDevPM_BWFilter                    int16u
+  0x0111   RawDevPMPictureTone                  int16u
+  0x0112   RawDevGradation                      int16s[3]
+
+=head3 Olympus ImageProcessing Tags
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+  0x0000   ImageProcessingVersion               undef[4]
+  0x0100   WB_RBLevels                          int16u[2]
+  0x0200   ColorMatrix                          int16u[9]
+  0x0300   SmoothingParameter1                  int16u
+  0x0310   SmoothingParameter2                  int16u
+  0x0600   SmoothingThresholds                  int16u[4]
+  0x0610   SmoothingThreshold2                  int16u
+  0x0611   ValidBits                            int16u[2]
+  0x0614   OlympusImageWidth2                   int32u
+  0x0615   OlympusImageHeight2                  int32u
+  0x1010   NoiseReduction2                      int16u
+  0x1011   DistortionCorrection2                int16u
+  0x1012   ShadingCompensation2                 int16u
+  0x1103   UnknownBlock?                        undef
+
+=head3 Olympus FocusInfo Tags
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+  0x0000   FocusInfoVersion                     undef[4]
+  0x0209   AutoFocus?                           int16u
+  0x0300   ZoomStepCount                        int16u
+  0x0301   FocusStepCount                       int16u
+  0x0305   FocusDistance                        rational64u
+  0x0308   AFPoint                              int16u
+  0x1201   ExternalFlash                        int16u[2]
+  0x1203   ExternalFlashGuideNumber?            rational64s
+  0x1204   ExternalFlashBounce                  int16u
+  0x1205   ExternalFlashZoom                    rational64u
+  0x1208   InternalFlash                        int16u[n]
+  0x1209   ManualFlash                          int16u[2]~
+  0x1500   SensorTemperature                    int16s
+  0x1600   ImageStabilization                   undef~
+
+=head3 Olympus FE Tags
+
+Some FE models write a large number of tags here, but most of this
+information remains unknown.
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+  0x0100   BodyFirmwareVersion                  string
+
+=head3 Olympus RawInfo Tags
+
+These tags are found only in ORF images of some models (ie. C8080WZ).
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+  0x0000   RawInfoVersion                       undef[4]
+  0x0100   WB_RBLevelsUsed                      int16[2]
+  0x0110   WB_RBLevelsAuto                      int16[2]
+  0x0120   WB_RBLevelsShade                     int16[2]
+  0x0121   WB_RBLevelsCloudy                    int16[2]
+  0x0122   WB_RBLevelsFineWeather               int16[2]
+  0x0123   WB_RBLevelsTungsten                  int16[2]
+  0x0124   WB_RBLevelsEveningSunlight           int16[2]
+  0x0130   WB_RBLevelsDaylightFluor             int16[2]
+  0x0131   WB_RBLevelsDayWhiteFluor             int16[2]
+  0x0132   WB_RBLevelsCoolWhiteFluor            int16[2]
+  0x0133   WB_RBLevelsWhiteFluorescent          int16[2]
+  0x0200   ColorMatrix2                         int16u[9]
+  0x0310   CoringFilter                         int16u
+  0x0311   CoringValues                         int16u[11]
+  0x0600   BlackLevel2                          int16u[4]
+  0x0601   YCbCrCoefficients                    N
+  0x0611   ValidPixelDepth                      int16u[2]
+  0x0612   StartOffsetX                         int16u
+  0x0613   StartOffsetY                         int16u
+  0x0614   FinalImageWidth                      int32u
+  0x0615   FinalImageHeight                     int32u
+  0x1000   LightSource                          int16u
+  0x1001   WhiteBalanceComp                     int16s[3]
+  0x1010   SaturationSetting                    int16s[3]
+  0x1011   HueSetting                           int16s[3]
+  0x1012   ContrastSetting                      int16s[3]
+  0x1013   SharpnessSetting                     int16s[3]
+  0x2000   CMExposureCompensation               rational64s
+  0x2001   CMWhiteBalance                       int16u
+  0x2002   CMWhiteBalanceComp                   int16s
+  0x2010   CMWhiteBalanceGrayPoint              int16u[3]
+  0x2020   CMSaturation                         int16s[3]
+  0x2021   CMHue                                int16s[3]
+  0x2022   CMContrast                           int16s[3]
+  0x2023   CMSharpness                          int16s[3]
+
+=head2 Panasonic Tags
+
+Panasonic tags are also used for Leica cameras.
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+  0x0001   ImageQuality                         int16u
+  0x0002   FirmwareVersion                      undef
+  0x0003   WhiteBalance                         int16u
+  0x0007   FocusMode                            int16u
+  0x000f   AFMode                               int8u[2]
+  0x001a   ImageStabilization                   int16u
+  0x001c   MacroMode                            int16u
+  0x001f   ShootingMode                         int16u
+  0x0020   Audio                                int16u
+  0x0021   DataDump                             N
+  0x0023   WhiteBalanceBias                     int16s
+  0x0024   FlashBias                            int16s
+  0x0025   InternalSerialNumber                 undef[16]
+  0x0026   PanasonicExifVersion                 undef
+  0x0028   ColorEffect                          int16u
+  0x0029   TimeSincePowerOn                     int32u
+  0x002a   BurstMode                            int16u
+  0x002b   SequenceNumber                       int32u
+  0x002c   Contrast                             int16u
+  0x002d   NoiseReduction                       int16u
+  0x002e   SelfTimer                            int16u
+  0x0030   Rotation                             int16u
+  0x0032   ColorMode                            int16u
+  0x0033   BabyAge                              string
+  0x0034   OpticalZoomMode                      int16u
+  0x0035   ConversionLens                       int16u
+  0x0036   TravelDay                            int16u
+  0x003a   WorldTimeLocation                    int16u
+  0x003c   ProgramISO                           int16u
+  0x0042   FilmMode                             int16u
+  0x0046   WBAdjustAB                           int16u
+  0x0047   WBAdjustGM                           int16u
+  0x0051   LensType                             string
+  0x0052   LensSerialNumber                     string
+  0x0053   AccessoryType                        string
+  0x0e00   PrintIM                              PrintIM
+  0x8000   MakerNoteVersion                     undef
+  0x8001   SceneMode                            int16u
+  0x8004   WBRedLevel                           int16u
+  0x8005   WBGreenLevel                         int16u
+  0x8006   WBBlueLevel                          int16u
+  0x8010   BabyAge                              string
+
+=head3 Panasonic Type2 Tags
+
+This type of maker notes is used by models such as the NV-DS65, PV-D2002,
+PV-DC3000, PV-DV203, PV-DV401, PV-DV702, PV-L2001, PV-SD4090, PV-SD5000 and
+iPalm.
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      0    MakerNoteType                        N
+      3    Gain                                 N
+
+=head3 Panasonic Raw Tags
+
+These tags are found in IFD0 of Panasonic RAW images.
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+  0x0001   PanasonicRawVersion                  undef
+  0x0002   SensorWidth                          N
+  0x0003   SensorHeight                         N
+  0x0004   SensorTopBorder                      N
+  0x0005   SensorLeftBorder                     N
+  0x0006   ImageHeight                          N
+  0x0007   ImageWidth                           N
+  0x0011   RedBalance                           int16u
+  0x0012   BlueBalance                          int16u
+  0x0017   ISO                                  int16u
+  0x0024   WBRedLevel                           int16u
+  0x0025   WBGreenLevel                         int16u
+  0x0026   WBBlueLevel                          int16u
+  0x010f   Make                                 string
+  0x0110   Model                                string
+  0x0111   StripOffsets                         N
+  0x0112   Orientation                          int16u
+  0x0116   RowsPerStrip                         N
+  0x0117   StripByteCounts                      N
+  0x8769   ExifOffset                           EXIF
+  0x8825   GPSInfo                              GPS
+
+=head2 Pentax Tags
+
+The Pentax tags are also used in Asahi cameras.
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+  0x0000   PentaxVersion                        int8u[4]
+  0x0001   PentaxModelType                      int16u
+  0x0002   PreviewImageSize                     int16u[2]~
+  0x0003   PreviewImageLength                   int32u*
+  0x0004   PreviewImageStart                    int32u*
+  0x0005   PentaxModelID                        int32u
+  0x0006   Date                                 undef[4]
+  0x0007   Time                                 undef[3]
+  0x0008   Quality                              int16u
+  0x0009   PentaxImageSize                      int16u
+  0x000b   PictureMode                          int16u
+  0x000c   FlashMode                            N
+  0x000d   FocusMode                            int16u
+  0x000e   AFPointSelected                      int16u
+  0x000f   AFPointsInFocus                      int16u
+  0x0010   FocusPosition                        int16u
+  0x0012   ExposureTime                         int32u
+  0x0013   FNumber                              int16u
+  0x0014   ISO                                  int16u
+  0x0016   ExposureCompensation                 int16u
+  0x0017   MeteringMode                         int16u
+  0x0018   AutoBracketing                       int16u[n]
+  0x0019   WhiteBalance                         int16u
+  0x001a   WhiteBalanceMode                     int16u
+  0x001b   BlueBalance                          int16u
+  0x001c   RedBalance                           int16u
+  0x001d   FocalLength                          int32u
+  0x001e   DigitalZoom                          int16u
+  0x001f   Saturation                           int16u
+  0x0020   Contrast                             int16u
+  0x0021   Sharpness                            int16u
+  0x0022   WorldTimeLocation                    int16u
+  0x0023   HometownCity                         int16u
+  0x0024   DestinationCity                      int16u
+  0x0025   HometownDST                          int16u
+  0x0026   DestinationDST                       int16u
+  0x0029   FrameNumber                          int32u
+  0x0032   ImageProcessing                      undef[4]
+  0x0033   PictureMode                          int8u[3]
+  0x0034   DriveMode                            int8u[4]
+  0x0037   ColorSpace                           int16u
+  0x0038   ImageAreaOffset                      int16u[2]
+  0x0039   RawImageSize                         int16u[2]~
+  0x003c   AFPointsInFocus                      N
+  0x003f   LensType                             int8u[2]
+  0x0047   CameraTemperature                    int8s
+  0x0049   NoiseReduction                       int16u
+  0x004d   FlashExposureComp                    int32s
+  0x004f   ImageTone                            int16u
+  0x005c   ShakeReductionInfo                   Pentax SRInfo
+  0x005d   ShutterCount                         undef[4]
+  0x0200   BlackPoint                           int16u[4]
+  0x0201   WhitePoint                           int16u[4]
+  0x0206   AEInfo                               Pentax AEInfo
+  0x0207   LensInfo                             Pentax LensInfo
+  0x0208   FlashInfo                            Pentax FlashInfo
+  0x0209   AEMeteringSegments                   int8u[16]
+  0x020a   FlashADump?                          Y
+  0x020b   FlashBDump?                          Y
+  0x020d   WB_RGGBLevelsDaylight                int16u[4]
+  0x020e   WB_RGGBLevelsShade                   int16u[4]
+  0x020f   WB_RGGBLevelsCloudy                  int16u[4]
+  0x0210   WB_RGGBLevelsTungsten                int16u[4]
+  0x0211   WB_RGGBLevelsFluorescentD            int16u[4]
+  0x0212   WB_RGGBLevelsFluorescentN            int16u[4]
+  0x0213   WB_RGGBLevelsFluorescentW            int16u[4]
+  0x0214   WB_RGGBLevelsFlash                   int16u[4]
+  0x0215   CameraInfo                           Pentax CameraInfo
+  0x0216   BatteryInfo                          Pentax BatteryInfo
+  0x021f   AFInfo                               Pentax AFInfo
+  0x03fe   DataDump                             N
+  0x0402   ToneCurve                            Y~
+  0x0403   ToneCurves                           Y~
+  0x0e00   PrintIM                              PrintIM
+
+=head3 Pentax SRInfo Tags
+
+Shake reduction information.
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      0    SRResult                             int8u
+      1    ShakeReduction                       int8u
+      2    SR_SWSToSWRTime                      int8u
+
+=head3 Pentax AEInfo Tags
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      0    AEExposureTime                       int8u
+      1    AEAperture                           int8u
+      2    AE_ISO                               int8u
+      3    AEXv                                 int8u
+      4    AEBXv                                int8s
+      5    AEFlashTv                            int8u
+      6    AEProgramMode                        int8u
+      7    AEExtra?                             int8u
+
+=head3 Pentax LensInfo Tags
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      0    LensType                             N
+      4    LensCodes                            int8u[16]
+      5    LensCodes                            int8u[16]
+
+=head3 Pentax FlashInfo Tags
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      0    FlashStatus                          int8u
+      1    FlashModeCode                        int8u
+      2    ExternalFlashMode                    int8u
+      3    InternalFlashMagni                   int8u
+      4    TTL_DA_AUp                           int8u
+      5    TTL_DA_ADown                         int8u
+      6    TTL_DA_BUp                           int8u
+      7    TTL_DA_BDown                         int8u
+
+=head3 Pentax CameraInfo Tags
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      0    PentaxModelID                        int32u
+      1    ManufactureDate                      int32u
+      2    ModelRevision                        int32u[2]
+      4    InternalSerialNumber                 int32u
+
+=head3 Pentax BatteryInfo Tags
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      0    BatteryType                          int8u
+      1    BatteryBodyGripStates                int8u
+      2    BatteryADBodyNoLoad                  int8u
+      3    BatteryADBodyLoad                    int8u
+      4    BatteryADGripNoLoad                  int8u
+      5    BatteryADGripLoad                    int8u
+
+=head3 Pentax AFInfo Tags
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+     11    AFPointsInFocus                      int8u
+
+=head3 Pentax Type2 Tags
+
+These tags are used by the Pentax Optio 330 and 430, and are similar to the
+tags used by Casio.
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+  0x0001   RecordingMode                        int16u
+  0x0002   Quality                              int16u
+  0x0003   FocusMode                            int16u
+  0x0004   FlashMode                            int16u
+  0x0007   WhiteBalance                         int16u
+  0x000a   DigitalZoom                          int32u
+  0x000b   Sharpness                            int16u
+  0x000c   Contrast                             int16u
+  0x000d   Saturation                           int16u
+  0x0014   ISO                                  int16u
+  0x0017   ColorFilter                          int16u
+  0x0e00   PrintIM                              PrintIM
+  0x1000   HometownCityCode                     undef[4]
+  0x1001   DestinationCityCode                  undef[4]
+
+=head3 Pentax Type4 Tags
+
+The following few tags are extracted from the wealth of information
+available in maker notes of the Optio E20.  These maker notes are stored as
+ASCII text in a format very similar to some HP models.
+
+  Tag ID               Tag Name                 Writable
+  ------               --------                 --------
+  'F/W Version'        FirmwareVersion          N
+
+=head3 Pentax MOV Tags
+
+This information is found in Pentax MOV video images.
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      0    Make                                 N
+     38    ExposureTime                         N
+     42    FNumber                              N
+     50    ExposureCompensation                 N
+     68    WhiteBalance                         N
+     72    FocalLength                          N
+    175    ISO                                  N
+
+=head2 Ricoh Tags
+
+These tags are used by Ricoh Caplio camera models.
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+  0x0001   MakerNoteType                        string
+  0x0002   MakerNoteVersion                     string
+  0x0e00   PrintIM                              PrintIM
+  0x1001   ImageInfo                            Ricoh ImageInfo
+  0x1003   Sharpness                            int32u
+  0x2001   RicohSubdir                          Ricoh Subdir
+           RicohRR1Subdir                       Ricoh Subdir
+
+=head3 Ricoh ImageInfo Tags
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      0    RicohImageWidth                      int16u
+      2    RicohImageHeight                     int16u
+      6    RicohDate                            int8u[7]
+     28    PreviewImageStart                    int16u*
+     30    PreviewImageLength                   int16u*
+     32    FlashMode                            int8u
+     33    Macro                                int8u
+     34    Sharpness                            int8u
+     38    WhiteBalance                         int8u
+     39    ISOSetting                           int8u
+     40    Saturation                           int8u
+
+=head3 Ricoh Subdir Tags
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+  0x0004   RicohDateTime1                       N
+  0x0005   RicohDateTime2                       N
+
+=head3 Ricoh Text Tags
+
+Ricoh RDC models such as the RDC-i700, RDC-5000, RDC-6000, RDC-7 and
+RDC-4300 use a text-based format for their maker notes instead of the IFD
+format used by the Caplio models.  Below is a list of known tags in this
+information.
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+  'Bg'     BlueGain                             N
+  'Gg'     GreenGain                            N
+  'Rev'    Revision                             N
+  'Rg'     RedGain                              N
+  'Rv'     Revision                             N
+
+=head3 Ricoh RMETA Tags
+
+The Ricoh Caplio Pro G3 has the ability to add custom fields to the APP5
+"RMETA" segment of JPEG images.  While only a few observed tags have been
+defined below, ExifTool will extract any information found here.
+
+  Tag ID               Tag Name                 Writable
+  ------               --------                 --------
+  'Azimuth'            Azimuth                  N
+  'Condition'          Condition                N
+  'Lit'                Lit                      N
+  'Location'           Location                 N
+  'Sign type'          SignType                 N
+
+=head2 Sanyo Tags
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+  0x00ff   MakerNoteOffset                      int32u
+  0x0100   SanyoThumbnail                       undef
+  0x0200   SpecialMode                          int32u[3]
+  0x0201   SanyoQuality                         int16u
+  0x0202   Macro                                int16u
+  0x0204   DigitalZoom                          rational64u
+  0x0207   SoftwareVersion                      Y
+  0x0208   PictInfo                             Y
+  0x0209   CameraID                             Y
+  0x020e   SequentialShot                       int16u
+  0x020f   WideRange                            int16u
+  0x0210   ColorAdjustmentMode                  int16u
+  0x0213   QuickShot                            int16u
+  0x0214   SelfTimer                            int16u
+  0x0216   VoiceMemo                            int16u
+  0x0217   RecordShutterRelease                 int16u
+  0x0218   FlickerReduce                        int16u
+  0x0219   OpticalZoomOn                        int16u
+  0x021b   DigitalZoomOn                        int16u
+  0x021d   LightSourceSpecial                   int16u
+  0x021e   Resaved                              int16u
+  0x021f   SceneSelect                          int16u
+  0x0223   ManualFocusDistance                  rational64u
+  0x0224   SequenceShotInterval                 int16u
+  0x0225   FlashMode                            int16u
+  0x0e00   PrintIM                              PrintIM
+  0x0f00   DataDump                             N
+
+=head3 Sanyo MOV Tags
+
+This information is found in Sanyo MOV video images, and is very similar to
+information found in Pentax MOV and Nikon QT videos.
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      0    Make                                 N
+     24    Model                                N
+     38    ExposureTime                         N
+     42    FNumber                              N
+     50    ExposureCompensation                 N
+     68    WhiteBalance                         N
+     72    FocalLength                          N
+
+=head3 Sanyo MP4 Tags
+
+This information is found in Sanyo MP4 video images.
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      0    Make                                 N
+     24    Model                                N
+     50    FNumber                              N
+    106    ISO                                  N
+   2048    ThumbnailImage                       N
+
+=head2 Sigma Tags
+
+These tags are used in Sigma/Foveon cameras.
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+  0x0002   SerialNumber                         string
+  0x0003   DriveMode                            string
+  0x0004   ResolutionMode                       string
+  0x0005   AFMode                               string
+  0x0006   FocusSetting                         string
+  0x0007   WhiteBalance                         string
+  0x0008   ExposureMode                         string
+  0x0009   MeteringMode                         string
+  0x000a   Lens                                 string
+  0x000b   ColorSpace                           string
+  0x000c   ExposureCompensation                 string
+           UnknownCompensation?                 rational64s
+  0x000d   Contrast                             string
+           Contrast                             rational64s
+  0x000e   Shadow                               string
+           Shadow                               rational64s
+  0x000f   Highlight                            string
+           Highlight                            rational64s
+  0x0010   Saturation                           string
+           Saturation                           rational64s
+  0x0011   Sharpness                            string
+           Sharpness                            rational64s
+  0x0012   X3FillLight                          string
+           X3FillLight                          rational64s
+  0x0014   ColorAdjustment                      string
+           ColorAdjustment                      rational64s[3]
+  0x0015   AdjustmentMode                       string
+  0x0016   Quality                              string
+  0x0017   Firmware                             string
+  0x0018   Software                             string
+  0x0019   AutoBracket                          string
+  0x001d   MakerNoteVersion                     string
+  0x0022   AdjustmentMode?                      string
+  0x0030   LensApertureRange                    string
+  0x0031   FNumber                              rational64u
+  0x0032   ExposureTime                         rational64u
+  0x0035   ExposureCompensation                 rational64s
+  0x003a   FlashExposureComp                    rational64s
+  0x003b   Firmware                             string
+  0x003c   WhiteBalance                         string
+
+=head2 Sony Tags
+
+The maker notes in images from current Sony camera models contain a wealth
+of information, but very little is known about these tags.  Use the ExifTool
+Unknown (-u) or Verbose (-v) options to see information about the unknown
+tags.
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+  0x0e00   PrintIM                              PrintIM
+  0xb021   ColorTemperature                     N
+  0xb023   SceneMode                            N
+  0xb024   ZoneMatching                         N
+  0xb025   DynamicRangeOptimizer                N
+  0xb026   ImageStabilization                   N
+  0xb027   LensID                               N
+  0xb028   MinoltaMakerNote                     Minolta
+  0xb029   ColorMode                            int32u
+  0xb040   Macro                                N
+  0xb041   ExposureMode                         N
+  0xb047   Quality                              N
+  0xb04e   LongExposureNoiseReduction           N
+
+=head3 Sony SRF Tags
+
+The maker notes in SRF (Sony Raw Format) images contain 7 IFD's (with family
+1 group names SRF0 through SRF6).  SRF0 through SRF5 use these Sony tags,
+while SRF6 uses standard EXIF tags.  All information other than SRF0 is
+encrypted, but thanks to Dave Coffin the decryption algorithm is known.
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+  0x0000   SRF2_Key                             N
+  0x0001   DataKey                              N
+
+=head3 Sony SR2Private Tags
+
+The SR2 format uses the DNGPrivateData tag to reference a private IFD
+containing these tags.
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+  0x7200   SR2SubIFDOffset                      N
+  0x7201   SR2SubIFDLength                      N
+  0x7221   SR2SubIFDKey                         N
+
+=head3 Sony SR2SubIFD Tags
+
+Tags in the encrypted SR2SubIFD
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+  0x7303   WB_GRBGLevels                        N
+  0x74a0   MaxApertureAtMaxFocal                N
+  0x74a1   MaxApertureAtMinFocal                N
+  0x74c0   SR2DataIFD                           Sony SR2DataIFD
+
+=head3 Sony SR2DataIFD Tags
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+  0x7770   ColorMode                            N
+
+=head2 Unknown Tags
+
+The following tags are decoded in unsupported maker notes.  Use the Unknown
+(-u) option to display other unknown tags.
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+  0x0e00   PrintIM                              PrintIM
+
+=head2 DNG Tags
+
+The main DNG tags are found in the EXIF table.  The tables below define only
+information found within structures of these main DNG tag values.
+
+=head3 DNG AdobeData Tags
+
+This information is found in the "Adobe" DNGPrivateData.
+
+The maker notes ('MakN') are processed by ExifTool, but some information may
+have been lost by the Adobe DNG Converter.  This is because the Adobe DNG
+Converter (as of version 3.3) doesn't properly handle information referenced
+from inside the maker notes that lies outside the original maker notes
+block, and this information is lost when only the maker note block is copied
+to the DNG image.   While this isn't a big problem for most camera makes, it
+is serious for some makes like Olympus.
+
+The CRW data is from DNG-converted CRW images, but it has been restructured
+to a proprietary Adobe format (which results in a loss of the original
+hierarchical information structure, and creates extra work for everyone else
+who wants to use this information).
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+  'CRW '   AdobeCRW                             CanonRaw
+  'MRW '   AdobeMRW                             MinoltaRaw
+  'MakN'   MakerNoteCanon                       Canon
+           MakerNoteCasio                       Casio
+           MakerNoteCasio2                      Casio Type2
+           MakerNoteFujiFilm                    FujiFilm
+           MakerNoteHP                          HP
+           MakerNoteHP2                         HP Type2
+           MakerNoteHP4                         HP Type4
+           MakerNoteHP6                         HP Type6
+           MakerNoteJVC                         JVC
+           MakerNoteJVCText                     JVC Text
+           MakerNoteKodak1a                     Kodak
+           MakerNoteKodak1b                     Kodak
+           MakerNoteKodak2                      Kodak Type2
+           MakerNoteKodak3                      Kodak Type3
+           MakerNoteKodak4                      Kodak Type4
+           MakerNoteKodak5                      Kodak Type5
+           MakerNoteKodak6a                     Kodak Type6
+           MakerNoteKodak6b                     Kodak Type6
+           MakerNoteKodak7                      Kodak Type7
+           MakerNoteKodakIFD                    Kodak IFD
+           MakerNoteKodakUnknown                Kodak Unknown
+           MakerNoteKyocera                     Unknown
+           MakerNoteMinolta                     Minolta
+           MakerNoteMinolta2                    Olympus
+           MakerNoteMinolta3                    undef
+           MakerNoteNikon                       Nikon
+           MakerNoteNikon2                      Nikon Type2
+           MakerNoteNikon3                      Nikon
+           MakerNoteOlympus                     Olympus
+           MakerNoteOlympus2                    Olympus
+           MakerNoteLeica                       Panasonic
+           MakerNotePanasonic                   Panasonic
+           MakerNotePanasonic2                  Panasonic Type2
+           MakerNotePentax                      Pentax
+           MakerNotePentax2                     Pentax Type2
+           MakerNotePentax3                     Casio Type2
+           MakerNotePentax4                     Pentax Type4
+           MakerNoteRicoh                       Ricoh
+           MakerNoteRicohText                   Ricoh Text
+           PreviewImage                         undef
+           MakerNoteSanyo                       Sanyo
+           MakerNoteSanyoC4                     Sanyo
+           MakerNoteSanyoPatch                  Sanyo
+           MakerNoteSigma                       Sigma
+           MakerNoteSony                        Sony
+           MakerNoteSonySRF                     Sony SRF
+           MakerNoteSonySR2                     Sony
+           MakerNoteUnknown                     Unknown
+  'RAF '   AdobeRAF?                            N
+  'SR2 '   AdobeSR2                             Sony SR2Private
+
+=head3 DNG OriginalRaw Tags
+
+This table defines tags extracted from the DNG OriginalRawFileData
+information.
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+  0x0000   OriginalRawImage                     N
+  0x0001   OriginalRawResource                  N
+  0x0002   OriginalRawFileType                  N
+  0x0003   OriginalRawCreator                   N
+  0x0004   OriginalTHMImage                     N
+  0x0005   OriginalTHMResource                  N
+  0x0006   OriginalTHMFileType                  N
+  0x0007   OriginalTHMCreator                   N
+
+=head2 CanonRaw Tags
+
+These tags apply to CRW-format Canon RAW files and information in the APP0
+"CIFF" segment of JPEG images.  When writing CanonRaw/CIFF information, the
+length of the information is preserved (and the new information is truncated
+or padded as required) unless B<Writable> is C<resize>. Currently, only
+JpgFromRaw and ThumbnailImage are allowed to change size.
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+  0x0000   NullRecord                           undef
+  0x0001   FreeBytes                            undef
+  0x0032   CanonColorInfo1                      N
+  0x0805   CanonFileDescription                 string[32]
+           UserComment                          string[256]
+  0x080a   CanonRawMakeModel                    CanonRaw MakeModel
+  0x080b   CanonFirmwareVersion                 string[32]
+  0x080c   ComponentVersion                     string
+  0x080d   ROMOperationMode                     string[8]
+  0x0810   OwnerName                            string[32]
+  0x0815   CanonImageType                       string[32]
+  0x0816   OriginalFileName                     string[32]
+  0x0817   ThumbnailFileName                    string[32]
+  0x100a   TargetImageType                      int16u
+  0x1010   ShutterReleaseMethod                 int16u
+  0x1011   ShutterReleaseTiming                 int16u
+  0x1016   ReleaseSetting                       int16u
+  0x101c   BaseISO                              int16u
+  0x1028   CanonFlashInfo?                      int16u[4]
+  0x1029   CanonFocalLength                     Canon FocalLength
+  0x102a   CanonShotInfo                        Canon ShotInfo
+  0x102c   CanonColorInfo2                      N
+  0x102d   CanonCameraSettings                  Canon CameraSettings
+  0x1031   SensorInfo                           Canon SensorInfo
+  0x1033   CustomFunctions10D                   CanonCustom Functions10D
+           CustomFunctionsD30                   CanonCustom FunctionsD30
+           CustomFunctionsD60                   CanonCustom FunctionsD30
+           CustomFunctionsUnknown               CanonCustom FuncsUnknown
+  0x1038   CanonAFInfo                          Canon AFInfo
+  0x1093   CanonFileInfo                        Canon FileInfo
+  0x10a9   ColorBalance                         Canon ColorBalance
+  0x10ae   ColorTemperature                     int16u
+  0x10b4   ColorSpace                           int16u
+  0x10b5   RawJpgInfo                           CanonRaw RawJpgInfo
+  0x1803   ImageFormat                          CanonRaw ImageFormat
+  0x1804   RecordID                             int32u
+  0x1806   SelfTimerTime                        int32u
+  0x1807   TargetDistanceSetting                float
+  0x180b   SerialNumber                         int32u
+  0x180e   TimeStamp                            CanonRaw TimeStamp
+  0x1810   ImageInfo                            CanonRaw ImageInfo
+  0x1813   FlashInfo                            CanonRaw FlashInfo
+  0x1814   MeasuredEV                           float
+  0x1817   FileNumber                           int32u
+  0x1818   ExposureInfo                         CanonRaw ExposureInfo
+  0x1834   CanonModelID                         int32u
+  0x1835   DecoderTable                         N
+  0x183b   SerialNumberFormat                   int32u
+  0x2005   RawData                              N
+  0x2007   JpgFromRaw                           resize
+  0x2008   ThumbnailImage                       resize
+  0x2804   ImageDescription                     CanonRaw
+  0x2807   CameraObject                         CanonRaw
+  0x3002   ShootingRecord                       CanonRaw
+  0x3003   MeasuredInfo                         CanonRaw
+  0x3004   CameraSpecification                  CanonRaw
+  0x300a   ImageProps                           CanonRaw
+  0x300b   ExifInformation                      CanonRaw
+
+=head3 CanonRaw MakeModel Tags
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      0    Make                                 string[6]
+      6    Model                                string[$size-6]
+
+=head3 CanonRaw RawJpgInfo Tags
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      1    RawJpgQuality                        int16u
+      2    RawJpgSize                           int16u
+      3    RawJpgWidth                          int16u
+      4    RawJpgHeight                         int16u
+
+=head3 CanonRaw ImageFormat Tags
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      0    FileFormat                           int32u
+      1    TargetCompressionRatio               float
+
+=head3 CanonRaw TimeStamp Tags
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      0    DateTimeOriginal                     int32u
+      1    TimeZoneCode                         int32s
+      2    TimeZoneInfo                         int32u
+
+=head3 CanonRaw ImageInfo Tags
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      0    ImageWidth                           N
+      1    ImageHeight                          N
+      2    PixelAspectRatio                     N
+      3    Rotation                             int32s
+      4    ComponentBitDepth                    N
+      5    ColorBitDepth                        N
+      6    ColorBW                              N
+
+=head3 CanonRaw FlashInfo Tags
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      0    FlashGuideNumber                     float
+      1    FlashThreshold                       float
+
+=head3 CanonRaw ExposureInfo Tags
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      0    ExposureCompensation                 float
+      1    TvValue                              float
+      2    AvValue                              float
+
+=head2 MinoltaRaw Tags
+
+These tags are used in Minolta RAW format (MRW) images.
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+  "\0PRD"  MinoltaPRD                           MinoltaRaw PRD
+  "\0RIF"  MinoltaRIF                           MinoltaRaw RIF
+  "\0TTW"  MinoltaTTW                           EXIF
+  "\0WBG"  MinoltaWBG                           MinoltaRaw WBG
+
+=head3 MinoltaRaw PRD Tags
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      0    FirmwareID                           string[8]
+      8    SensorHeight                         int16u
+     10    SensorWidth                          int16u
+     12    ImageHeight                          int16u
+     14    ImageWidth                           int16u
+     16    RawDepth                             int8u
+     17    BitDepth                             int8u
+     18    StorageMethod                        int8u
+     23    BayerPattern                         int8u
+
+=head3 MinoltaRaw RIF Tags
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      1    Saturation                           int8s
+      2    Contrast                             int8s
+      3    Sharpness                            int8s
+      4    WBMode                               int8u~
+      5    ProgramMode                          int8u
+      6    ISOSetting                           int8u
+      7    ColorMode                            int8u
+     56    ColorFilter                          int8s
+     57    BWFilter                             int8u
+     58    ZoneMatching                         int8u
+     59    Hue                                  int8s
+     60    ColorTemperature                     int8u
+
+=head3 MinoltaRaw WBG Tags
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      0    WBScale                              int8u[4]
+      4    WBLevels                             int16u[4]
+
+=head2 KyoceraRaw Tags
+
+Tags for Kyocera Contax N Digital RAW images.
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      1    FirmwareVersion                      N
+     12    Model                                N
+     25    Make                                 N
+     33    DateTimeOriginal                     N
+     52    ISO                                  N
+     56    ExposureTime                         N
+     60    WB_RGGBLevels                        N
+     88    FNumber                              N
+    104    MaxAperture                          N
+    112    FocalLength                          N
+    124    Lens                                 N
+
+=head2 SigmaRaw Tags
+
+These tags are used in Sigma and Foveon RAW (.X3F) images.
+
+  Tag ID               Tag Name                 Writable
+  ------               --------                 --------
+  'Header'             Header                   SigmaRaw Header
+  'HeaderExt'          HeaderExt                SigmaRaw HeaderExt
+  'IMA2'               PreviewImage             N
+  'IMAG'               PreviewImage             N
+  'PROP'               Properties               SigmaRaw Properties
+
+=head3 SigmaRaw Header Tags
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+  0x0001   FileVersion                          N
+  0x0007   ImageWidth                           N
+  0x0008   ImageHeight                          N
+  0x0009   Rotation                             N
+  0x000a   WhiteBalance                         N
+
+=head3 SigmaRaw HeaderExt Tags
+
+Extended header data found in version 2.1 and 2.2 files
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+  0x0000   Unused                               N
+  0x0001   ExposureAdjust                       N
+  0x0002   Contrast                             N
+  0x0003   Shadow                               N
+  0x0004   Highlight                            N
+  0x0005   Saturation                           N
+  0x0006   Sharpness                            N
+  0x0007   RedAdjust                            N
+  0x0008   GreenAdjust                          N
+  0x0009   BlueAdjust                           N
+  0x000a   X3FillLight                          N
+
+=head3 SigmaRaw Properties Tags
+
+  Tag ID               Tag Name                 Writable
+  ------               --------                 --------
+  'AEMODE'             MeteringMode             N
+  'AFAREA'             AFArea                   N
+  'AFINFOCUS'          AFInFocus                N
+  'AFMODE'             FocusMode                N
+  'APERTURE'           FNumber                  N
+  'AP_DESC'            ApertureDisplayed        N
+  'BRACKET'            BracketShot              N
+  'BURST'              BurstShot                N
+  'CAMMANUF'           Make                     N
+  'CAMMODEL'           Model                    N
+  'CAMNAME'            CameraName               N
+  'CAMSERIAL'          SerialNumber             N
+  'COLORSPACE'         ColorSpace               N
+  'DRIVE'              DriveMode                N
+  'EVAL_STATE'         EvalState                N
+  'EXPCOMP'            ExposureCompensation     N
+  'EXPNET'             NetExposureCompensation  N
+  'EXPTIME'            IntegrationTime          N
+  'FIRMVERS'           FirmwareVersion          N
+  'FLASH'              FlashMode                N
+  'FLASHEXPCOMP'       FlashExpComp             N
+  'FLASHPOWER'         FlashPower               N
+  'FLASHTTLMODE'       FlashTTLMode             N
+  'FLASHTYPE'          FlashType                N
+  'FLENGTH'            FocalLength              N
+  'FLEQ35MM'           FocalLengthIn35mmFormat  N
+  'FOCUS'              Focus                    N
+  'IMAGERBOARDID'      ImagerBoardID            N
+  'IMAGERTEMP'         SensorTemperature        N
+  'ISO'                ISO                      N
+  'LENSARANGE'         LensApertureRange        N
+  'LENSFRANGE'         LensFocalRange           N
+  'LENSMODEL'          LensID                   N
+  'PMODE'              ExposureProgram          N
+  'RESOLUTION'         Quality                  N
+  'SENSORID'           SensorID                 N
+  'SHUTTER'            ExposureTime             N
+  'SH_DESC'            ShutterSpeedDisplayed    N
+  'TIME'               DateTimeOriginal         N
+  'VERSION_BF'         VersionBF                N
+  'WB_DESC'            WhiteBalance             N
+
+=head2 JFIF Tags
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      0    JFIFVersion                          N
+      2    ResolutionUnit                       int8u
+      3    XResolution                          int16u
+      5    YResolution                          int16u
+
+=head3 JFIF Extension Tags
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+  0x0010   ThumbnailImage                       N
+
+=head2 FlashPix Tags
+
+The FlashPix file format, introduced in 1996, was developed by Kodak,
+Hewlett-Packard and Microsoft.  Internally the FPX file structure mimics
+that of an old DOS disk with fixed-sized "sectors" (usually 512 bytes) and a
+"file allocation table" (FAT).  No wonder the format never became popular.
+
+However, some of the structures used in FlashPix streams are part of the
+EXIF specification, and are still being used in the APP2 FPXR segment of
+JPEG images by some Kodak and Hewlett-Packard digital cameras.
+
+ExifTool extracts FlashPix information from both FPX images and the APP2
+FPXR segment of JPEG images.  As well, FlashPix information is extracted
+from DOC, XLS and PPT (Microsoft Word, Excel and PowerPoint) documents since
+the FlashPix file format is closely related to the formats of these files.
+
+  Tag ID                           Tag Name     Writable
+  ------                           --------     --------
+  "\x01CompObj"                    CompObj      FlashPix CompObj
+  "\x05Audio Info"                 AudioInfo    FlashPix AudioInfo
+  "\x05Data Object"                DataObject   FlashPix DataObject
+  "\x05DocumentSummaryInformation" DocumentInfo FlashPix DocumentInfo
+  "\x05Extension List"             Extensions   FlashPix Extensions
+  "\x05Global Info"                GlobalInfo   FlashPix GlobalInfo
+  "\x05Image Contents"             Image        FlashPix Image
+  "\x05Image Info"                 ImageInfo    FlashPix ImageInfo
+  "\x05Operation"                  Operation    FlashPix Operation
+  "\x05Screen Nail"                ScreenNail   N
+  "\x05SummaryInformation"         SummaryInfo  FlashPix SummaryInfo
+  "\x05Transform"                  Transform    FlashPix Transform
+  'Audio Stream'                   AudioStream  N
+  'Current User'                   CurrentUser  N
+  'ICC Profile 0001'               ICC_Profile  ICC_Profile
+  'Subimage 0000 Header'           SubimageHdr  FlashPix SubimageHdr
+
+=head3 FlashPix CompObj Tags
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      0    CompObjUserTypeLen                   N
+      1    CompObjUserType                      N
+
+=head3 FlashPix AudioInfo Tags
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+  [no tags known]
+
+=head3 FlashPix DataObject Tags
+
+  Tag ID       Tag Name                         Writable
+  ------       --------                         --------
+  0x10000      DataObjectID                     N
+  0x10002      LockedPropertyList               N
+  0x10003      DataObjectTitle                  N
+  0x10004      LastModifier                     N
+  0x10005      RevisionNumber                   N
+  0x10006      DataCreateDate                   N
+  0x10007      DataModifyDate                   N
+  0x10008      CreatingApplication              N
+  0x10100      DataObjectStatus                 N
+  0x10101      CreatingTransform                N
+  0x10102      UsingTransforms                  N
+  0x10000000   CachedImageHeight                N
+  0x10000001   CachedImageWidth                 N
+
+=head3 FlashPix DocumentInfo Tags
+
+The DocumentSummaryInformation property set includes a UserDefined property
+set for which only the Hyperlinks and HyperlinkBase tags are pre-defined.
+However, ExifTool will also extract any other information found in the
+UserDefined properties.
+
+  Tag ID               Tag Name                 Writable
+  ------               --------                 --------
+  0x0002               Category                 N
+  0x0003               PresentationTarget       N
+  0x0004               Bytes                    N
+  0x0005               Lines                    N
+  0x0006               Paragraphs               N
+  0x0007               Slides                   N
+  0x0008               Notes                    N
+  0x0009               HiddenSlides             N
+  0x000a               MMClips                  N
+  0x000b               ScaleCrop                N
+  0x000c               HeadingPairs             N
+  0x000d               TitleOfParts             N
+  0x000e               Manager                  N
+  0x000f               Company                  N
+  0x0010               LinksUpToDate            N
+  0x0011               CharCountWithSpaces      N
+  0x0013               SharedDoc                N
+  0x0016               HyperlinksChanged        N
+  0x0017               AppVersion               N
+  '_PID_HLINKS'        Hyperlinks               N
+  '_PID_LINKBASE'      HyperlinkBase            N
+
+=head3 FlashPix Extensions Tags
+
+  Tag ID       Tag Name                         Writable
+  ------       --------                         --------
+  0x0001       ExtensionName                    N
+  0x0002       ExtensionClassID                 N
+  0x0003       ExtensionPersistence             N
+  0x0004       ExtensionCreateDate              N
+  0x0005       ExtensionModifyDate              N
+  0x0006       CreatingApplication              N
+  0x0007       ExtensionDescription             N
+  0x1000       Storage-StreamPathname           N
+  0x2000       FlashPixStreamPathname           N
+  0x2001       FlashPixStreamFieldOffset        N
+  0x3000       PropertySetPathname              N
+  0x3001       PropertySetIDCodes               N
+  0x3002       PropertyVectorElements           N
+  0x4000       SubimageResolutions              N
+  0x10000000   UsedExtensionNumbers             N
+
+=head3 FlashPix GlobalInfo Tags
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+  0x10002  LockedPropertyList                   N
+  0x10003  TransformedImageTitle                N
+  0x10004  LastModifier                         N
+  0x10100  VisibleOutputs                       N
+  0x10101  MaximumImageIndex                    N
+  0x10102  MaximumTransformIndex                N
+  0x10103  MaximumOperationIndex                N
+
+=head3 FlashPix Image Tags
+
+  Tag ID       Tag Name                         Writable
+  ------       --------                         --------
+  0x1000000    NumberOfResolutions              N
+  0x1000002    ImageWidth                       N
+  0x1000003    ImageHeight                      N
+  0x1000004    DefaultDisplayHeight             N
+  0x1000005    DefaultDisplayWidth              N
+  0x1000006    DisplayUnits                     N
+  0x2000000    SubimageWidth                    N
+  0x2000001    SubimageHeight                   N
+  0x2000002    SubimageColor                    N
+  0x2000003    SubimageNumericalFormat          N
+  0x2000004    DecimationMethod                 N
+  0x2000005    DecimationPrefilterWidth         N
+  0x2000007    SubimageICC_Profile              N
+  0x3000001    JPEGTables                       N
+  0x3000002    MaxJPEGTableIndex                N
+
+=head3 FlashPix ImageInfo Tags
+
+  Tag ID       Tag Name                         Writable
+  ------       --------                         --------
+  0x21000000   FileSource                       N
+  0x21000001   SceneType                        N
+  0x21000002   CreationPathVector               N
+  0x21000003   SoftwareRelease                  N
+  0x21000004   UserDefinedID                    N
+  0x21000005   SharpnessApproximation           N
+  0x22000000   Copyright                        N
+  0x22000001   OriginalImageBroker              N
+  0x22000002   DigitalImageBroker               N
+  0x22000003   Authorship                       N
+  0x22000004   IntellectualPropertyNotes        N
+  0x23000000   TestTarget                       N
+  0x23000002   GroupCaption                     N
+  0x23000003   CaptionText                      N
+  0x23000004   People                           N
+  0x23000007   Things                           N
+  0x2300000a   DateTimeOriginal                 N
+  0x2300000b   Events                           N
+  0x2300000c   Places                           N
+  0x2300000f   ContentDescriptionNotes          N
+  0x24000000   Make                             N
+  0x24000001   Model                            N
+  0x24000002   SerialNumber                     N
+  0x25000000   CreateDate                       N
+  0x25000001   ExposureTime                     N
+  0x25000002   FNumber                          N
+  0x25000003   ExposureProgram                  N
+  0x25000004   BrightnessValue                  N
+  0x25000005   ExposureCompensation             N
+  0x25000006   SubjectDistance                  N
+  0x25000007   MeteringMode                     N
+  0x25000008   LightSource                      N
+  0x25000009   FocalLength                      N
+  0x2500000a   MaxApertureValue                 N
+  0x2500000b   Flash                            N
+  0x2500000c   FlashEnergy                      N
+  0x2500000d   FlashReturn                      N
+  0x2500000e   BackLight                        N
+  0x2500000f   SubjectLocation                  N
+  0x25000010   ExposureIndex                    N
+  0x25000011   SpecialEffectsOpticalFilter      N
+  0x25000012   PerPictureNotes                  N
+  0x26000000   SensingMethod                    N
+  0x26000001   FocalPlaneXResolution            N
+  0x26000002   FocalPlaneYResolution            N
+  0x26000003   FocalPlaneResolutionUnit         N
+  0x26000004   SpatialFrequencyResponse         N
+  0x26000005   CFAPattern                       N
+  0x26000007   ISO                              N
+  0x26000008   Opto-ElectricConvFactor          N
+  0x27000000   FilmBrand                        N
+  0x27000001   FilmCategory                     N
+  0x27000002   FilmSize                         N
+  0x27000003   FilmRollNumber                   N
+  0x27000004   FilmFrameNumber                  N
+  0x28000000   ScannerMake                      N
+  0x28000001   ScannerModel                     N
+  0x28000002   ScannerSerialNumber              N
+  0x28000003   ScanSoftware                     N
+  0x28000004   ScanSoftwareRevisionDate         N
+  0x28000005   ServiceOrganizationName          N
+  0x28000006   ScanOperatorID                   N
+  0x28000008   ScanDate                         N
+  0x28000009   ModifyDate                       N
+  0x2800000a   ScannerPixelSize                 N
+  0x29000000   OriginalScannedImageSize         N
+  0x29000001   OriginalDocumentSize             N
+  0x29000002   OriginalMedium                   N
+  0x29000003   TypeOfOriginal                   N
+
+=head3 FlashPix Operation Tags
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+  0x10000  OperationID                          N
+
+=head3 FlashPix SummaryInfo Tags
+
+The Dictionary, CodePage and LocalIndicator tags are common to all FlashPix
+property tables, even though they are only listed in the SummaryInfo table.
+
+  Tag ID       Tag Name                         Writable
+  ------       --------                         --------
+  0x0000       Dictionary                       N
+  0x0001       CodePage                         N
+  0x0002       Title                            N
+  0x0003       Subject                          N
+  0x0004       Author                           N
+  0x0005       Keywords                         N
+  0x0006       Comments                         N
+  0x0007       Template                         N
+  0x0008       LastSavedBy                      N
+  0x0009       RevisionNumber                   N
+  0x000a       TotalEditTime                    N
+  0x000b       LastPrinted                      N
+  0x000c       CreateDate                       N
+  0x000d       ModifyDate                       N
+  0x000e       PageCount                        N
+  0x000f       WordCount                        N
+  0x0010       CharCount                        N
+  0x0011       ThumbnailClip                    N
+  0x0012       Software                         N
+  0x0013       Security                         N
+  0x80000000   LocaleIndicator                  N
+
+=head3 FlashPix Transform Tags
+
+  Tag ID       Tag Name                         Writable
+  ------       --------                         --------
+  0x10000      TransformNodeID                  N
+  0x10001      OperationClassID                 N
+  0x10002      LockedPropertyList               N
+  0x10003      TransformTitle                   N
+  0x10004      LastModifier                     N
+  0x10005      RevisionNumber                   N
+  0x10006      TransformCreateDate              N
+  0x10007      TransformModifyDate              N
+  0x10008      CreatingApplication              N
+  0x10100      InputDataObjectList              N
+  0x10101      OutputDataObjectList             N
+  0x10102      OperationNumber                  N
+  0x10000000   ResultAspectRatio                N
+  0x10000001   RectangleOfInterest              N
+  0x10000002   Filtering                        N
+  0x10000003   SpatialOrientation               N
+  0x10000004   ColorTwistMatrix                 N
+  0x10000005   ContrastAdjustment               N
+
+=head3 FlashPix SubimageHdr Tags
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      1    SubimageWidth                        N
+      2    SubimageHeight                       N
+      3    SubimageTileCount                    N
+      4    SubimageTileWidth                    N
+      5    SubimageTileHeight                   N
+      6    NumChannels                          N
+
+=head2 APP12 Tags
+
+=head3 APP12 PictureInfo Tags
+
+The JPEG APP12 "Picture Info" segment was used by some older cameras, and
+contains ASCII-based meta information.  Below are some tags which have been
+observed Agfa and Polaroid images, however ExifTool will extract information
+from any tags found in this segment.
+
+  Tag ID               Tag Name                 Writable
+  ------               --------                 --------
+  'Aperture'           Aperture                 N
+  'ColorMode'          ColorMode                N
+  'ConTake'            ConTake                  N
+  'ExpBias'            ExposureCompensation     N
+  'FNumber'            FNumber                  N
+  'FWare'              FirmwareVersion          N
+  'Flash'              Flash                    N
+  'FocusMode'          FocusMode                N
+  'FocusPos'           FocusPos                 N
+  'ID'                 ID                       N
+  'ImageSize'          ImageSize                N
+  'LightS'             LightS                   N
+  'Macro'              Macro                    N
+  'Protect'            Protect                  N
+  'Quality'            Quality                  N
+  'Resolution'         Resolution               N
+  'Serial#'            SerialNumber             N
+  'Shutter'            ExposureTime             N
+  'StrobeTime'         StrobeTime               N
+  'TimeDate'           DateTimeOriginal         N
+  'Type'               CameraType               N
+  'Version'            Version                  N
+  'Ytarget'            YTarget                  N
+  'Zoom'               Zoom                     N
+  'ZoomPos'            ZoomPos                  N
+  'shtr'               ExposureTime             N
+  'ylevel'             YLevel                   N
+
+=head3 APP12 Ducky Tags
+
+Photoshop uses the JPEG APP12 "Ducky" segment to store some information in
+"Save for Web" images.
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+  0x0001   Quality                              int32u/
+  0x0002   Comment                              string/
+  0x0003   Copyright                            string/
+
+=head2 AFCP Tags
+
+AFCP stands for AXS File Concatenation Protocol, and is a poorly designed
+protocol for appending information to the end of files.  This can be used as
+an auxiliary technique to store IPTC information in images, but is
+incompatible with some file formats.
+
+ExifTool will read and write (but not create) AFCP IPTC information in JPEG
+images, but not other image formats.
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+  'IPTC'   IPTC                                 IPTC
+  'Nail'   ThumbnailImage                       N
+  'PrVw'   PreviewImage                         N
+  'TEXT'   Text                                 N
+
+=head2 CanonVRD Tags
+
+Canon Digital Photo Professional writes VRD (Recipe Data) information as a
+trailer record to JPEG, TIFF, CRW and CR2 images, or as a stand-alone VRD
+file. The tags listed below represent information found in this record.  The
+complete VRD data record may be accessed as a block using the Extra
+'CanonVRD' tag, but this tag is not extracted or copied unless specified
+explicitly.
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      0    VRD1                                 CanonVRD Ver1
+      1    VRDStampTool                         CanonVRD StampTool
+      2    VRD2                                 CanonVRD Ver2
+
+=head3 CanonVRD Ver1 Tags
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      2    VRDVersion                           N
+      8    WBAdjRGBLevels                       int16u[3]
+     24    WhiteBalanceAdj                      int16u
+     26    WBAdjColorTemp                       int16u
+     36    WBFineTuneActive                     int16u
+     40    WBFineTuneSaturation                 int16u
+     44    WBFineTuneTone                       int16u
+     46    RawColorAdj                          int16u
+     48    RawCustomSaturation                  int32s
+     52    RawCustomTone                        int32s
+     56    RawBrightnessAdj                     int32s
+     60    ToneCurveProperty                    int16u
+    122    DynamicRangeMin                      int16u
+    124    DynamicRangeMax                      int16u
+    272    ToneCurveActive                      int16u
+    276    BrightnessAdj                        int8s
+    277    ContrastAdj                          int8s
+    278    SaturationAdj                        int16s
+    286    ColorToneAdj                         int32s
+    352    RedCurvePoints                       int16u[21]
+    394    RedCurveLimits                       int16u[4]
+    410    GreenCurvePoints                     int16u[21]
+    452    GreenCurveLimits                     int16u[4]
+    468    BlueCurvePoints                      int16u[21]
+    510    BlueCurveLimits                      int16u[4]
+    526    RGBCurvePoints                       int16u[21]
+    568    RGBCurveLimits                       int16u[4]
+    580    CropActive                           int16u
+    582    CropLeft                             int16u
+    584    CropTop                              int16u
+    586    CropWidth                            int16u
+    588    CropHeight                           int16u
+    608    CropAspectRatio                      int16u
+    610    ConstrainedCropWidth                 float
+    614    ConstrainedCropHeight                float
+    618    CheckMark                            int16u
+    622    Rotation                             int16u
+    624    WorkColorSpace                       int16u
+
+=head3 CanonVRD StampTool Tags
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      0    StampToolCount                       N
+
+=head3 CanonVRD Ver2 Tags
+
+Tags added in DPP version 2.0 and later.
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      4    PictureStyle                         int16u
+     26    RawColorToneAdj                      int16s
+     28    RawSaturationAdj                     int16s
+     30    RawContrastAdj                       int16s
+     32    RawLinear                            int16u
+     34    RawSharpnessAdj                      int16s
+     36    RawHighlightPoint                    int16s
+     38    RawShadowPoint                       int16s
+    116    MonochromeFilterEffect               int16s
+    118    MonochromeToningEffect               int16s
+    120    MonochromeContrast                   int16s
+    122    MonochromeLinear                     int16u
+    124    MonochromeSharpness                  int16s
+    188    ChrominanceNoiseReduction            int16u
+    190    LuminanceNoiseReduction              int16u
+    192    ChrominanceNR_TIFF_JPEG              int16u
+
+=head2 FotoStation Tags
+
+The following tables define information found in the FotoWare FotoStation
+trailer.
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+  0x0001   IPTC                                 IPTC
+  0x0002   SoftEdit                             FotoStation SoftEdit
+  0x0003   ThumbnailImage                       Y
+  0x0004   PreviewImage                         Y
+
+=head3 FotoStation SoftEdit Tags
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      0    OriginalImageWidth                   int32s
+      1    OriginalImageHeight                  int32s
+      2    ColorPlanes                          int32s
+      3    XYResolution                         int32s
+      4    Rotation                             int32s
+      6    CropLeft                             int32s
+      7    CropTop                              int32s
+      8    CropRight                            int32s
+      9    CropBottom                           int32s
+     11    CropRotation                         int32s
+
+=head2 PhotoMechanic Tags
+
+The Photo Mechanic trailer contains data in an IPTC-format structure, with
+soft edit information stored under record number 2.
+
+  Record   Tag Name                             Writable
+  ------   --------                             --------
+      2    SoftEdit                             PhotoMechanic SoftEdit
+
+=head3 PhotoMechanic SoftEdit Tags
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+    209    RawCropLeft                          int32s
+    210    RawCropTop                           int32s
+    211    RawCropRight                         int32s
+    212    RawCropBottom                        int32s
+    213    ConstrainedCropWidth                 int32s
+    214    ConstrainedCropHeight                int32s
+    215    FrameNum                             int32s
+    216    Rotation                             int32s
+    217    CropLeft                             int32s
+    218    CropTop                              int32s
+    219    CropRight                            int32s
+    220    CropBottom                           int32s
+    221    Tagged                               int32s
+    222    ColorClass                           int32s
+    223    Rating                               int32s
+
+=head3 PhotoMechanic XMP Tags
+
+Below is a list of the observed PhotoMechanic XMP tags.  The actual
+namespace prefix is "photomechanic" but ExifTool shortens this in
+the "XMP-photomech" family 1 group name.
+
+  Tag ID               Tag Name                 Writable
+  ------               --------                 --------
+  'CountryCode'        CountryCode              string/
+  'EditStatus'         EditStatus               string
+  'Prefs'              Prefs                    string
+  'TimeCreated'        TimeCreated              string/
+
+=head2 MIE Tags
+
+MIE is a flexible format which may be used as a stand-alone meta information
+format, for encapsulation of other files and information, or as a trailer
+appended to other file formats.  The tables below represent currently
+defined MIE tags, however ExifTool will also extract any other information
+present in a MIE file.
+
+When writing MIE information, some special features are supported:
+
+1) String values may be written as ASCII (ISO 8859-1) or UTF-8.  ExifTool
+automatically detects the presence of wide characters and treats the string
+appropriately. Internally, UTF-8 text may be converted to UTF-16 or UTF-32
+and stored in this format in the file if it is more compact.
+
+2) All MIE string-value tags support localized text.  Localized values are
+written by adding a language/country code to the tag name in the form
+C<TAG-xx_YY>, where C<TAG> is the tag name, C<xx> is a 2-character lower
+case ISO 639-1 language code, and C<YY> is a 2-character upper case ISO
+3166-1 alpha 2 country code (ie. C<Title-en_US>).  But as usual, the user
+interface is case-insensitive, and ExifTool will write the correct case to
+the file.
+
+3) Some numerical MIE tags allow units of measurement to be specified.  For
+these tags, units may be added in brackets immediately following the value
+(ie. C<55(mi/h)>).  If no units are specified, the default units are
+written.
+
+  Tag ID               Tag Name                 Writable
+  ------               --------                 --------
+  '0Type'              SubfileType              string
+  '0Vers'              MIEVersion               string
+  '1Directory'         SubfileDirectory         string
+  '1Name'              SubfileName              string
+  '2MIME'              SubfileMIMEType          string
+  'Meta'               Meta                     MIE Meta
+  'data'               SubfileData              undef
+  'rsrc'               SubfileResource          undef
+  'zmd5'               MD5Digest                string
+  'zmie'               TrailerSignature         undef
+
+=head3 MIE Meta Tags
+
+  Tag ID               Tag Name                 Writable
+  ------               --------                 --------
+  'Audio'              Audio                    MIE Audio
+  'Camera'             Camera                   MIE Camera
+  'Document'           Document                 MIE Doc
+  'EXIF'               EXIF                     EXIF
+  'Geo'                Geo                      MIE Geo
+  'ICCProfile'         ICC_Profile              ICC_Profile
+  'ID3'                ID3                      ID3
+  'IPTC'               IPTC                     IPTC
+  'Image'              Image                    MIE Image
+  'MakerNotes'         MakerNotes               MIE MakerNotes
+  'Preview'            Preview                  MIE Preview
+  'Thumbnail'          Thumbnail                MIE Thumbnail
+  'Video'              Video                    MIE Video
+  'XMP'                XMP                      XMP
+
+=head3 MIE Audio Tags
+
+For the Audio group (and any other group containing a 'data' element), tags
+refer to the contained data if present, otherwise they refer to the main
+SubfileData.  The C<0Type> and C<1Name> elements should exist only if C<data>
+is present.
+
+  Tag ID               Tag Name                 Writable
+  ------               --------                 --------
+  '0Type'              RelatedAudioFileType     string
+  '1Name'              RelatedAudioFileName     string
+  'Channels'           Channels                 int8u
+  'Compression'        AudioCompression         string
+  'Duration'           Duration                 rational64u
+  'SampleBits'         SampleBits               int16u
+  'SampleRate'         SampleRate               int32u
+  'data'               RelatedAudioFile         undef
+
+=head3 MIE Camera Tags
+
+  Tag ID               Tag Name                 Writable
+  ------               --------                 --------
+  'Brightness'         Brightness               int8s
+  'ColorBalance'       ColorBalance             rational64u[3]
+  'ColorTemperature'   ColorTemperature         int32u
+  'Contrast'           Contrast                 int8s
+  'DigitalZoom'        DigitalZoom              rational64u
+  'ExposureComp'       ExposureCompensation     rational64s
+  'ExposureMode'       ExposureMode             string
+  'ExposureTime'       ExposureTime             rational64u
+  'FirmwareVersion'    FirmwareVersion          string
+  'Flash'              Flash                    MIE Flash
+  'FocusMode'          FocusMode                string
+  'ISO'                ISO                      int16u
+  'ISOSetting'         ISOSetting               int16u
+  'ImageNumber'        ImageNumber              int32u
+  'ImageQuality'       ImageQuality             string
+  'ImageStabilization' ImageStabilization       int8u
+  'Lens'               Lens                     MIE Lens
+  'Make'               Make                     string
+  'MeasuredEV'         MeasuredEV               rational64s
+  'Model'              Model                    string
+  'Orientation'        Orientation              MIE Orient
+  'OwnerName'          OwnerName                string
+  'Saturation'         Saturation               int8s
+  'SensorSize'         SensorSize               rational64u[2]
+  'SerialNumber'       SerialNumber             string
+  'Sharpness'          Sharpness                int8s
+  'ShootingMode'       ShootingMode             string
+
+=head3 MIE Flash Tags
+
+  Tag ID               Tag Name                 Writable
+  ------               --------                 --------
+  'ExposureComp'       FlashExposureComp        rational64s
+  'Fired'              FlashFired               int8u
+  'GuideNumber'        FlashGuideNumber         string
+  'Make'               FlashMake                string
+  'Mode'               FlashMode                string
+  'Model'              FlashModel               string
+  'SerialNumber'       FlashSerialNumber        string
+  'Type'               FlashType                string
+
+=head3 MIE Lens Tags
+
+All recorded lens parameters (focal length, aperture, etc) include the
+effects of the extender if present.
+
+  Tag ID                  Tag Name              Writable
+  ------                  --------              --------
+  'Extender'              Extender              MIE Extender
+  'FNumber'               FNumber               rational64u
+  'FocalLength'           FocalLength           rational64u
+  'FocusDistance'         FocusDistance         rational64u
+  'Make'                  LensMake              string
+  'MaxAperture'           MaxAperture           rational64u
+  'MaxApertureAtMaxFocal' MaxApertureAtMaxFocal rational64u
+  'MaxFocalLength'        MaxFocalLength        rational64u
+  'MinAperture'           MinAperture           rational64u
+  'MinFocalLength'        MinFocalLength        rational64u
+  'Model'                 LensModel             string
+  'OpticalZoom'           OpticalZoom           rational64u
+  'SerialNumber'          LensSerialNumber      string
+
+=head3 MIE Extender Tags
+
+  Tag ID               Tag Name                 Writable
+  ------               --------                 --------
+  'Magnification'      ExtenderMagnification    rational64s
+  'Make'               ExtenderMake             string
+  'Model'              ExtenderModel            string
+  'SerialNumber'       ExtenderSerialNumber     string
+
+=head3 MIE Orient Tags
+
+These tags describe the camera orientation.
+
+  Tag ID               Tag Name                 Writable
+  ------               --------                 --------
+  'Azimuth'            Azimuth                  rational64s
+  'Declination'        Declination              rational64s
+  'Elevation'          Elevation                rational64s
+  'RightAscension'     RightAscension           rational64s
+  'Rotation'           Rotation                 rational64s
+
+=head3 MIE Doc Tags
+
+Information describing the main document, image or file.
+
+  Tag ID               Tag Name                 Writable
+  ------               --------                 --------
+  'Author'             Author                   string
+  'Comment'            Comment                  string
+  'Contributors'       Contributors             string+
+  'Copyright'          Copyright                string
+  'CreateDate'         CreateDate               string
+  'EMail'              EMail                    string
+  'Keywords'           Keywords                 string+
+  'ModifyDate'         ModifyDate               string
+  'OriginalDate'       DateTimeOriginal         string
+  'Phone'              PhoneNumber              string
+  'References'         References               string+
+  'Software'           Software                 string
+  'Title'              Title                    string
+  'URL'                URL                      string
+
+=head3 MIE Geo Tags
+
+Information related to geographic location.
+
+  Tag ID               Tag Name                 Writable
+  ------               --------                 --------
+  'Address'            Address                  string
+  'City'               City                     string
+  'Country'            Country                  string
+  'GPS'                GPS                      MIE GPS
+  'PostalCode'         PostalCode               string
+  'State'              State                    string
+  'UTM'                UTM                      MIE UTM
+
+=head3 MIE GPS Tags
+
+  Tag ID               Tag Name                 Writable
+  ------               --------                 --------
+  'Altitude'           GPSAltitude              rational64s
+  'Bearing'            GPSDestBearing           rational64s
+  'DateTime'           GPSDateTime              string
+  'Datum'              GPSMapDatum              string
+  'Differential'       GPSDifferential          int8u
+  'Distance'           GPSDestDistance          rational64s
+  'Heading'            GPSTrack                 rational64s
+  'Latitude'           GPSLatitude              rational64s[n]
+  'Longitude'          GPSLongitude             rational64s[n]
+  'MeasureMode'        GPSMeasureMode           int8u
+  'Satellites'         GPSSatellites            string
+  'Speed'              GPSSpeed                 rational64s
+
+=head3 MIE UTM Tags
+
+  Tag ID       Tag Name                         Writable
+  ------       --------                         --------
+  'Datum'      UTMMapDatum                      string
+  'Easting'    UTMEasting                       string
+  'Northing'   UTMNorthing                      string
+  'Zone'       UTMZone                          int8s
+
+=head3 MIE Image Tags
+
+  Tag ID               Tag Name                 Writable
+  ------               --------                 --------
+  '0Type'              FullSizeImageType        string
+  '1Name'              FullSizeImageName        string
+  'BitDepth'           BitDepth                 int16u
+  'ColorSpace'         ColorSpace               string
+  'Components'         ComponentsConfiguration  string
+  'Compression'        CompressionRatio         rational32u
+  'ImageSize'          ImageSize                int16u[n]
+  'Resolution'         Resolution               rational64u[n]
+  'data'               FullSizeImage            undef
+
+=head3 MIE MakerNotes Tags
+
+MIE maker notes are contained within separate groups for each manufacturer
+to avoid name conflicts.  Currently no specific manufacturer information has
+been defined.
+
+  Tag ID               Tag Name                 Writable
+  ------               --------                 --------
+  'Canon'              Canon                    MIE Unknown
+  'Casio'              Casio                    MIE Unknown
+  'FujiFilm'           FujiFilm                 MIE Unknown
+  'Kodak'              Kodak                    MIE Unknown
+  'KonicaMinolta'      KonicaMinolta            MIE Unknown
+  'Nikon'              Nikon                    MIE Unknown
+  'Olympus'            Olympus                  MIE Unknown
+  'Panasonic'          Panasonic                MIE Unknown
+  'Pentax'             Pentax                   MIE Unknown
+  'Ricoh'              Ricoh                    MIE Unknown
+  'Sigma'              Sigma                    MIE Unknown
+  'Sony'               Sony                     MIE Unknown
+
+=head3 MIE Unknown Tags
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+  [no tags known]
+
+=head3 MIE Preview Tags
+
+  Tag ID               Tag Name                 Writable
+  ------               --------                 --------
+  '0Type'              PreviewImageType         string
+  '1Name'              PreviewImageName         string
+  'ImageSize'          PreviewImageSize         int16u[n]
+  'data'               PreviewImage             undef
+
+=head3 MIE Thumbnail Tags
+
+  Tag ID               Tag Name                 Writable
+  ------               --------                 --------
+  '0Type'              ThumbnailImageType       string
+  '1Name'              ThumbnailImageName       string
+  'ImageSize'          ThumbnailImageSize       int16u[n]
+  'data'               ThumbnailImage           undef
+
+=head3 MIE Video Tags
+
+  Tag ID       Tag Name                         Writable
+  ------       --------                         --------
+  '0Type'      RelatedVideoFileType             string
+  '1Name'      RelatedVideoFileName             string
+  'Codec'      Codec                            string
+  'Duration'   Duration                         rational64u
+  'data'       RelatedVideoFile                 undef
+
+=head2 ID3 Tags
+
+ExifTool extracts ID3 information from MP3, MPEG, AIFF, OGG, FLAC, APE and
+RealAudio files.
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+  'ID3v1'  ID3v1                                ID3 v1
+  'ID3v22' ID3v2_2                              ID3 v2_2
+  'ID3v23' ID3v2_3                              ID3 v2_3
+  'ID3v24' ID3v2_4                              ID3 v2_4
+
+=head3 ID3 v1 Tags
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      3    Title                                N
+     33    Artist                               N
+     63    Album                                N
+     93    Year                                 N
+     97    Comment                              N
+    127    Genre                                N
+
+=head3 ID3 v2_2 Tags
+
+ExifTool extracts mainly text-based tags from ID3v2 information.  The tags
+in the tables below are those extracted by ExifTool, and don't represent a
+complete list of available ID3v2 tags.
+
+ID3 version 2.2 tags.  (These are the tags written by iTunes 5.0.)
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+  'CNT'    PlayCounter                          N
+  'COM'    Comment                              N
+  'IPL'    InvolvedPeople                       N
+  'PIC'    Picture                              N
+  'TAL'    Album                                N
+  'TBP'    BeatsPerMinute                       N
+  'TCM'    Composer                             N
+  'TCO'    Genre                                N
+  'TCP'    Compilation                          N
+  'TCR'    Copyright                            N
+  'TDA'    Date                                 N
+  'TDY'    PlaylistDelay                        N
+  'TEN'    EncodedBy                            N
+  'TFT'    FileType                             N
+  'TIM'    Time                                 N
+  'TKE'    InitialKey                           N
+  'TLA'    Language                             N
+  'TLE'    Length                               N
+  'TMT'    Media                                N
+  'TOA'    OriginalArtist                       N
+  'TOF'    OriginalFilename                     N
+  'TOL'    OriginalLyricist                     N
+  'TOR'    OriginalReleaseYear                  N
+  'TOT'    OriginalAlbum                        N
+  'TP1'    Artist                               N
+  'TP2'    Band                                 N
+  'TP3'    Conductor                            N
+  'TP4'    InterpretedBy                        N
+  'TPA'    PartOfSet                            N
+  'TPB'    Publisher                            N
+  'TRC'    ISRC                                 N
+  'TRD'    RecordingDates                       N
+  'TRK'    Track                                N
+  'TSI'    Size                                 N
+  'TSS'    EncoderSettings                      N
+  'TT1'    Grouping                             N
+  'TT2'    Title                                N
+  'TT3'    Subtitle                             N
+  'TXT'    Lyricist                             N
+  'TXX'    UserDefinedText                      N
+  'TYE'    Year                                 N
+  'ULT'    Lyrics                               N
+  'WAF'    FileRUL                              N
+  'WAR'    ArtistURL                            N
+  'WAS'    SourceURL                            N
+  'WCM'    CommercialURL                        N
+  'WCP'    CopyrightURL                         N
+  'WPB'    PublisherURL                         N
+  'WXX'    UserDefinedURL                       N
+
+=head3 ID3 v2_3 Tags
+
+ID3 version 2.3 tags
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+  'APIC'   Picture                              N
+  'COMM'   Comment                              N
+  'IPLS'   InvolvedPeople                       N
+  'MCDI'   MusicCDIdentifier                    N
+  'PCNT'   PlayCounter                          N
+  'PRIV'   Private                              ID3 Private
+  'TALB'   Album                                N
+  'TBPM'   BeatsPerMinute                       N
+  'TCOM'   Composer                             N
+  'TCON'   Genre                                N
+  'TCOP'   Copyright                            N
+  'TDAT'   Date                                 N
+  'TDLY'   PlaylistDelay                        N
+  'TENC'   EncodedBy                            N
+  'TEXT'   Lyricist                             N
+  'TFLT'   FileType                             N
+  'TIME'   Time                                 N
+  'TIT1'   Grouping                             N
+  'TIT2'   Title                                N
+  'TIT3'   Subtitle                             N
+  'TKEY'   InitialKey                           N
+  'TLAN'   Language                             N
+  'TLEN'   Length                               N
+  'TMED'   Media                                N
+  'TOAL'   OriginalAlbum                        N
+  'TOFN'   OriginalFilename                     N
+  'TOLY'   OriginalLyricist                     N
+  'TOPE'   OriginalArtist                       N
+  'TORY'   OriginalReleaseYear                  N
+  'TOWN'   FileOwner                            N
+  'TPE1'   Artist                               N
+  'TPE2'   Band                                 N
+  'TPE3'   Conductor                            N
+  'TPE4'   InterpretedBy                        N
+  'TPOS'   PartOfSet                            N
+  'TPUB'   Publisher                            N
+  'TRCK'   Track                                N
+  'TRDA'   RecordingDates                       N
+  'TRSN'   InternetRadioStationName             N
+  'TRSO'   InternetRadioStationOwner            N
+  'TSIZ'   Size                                 N
+  'TSRC'   ISRC                                 N
+  'TSSE'   EncoderSettings                      N
+  'TXXX'   UserDefinedText                      N
+  'TYER'   Year                                 N
+  'USER'   TermsOfUse                           N
+  'USLT'   Lyrics                               N
+  'WCOM'   CommercialURL                        N
+  'WCOP'   CopyrightURL                         N
+  'WOAF'   FileRUL                              N
+  'WOAR'   ArtistURL                            N
+  'WOAS'   SourceURL                            N
+  'WORS'   InternetRadioStationURL              N
+  'WPAY'   PaymentURL                           N
+  'WPUB'   PublisherURL                         N
+  'WXXX'   UserDefinedURL                       N
+
+=head3 ID3 Private Tags
+
+ID3 private (PRIV) tags.
+
+  Tag ID               Tag Name                 Writable
+  ------               --------                 --------
+  'AverageLevel'       AverageLevel             N
+  'PeakValue'          PeakValue                N
+  'XMP'                XMP                      XMP
+
+=head3 ID3 v2_4 Tags
+
+ID3 version 2.4 tags
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+  'APIC'   Picture                              N
+  'COMM'   Comment                              N
+  'MCDI'   MusicCDIdentifier                    N
+  'PCNT'   PlayCounter                          N
+  'PRIV'   Private                              ID3 Private
+  'TALB'   Album                                N
+  'TBPM'   BeatsPerMinute                       N
+  'TCOM'   Composer                             N
+  'TCON'   Genre                                N
+  'TCOP'   Copyright                            N
+  'TDEN'   EncodingTime                         N
+  'TDLY'   PlaylistDelay                        N
+  'TDOR'   OriginalReleaseTime                  N
+  'TDRC'   RecordingTime                        N
+  'TDRL'   ReleaseTime                          N
+  'TDTG'   TaggingTime                          N
+  'TENC'   EncodedBy                            N
+  'TEXT'   Lyricist                             N
+  'TFLT'   FileType                             N
+  'TIPL'   InvolvedPeople                       N
+  'TIT1'   Grouping                             N
+  'TIT2'   Title                                N
+  'TIT3'   Subtitle                             N
+  'TKEY'   InitialKey                           N
+  'TLAN'   Language                             N
+  'TLEN'   Length                               N
+  'TMCL'   MusicianCredits                      N
+  'TMED'   Media                                N
+  'TMOO'   Mood                                 N
+  'TOAL'   OriginalAlbum                        N
+  'TOFN'   OriginalFilename                     N
+  'TOLY'   OriginalLyricist                     N
+  'TOPE'   OriginalArtist                       N
+  'TOWN'   FileOwner                            N
+  'TPE1'   Artist                               N
+  'TPE2'   Band                                 N
+  'TPE3'   Conductor                            N
+  'TPE4'   InterpretedBy                        N
+  'TPOS'   PartOfSet                            N
+  'TPRO'   ProducedNotice                       N
+  'TPUB'   Publisher                            N
+  'TRCK'   Track                                N
+  'TRSN'   InternetRadioStationName             N
+  'TRSO'   InternetRadioStationOwner            N
+  'TSOA'   AlbumSortOrder                       N
+  'TSOP'   PerformerSortOrder                   N
+  'TSOT'   TitleSortOrder                       N
+  'TSRC'   ISRC                                 N
+  'TSSE'   EncoderSettings                      N
+  'TSST'   SetSubtitle                          N
+  'TXXX'   UserDefinedText                      N
+  'USER'   TermsOfUse                           N
+  'USLT'   Lyrics                               N
+  'WCOM'   CommercialURL                        N
+  'WCOP'   CopyrightURL                         N
+  'WOAF'   FileRUL                              N
+  'WOAR'   ArtistURL                            N
+  'WOAS'   SourceURL                            N
+  'WORS'   InternetRadioStationURL              N
+  'WPAY'   PaymentURL                           N
+  'WPUB'   PublisherURL                         N
+  'WXXX'   UserDefinedURL                       N
+
+=head2 Jpeg2000 Tags
+
+The tags below are extracted from JPEG 2000 images, however ExifTool
+currently writes only EXIF, IPTC and XMP tags in these images.
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+  'asoc'   Association                          N
+  'bfil'   BinaryFilter                         N
+  'bpcc'   BitsPerComponent                     N
+  'cdef'   ComponentDefinition                  N
+  'cgrp'   ColorGroup                           N
+  'chck'   DigitalSignature                     N
+  'cmap'   ComponentMapping                     N
+  'colr'   ICC_Profile                          ICC_Profile
+           Colorspace                           N
+           ColorSpecification                   N
+  'comp'   Composition                          N
+  'copt'   CompositionOptions                   N
+  'cref'   Cross-Reference                      N
+  'creg'   CodestreamRegistration               N
+  'drep'   DesiredReproductions                 N
+  'dtbl'   DataReference                        N
+  'flst'   FragmentList                         N
+  'free'   Free                                 N
+  'ftbl'   FragmentTable                        N
+  'ftyp'   FileType                             N
+  'gtso'   GraphicsTechnologyStandardOutput     N
+  'ihdr'   ImageHeader                          Jpeg2000 ImageHeader
+  'inst'   InstructionSet                       N
+  'jP  '   JP2Signature                         N
+  'jp2c'   ContiguousCodestream                 N
+  'jp2h'   JP2Header                            Jpeg2000
+  'jp2i'   IntellectualProperty                 XMP
+  'jpch'   CodestreamHeader                     Jpeg2000
+  'jplh'   CompositingLayerHeader               Jpeg2000
+  'lbl '   Label                                N
+  'mdat'   MediaData                            N
+  'mp7b'   MPEG7Binary                          N
+  'nlst'   NumberList                           N
+  'opct'   Opacity                              N
+  'pclr'   Palette                              N
+  'prfl'   Profile                              N
+  'res '   Resolution                           Jpeg2000
+  'resc'   CaptureResolution                    Jpeg2000 CaptureResolution
+  'resd'   DisplayResolution                    Jpeg2000 DisplayResolution
+  'roid'   ROIDescription                       N
+  'rreq'   ReaderRequirements                   N
+  'uinf'   UUIDInfo                             Jpeg2000
+  'ulst'   UUIDList                             N
+  'url '   URL                                  N
+  'uuid'   UUID-EXIF                            EXIF
+           UUID-IPTC                            IPTC
+           UUID-XMP                             XMP
+           UUID-GeoJP2                          EXIF
+           UUID-Unknown                         N
+  'xml '   XML                                  XMP
+
+=head3 Jpeg2000 ImageHeader Tags
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      0    ImageHeight                          N
+      4    ImageWidth                           N
+      8    NumberOfComponents                   N
+     10    BitsPerComponent                     N
+     11    Compression                          N
+
+=head3 Jpeg2000 CaptureResolution Tags
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      0    CaptureYResolution                   N
+      4    CaptureXResolution                   N
+      8    CaptureYResolutionUnit               N
+      9    CaptureXResolutionUnit               N
+
+=head3 Jpeg2000 DisplayResolution Tags
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      0    DisplayYResolution                   N
+      4    DisplayXResolution                   N
+      8    DisplayYResolutionUnit               N
+      9    DisplayXResolutionUnit               N
+
+=head2 BMP Tags
+
+There really isn't much meta information in a BMP file as such, just a bit
+of image related information.
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      4    ImageWidth                           N
+      8    ImageHeight                          N
+     12    Planes                               N
+     14    BitDepth                             N
+     16    Compression                          N
+     20    ImageLength                          N
+     24    PixelsPerMeterX                      N
+     28    PixelsPerMeterY                      N
+     32    NumColors                            N
+     36    NumImportantColors                   N
+
+=head2 PICT Tags
+
+The PICT format contains no true meta information, except for the possible
+exception of the LongComment opcode.  By default, only ImageWidth,
+ImageHeight and X/YResolution are extracted from a PICT image.  Tags in the
+following table represent image opcodes.  Extraction of these tags is
+experimental, and is only enabled with the Verbose or Unknown options.
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+  0x0000   Nop                                  N
+  0x0001   ClipRgn                              N
+  0x0002   BkPat                                N
+  0x0003   TxFont                               N
+  0x0004   TxFace                               N
+  0x0005   TxMode                               N
+  0x0006   SpExtra                              N
+  0x0007   PnSize                               N
+  0x0008   PnMode                               N
+  0x0009   PnPat                                N
+  0x000a   FillPat                              N
+  0x000b   OvSize                               N
+  0x000c   Origin                               N
+  0x000d   TxSize                               N
+  0x000e   FgColor                              N
+  0x000f   BkColor                              N
+  0x0010   TxRatio                              N
+  0x0011   VersionOp                            N
+  0x0012   BkPixPat                             N
+  0x0013   PnPixPat                             N
+  0x0014   FillPixPat                           N
+  0x0015   PnLocHFrac                           N
+  0x0016   ChExtra                              N
+  0x0017   Reserved                             N
+  0x001a   RGBFgCol                             N
+  0x001b   RGBBkCol                             N
+  0x001c   HiliteMode                           N
+  0x001d   HiliteColor                          N
+  0x001e   DefHilite                            N
+  0x001f   OpColor                              N
+  0x0020   Line                                 N
+  0x0021   LineFrom                             N
+  0x0022   ShortLine                            N
+  0x0023   ShortLineFrom                        N
+  0x0024   Reserved                             N
+  0x0028   LongText                             N
+  0x0029   DHText                               N
+  0x002a   DVText                               N
+  0x002b   DHDVText                             N
+  0x002c   FontName                             N
+  0x002d   LineJustify                          N
+  0x002e   GlyphState                           N
+  0x002f   Reserved                             N
+  0x0030   FrameRect                            N
+  0x0031   PaintRect                            N
+  0x0032   EraseRect                            N
+  0x0033   InvertRect                           N
+  0x0034   FillRect                             N
+  0x0035   Reserved                             N
+  0x0038   FrameSameRect                        N
+  0x0039   PaintSameRect                        N
+  0x003a   EraseSameRect                        N
+  0x003b   InvertSameRect                       N
+  0x003c   FillSameRect                         N
+  0x003d   Reserved                             N
+  0x0040   FrameRRect                           N
+  0x0041   PaintRRect                           N
+  0x0042   EraseRRect                           N
+  0x0043   InvertRRect                          N
+  0x0044   FillRRect                            N
+  0x0045   Reserved                             N
+  0x0048   FrameSameRRect                       N
+  0x0049   PaintSameRRect                       N
+  0x004a   EraseSameRRect                       N
+  0x004b   InvertSameRRect                      N
+  0x004c   FillSameRRect                        N
+  0x004d   Reserved                             N
+  0x0050   FrameOval                            N
+  0x0051   PaintOval                            N
+  0x0052   EraseOval                            N
+  0x0053   InvertOval                           N
+  0x0054   FillOval                             N
+  0x0055   Reserved                             N
+  0x0058   FrameSameOval                        N
+  0x0059   PaintSameOval                        N
+  0x005a   EraseSameOval                        N
+  0x005b   InvertSameOval                       N
+  0x005c   FillSameOval                         N
+  0x005d   Reserved                             N
+  0x0060   FrameArc                             N
+  0x0061   PaintArc                             N
+  0x0062   EraseArc                             N
+  0x0063   InvertArc                            N
+  0x0064   FillArc                              N
+  0x0065   Reserved                             N
+  0x0068   FrameSameArc                         N
+  0x0069   PaintSameArc                         N
+  0x006a   EraseSameArc                         N
+  0x006b   InvertSameArc                        N
+  0x006c   FillSameArc                          N
+  0x006d   Reserved                             N
+  0x0070   FramePoly                            N
+  0x0071   PaintPoly                            N
+  0x0072   ErasePoly                            N
+  0x0073   InvertPoly                           N
+  0x0074   FillPoly                             N
+  0x0075   Reserved                             N
+  0x0078   FrameSamePoly                        N
+  0x0079   PaintSamePoly                        N
+  0x007a   EraseSamePoly                        N
+  0x007b   InvertSamePoly                       N
+  0x007c   FillSamePoly                         N
+  0x007d   Reserved                             N
+  0x0080   FrameRgn                             N
+  0x0081   PaintRgn                             N
+  0x0082   EraseRgn                             N
+  0x0083   InvertRgn                            N
+  0x0084   FillRgn                              N
+  0x0085   Reserved                             N
+  0x0088   FrameSameRgn                         N
+  0x0089   PaintSameRgn                         N
+  0x008a   EraseSameRgn                         N
+  0x008b   InvertSameRgn                        N
+  0x008c   FillSameRgn                          N
+  0x008d   Reserved                             N
+  0x0090   BitsRect                             N
+  0x0091   BitsRgn                              N
+  0x0092   Reserved                             N
+  0x0098   PackBitsRect                         N
+  0x0099   PackBitsRgn                          N
+  0x009a   DirectBitsRect                       N
+  0x009b   DirectBitsRgn                        N
+  0x009c   Reserved                             N
+  0x009d   Reserved                             N
+  0x009e   Reserved                             N
+  0x009f   Reserved                             N
+  0x00a0   ShortComment                         N
+  0x00a1   LongComment                          Photoshop
+                                                ICC_Profile
+  0x00a2   Reserved                             N
+  0x00b0   Reserved                             N
+  0x00d0   Reserved                             N
+  0x00ff   OpEndPic                             N
+  0x0100   Reserved                             N
+  0x0200   Reserved                             N
+  0x02ff   Version                              N
+  0x0300   Reserved                             N
+  0x0bff   Reserved                             N
+  0x0c00   HeaderOp                             N
+  0x0c01   Reserved                             N
+  0x7f00   Reserved                             N
+  0x8000   Reserved                             N
+  0x8100   Reserved                             N
+  0x8200   CompressedQuickTime                  N
+  0x8201   UncompressedQuickTime                N
+  0xffff   Reserved                             N
+
+=head2 PNG Tags
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+  'IHDR'   ImageHeader                          PNG ImageHeader
+  'PLTE'   Palette                              N
+  'bKGD'   BackgroundColor                      N
+  'cHRM'   PrimaryChromaticities                PNG PrimaryChromaticities
+  'fRAc'   FractalParameters                    N
+  'gAMA'   Gamma                                N
+  'gIFg'   GIFGraphicControlExtension           N
+  'gIFt'   GIFPlainTextExtension                N
+  'gIFx'   GIFApplicationExtension              N
+  'hIST'   PaletteHistogram                     N
+  'iCCP'   ICC_Profile                          ICC_Profile
+  'iTXt'   InternationalText                    PNG TextualData
+  'oFFs'   ImageOffset                          N
+  'pCAL'   PixelCalibration                     N
+  'pHYs'   PhysicalPixel                        PNG PhysicalPixel
+  'sBIT'   SignificantBits                      N
+  'sPLT'   SuggestedPalette                     N
+  'sRGB'   SRGBRendering                        N
+  'tEXt'   TextualData                          PNG TextualData
+  'tIME'   ModifyDate                           Y
+  'tRNS'   Transparency                         N
+  'tXMP'   XMP                                  XMP
+  'zTXt'   CompressedText                       PNG TextualData
+
+=head3 PNG ImageHeader Tags
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      0    ImageWidth                           N
+      4    ImageHeight                          N
+      8    BitDepth                             N
+      9    ColorType                            N
+     10    Compression                          N
+     11    Filter                               N
+     12    Interlace                            N
+
+=head3 PNG PrimaryChromaticities Tags
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      0    WhitePointX                          N
+      1    WhitePointY                          N
+      2    RedX                                 N
+      3    RedY                                 N
+      4    GreenX                               N
+      5    GreenY                               N
+      6    BlueX                                N
+      7    BlueY                                N
+
+=head3 PNG TextualData Tags
+
+The PNG TextualData format allows aribrary tag names to be used.  The tags
+listed below are the only ones that can be written (unless new user-defined
+tags are added via the configuration file), however ExifTool will extract
+any other TextualData tags that are found.
+
+The information for the TextualData tags may be stored as tEXt, zTXt or iTXt
+chunks in the PNG image.  ExifTool will read and edit tags in their original
+form, but tEXt chunks are written by default when creating new tags.
+Compressed zTXt chunks are written only if Compress::Zlib is available, and
+only for profile information or when the -z (Compress) option is specified.
+
+Some of the tags below are not registered as part of the PNG specification,
+but are included here because they are generated by other software such as
+ImageMagick.
+
+  Tag ID                  Tag Name              Writable
+  ------                  --------              --------
+  'Artist'                Artist                string
+  'Author'                Author                string
+  'Comment'               Comment               string
+  'Copyright'             Copyright             string
+  'Creation Time'         CreationTime          string
+  'Description'           Description           string
+  'Disclaimer'            Disclaimer            string
+  'Document'              Document              string
+  'Label'                 Label                 string
+  'Make'                  Make                  string
+  'Model'                 Model                 string
+  'Raw profile type APP1' APP1_Profile          EXIF
+                                                XMP
+  'Raw profile type exif' EXIF_Profile          EXIF
+  'Raw profile type icc'  ICC_Profile           ICC_Profile
+  'Raw profile type icm'  ICC_Profile           ICC_Profile
+  'Raw profile type iptc' IPTC_Profile          Photoshop
+  'Raw profile type xmp'  XMP_Profile           XMP
+  'Software'              Software              string
+  'Source'                Source                string
+  'TimeStamp'             TimeStamp             string
+  'Title'                 Title                 string
+  'URL'                   URL                   string
+  'Warning'               PNGWarning            string
+  'XML:com.adobe.xmp'     XMP                   XMP
+
+=head3 PNG PhysicalPixel Tags
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      0    PixelsPerUnitX                       N
+      4    PixelsPerUnitY                       N
+      8    PixelUnits                           N
+
+=head2 MNG Tags
+
+This table contains definitions for tags found in MNG and JNG images.  MNG
+is a superset of PNG and JNG, so a MNG image may contain any of these tags
+as well as any PNG tags.  Conversely, only some of these tags are valid for
+JNG images.
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+  'BACK'   Background                           MNG Background
+  'BASI'   BasisObject                          MNG BasisObject
+  'CLIP'   ClipObjects                          MNG ClipObjects
+  'CLON'   CloneObject                          MNG CloneObject
+  'DBYK'   DropByKeyword                        N
+  'DEFI'   DefineObject                         MNG DefineObject
+  'DHDR'   DeltaPNGHeader                       MNG DeltaPNGHeader
+  'DISC'   DiscardObjects                       N
+  'DROP'   DropChunks                           N
+  'FRAM'   Frame                                N
+  'JHDR'   JNGHeader                            MNG JNGHeader
+  'LOOP'   Loop                                 MNG Loop
+  'MAGN'   MagnifyObject                        MNG MagnifyObject
+  'MHDR'   MNGHeader                            MNG MNGHeader
+  'MOVE'   MoveObjects                          MNG MoveObjects
+  'ORDR'   OrderingRestrictions                 N
+  'PAST'   PasteImage                           MNG PasteImage
+  'PPLT'   PartialPalette                       N
+  'PROM'   PromoteParent                        MNG PromoteParent
+  'SAVE'   SaveObjects                          N
+  'SEEK'   SeekPoint                            N
+  'SHOW'   ShowObjects                          MNG ShowObjects
+  'TERM'   TerminationAction                    MNG TerminationAction
+  'eXPi'   ExportImage                          MNG ExportImage
+  'fPRI'   FramePriority                        MNG FramePriority
+  'nEED'   ResourcesNeeded                      N
+  'pHYg'   GlobalPixelSize                      PNG PhysicalPixel
+
+=head3 MNG Background Tags
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      0    BackgroundColor                      N
+      6    MandatoryBackground                  N
+      7    BackgroundImageID                    N
+      9    BackgroundTiling                     N
+
+=head3 MNG BasisObject Tags
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      0    ImageWidth                           N
+      4    ImageHeight                          N
+      8    BitDepth                             N
+      9    ColorType                            N
+     10    Compression                          N
+     11    Filter                               N
+     12    Interlace                            N
+     13    RedSample                            N
+     17    GreenSample                          N
+     21    BlueSample                           N
+     25    AlphaSample                          N
+     26    Viewable                             N
+
+=head3 MNG ClipObjects Tags
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      0    FirstObject                          N
+      2    LastObject                           N
+      4    DeltaType                            N
+      5    ClipBoundary                         N
+
+=head3 MNG CloneObject Tags
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      0    SourceID                             N
+      2    CloneID                              N
+      4    CloneType                            N
+      5    DoNotShow                            N
+      6    ConcreteFlag                         N
+      7    LocalDeltaType                       N
+      8    DeltaXY                              N
+
+=head3 MNG DefineObject Tags
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      0    ObjectID                             N
+      2    DoNotShow                            N
+      3    ConcreteFlag                         N
+      4    XYLocation                           N
+     12    ClippingBoundary                     N
+
+=head3 MNG DeltaPNGHeader Tags
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      0    ObjectID                             N
+      2    ImageType                            N
+      3    DeltaType                            N
+      4    BlockSize                            N
+     12    BlockLocation                        N
+
+=head3 MNG JNGHeader Tags
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      0    ImageWidth                           N
+      4    ImageHeight                          N
+      8    ColorType                            N
+      9    BitDepth                             N
+     10    Compression                          N
+     11    Interlace                            N
+     12    AlphaBitDepth                        N
+     13    AlphaCompression                     N
+     14    AlphaFilter                          N
+     15    AlphaInterlace                       N
+
+=head3 MNG Loop Tags
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      0    NestLevel                            N
+      1    IterationCount                       N
+      5    TerminationCondition                 N
+      6    IterationMinMax                      N
+     14    SignalNumber                         N
+
+=head3 MNG MagnifyObject Tags
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      0    FirstObjectID                        N
+      2    LastObjectID                         N
+      4    XMethod                              N
+      5    XMag                                 N
+      7    YMag                                 N
+      9    LeftMag                              N
+     11    RightMag                             N
+     13    TopMag                               N
+     15    BottomMag                            N
+     17    YMethod                              N
+
+=head3 MNG MNGHeader Tags
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      0    ImageWidth                           N
+      1    ImageHeight                          N
+      2    TicksPerSecond                       N
+      3    NominalLayerCount                    N
+      4    NominalFrameCount                    N
+      5    NominalPlayTime                      N
+      6    SimplicityProfile                    N
+
+=head3 MNG MoveObjects Tags
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      0    FirstObject                          N
+      2    LastObject                           N
+      4    DeltaType                            N
+      5    DeltaXY                              N
+
+=head3 MNG PasteImage Tags
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      0    DestinationID                        N
+      2    TargetDeltaType                      N
+      3    TargetXY                             N
+     11    SourceID                             N
+     13    CompositionMode                      N
+     14    Orientation                          N
+     15    OffsetOrigin                         N
+     16    OffsetXY                             N
+     24    BoundaryOrigin                       N
+     25    PastClippingBoundary                 N
+
+=head3 MNG PromoteParent Tags
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      0    NewColorType                         N
+      1    NewBitDepth                          N
+      2    FillMethod                           N
+
+=head3 MNG ShowObjects Tags
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      0    FirstObject                          N
+      2    LastObject                           N
+      4    ShowMode                             N
+
+=head3 MNG TerminationAction Tags
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      0    TerminationAction                    N
+      1    IterationEndAction                   N
+      2    Delay                                N
+      6    IterationMax                         N
+
+=head3 MNG ExportImage Tags
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      0    SnapshotID                           N
+      2    SnapshotName                         N
+
+=head3 MNG FramePriority Tags
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      0    DeltaType                            N
+      2    Priority                             N
+
+=head2 MIFF Tags
+
+The MIFF format allows aribrary tag names to be used.  Only the standard tag
+names are listed below, however ExifTool will decode any tags found in the
+image.
+
+  Tag ID               Tag Name                 Writable
+  ------               --------                 --------
+  'background-color'   BackgroundColor          N
+  'blue-primary'       BluePrimary              N
+  'border-color'       BorderColor              N
+  'class'              Class                    N
+  'colors'             Colors                   N
+  'colorspace'         Colorspace               N
+  'columns'            ImageWidth               N
+  'compression'        Compression              N
+  'delay'              Delay                    N
+  'depth'              Depth                    N
+  'dispose'            Dispose                  N
+  'gamma'              Gamma                    N
+  'green-primary'      GreenPrimary             N
+  'id'                 ID                       N
+  'iterations'         Iterations               N
+  'label'              Label                    N
+  'matt-color'         MattColor                N
+  'matte'              Matte                    N
+  'montage'            Montage                  N
+  'packets'            Packets                  N
+  'page'               Page                     N
+  'profile-APP1'       APP1_Profile             EXIF
+                                                XMP
+  'profile-exif'       EXIF_Profile             EXIF
+  'profile-icc'        ICC_Profile              ICC_Profile
+  'profile-iptc'       IPTC_Profile             Photoshop
+  'profile-xmp'        XMP_Profile              XMP
+  'red-primary'        RedPrimary               N
+  'rendering-intent'   RenderingIntent          N
+  'resolution'         Resolution               N
+  'rows'               ImageHeight              N
+  'scene'              Scene                    N
+  'signature'          Signature                N
+  'units'              Units                    N
+  'white-point'        WhitePoint               N
+
+=head2 PDF Tags
+
+The tags listed in the PDF tables below are those which are used by ExifTool
+to extract meta information, but they are only a small fraction of the total
+number of available PDF tags.
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+  'Info'   Info                                 PDF Info
+  'Root'   Root                                 PDF Root
+
+=head3 PDF Info Tags
+
+As well as the tags listed below, the PDF specification allows for
+user-defined tags to exist in the Info dictionary.  These tags, which should
+have corresponding XMP-pdfx entries in the PDF Metadata, are also extracted
+by ExifTool.
+
+  Tag ID               Tag Name                 Writable
+  ------               --------                 --------
+  'Author'             Author                   N
+  'CreationDate'       CreateDate               N
+  'Creator'            Creator                  N
+  'Keywords'           Keywords                 N+
+  'ModDate'            ModifyDate               N
+  'Producer'           Producer                 N
+  'Subject'            Subject                  N
+  'Title'              Title                    N
+  'Trapped'            Trapped                  N
+
+=head3 PDF Root Tags
+
+This is the PDF document catalog.
+
+  Tag ID       Tag Name                         Writable
+  ------       --------                         --------
+  'Metadata'   Metadata                         PDF Metadata
+  'Pages'      Pages                            PDF Pages
+
+=head3 PDF Metadata Tags
+
+  Tag ID               Tag Name                 Writable
+  ------               --------                 --------
+  'XML_stream'         XMP                      XMP
+
+=head3 PDF Pages Tags
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+  'Count'  PageCount                            N
+  'Kids'   Kids                                 PDF Kids
+
+=head3 PDF Kids Tags
+
+  Tag ID               Tag Name                 Writable
+  ------               --------                 --------
+  'Metadata'           Metadata                 PDF Metadata
+  'PieceInfo'          PieceInfo                PDF PieceInfo
+  'Resources'          Resources                PDF Resources
+
+=head3 PDF PieceInfo Tags
+
+  Tag ID               Tag Name                 Writable
+  ------               --------                 --------
+  'AdobePhotoshop'     AdobePhotoshop           PDF AdobePhotoshop
+
+=head3 PDF AdobePhotoshop Tags
+
+  Tag ID       Tag Name                         Writable
+  ------       --------                         --------
+  'Private'    Private                          PDF Private
+
+=head3 PDF Private Tags
+
+  Tag ID               Tag Name                 Writable
+  ------               --------                 --------
+  'ImageResources'     ImageResources           PDF ImageResources
+
+=head3 PDF ImageResources Tags
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+  'stream' Stream                               Photoshop
+
+=head3 PDF Resources Tags
+
+  Tag ID               Tag Name                 Writable
+  ------               --------                 --------
+  'ColorSpace'         ColorSpace               PDF ColorSpace
+
+=head3 PDF ColorSpace Tags
+
+  Tag ID               Tag Name                 Writable
+  ------               --------                 --------
+  'DefaultRGB'         DefaultRGB               PDF DefaultRGB
+
+=head3 PDF DefaultRGB Tags
+
+  Tag ID       Tag Name                         Writable
+  ------       --------                         --------
+  'ICCBased'   ICCBased                         PDF ICCBased
+
+=head3 PDF ICCBased Tags
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+  'stream' Stream                               ICC_Profile
+
+=head2 PostScript Tags
+
+  Tag ID               Tag Name                 Writable
+  ------               --------                 --------
+  'Author'             Author                   string
+  'BeginICCProfile'    ICC_Profile              ICC_Profile
+  'BeginPhotoshop'     PhotoshopData            Photoshop
+  'BoundingBox'        BoundingBox              N
+  'Copyright'          Copyright                string
+  'CreationDate'       CreateDate               string
+  'Creator'            Creator                  string
+  'For'                For                      string
+  'ImageData'          ImageData                N
+  'Keywords'           Keywords                 string
+  'ModDate'            ModifyDate               string
+  'Pages'              Pages                    N
+  'Routing'            Routing                  string
+  'Subject'            Subject                  string
+  'TIFFPreview'        TIFFPreview              N
+  'Title'              Title                    string
+  'Version'            Version                  string
+  'begin_xml_packet'   XMP                      XMP
+
+=head2 Vorbis Tags
+
+ExifTool extracts the following Vorbis information from Ogg files.  As well
+as this, ExifTool also extracts FLAC and ID3 information from Ogg files.
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+  0x0001   Identification                       Vorbis Identification
+  0x0003   Comments                             Vorbis Comments
+
+=head3 Vorbis Identification Tags
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      0    VorbisVersion                        N
+      4    AudioChannels                        N
+      5    SampleRate                           N
+      9    MaximumBitrate                       N
+     13    NominalBitrate                       N
+     17    MinimumBitrate                       N
+
+=head3 Vorbis Comments Tags
+
+The tags below are only some common tags found in the Vorbis comments of Ogg
+Vorbis and Ogg FLAC audio files, however ExifTool will extract values from
+any tag found, even if not listed here.
+
+  Tag ID                  Tag Name              Writable
+  ------                  --------              --------
+  'ALBUM'                 Album                 N
+  'ARTIST'                Artist                N+
+  'COMMENT'               Comment               N
+  'CONTACT'               Contact               N+
+  'COPYRIGHT'             Copyright             N
+  'COVERART'              CoverArt              N
+  'COVERARTMIME'          CoverArtMIMEType      N
+  'DATE'                  Date                  N
+  'DESCRIPTION'           Description           N
+  'ENCODED_BY'            EncodedBy             N
+  'ENCODED_USING'         EncodedUsing          N
+  'GENRE'                 Genre                 N
+  'ISRC'                  ISRCNumber            N
+  'LICENSE'               License               N
+  'LOCATION'              Location              N
+  'ORGANIZATION'          Organization          N
+  'PERFORMER'             Performer             N+
+  'REPLAYGAIN_ALBUM_GAIN' ReplayGainAlbumGain   N
+  'REPLAYGAIN_ALBUM_PEAK' ReplayGainAlbumPeak   N
+  'REPLAYGAIN_TRACK_GAIN' ReplayGainTrackGain   N
+  'REPLAYGAIN_TRACK_PEAK' ReplayGainTrackPeak   N
+  'TITLE'                 Title                 N
+  'TRACKNUMBER'           TrackNumber           N
+  'VERSION'               Version               N
+  'vendor'                Vendor                N
+
+=head2 FLAC Tags
+
+Free Lossless Audio Codec (FLAC) meta information.  ExifTool also extracts
+ID3 information from these files.
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+  0x0000   StreamInfo                           FLAC StreamInfo
+  0x0004   VorbisComment                        Vorbis Comments
+
+=head3 FLAC StreamInfo Tags
+
+FLAC is big-endian, so bit 0 is the high-order bit in this table.
+
+  Tag ID               Tag Name                 Writable
+  ------               --------                 --------
+  'Bit000-015'         BlockSizeMin             N
+  'Bit016-031'         BlockSizeMax             N
+  'Bit032-055'         FrameSizeMin             N
+  'Bit056-079'         FrameSizeMax             N
+  'Bit080-099'         SampleRate               N
+  'Bit100-102'         Channels                 N
+  'Bit103-107'         BitsPerSample            N
+  'Bit108-143'         TotalSamples             N
+
+=head2 APE Tags
+
+Tags found in Monkey's Audio (APE) information.  Only a few common tags are
+listed below, but ExifTool will extract any tag found.  ExifTool supports
+APEv1 and APEv2 tags, as well as ID3 information in APE files.
+
+  Tag ID               Tag Name                 Writable
+  ------               --------                 --------
+  'Album'              Album                    N
+  'Artist'             Artist                   N
+  'Genre'              Genre                    N
+  'Title'              Title                    N
+  'Tool Name'          ToolName                 N
+  'Tool Version'       ToolVersion              N
+  'Track'              Track                    N
+  'Year'               Year                     N
+
+=head3 APE NewHeader Tags
+
+APE MAC audio header for version 3.98 or later.
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      0    CompressionLevel                     N
+      2    BlocksPerFrame                       N
+      4    FinalFrameBlocks                     N
+      6    TotalFrames                          N
+      8    BitsPerSample                        N
+      9    Channels                             N
+     10    SampleRate                           N
+
+=head3 APE OldHeader Tags
+
+APE MAC audio header for version 3.97 or earlier.
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      0    APEVersion                           N
+      1    CompressionLevel                     N
+      3    Channels                             N
+      4    SampleRate                           N
+     10    TotalFrames                          N
+     12    FinalFrameBlocks                     N
+
+=head2 MPC Tags
+
+Tags used in Musepack (MPC) audio files.  ExifTool also extracts ID3 and APE
+information from these files.
+
+  Tag ID               Tag Name                 Writable
+  ------               --------                 --------
+  'Bit032-063'         TotalFrames              N
+  'Bit080-081'         SampleRate               N
+  'Bit084-087'         Quality                  N
+  'Bit088-093'         MaxBand                  N
+  'Bit096-111'         ReplayGainTrackPeak      N
+  'Bit112-127'         ReplayGainTrackGain      N
+  'Bit128-143'         ReplayGainAlbumPeak      N
+  'Bit144-159'         ReplayGainAlbumGain      N
+  'Bit179'             FastSeek                 N
+  'Bit191'             Gapless                  N
+  'Bit216-223'         EncoderVersion           N
+
+=head2 MPEG Tags
+
+The MPEG format doesn't specify any file-level meta information.  In lieu of
+this, information is extracted from the first audio and video frame headers
+in the file.
+
+=head3 MPEG Audio Tags
+
+  Tag ID       Tag Name                         Writable
+  ------       --------                         --------
+  'Bit11-12'   MPEGAudioVersion                 N
+  'Bit13-14'   AudioLayer                       N
+  'Bit16-19'   AudioBitrate                     N
+  'Bit20-21'   SampleRate                       N
+  'Bit24-25'   ChannelMode                      N
+  'Bit26'      MSStereo                         N
+  'Bit26-27'   ModeExtension                    N
+  'Bit27'      IntensityStereo                  N
+  'Bit28'      CopyrightFlag                    N
+  'Bit29'      OriginalMedia                    N
+  'Bit30-31'   Emphasis                         N
+
+=head3 MPEG Video Tags
+
+  Tag ID       Tag Name                         Writable
+  ------       --------                         --------
+  'Bit00-11'   ImageWidth                       N
+  'Bit12-23'   ImageHeight                      N
+  'Bit24-27'   AspectRatio                      N
+  'Bit28-31'   FrameRate                        N
+  'Bit32-49'   VideoBitrate                     N
+
+=head2 QuickTime Tags
+
+These tags are used in QuickTime MOV and MP4 videos, and QTIF images.  Tags
+with a question mark after their name are not extracted unless the Unknown
+option is set.
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+  'PICT'   PreviewPICT                          N
+  'free'   Free?                                N
+  'ftyp'   FrameType?                           N
+  'mdat'   Mdat?                                N
+  'moov'   Movie                                QuickTime Movie
+  'pnot'   Preview                              QuickTime Preview
+  'skip'   Skip?                                N
+  'wide'   Wide?                                N
+
+=head3 QuickTime Movie Tags
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+  'mvhd'   MovieHeader                          QuickTime MovieHdr
+  'trak'   Track                                QuickTime Track
+  'udta'   UserData                             QuickTime UserData
+
+=head3 QuickTime MovieHdr Tags
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      0    Version                              N
+      1    CreateDate                           N
+      2    ModifyDate                           N
+      3    TimeScale                            N
+      4    Duration                             N
+      5    PreferredRate                        N
+      6    PreferredVolume                      N
+     18    PreviewTime                          N
+     19    PreviewDuration                      N
+     20    PosterTime                           N
+     21    SelectionTime                        N
+     22    SelectionDuration                    N
+     23    CurrentTime                          N
+     24    NextTrackID                          N
+
+=head3 QuickTime Track Tags
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+  'mdia'   Media                                QuickTime Media
+  'tkhd'   TrackHeader                          QuickTime TrackHdr
+  'udta'   UserData                             QuickTime UserData
+
+=head3 QuickTime Media Tags
+
+MP4 only (most tags unknown because ISO charges for the specification).
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+  'minf'   Minf                                 QuickTime Minf
+
+=head3 QuickTime Minf Tags
+
+MP4 only (most tags unknown because ISO charges for the specification).
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+  'dinf'   Dinf                                 QuickTime Dinf
+  'stbl'   Stbl                                 QuickTime Stbl
+
+=head3 QuickTime Dinf Tags
+
+MP4 only (most tags unknown because ISO charges for the specification).
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+  [no tags known]
+
+=head3 QuickTime Stbl Tags
+
+MP4 only (most tags unknown because ISO charges for the specification).
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+  [no tags known]
+
+=head3 QuickTime TrackHdr Tags
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      0    TrackVersion                         N
+      1    TrackCreateDate                      N
+      2    TrackModifyDate                      N
+      3    TrackID                              N
+      5    TrackDuration                        N
+      8    TrackLayer                           N
+      9    TrackVolume                          N
+     19    ImageWidth                           N
+     20    ImageHeight                          N
+
+=head3 QuickTime UserData Tags
+
+Tag ID's beginning with the copyright symbol (hex 0xa9) are multi-language
+text, but ExifTool only extracts the text from the first language in the
+record.  ExifTool will extract any multi-language user data tags found, even
+if they don't exist in this table.
+
+  Tag ID       Tag Name                         Writable
+  ------       --------                         --------
+  'AllF'       PlayAllFrames                    N
+  'LOOP'       LoopStyle                        N
+  'SelO'       PlaySelection                    N
+  'TAGS'       PentaxTags                       Pentax MOV
+               NikonTags                        Nikon MOV
+               SanyoMOV                         Sanyo MOV
+               SanyoMP4                         Sanyo MP4
+               UnknownTags?                     N
+  'WLOC'       WindowLocation                   N
+  'meta'       Meta                             QuickTime Meta
+  'name'       Name                             N
+  'ptv '       PrintToVideo                     QuickTime Video
+  "\xa9cpy"    Copyright                        N
+  "\xa9day"    CreateDate                       N
+  "\xa9dir"    Director                         N
+  "\xa9ed1"    Edit1                            N
+  "\xa9ed2"    Edit2                            N
+  "\xa9ed3"    Edit3                            N
+  "\xa9ed4"    Edit4                            N
+  "\xa9ed5"    Edit5                            N
+  "\xa9ed6"    Edit6                            N
+  "\xa9ed7"    Edit7                            N
+  "\xa9ed8"    Edit8                            N
+  "\xa9ed9"    Edit9                            N
+  "\xa9fmt"    Format                           N
+  "\xa9inf"    Information                      N
+  "\xa9prd"    Producer                         N
+  "\xa9prf"    Performers                       N
+  "\xa9req"    Requirements                     N
+  "\xa9src"    Source                           N
+  "\xa9wrt"    Writer                           N
+
+=head3 QuickTime Meta Tags
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+  'ilst'   InfoList                             QuickTime InfoList
+
+=head3 QuickTime InfoList Tags
+
+  Tag ID       Tag Name                         Writable
+  ------       --------                         --------
+  '----'       iTunesInfo                       QuickTime iTunesInfo
+  'aART'       AlbumArtist                      N
+  'apid'       AppleStoreID                     N
+  'auth'       Author                           N
+  'covr'       CoverArt                         N
+  'cpil'       Compilation                      N
+  'cprt'       Copyright                        N
+  'disk'       DiskNumber                       N
+  'dscp'       Description                      N
+  'gnre'       Genre                            N
+  'perf'       Performer                        N
+  'pgap'       PlayGap                          N
+  'rtng'       Rating                           N
+  'titl'       Title                            N
+  'tmpo'       BeatsPerMinute                   N
+  'trkn'       TrackNumber                      N
+  "\xa9ART"    Artist                           N
+  "\xa9alb"    Album                            N
+  "\xa9cmt"    Comment                          N
+  "\xa9com"    Composer                         N
+  "\xa9day"    Year                             N
+  "\xa9des"    Description                      N
+  "\xa9gen"    Genre                            N
+  "\xa9grp"    Grouping                         N
+  "\xa9lyr"    Lyrics                           N
+  "\xa9nam"    Title                            N
+  "\xa9too"    Encoder                          N
+  "\xa9trk"    Track                            N
+  "\xa9wrt"    Composer                         N
+
+=head3 QuickTime iTunesInfo Tags
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+  [no tags known]
+
+=head3 QuickTime Video Tags
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      0    DisplaySize                          N
+      6    SlideShow                            N
+
+=head3 QuickTime Preview Tags
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      0    PreviewDate                          N
+      2    PreviewVersion                       N
+      3    PreviewAtomType                      N
+      5    PreviewAtomIndex                     N
+
+=head3 QuickTime ImageFile Tags
+
+Tags used in QTIF QuickTime Image Files.
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+  'idat'   ImageData                            N
+  'idsc'   ImageDescription                     QuickTime ImageDesc
+  'iicc'   ICC_Profile                          ICC_Profile
+
+=head3 QuickTime ImageDesc Tags
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      4    CompressorID                         N
+     20    VendorID                             N
+     28    Quality                              N
+     32    ImageWidth                           N
+     34    ImageHeight                          N
+     36    XResolution                          N
+     40    YResolution                          N
+     48    FrameCount                           N
+     50    NameLength                           N
+     51    Compressor                           N
+     82    BitDepth                             N
+
+=head2 Flash Tags
+
+The information below is extracted from the header of SWF (Shockwave Flash)
+files.
+
+  Tag ID               Tag Name                 Writable
+  ------               --------                 --------
+  'Compressed'         Compressed               N
+  'Duration'           Duration                 N
+  'FlashVersion'       FlashVersion             N
+  'FrameCount'         FrameCount               N
+  'FrameRate'          FrameRate                N
+  'ImageHeight'        ImageHeight              N
+  'ImageWidth'         ImageWidth               N
+
+=head3 Flash FLV Tags
+
+Information is extracted from the following packets in FLV (Flash Video)
+files.
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+  0x0008   Audio                                Flash Audio
+  0x0009   Video                                Flash Video
+  0x0012   Meta                                 Flash Meta
+
+=head3 Flash Audio Tags
+
+Information extracted from the Flash Audio header.
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+  'Bit0-3' AudioEncoding                        N
+  'Bit4-5' AudioSampleRate                      N
+  'Bit6'   AudioSampleBits                      N
+  'Bit7'   AudioChannels                        N
+
+=head3 Flash Video Tags
+
+Information extracted from the Flash Video header.
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+  'Bit4-7' VideoEncoding                        N
+
+=head3 Flash Meta Tags
+
+Below are a few observed FLV Meta tags, but ExifTool will attempt to extract
+information from any tag found.
+
+  Tag ID                   Tag Name             Writable
+  ------                   --------             --------
+  'audiocodecid'           AudioCodecID         N
+  'audiodatarate'          AudioBitrate         N
+  'audiodelay'             AudioDelay           N
+  'audiosamplerate'        AudioSampleRate      N
+  'audiosamplesize'        AudioSampleSize      N
+  'audiosize'              AudioSize            N
+  'canSeekToEnd'           CanSeekToEnd         N
+  'creationdate'           CreateDate           N
+  'cuePoints'              CuePoint             Flash CuePoint
+  'datasize'               DataSize             N
+  'duration'               Duration             N
+  'filesize'               FileSizeBytes        N
+  'framerate'              FrameRate            N
+  'hasAudio'               HasAudio             N
+  'hasCuePoints'           HasCuePoints         N
+  'hasKeyframes'           HasKeyFrames         N
+  'hasMetadata'            HasMetadata          N
+  'hasVideo'               HasVideo             N
+  'height'                 ImageHeight          N
+  'keyframesFilepositions' KeyFramePositions    N
+  'keyframesTimes'         KeyFramesTimes       N
+  'lastkeyframetimestamp'  LastKeyFrameTime     N
+  'lasttimestamp'          LastTimeStamp        N
+  'metadatacreator'        MetadataCreator      N
+  'metadatadate'           MetadataDate         N
+  'stereo'                 Stereo               N
+  'videocodecid'           VideoCodecID         N
+  'videodatarate'          VideoBitrate         N
+  'videosize'              VideoSize            N
+  'width'                  ImageWidth           N
+
+=head3 Flash CuePoint Tags
+
+These tag names are added to the CuePoint name to generate complete tag
+names like "CuePoint0Name".
+
+  Tag ID               Tag Name                 Writable
+  ------               --------                 --------
+  'name'               Name                     N
+  'parameters'         Parameter                Flash Parameter
+  'time'               Time                     N
+  'type'               Type                     N
+
+=head3 Flash Parameter Tags
+
+There are no pre-defined parameter tags, but ExifTool will extract any
+existing parameters, with tag names like "CuePoint0ParameterXxx".
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+  [no tags known]
+
+=head2 Real Tags
+
+ExifTool recognizes three basic types of Real audio/video files: 1)
+RealMedia (RM, RV and RMVB), 2) RealAudio (RA), and 3) Real Metafile (RAM
+and RPM).
+
+=head3 Real Media Tags
+
+These B<Tag ID>'s are Chunk ID's used in RealMedia and RealVideo (RM, RV and
+RMVB) files.
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+  'CONT'   CONT                                 Real ContentDescr
+  'MDPR'   MDPR                                 Real MediaProps
+  'PROP'   PROP                                 Real Properties
+  'RJMD'   RJMD                                 Real Metadata
+
+=head3 Real ContentDescr Tags
+
+  Sequence Tag Name                             Writable
+  -------- --------                             --------
+      0    TitleLen?                            N
+      1    Title                                N
+      2    AuthorLen?                           N
+      3    Author                               N
+      4    CopyrightLen?                        N
+      5    Copyright                            N
+      6    CommentLen?                          N
+      7    Comment                              N
+
+=head3 Real MediaProps Tags
+
+  Sequence Tag Name                             Writable
+  -------- --------                             --------
+      0    StreamNumber                         N
+      1    StreamMaxBitRate                     N
+      2    StreamAvgBitRate                     N
+      3    StreamMaxPacketSize                  N
+      4    StreamAvgPacketSize                  N
+      5    StreamStartTime                      N
+      6    StreamPreroll                        N
+      7    StreamDuration                       N
+      8    StreamNameLen?                       N
+      9    StreamName                           N
+     10    StreamMimeLen?                       N
+     11    StreamMimeType                       N
+     12    FileInfoLen?                         N
+     13    FileInfoLen2?                        N
+     14    FileInfoVersion                      N
+     15    PhysicalStreams?                     N
+     16    PhysicalStreamNumbers?               N
+     17    DataOffsets?                         N
+     18    NumRules?                            N
+     19    PhysicalStreamNumberMap?             N
+     20    NumProperties?                       N
+     21    FileInfoProperties                   Real FileInfo
+
+=head3 Real FileInfo Tags
+
+The following tags have been observed in the FileInfo properties, but any
+other existing information will also be extracted.
+
+  Tag ID               Tag Name                 Writable
+  ------               --------                 --------
+  'Audiences'          Audiences                N
+  'Audio Format'       AudioFormat              N
+  'Content Rating'     ContentRating            N
+  'Creation Date'      CreateDate               N
+  'Description'        Description              N
+  'File ID'            FileID                   N
+  'Generated By'       Software                 N
+  'Indexable'          Indexable                N
+  'Keywords'           Keywords                 N
+  'Modification Date'  ModifyDate               N
+  'Target Audiences'   TargetAudiences          N
+  'Video Quality'      VideoQuality             N
+  'audioMode'          AudioMode                N
+  'videoMode'          VideoMode                N
+
+=head3 Real Properties Tags
+
+  Sequence Tag Name                             Writable
+  -------- --------                             --------
+      0    MaxBitRate                           N
+      1    AvgBitRate                           N
+      2    MaxPacketSize                        N
+      3    AvgPacketSize                        N
+      4    NumPackets                           N
+      5    Duration                             N
+      6    Preroll                              N
+      7    IndexOffset?                         N
+      8    DataOffset?                          N
+      9    NumStreams                           N
+     10    Flags                                N
+
+=head3 Real Metadata Tags
+
+The tags below represent information which has been observed in the Real
+Metadata format, but ExifTool will extract any information it finds in this
+format.  (As far as I can tell from the referenced documentation, string
+values should be plain text, but this is not the case for the only sample
+file I have been able to obtain containing this information.  These tags
+could also be split into separate sub-directories, but this will wait until
+I have better documentation or a more complete set of samples.)
+
+  Tag ID               Tag Name                 Writable
+  ------               --------                 --------
+  'Album/Name'         AlbumName                N
+  'Track/Category'     TrackCategory            N
+  'Track/Comments'     TrackComments            N
+  'Track/Lyrics'       TrackLyrics              N
+
+=head3 Real Audio Tags
+
+Tags in the following table reference information extracted from various
+versions of RealAudio (RA) files.
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+  '.ra3'   RA3                                  Real AudioV3
+  '.ra4'   RA4                                  Real AudioV4
+  '.ra5'   RA5                                  Real AudioV5
+
+=head3 Real AudioV3 Tags
+
+  Sequence Tag Name                             Writable
+  -------- --------                             --------
+      0    Channels                             N
+      1    Unknown?                             N
+      2    BytesPerMinute                       N
+      3    AudioBytes                           N
+      4    TitleLen?                            N
+      5    Title                                N
+      6    ArtistLen?                           N
+      7    Artist                               N
+      8    CopyrightLen?                        N
+      9    Copyright                            N
+     10    CommentLen?                          N
+     11    Comment                              N
+
+=head3 Real AudioV4 Tags
+
+  Sequence Tag Name                             Writable
+  -------- --------                             --------
+      0    FourCC1?                             N
+      1    AudioFileSize?                       N
+      2    Version2?                            N
+      3    HeaderSize?                          N
+      4    CodecFlavorID?                       N
+      5    CodedFrameSize?                      N
+      6    AudioBytes                           N
+      7    BytesPerMinute                       N
+      8    Unknown?                             N
+      9    SubPacketH?                          N
+     10    AudioFrameSize                       N
+     11    SubPacketSize?                       N
+     12    Unknown?                             N
+     13    SampleRate                           N
+     14    Unknown?                             N
+     15    BitsPerSample                        N
+     16    Channels                             N
+     17    FourCC2Len?                          N
+     18    FourCC2?                             N
+     19    FourCC3Len?                          N
+     20    FourCC3?                             N
+     21    Unknown?                             N
+     22    Unknown?                             N
+     23    TitleLen?                            N
+     24    Title                                N
+     25    ArtistLen?                           N
+     26    Artist                               N
+     27    CopyrightLen?                        N
+     28    Copyright                            N
+     29    CommentLen?                          N
+     30    Comment                              N
+
+=head3 Real AudioV5 Tags
+
+  Sequence Tag Name                             Writable
+  -------- --------                             --------
+      0    FourCC1?                             N
+      1    AudioFileSize?                       N
+      2    Version2?                            N
+      3    HeaderSize?                          N
+      4    CodecFlavorID?                       N
+      5    CodedFrameSize?                      N
+      6    AudioBytes                           N
+      7    BytesPerMinute                       N
+      8    Unknown?                             N
+      9    SubPacketH?                          N
+     10    FrameSize?                           N
+     11    SubPacketSize?                       N
+     12    SampleRate                           N
+     13    SampleRate2?                         N
+     14    BitsPerSample                        N
+     15    Channels                             N
+     16    Genr?                                N
+     17    FourCC3?                             N
+
+=head3 Real Metafile Tags
+
+Tags representing information extracted from Real Audio Metafile and
+RealMedia Plug-in Metafile (RAM and RPM) files.
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+  'txt'    Text                                 N
+  'url'    URL                                  N
+
+=head2 RIFF Tags
+
+Windows WAV and AVI files are RIFF format files.  Meta information embedded
+in two types of RIFF C<LIST> chunks: C<INFO> and C<exif>.  As well, some
+information about the audio content is extracted from the C<fmt > chunk.
+
+  Tag ID               Tag Name                 Writable
+  ------               --------                 --------
+  'LIST_INFO'          Info                     RIFF Info
+  'LIST_exif'          Exif                     RIFF Exif
+  'LIST_hdrl'          Hdrl                     RIFF Hdrl
+  'fmt '               AudioFormat              RIFF AudioFormat
+
+=head3 RIFF Info Tags
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+  'IARL'   ArchivalLocation                     N
+  'IART'   Artist                               N
+  'ICMS'   Commissioned                         N
+  'ICMT'   Comment                              N
+  'ICOP'   Copyright                            N
+  'ICRD'   DateCreated                          N
+  'ICRP'   Cropped                              N
+  'IDIM'   Dimensions                           N
+  'IDPI'   DotsPerInch                          N
+  'IENG'   Engineer                             N
+  'IGNR'   Genre                                N
+  'IKEY'   Keywords                             N
+  'ILGT'   Lightness                            N
+  'IMED'   Medium                               N
+  'INAM'   Title                                N
+  'IPLT'   NumColors                            N
+  'IPRD'   Product                              N
+  'ISBJ'   Subject                              N
+  'ISFT'   Software                             N
+  'ISHP'   Sharpness                            N
+  'ISRC'   Source                               N
+  'ISRF'   SourceForm                           N
+  'ITCH'   Technician                           N
+
+=head3 RIFF Exif Tags
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+  'ecor'   Make                                 N
+  'emdl'   Model                                N
+  'emnt'   MakerNotes                           N
+  'erel'   RelatedImageFile                     N
+  'etim'   TimeCreated                          N
+  'eucm'   UserComment                          N
+  'ever'   ExifVersion                          N
+
+=head3 RIFF Hdrl Tags
+
+  Tag ID               Tag Name                 Writable
+  ------               --------                 --------
+  'IDIT'               DateTimeOriginal         N
+  'ISMP'               TimeCode                 N
+  'LIST_strl'          Stream                   RIFF Stream
+  'avih'               AVIHeader                RIFF AVIHeader
+
+=head3 RIFF Stream Tags
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+  'strd'   StreamData                           RIFF StreamData
+  'strf'   AudioFormat                          RIFF AudioFormat
+           VideoFormat                          BMP
+  'strh'   StreamHeader                         RIFF StreamHeader
+  'strn'   StreamName                           N
+
+=head3 RIFF StreamData Tags
+
+This chunk contains EXIF information in FujiFilm F30 AVI files.
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+  'AVIF'   AVIF                                 EXIF
+
+=head3 RIFF AudioFormat Tags
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      0    Encoding                             N
+      1    NumChannels                          N
+      2    SampleRate                           N
+      4    AvgBytesPerSec                       N
+      7    BitsPerSample                        N
+
+=head3 RIFF StreamHeader Tags
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      0    StreamType                           N
+      1    Codec                                N
+     10    Quality                              N
+     11    SampleSize                           N
+
+=head3 RIFF AVIHeader Tags
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      0    FrameRate                            N
+      1    MaxDataRate                          N
+      4    FrameCount                           N
+      6    StreamCount                          N
+      8    ImageWidth                           N
+      9    ImageHeight                          N
+
+=head2 AIFF Tags
+
+Only the tags decoded by ExifTool are listed in this table.
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+  '(c) '   Copyright                            N
+  'ANNO'   Annotation                           N
+  'AUTH'   Author                               N
+  'COMM'   Common                               AIFF Common
+  'COMT'   Comment                              AIFF Comment
+  'FVER'   FormatVersion                        AIFF FormatVers
+  'ID3 '   ID3                                  ID3
+  'NAME'   Name                                 N
+
+=head3 AIFF Common Tags
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      0    NumChannels                          N
+      1    NumSampleFrames                      N
+      3    SampleSize                           N
+      4    SampleRate                           N
+      9    CompressionType                      N
+
+=head3 AIFF Comment Tags
+
+  Tag ID   Tag Name                             Writable
+  ------   --------                             --------
+  0x0000   CommentTime                          N
+  0x0001   MarkerID                             N
+  0x0002   Comment                              N
+
+=head3 AIFF FormatVers Tags
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      0    FormatVersionTime                    N
+
+=head2 ASF Tags
+
+ASF format is used by Windows WMA and WMV files.  Tag ID's aren't listed
+because they are huge 128-bit GUID's that would ruin the formatting of this
+table.
+
+  Tag Name                                      Writable
+  --------                                      --------
+  SimpleIndex                                   N
+  TimecodeIndex                                 N
+  Header                                        ASF Header
+  Data                                          N
+  Index                                         N
+  MediaIndex                                    N
+
+=head3 ASF Header Tags
+
+  Tag Name                                      Writable
+  --------                                      --------
+  Padding                                       N
+  ScriptCommand                                 N
+  ContentBranding                               ASF ContentBranding
+  ContentEncryption                             N
+  DigitalSignature                              N
+  ExtendedContentEncryption                     N
+  HeaderExtension                               ASF HeaderExtension
+  ContentDescription                            ASF ContentDescr
+  ErrorCorrection                               N
+  StreamBitrateProps                            N
+  CodecList                                     ASF CodecList
+  FileProperties                                ASF FileProperties
+  StreamProperties                              ASF StreamProperties
+  ExtendedContentDescr                          ASF ExtendedDescr
+  BitrateMutualExclusion                        N
+  Marker                                        N
+
+=head3 ASF ContentBranding Tags
+
+  Tag Name                                      Writable
+  --------                                      --------
+  BannerImageType                               N
+  BannerImage                                   N
+  BannerImageURL                                N
+  CopyrightURL                                  N
+
+=head3 ASF HeaderExtension Tags
+
+  Tag Name                                      Writable
+  --------                                      --------
+  ExtendedStreamProps                           N
+  AdvancedContentEncryption                     N
+  MetadataLibrary                               ASF Metadata
+  TimecodeIndexParms                            N
+  Compatibility                                 N
+  LanguageList                                  N
+  AdvancedMutualExcl                            N
+  BandwidthSharing                              N
+  Reserved1                                     N
+  Metadata                                      ASF Metadata
+  GroupMutualExclusion                          N
+  StreamPrioritization                          N
+  IndexParameters                               N
+
+=head3 ASF Metadata Tags
+
+  Tag Name                                      Writable
+  --------                                      --------
+  [no tags known]
+
+=head3 ASF ContentDescr Tags
+
+  Tag Name                                      Writable
+  --------                                      --------
+  Title                                         N
+  Author                                        N
+  Copyright                                     N
+  Description                                   N
+  Rating                                        N
+
+=head3 ASF CodecList Tags
+
+  Tag Name                                      Writable
+  --------                                      --------
+  AudioCodecDescription                         N
+  AudioCodecName                                N
+  OtherCodecDescription                         N
+  OtherCodecName                                N
+  VideoCodecDescription                         N
+  VideoCodecName                                N
+
+=head3 ASF FileProperties Tags
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      0    FileID                               N
+     16    FileSize                             N
+     24    CreationDate                         N
+     32    DataPackets                          N
+     40    PlayDuration                         N
+     48    SendDuration                         N
+     56    Preroll                              N
+     64    Flags                                N
+     68    MinPacketSize                        N
+     72    MaxPacketSize                        N
+     76    MaxBitrate                           N
+
+=head3 ASF StreamProperties Tags
+
+Tags with index 54 and greater are conditional based on the StreamType.
+
+   Index   Tag Name                             Writable
+   -----   --------                             --------
+      0    StreamType                           N
+     16    ErrorCorrectionType                  N
+     32    TimeOffset                           N
+     48    StreamNumber                         N
+     54    AudioCodecID                         N
+           ImageWidth                           N
+     56    AudioChannels                        N
+     58    AudioSampleRate                      N
+           ImageHeight                          N
+
+=head3 ASF ExtendedDescr Tags
+
+  Tag Name                                      Writable
+  --------                                      --------
+  ASFLeakyBucketPairs                           N
+  ASFPacketCount                                N
+  ASFSecurityObjectsSize                        N
+  AlbumArtist                                   N
+  AlbumCoverURL                                 N
+  AlbumTitle                                    N
+  AspectRatioX                                  N
+  AspectRatioY                                  N
+  AudioFileURL                                  N
+  AudioSourceURL                                N
+  Author                                        N
+  AuthorURL                                     N
+  AverageLevel                                  N
+  BannerImageData                               N
+  BannerImageType                               N
+  BannerImageURL                                N
+  BeatsPerMinute                                N
+  Bitrate                                       N
+  Broadcast                                     N
+  BufferAverage                                 N
+  Can_Skip_Backward                             N
+  Can_Skip_Forward                              N
+  Category                                      N
+  Codec                                         N
+  Composer                                      N
+  Conductor                                     N
+  ContainerFormat                               N
+  ContentDistributor                            N
+  ContentGroupDescription                       N
+  Copyright                                     N
+  CopyrightURL                                  N
+  CurrentBitrate                                N
+  DRM                                           N
+  DRM_ContentID                                 N
+  DRM_DRMHeader                                 N
+  DRM_DRMHeader_ContentDistributor              N
+  DRM_DRMHeader_ContentID                       N
+  DRM_DRMHeader_IndividualizedVersion           N
+  DRM_DRMHeader_KeyID                           N
+  DRM_DRMHeader_LicenseAcqURL                   N
+  DRM_DRMHeader_SubscriptionContentID           N
+  DRM_IndividualizedVersion                     N
+  DRM_KeyID                                     N
+  DRM_LASignatureCert                           N
+  DRM_LASignatureLicSrvCert                     N
+  DRM_LASignaturePrivKey                        N
+  DRM_LASignatureRootCert                       N
+  DRM_LicenseAcqURL                             N
+  DRM_V1LicenseAcqURL                           N
+  DVDID                                         N
+  Description                                   N
+  Director                                      N
+  Duration                                      N
+  EncodedBy                                     N
+  EncodingSettings                              N
+  EncodingTime                                  N
+  FileSize                                      N
+  Genre                                         N
+  GenreID                                       N
+  HasArbitraryDataStream                        N
+  HasAttachedImages                             N
+  HasAudio                                      N
+  HasFileTransferStream                         N
+  HasImage                                      N
+  HasScript                                     N
+  HasVideo                                      N
+  ISRC                                          N
+  InitialKey                                    N
+  IsVBR                                         N
+  Is_Protected                                  N
+  Is_Trusted                                    N
+  Language                                      N
+  Lyrics                                        N
+  Lyrics_Synchronised                           N
+  MCDI                                          N
+  MediaClassPrimaryID                           N
+  MediaClassSecondaryID                         N
+  MediaCredits                                  N
+  MediaIsDelay                                  N
+  MediaIsFinale                                 N
+  MediaIsLive                                   N
+  MediaIsPremiere                               N
+  MediaIsRepeat                                 N
+  MediaIsSAP                                    N
+  MediaIsStereo                                 N
+  MediaIsSubtitled                              N
+  MediaIsTape                                   N
+  MediaNetworkAffiliation                       N
+  MediaOriginalBroadcastDateTime                N
+  MediaOriginalChannel                          N
+  MediaStationCallSign                          N
+  MediaStationName                              N
+  ModifiedBy                                    N
+  Mood                                          N
+  NSC_Address                                   N
+  NSC_Description                               N
+  NSC_Email                                     N
+  NSC_Name                                      N
+  NSC_Phone                                     N
+  NumberOfFrames                                N
+  OptimalBitrate                                N
+  OriginalAlbumTitle                            N
+  OriginalArtist                                N
+  OriginalFilename                              N
+  OriginalLyricist                              N
+  OriginalReleaseTime                           N
+  OriginalReleaseYear                           N
+  ParentalRating                                N
+  ParentalRatingReason                          N
+  PartOfSet                                     N
+  PeakBitrate                                   N
+  PeakValue                                     N
+  Period                                        N
+  Picture                                       ASF Preview
+  PlaylistDelay                                 N
+  Producer                                      N
+  PromotionURL                                  N
+  ProtectionType                                N
+  Provider                                      N
+  ProviderCopyright                             N
+  ProviderRating                                N
+  ProviderStyle                                 N
+  Publisher                                     N
+  RadioStationName                              N
+  RadioStationOwner                             N
+  Rating                                        N
+  Seekable                                      N
+  SharedUserRating                              N
+  Signature_Name                                N
+  StreamTypeInfo                                N
+  Stridable                                     N
+  SubTitle                                      N
+  SubTitleDescription                           N
+  SubscriptionContentID                         N
+  Text                                          N
+  Title                                         N
+  ToolName                                      N
+  ToolVersion                                   N
+  Track                                         N
+  TrackNumber                                   N
+  UniqueFileIdentifier                          N
+  UserWebURL                                    N
+  VBRPeak                                       N
+  VideoClosedCaptioning                         N
+  VideoFrameRate                                N
+  VideoHeight                                   N
+  VideoWidth                                    N
+  WMADRCAverageReference                        N
+  WMADRCAverageTarget                           N
+  WMADRCPeakReference                           N
+  WMADRCPeakTarget                              N
+  WMCollectionGroupID                           N
+  WMCollectionID                                N
+  WMContentID                                   N
+  Writer                                        N
+  Year                                          N
+
+=head3 ASF Preview Tags
+
+  Tag Name                                      Writable
+  --------                                      --------
+  PreviewType                                   N
+  PreviewMimeType                               N
+  PreviewDescription                            N
+  PreviewImage                                  N
+
+=head2 DICOM Tags
+
+The DICOM format is based on the ACR-NEMA specification, but adds a file
+header and a number of new tags.  ExifTool will extract information from
+either type of file.  The Tag ID's in the following table are the tag group
+and element numbers in hexadecimal, as given in the DICOM specification.
+
+  Tag ID     Tag Name                           Writable
+  ------     --------                           --------
+  0002,0000  FileMetaInfoGroupLength            N
+  0002,0001  FileMetaInfoVersion                N
+  0002,0002  MediaStorageSOPClassUID            N
+  0002,0003  MediaStorageSOPInstanceUID         N
+  0002,0010  TransferSyntaxUID                  N
+  0002,0012  ImplementationClassUID             N
+  0002,0013  ImplementationVersionName          N
+  0002,0016  SourceApplicationEntityTitle       N
+  0002,0100  PrivateInformationCreatorUID       N
+  0002,0102  PrivateInformation                 N
+  0004,1130  FileSetID                          N
+  0004,1141  FileSetDescriptorFileID            N
+  0004,1142  SpecificCharacterSetOfFile         N
+  0004,1200  FirstDirectoryRecordOffset         N
+  0004,1202  LastDirectoryRecordOffset          N
+  0004,1212  FileSetConsistencyFlag             N
+  0004,1220  DirectoryRecordSequence            N
+  0004,1400  OffsetOfNextDirectoryRecord        N
+  0004,1410  RecordInUseFlag                    N
+  0004,1420  LowerLevelDirectoryEntityOffset    N
+  0004,1430  DirectoryRecordType                N
+  0004,1432  PrivateRecordUID                   N
+  0004,1500  ReferencedFileID                   N
+  0004,1504  MRDRDirectoryRecordOffset          N
+  0004,1510  ReferencedSOPClassUIDInFile        N
+  0004,1511  ReferencedSOPInstanceUIDInFile     N
+  0004,1512  ReferencedTransferSyntaxUIDInFile  N
+  0004,151A  ReferencedRelatedSOPClassUIDInFile N
+  0004,1600  NumberOfReferences                 N
+  0008,0000  IdentifyingGroupLength             N
+  0008,0001  LengthToEnd                        N
+  0008,0005  SpecificCharacterSet               N
+  0008,0008  ImageType                          N
+  0008,0010  RecognitionCode                    N
+  0008,0012  InstanceCreationDate               N
+  0008,0013  InstanceCreationTime               N
+  0008,0014  InstanceCreatorUID                 N
+  0008,0016  SOPClassUID                        N
+  0008,0018  SOPInstanceUID                     N
+  0008,001A  RelatedGeneralSOPClassUID          N
+  0008,001B  OriginalSpecializedSOPClassUID     N
+  0008,0020  StudyDate                          N
+  0008,0021  SeriesDate                         N
+  0008,0022  AcquisitionDate                    N
+  0008,0023  ContentDate                        N
+  0008,0024  OverlayDate                        N
+  0008,0025  CurveDate                          N
+  0008,002A  AcquisitionDatetime                N
+  0008,0030  StudyTime                          N
+  0008,0031  SeriesTime                         N
+  0008,0032  AcquisitionTime                    N
+  0008,0033  ContentTime                        N
+  0008,0034  OverlayTime                        N
+  0008,0035  CurveTime                          N
+  0008,0040  DataSetType                        N
+  0008,0041  DataSetSubtype                     N
+  0008,0042  NuclearMedicineSeriesType          N
+  0008,0050  AccessionNumber                    N
+  0008,0052  Query-RetrieveLevel                N
+  0008,0054  RetrieveAETitle                    N
+  0008,0056  InstanceAvailability               N
+  0008,0058  FailedSOPInstanceUIDList           N
+  0008,0060  Modality                           N
+  0008,0061  ModalitiesInStudy                  N
+  0008,0062  SOPClassesInStudy                  N
+  0008,0064  ConversionType                     N
+  0008,0068  PresentationIntentType             N
+  0008,0070  Manufacturer                       N
+  0008,0080  InstitutionName                    N
+  0008,0081  InstitutionAddress                 N
+  0008,0082  InstitutionCodeSequence            N
+  0008,0090  ReferringPhysiciansName            N
+  0008,0092  ReferringPhysiciansAddress         N
+  0008,0094  ReferringPhysiciansTelephoneNumber N
+  0008,0096  ReferringPhysicianIDSequence       N
+  0008,0100  CodeValue                          N
+  0008,0102  CodingSchemeDesignator             N
+  0008,0103  CodingSchemeVersion                N
+  0008,0104  CodeMeaning                        N
+  0008,0105  MappingResource                    N
+  0008,0106  ContextGroupVersion                N
+  0008,0107  ContextGroupLocalVersion           N
+  0008,010B  ContextGroupExtensionFlag          N
+  0008,010C  CodingSchemeUID                    N
+  0008,010D  ContextGroupExtensionCreatorUID    N
+  0008,010F  ContextIdentifier                  N
+  0008,0110  CodingSchemeIDSequence             N
+  0008,0112  CodingSchemeRegistry               N
+  0008,0114  CodingSchemeExternalID             N
+  0008,0115  CodingSchemeName                   N
+  0008,0116  ResponsibleOrganization            N
+  0008,0201  TimezoneOffsetFromUTC              N
+  0008,1000  NetworkID                          N
+  0008,1010  StationName                        N
+  0008,1030  StudyDescription                   N
+  0008,1032  ProcedureCodeSequence              N
+  0008,103E  SeriesDescription                  N
+  0008,1040  InstitutionalDepartmentName        N
+  0008,1048  PhysicianOfRecord                  N
+  0008,1049  PhysicianOfRecordIDSequence        N
+  0008,1050  PerformingPhysiciansName           N
+  0008,1052  PerformingPhysicianIDSequence      N
+  0008,1060  NameOfPhysicianReadingStudy        N
+  0008,1062  PhysicianReadingStudyIDSequence    N
+  0008,1070  OperatorsName                      N
+  0008,1072  OperatorIDSequence                 N
+  0008,1080  AdmittingDiagnosesDescription      N
+  0008,1084  AdmittingDiagnosesCodeSequence     N
+  0008,1090  ManufacturersModelName             N
+  0008,1100  ReferencedResultsSequence          N
+  0008,1110  ReferencedStudySequence            N
+  0008,1111  ReferencedProcedureStepSequence    N
+  0008,1115  ReferencedSeriesSequence           N
+  0008,1120  ReferencedPatientSequence          N
+  0008,1125  ReferencedVisitSequence            N
+  0008,1130  ReferencedOverlaySequence          N
+  0008,113A  ReferencedWaveformSequence         N
+  0008,1140  ReferencedImageSequence            N
+  0008,1145  ReferencedCurveSequence            N
+  0008,114A  ReferencedInstanceSequence         N
+  0008,1150  ReferencedSOPClassUID              N
+  0008,1155  ReferencedSOPInstanceUID           N
+  0008,115A  SOPClassesSupported                N
+  0008,1160  ReferencedFrameNumber              N
+  0008,1195  TransactionUID                     N
+  0008,1197  FailureReason                      N
+  0008,1198  FailedSOPSequence                  N
+  0008,1199  ReferencedSOPSequence              N
+  0008,1200  OtherReferencedStudiesSequence     N
+  0008,1250  RelatedSeriesSequence              N
+  0008,2110  LossyImageCompression              N
+  0008,2111  DerivationDescription              N
+  0008,2112  SourceImageSequence                N
+  0008,2120  StageName                          N
+  0008,2122  StageNumber                        N
+  0008,2124  NumberOfStages                     N
+  0008,2127  ViewName                           N
+  0008,2128  ViewNumber                         N
+  0008,2129  NumberOfEventTimers                N
+  0008,212A  NumberOfViewsInStage               N
+  0008,2130  EventElapsedTime                   N
+  0008,2132  EventTimerName                     N
+  0008,2142  StartTrim                          N
+  0008,2143  StopTrim                           N
+  0008,2144  RecommendedDisplayFrameRate        N
+  0008,2200  TransducerPosition                 N
+  0008,2204  TransducerOrientation              N
+  0008,2208  AnatomicStructure                  N
+  0008,2218  AnatomicRegionSequence             N
+  0008,2220  AnatomicRegionModifierSequence     N
+  0008,2228  PrimaryAnatomicStructureSequence   N
+  0008,2229  AnatomicStructureOrRegionSequence  N
+  0008,2230  AnatomicStructureModifierSequence  N
+  0008,2240  TransducerPositionSequence         N
+  0008,2242  TransducerPositionModifierSequence N
+  0008,2244  TransducerOrientationSequence      N
+  0008,2246  TransducerOrientationModifierSeq   N
+  0008,3001  AlternateRepresentationSequence    N
+  0008,4000  IdentifyingComments                N
+  0008,9007  FrameType                          N
+  0008,9092  ReferencedImageEvidenceSequence    N
+  0008,9121  ReferencedRawDataSequence          N
+  0008,9123  CreatorVersionUID                  N
+  0008,9124  DerivationImageSequence            N
+  0008,9154  SourceImageEvidenceSequence        N
+  0008,9205  PixelPresentation                  N
+  0008,9206  VolumetricProperties               N
+  0008,9207  VolumeBasedCalculationTechnique    N
+  0008,9208  ComplexImageComponent              N
+  0008,9209  AcquisitionContrast                N
+  0008,9215  DerivationCodeSequence             N
+  0008,9237  GrayscalePresentationStateSequence N
+  0010,0000  PatientGroupLength                 N
+  0010,0010  PatientsName                       N
+  0010,0020  PatientID                          N
+  0010,0021  IssuerOfPatientID                  N
+  0010,0030  PatientsBirthDate                  N
+  0010,0032  PatientsBirthTime                  N
+  0010,0040  PatientsSex                        N
+  0010,0050  PatientsInsurancePlanCodeSequence  N
+  0010,0101  PatientsPrimaryLanguageCodeSeq     N
+  0010,0102  PatientsPrimaryLanguageCodeModSeq  N
+  0010,1000  OtherPatientIDs                    N
+  0010,1001  OtherPatientNames                  N
+  0010,1005  PatientsBirthName                  N
+  0010,1010  PatientsAge                        N
+  0010,1020  PatientsSize                       N
+  0010,1030  PatientsWeight                     N
+  0010,1040  PatientsAddress                    N
+  0010,1050  InsurancePlanIdentification        N
+  0010,1060  PatientsMothersBirthName           N
+  0010,1080  MilitaryRank                       N
+  0010,1081  BranchOfService                    N
+  0010,1090  MedicalRecordLocator               N
+  0010,2000  MedicalAlerts                      N
+  0010,2110  ContrastAllergies                  N
+  0010,2150  CountryOfResidence                 N
+  0010,2152  RegionOfResidence                  N
+  0010,2154  PatientsTelephoneNumbers           N
+  0010,2160  EthnicGroup                        N
+  0010,2180  Occupation                         N
+  0010,21A0  SmokingStatus                      N
+  0010,21B0  AdditionalPatientHistory           N
+  0010,21C0  PregnancyStatus                    N
+  0010,21D0  LastMenstrualDate                  N
+  0010,21F0  PatientsReligiousPreference        N
+  0010,4000  PatientComments                    N
+  0012,0010  ClinicalTrialSponsorName           N
+  0012,0020  ClinicalTrialProtocolID            N
+  0012,0021  ClinicalTrialProtocolName          N
+  0012,0030  ClinicalTrialSiteID                N
+  0012,0031  ClinicalTrialSiteName              N
+  0012,0040  ClinicalTrialSubjectID             N
+  0012,0042  ClinicalTrialSubjectReadingID      N
+  0012,0050  ClinicalTrialTimePointID           N
+  0012,0051  ClinicalTrialTimePointDescription  N
+  0012,0060  ClinicalTrialCoordinatingCenter    N
+  0018,0000  AcquisitionGroupLength             N
+  0018,0010  Contrast-BolusAgent                N
+  0018,0012  Contrast-BolusAgentSequence        N
+  0018,0014  Contrast-BolusAdministrationRoute  N
+  0018,0015  BodyPartExamined                   N
+  0018,0020  ScanningSequence                   N
+  0018,0021  SequenceVariant                    N
+  0018,0022  ScanOptions                        N
+  0018,0023  MRAcquisitionType                  N
+  0018,0024  SequenceName                       N
+  0018,0025  AngioFlag                          N
+  0018,0026  InterventionDrugInformationSeq     N
+  0018,0027  InterventionDrugStopTime           N
+  0018,0028  InterventionDrugDose               N
+  0018,0029  InterventionDrugSequence           N
+  0018,002A  AdditionalDrugSequence             N
+  0018,0030  Radionuclide                       N
+  0018,0031  Radiopharmaceutical                N
+  0018,0032  EnergyWindowCenterline             N
+  0018,0033  EnergyWindowTotalWidth             N
+  0018,0034  InterventionDrugName               N
+  0018,0035  InterventionDrugStartTime          N
+  0018,0036  InterventionSequence               N
+  0018,0037  TherapyType                        N
+  0018,0038  InterventionStatus                 N
+  0018,0039  TherapyDescription                 N
+  0018,003A  InterventionDescription            N
+  0018,0040  CineRate                           N
+  0018,0050  SliceThickness                     N
+  0018,0060  KVP                                N
+  0018,0070  CountsAccumulated                  N
+  0018,0071  AcquisitionTerminationCondition    N
+  0018,0072  EffectiveDuration                  N
+  0018,0073  AcquisitionStartCondition          N
+  0018,0074  AcquisitionStartConditionData      N
+  0018,0075  AcquisitionEndConditionData        N
+  0018,0080  RepetitionTime                     N
+  0018,0081  EchoTime                           N
+  0018,0082  InversionTime                      N
+  0018,0083  NumberOfAverages                   N
+  0018,0084  ImagingFrequency                   N
+  0018,0085  ImagedNucleus                      N
+  0018,0086  EchoNumber                         N
+  0018,0087  MagneticFieldStrength              N
+  0018,0088  SpacingBetweenSlices               N
+  0018,0089  NumberOfPhaseEncodingSteps         N
+  0018,0090  DataCollectionDiameter             N
+  0018,0091  EchoTrainLength                    N
+  0018,0093  PercentSampling                    N
+  0018,0094  PercentPhaseFieldOfView            N
+  0018,0095  PixelBandwidth                     N
+  0018,1000  DeviceSerialNumber                 N
+  0018,1004  PlateID                            N
+  0018,1010  SecondaryCaptureDeviceID           N
+  0018,1011  HardcopyCreationDeviceID           N
+  0018,1012  DateOfSecondaryCapture             N
+  0018,1014  TimeOfSecondaryCapture             N
+  0018,1016  SecondaryCaptureDeviceManufacturer N
+  0018,1017  HardcopyDeviceManufacturer         N
+  0018,1018  SecondaryCaptureDeviceModelName    N
+  0018,1019  SecondaryCaptureDeviceSoftwareVers N
+  0018,101A  HardcopyDeviceSoftwareVersion      N
+  0018,101B  HardcopyDeviceModelName            N
+  0018,1020  SoftwareVersion                    N
+  0018,1022  VideoImageFormatAcquired           N
+  0018,1023  DigitalImageFormatAcquired         N
+  0018,1030  ProtocolName                       N
+  0018,1040  Contrast-BolusRoute                N
+  0018,1041  Contrast-BolusVolume               N
+  0018,1042  Contrast-BolusStartTime            N
+  0018,1043  Contrast-BolusStopTime             N
+  0018,1044  Contrast-BolusTotalDose            N
+  0018,1045  SyringeCounts                      N
+  0018,1046  ContrastFlowRate                   N
+  0018,1047  ContrastFlowDuration               N
+  0018,1048  Contrast-BolusIngredient           N
+  0018,1049  Contrast-BolusConcentration        N
+  0018,1050  SpatialResolution                  N
+  0018,1060  TriggerTime                        N
+  0018,1061  TriggerSourceOrType                N
+  0018,1062  NominalInterval                    N
+  0018,1063  FrameTime                          N
+  0018,1064  FramingType                        N
+  0018,1065  FrameTimeVector                    N
+  0018,1066  FrameDelay                         N
+  0018,1067  ImageTriggerDelay                  N
+  0018,1068  MultiplexGroupTimeOffset           N
+  0018,1069  TriggerTimeOffset                  N
+  0018,106A  SynchronizationTrigger             N
+  0018,106C  SynchronizationChannel             N
+  0018,106E  TriggerSamplePosition              N
+  0018,1070  RadiopharmaceuticalRoute           N
+  0018,1071  RadiopharmaceuticalVolume          N
+  0018,1072  RadiopharmaceuticalStartTime       N
+  0018,1073  RadiopharmaceuticalStopTime        N
+  0018,1074  RadionuclideTotalDose              N
+  0018,1075  RadionuclideHalfLife               N
+  0018,1076  RadionuclidePositronFraction       N
+  0018,1077  RadiopharmaceuticalSpecActivity    N
+  0018,1080  BeatRejectionFlag                  N
+  0018,1081  LowRRValue                         N
+  0018,1082  HighRRValue                        N
+  0018,1083  IntervalsAcquired                  N
+  0018,1084  IntervalsRejected                  N
+  0018,1085  PVCRejection                       N
+  0018,1086  SkipBeats                          N
+  0018,1088  HeartRate                          N
+  0018,1090  CardiacNumberOfImages              N
+  0018,1094  TriggerWindow                      N
+  0018,1100  ReconstructionDiameter             N
+  0018,1110  DistanceSourceToDetector           N
+  0018,1111  DistanceSourceToPatient            N
+  0018,1114  EstimatedRadiographicMagnification N
+  0018,1120  Gantry-DetectorTilt                N
+  0018,1121  Gantry-DetectorSlew                N
+  0018,1130  TableHeight                        N
+  0018,1131  TableTraverse                      N
+  0018,1134  TableMotion                        N
+  0018,1135  TableVerticalIncrement             N
+  0018,1136  TableLateralIncrement              N
+  0018,1137  TableLongitudinalIncrement         N
+  0018,1138  TableAngle                         N
+  0018,113A  TableType                          N
+  0018,1140  RotationDirection                  N
+  0018,1141  AngularPosition                    N
+  0018,1142  RadialPosition                     N
+  0018,1143  ScanArc                            N
+  0018,1144  AngularStep                        N
+  0018,1145  CenterOfRotationOffset             N
+  0018,1146  RotationOffset                     N
+  0018,1147  FieldOfViewShape                   N
+  0018,1149  FieldOfViewDimensions              N
+  0018,1150  ExposureTime                       N
+  0018,1151  XRayTubeCurrent                    N
+  0018,1152  Exposure                           N
+  0018,1153  ExposureInMicroAmpSec              N
+  0018,1154  AveragePulseWidth                  N
+  0018,1155  RadiationSetting                   N
+  0018,1156  RectificationType                  N
+  0018,115A  RadiationMode                      N
+  0018,115E  ImageAreaDoseProduct               N
+  0018,1160  FilterType                         N
+  0018,1161  TypeOfFilters                      N
+  0018,1162  IntensifierSize                    N
+  0018,1164  ImagerPixelSpacing                 N
+  0018,1166  Grid                               N
+  0018,1170  GeneratorPower                     N
+  0018,1180  Collimator-GridName                N
+  0018,1181  CollimatorType                     N
+  0018,1182  FocalDistance                      N
+  0018,1183  XFocusCenter                       N
+  0018,1184  YFocusCenter                       N
+  0018,1190  FocalSpot                          N
+  0018,1191  AnodeTargetMaterial                N
+  0018,11A0  BodyPartThickness                  N
+  0018,11A2  CompressionForce                   N
+  0018,1200  DateOfLastCalibration              N
+  0018,1201  TimeOfLastCalibration              N
+  0018,1210  ConvolutionKernel                  N
+  0018,1240  Upper-LowerPixelValues             N
+  0018,1242  ActualFrameDuration                N
+  0018,1243  CountRate                          N
+  0018,1244  PreferredPlaybackSequencing        N
+  0018,1250  ReceiveCoilName                    N
+  0018,1251  TransmitCoilName                   N
+  0018,1260  PlateType                          N
+  0018,1261  PhosphorType                       N
+  0018,1300  ScanVelocity                       N
+  0018,1301  WholeBodyTechnique                 N
+  0018,1302  ScanLength                         N
+  0018,1310  AcquisitionMatrix                  N
+  0018,1312  InPlanePhaseEncodingDirection      N
+  0018,1314  FlipAngle                          N
+  0018,1315  VariableFlipAngleFlag              N
+  0018,1316  SAR                                N
+  0018,1318  DB-Dt                              N
+  0018,1400  AcquisitionDeviceProcessingDescr   N
+  0018,1401  AcquisitionDeviceProcessingCode    N
+  0018,1402  CassetteOrientation                N
+  0018,1403  CassetteSize                       N
+  0018,1404  ExposuresonPlate                   N
+  0018,1405  RelativeXRayExposure               N
+  0018,1450  ColumnAngulation                   N
+  0018,1460  TomoLayerHeight                    N
+  0018,1470  TomoAngle                          N
+  0018,1480  TomoTime                           N
+  0018,1490  TomoType                           N
+  0018,1491  TomoClass                          N
+  0018,1495  NumberOfTomosynthesisSourceImages  N
+  0018,1500  PositionerMotion                   N
+  0018,1508  PositionerType                     N
+  0018,1510  PositionerPrimaryAngle             N
+  0018,1511  PositionerSecondaryAngle           N
+  0018,1520  PositionerPrimaryAngleIncrement    N
+  0018,1521  PositionerSecondaryAngleIncrement  N
+  0018,1530  DetectorPrimaryAngle               N
+  0018,1531  DetectorSecondaryAngle             N
+  0018,1600  ShutterShape                       N
+  0018,1602  ShutterLeftVerticalEdge            N
+  0018,1604  ShutterRightVerticalEdge           N
+  0018,1606  ShutterUpperHorizontalEdge         N
+  0018,1608  ShutterLowerHorizontalEdge         N
+  0018,1610  CenterOfCircularShutter            N
+  0018,1612  RadiusOfCircularShutter            N
+  0018,1620  VerticesOfPolygonalShutter         N
+  0018,1622  ShutterPresentationValue           N
+  0018,1623  ShutterOverlayGroup                N
+  0018,1700  CollimatorShape                    N
+  0018,1702  CollimatorLeftVerticalEdge         N
+  0018,1704  CollimatorRightVerticalEdge        N
+  0018,1706  CollimatorUpperHorizontalEdge      N
+  0018,1708  CollimatorLowerHorizontalEdge      N
+  0018,1710  CenterOfCircularCollimator         N
+  0018,1712  RadiusOfCircularCollimator         N
+  0018,1720  VerticesOfPolygonalCollimator      N
+  0018,1800  AcquisitionTimeSynchronized        N
+  0018,1801  TimeSource                         N
+  0018,1802  TimeDistributionProtocol           N
+  0018,1803  NTPSourceAddress                   N
+  0018,2001  PageNumberVector                   N
+  0018,2002  FrameLabelVector                   N
+  0018,2003  FramePrimaryAngleVector            N
+  0018,2004  FrameSecondaryAngleVector          N
+  0018,2005  SliceLocationVector                N
+  0018,2006  DisplayWindowLabelVector           N
+  0018,2010  NominalScannedPixelSpacing         N
+  0018,2020  DigitizingDeviceTransportDirection N
+  0018,2030  RotationOfScannedFilm              N
+  0018,3100  IVUSAcquisition                    N
+  0018,3101  IVUSPullbackRate                   N
+  0018,3102  IVUSGatedRate                      N
+  0018,3103  IVUSPullbackStartFrameNumber       N
+  0018,3104  IVUSPullbackStopFrameNumber        N
+  0018,3105  LesionNumber                       N
+  0018,4000  AcquisitionComments                N
+  0018,5000  OutputPower                        N
+  0018,5010  TransducerData                     N
+  0018,5012  FocusDepth                         N
+  0018,5020  ProcessingFunction                 N
+  0018,5021  PostprocessingFunction             N
+  0018,5022  MechanicalIndex                    N
+  0018,5024  BoneThermalIndex                   N
+  0018,5026  CranialThermalIndex                N
+  0018,5027  SoftTissueThermalIndex             N
+  0018,5028  SoftTissueFocusThermalIndex        N
+  0018,5029  SoftTissueSurfaceThermalIndex      N
+  0018,5030  DynamicRange                       N
+  0018,5040  TotalGain                          N
+  0018,5050  DepthOfScanField                   N
+  0018,5100  PatientPosition                    N
+  0018,5101  ViewPosition                       N
+  0018,5104  ProjectionEponymousNameCodeSeq     N
+  0018,5210  ImageTransformationMatrix          N
+  0018,5212  ImageTranslationVector             N
+  0018,6000  Sensitivity                        N
+  0018,6011  SequenceOfUltrasoundRegions        N
+  0018,6012  RegionSpatialFormat                N
+  0018,6014  RegionDataType                     N
+  0018,6016  RegionFlags                        N
+  0018,6018  RegionLocationMinX0                N
+  0018,601A  RegionLocationMinY0                N
+  0018,601C  RegionLocationMaxX1                N
+  0018,601E  RegionLocationMaxY1                N
+  0018,6020  ReferencePixelX0                   N
+  0018,6022  ReferencePixelY0                   N
+  0018,6024  PhysicalUnitsXDirection            N
+  0018,6026  PhysicalUnitsYDirection            N
+  0018,6028  ReferencePixelPhysicalValueX       N
+  0018,602A  ReferencePixelPhysicalValueY       N
+  0018,602C  PhysicalDeltaX                     N
+  0018,602E  PhysicalDeltaY                     N
+  0018,6030  TransducerFrequency                N
+  0018,6031  TransducerType                     N
+  0018,6032  PulseRepetitionFrequency           N
+  0018,6034  DopplerCorrectionAngle             N
+  0018,6036  SteeringAngle                      N
+  0018,6038  DopplerSampleVolumeXPositionUL     N
+  0018,6039  DopplerSampleVolumeXPosition       N
+  0018,603A  DopplerSampleVolumeYPositionUL     N
+  0018,603B  DopplerSampleVolumeYPosition       N
+  0018,603C  TMLinePositionX0UL                 N
+  0018,603D  TMLinePositionX0                   N
+  0018,603E  TMLinePositionY0UL                 N
+  0018,603F  TMLinePositionY0                   N
+  0018,6040  TMLinePositionX1UL                 N
+  0018,6041  TMLinePositionX1                   N
+  0018,6042  TMLinePositionY1UL                 N
+  0018,6043  TMLinePositionY1                   N
+  0018,6044  PixelComponentOrganization         N
+  0018,6046  PixelComponentMask                 N
+  0018,6048  PixelComponentRangeStart           N
+  0018,604A  PixelComponentRangeStop            N
+  0018,604C  PixelComponentPhysicalUnits        N
+  0018,604E  PixelComponentDataType             N
+  0018,6050  NumberOfTableBreakPoints           N
+  0018,6052  TableOfXBreakPoints                N
+  0018,6054  TableOfYBreakPoints                N
+  0018,6056  NumberOfTableEntries               N
+  0018,6058  TableOfPixelValues                 N
+  0018,605A  TableOfParameterValues             N
+  0018,6060  RWaveTimeVector                    N
+  0018,7000  DetectorConditionsNominalFlag      N
+  0018,7001  DetectorTemperature                N
+  0018,7004  DetectorType                       N
+  0018,7005  DetectorConfiguration              N
+  0018,7006  DetectorDescription                N
+  0018,7008  DetectorMode                       N
+  0018,700A  DetectorID                         N
+  0018,700C  DateOfLastDetectorCalibration      N
+  0018,700E  TimeOfLastDetectorCalibration      N
+  0018,7010  DetectorExposuresSinceCalibration  N
+  0018,7011  DetectorExposuresSinceManufactured N
+  0018,7012  DetectorTimeSinceLastExposure      N
+  0018,7014  DetectorActiveTime                 N
+  0018,7016  DetectorActiveOffsetFromExposure   N
+  0018,701A  DetectorBinning                    N
+  0018,7020  DetectorElementPhysicalSize        N
+  0018,7022  DetectorElementSpacing             N
+  0018,7024  DetectorActiveShape                N
+  0018,7026  DetectorActiveDimensions           N
+  0018,7028  DetectorActiveOrigin               N
+  0018,702A  DetectorManufacturerName           N
+  0018,702B  DetectorManufacturersModelName     N
+  0018,7030  FieldOfViewOrigin                  N
+  0018,7032  FieldOfViewRotation                N
+  0018,7034  FieldOfViewHorizontalFlip          N
+  0018,7040  GridAbsorbingMaterial              N
+  0018,7041  GridSpacingMaterial                N
+  0018,7042  GridThickness                      N
+  0018,7044  GridPitch                          N
+  0018,7046  GridAspectRatio                    N
+  0018,7048  GridPeriod                         N
+  0018,704C  GridFocalDistance                  N
+  0018,7050  FilterMaterial                     N
+  0018,7052  FilterThicknessMinimum             N
+  0018,7054  FilterThicknessMaximum             N
+  0018,7060  ExposureControlMode                N
+  0018,7062  ExposureControlModeDescription     N
+  0018,7064  ExposureStatus                     N
+  0018,7065  PhototimerSetting                  N
+  0018,8150  ExposureTimeInMicroSec             N
+  0018,8151  XRayTubeCurrentInMicroAmps         N
+  0018,9004  ContentQualification               N
+  0018,9005  PulseSequenceName                  N
+  0018,9006  MRImagingModifierSequence          N
+  0018,9008  EchoPulseSequence                  N
+  0018,9009  InversionRecovery                  N
+  0018,9010  FlowCompensation                   N
+  0018,9011  MultipleSpinEcho                   N
+  0018,9012  MultiPlanarExcitation              N
+  0018,9014  PhaseContrast                      N
+  0018,9015  TimeOfFlightContrast               N
+  0018,9016  Spoiling                           N
+  0018,9017  SteadyStatePulseSequence           N
+  0018,9018  EchoPlanarPulseSequence            N
+  0018,9019  TagAngleFirstAxis                  N
+  0018,9020  MagnetizationTransfer              N
+  0018,9021  T2Preparation                      N
+  0018,9022  BloodSignalNulling                 N
+  0018,9024  SaturationRecovery                 N
+  0018,9025  SpectrallySelectedSuppression      N
+  0018,9026  SpectrallySelectedExcitation       N
+  0018,9027  SpatialPreSaturation               N
+  0018,9028  Tagging                            N
+  0018,9029  OversamplingPhase                  N
+  0018,9030  TagSpacingFirstDimension           N
+  0018,9032  GeometryOfKSpaceTraversal          N
+  0018,9033  SegmentedKSpaceTraversal           N
+  0018,9034  RectilinearPhaseEncodeReordering   N
+  0018,9035  TagThickness                       N
+  0018,9036  PartialFourierDirection            N
+  0018,9037  CardiacSynchronizationTechnique    N
+  0018,9041  ReceiveCoilManufacturerName        N
+  0018,9042  MRReceiveCoilSequence              N
+  0018,9043  ReceiveCoilType                    N
+  0018,9044  QuadratureReceiveCoil              N
+  0018,9045  MultiCoilDefinitionSequence        N
+  0018,9046  MultiCoilConfiguration             N
+  0018,9047  MultiCoilElementName               N
+  0018,9048  MultiCoilElementUsed               N
+  0018,9049  MRTransmitCoilSequence             N
+  0018,9050  TransmitCoilManufacturerName       N
+  0018,9051  TransmitCoilType                   N
+  0018,9052  SpectralWidth                      N
+  0018,9053  ChemicalShiftReference             N
+  0018,9054  VolumeLocalizationTechnique        N
+  0018,9058  MRAcquisitionFrequencyEncodeSteps  N
+  0018,9059  Decoupling                         N
+  0018,9060  DecoupledNucleus                   N
+  0018,9061  DecouplingFrequency                N
+  0018,9062  DecouplingMethod                   N
+  0018,9063  DecouplingChemicalShiftReference   N
+  0018,9064  KSpaceFiltering                    N
+  0018,9065  TimeDomainFiltering                N
+  0018,9066  NumberOfZeroFills                  N
+  0018,9067  BaselineCorrection                 N
+  0018,9069  ParallelReductionFactorInPlane     N
+  0018,9070  CardiacRRIntervalSpecified         N
+  0018,9073  AcquisitionDuration                N
+  0018,9074  FrameAcquisitionDatetime           N
+  0018,9075  DiffusionDirectionality            N
+  0018,9076  DiffusionGradientDirectionSequence N
+  0018,9077  ParallelAcquisition                N
+  0018,9078  ParallelAcquisitionTechnique       N
+  0018,9079  InversionTimes                     N
+  0018,9080  MetaboliteMapDescription           N
+  0018,9081  PartialFourier                     N
+  0018,9082  EffectiveEchoTime                  N
+  0018,9083  MetaboliteMapCodeSequence          N
+  0018,9084  ChemicalShiftSequence              N
+  0018,9085  CardiacSignalSource                N
+  0018,9087  DiffusionBValue                    N
+  0018,9089  DiffusionGradientOrientation       N
+  0018,9090  VelocityEncodingDirection          N
+  0018,9091  VelocityEncodingMinimumValue       N
+  0018,9093  NumberOfKSpaceTrajectories         N
+  0018,9094  CoverageOfKSpace                   N
+  0018,9095  SpectroscopyAcquisitionPhaseRows   N
+  0018,9098  TransmitterFrequency               N
+  0018,9100  ResonantNucleus                    N
+  0018,9101  FrequencyCorrection                N
+  0018,9103  MRSpectroscopyFOV-GeometrySequence N
+  0018,9104  SlabThickness                      N
+  0018,9105  SlabOrientation                    N
+  0018,9106  MidSlabPosition                    N
+  0018,9107  MRSpatialSaturationSequence        N
+  0018,9112  MRTimingAndRelatedParametersSeq    N
+  0018,9114  MREchoSequence                     N
+  0018,9115  MRModifierSequence                 N
+  0018,9117  MRDiffusionSequence                N
+  0018,9118  CardiacTriggerSequence             N
+  0018,9119  MRAveragesSequence                 N
+  0018,9125  MRFOV-GeometrySequence             N
+  0018,9126  VolumeLocalizationSequence         N
+  0018,9127  SpectroscopyAcquisitionDataColumns N
+  0018,9147  DiffusionAnisotropyType            N
+  0018,9151  FrameReferenceDatetime             N
+  0018,9152  MRMetaboliteMapSequence            N
+  0018,9155  ParallelReductionFactorOutOfPlane  N
+  0018,9159  SpectroscopyOutOfPlanePhaseSteps   N
+  0018,9166  BulkMotionStatus                   N
+  0018,9168  ParallelReductionFactSecondInPlane N
+  0018,9169  CardiacBeatRejectionTechnique      N
+  0018,9170  RespiratoryMotionCompTechnique     N
+  0018,9171  RespiratorySignalSource            N
+  0018,9172  BulkMotionCompensationTechnique    N
+  0018,9173  BulkMotionSignalSource             N
+  0018,9174  ApplicableSafetyStandardAgency     N
+  0018,9175  ApplicableSafetyStandardDescr      N
+  0018,9176  OperatingModeSequence              N
+  0018,9177  OperatingModeType                  N
+  0018,9178  OperatingMode                      N
+  0018,9179  SpecificAbsorptionRateDefinition   N
+  0018,9180  GradientOutputType                 N
+  0018,9181  SpecificAbsorptionRateValue        N
+  0018,9182  GradientOutput                     N
+  0018,9183  FlowCompensationDirection          N
+  0018,9184  TaggingDelay                       N
+  0018,9195  ChemicalShiftsMinIntegrateLimitHz  N
+  0018,9196  ChemicalShiftsMaxIntegrateLimitHz  N
+  0018,9197  MRVelocityEncodingSequence         N
+  0018,9198  FirstOrderPhaseCorrection          N
+  0018,9199  WaterReferencedPhaseCorrection     N
+  0018,9200  MRSpectroscopyAcquisitionType      N
+  0018,9214  RespiratoryCyclePosition           N
+  0018,9217  VelocityEncodingMaximumValue       N
+  0018,9218  TagSpacingSecondDimension          N
+  0018,9219  TagAngleSecondAxis                 N
+  0018,9220  FrameAcquisitionDuration           N
+  0018,9226  MRImageFrameTypeSequence           N
+  0018,9227  MRSpectroscopyFrameTypeSequence    N
+  0018,9231  MRAcqPhaseEncodingStepsInPlane     N
+  0018,9232  MRAcqPhaseEncodingStepsOutOfPlane  N
+  0018,9234  SpectroscopyAcqPhaseColumns        N
+  0018,9236  CardiacCyclePosition               N
+  0018,9239  SpecificAbsorptionRateSequence     N
+  0018,9240  RFEchoTrainLength                  N
+  0018,9241  GradientEchoTrainLength            N
+  0018,9295  ChemicalShiftsMinIntegrateLimitPPM N
+  0018,9296  ChemicalShiftsMaxIntegrateLimitPPM N
+  0018,9301  CTAcquisitionTypeSequence          N
+  0018,9302  AcquisitionType                    N
+  0018,9303  TubeAngle                          N
+  0018,9304  CTAcquisitionDetailsSequence       N
+  0018,9305  RevolutionTime                     N
+  0018,9306  SingleCollimationWidth             N
+  0018,9307  TotalCollimationWidth              N
+  0018,9308  CTTableDynamicsSequence            N
+  0018,9309  TableSpeed                         N
+  0018,9310  TableFeedPerRotation               N
+  0018,9311  SpiralPitchFactor                  N
+  0018,9312  CTGeometrySequence                 N
+  0018,9313  DataCollectionCenterPatient        N
+  0018,9314  CTReconstructionSequence           N
+  0018,9315  ReconstructionAlgorithm            N
+  0018,9316  ConvolutionKernelGroup             N
+  0018,9317  ReconstructionFieldOfView          N
+  0018,9318  ReconstructionTargetCenterPatient  N
+  0018,9319  ReconstructionAngle                N
+  0018,9320  ImageFilter                        N
+  0018,9321  CTExposureSequence                 N
+  0018,9322  ReconstructionPixelSpacing         N
+  0018,9323  ExposureModulationType             N
+  0018,9324  EstimatedDoseSaving                N
+  0018,9325  CTXRayDetailsSequence              N
+  0018,9326  CTPositionSequence                 N
+  0018,9327  TablePosition                      N
+  0018,9328  ExposureTimeInMilliSec             N
+  0018,9329  CTImageFrameTypeSequence           N
+  0018,9330  XRayTubeCurrentInMilliAmps         N
+  0018,9332  ExposureInMilliAmpSec              N
+  0018,9333  ConstantVolumeFlag                 N
+  0018,9334  FluoroscopyFlag                    N
+  0018,9335  SourceToDataCollectionCenterDist   N
+  0018,9337  Contrast-BolusAgentNumber          N
+  0018,9338  Contrast-BolusIngredientCodeSeq    N
+  0018,9340  ContrastAdministrationProfileSeq   N
+  0018,9341  Contrast-BolusUsageSequence        N
+  0018,9342  Contrast-BolusAgentAdministered    N
+  0018,9343  Contrast-BolusAgentDetected        N
+  0018,9344  Contrast-BolusAgentPhase           N
+  0018,9345  CTDIvol                            N
+  0018,A001  ContributingEquipmentSequence      N
+  0018,A002  ContributionDateTime               N
+  0018,A003  ContributionDescription            N
+  0020,0000  RelationshipGroupLength            N
+  0020,000D  StudyInstanceUID                   N
+  0020,000E  SeriesInstanceUID                  N
+  0020,0010  StudyID                            N
+  0020,0011  SeriesNumber                       N
+  0020,0012  AcquisitionNumber                  N
+  0020,0013  InstanceNumber                     N
+  0020,0014  IsotopeNumber                      N
+  0020,0015  PhaseNumber                        N
+  0020,0016  IntervalNumber                     N
+  0020,0017  TimeSlotNumber                     N
+  0020,0018  AngleNumber                        N
+  0020,0019  ItemNumber                         N
+  0020,0020  PatientOrientation                 N
+  0020,0022  OverlayNumber                      N
+  0020,0024  CurveNumber                        N
+  0020,0026  LookupTableNumber                  N
+  0020,0030  ImagePosition                      N
+  0020,0032  ImagePositionPatient               N
+  0020,0035  ImageOrientation                   N
+  0020,0037  ImageOrientationPatient            N
+  0020,0050  Location                           N
+  0020,0052  FrameOfReferenceUID                N
+  0020,0060  Laterality                         N
+  0020,0062  ImageLaterality                    N
+  0020,0070  ImageGeometryType                  N
+  0020,0080  MaskingImage                       N
+  0020,0100  TemporalPositionIdentifier         N
+  0020,0105  NumberOfTemporalPositions          N
+  0020,0110  TemporalResolution                 N
+  0020,0200  SynchronizationFrameOfReferenceUID N
+  0020,1000  SeriesInStudy                      N
+  0020,1001  AcquisitionsInSeries               N
+  0020,1002  ImagesInAcquisition                N
+  0020,1004  AcquisitionsInStudy                N
+  0020,1020  Reference                          N
+  0020,1040  PositionReferenceIndicator         N
+  0020,1041  SliceLocation                      N
+  0020,1070  OtherStudyNumbers                  N
+  0020,1200  NumberOfPatientRelatedStudies      N
+  0020,1202  NumberOfPatientRelatedSeries       N
+  0020,1204  NumberOfPatientRelatedInstances    N
+  0020,1206  NumberOfStudyRelatedSeries         N
+  0020,1208  NumberOfStudyRelatedInstances      N
+  0020,1209  NumberOfSeriesRelatedInstances     N
+  0020,31xx  SourceImageIDs                     N
+  0020,3401  ModifyingDeviceID                  N
+  0020,3402  ModifiedImageID                    N
+  0020,3403  ModifiedImageDate                  N
+  0020,3404  ModifyingDeviceManufacturer        N
+  0020,3405  ModifiedImageTime                  N
+  0020,3406  ModifiedImageDescription           N
+  0020,4000  ImageComments                      N
+  0020,5000  OriginalImageIdentification        N
+  0020,5002  OriginalImageIdentNomenclature     N
+  0020,9056  StackID                            N
+  0020,9057  InStackPositionNumber              N
+  0020,9071  FrameAnatomySequence               N
+  0020,9072  FrameLaterality                    N
+  0020,9111  FrameContentSequence               N
+  0020,9113  PlanePositionSequence              N
+  0020,9116  PlaneOrientationSequence           N
+  0020,9128  TemporalPositionIndex              N
+  0020,9153  TriggerDelayTime                   N
+  0020,9156  FrameAcquisitionNumber             N
+  0020,9157  DimensionIndexValues               N
+  0020,9158  FrameComments                      N
+  0020,9161  ConcatenationUID                   N
+  0020,9162  InconcatenationNumber              N
+  0020,9163  InconcatenationTotalNumber         N
+  0020,9164  DimensionOrganizationUID           N
+  0020,9165  DimensionIndexPointer              N
+  0020,9167  FunctionalGroupPointer             N
+  0020,9213  DimensionIndexPrivateCreator       N
+  0020,9221  DimensionOrganizationSequence      N
+  0020,9222  DimensionIndexSequence             N
+  0020,9228  ConcatenationFrameOffsetNumber     N
+  0020,9238  FunctionalGroupPrivateCreator      N
+  0022,0001  LightPathFilterPassThroughWavelen  N
+  0022,0002  LightPathFilterPassBand            N
+  0022,0003  ImagePathFilterPassThroughWavelen  N
+  0022,0004  ImagePathFilterPassBand            N
+  0022,0005  PatientEyeMovementCommanded        N
+  0022,0006  PatientEyeMovementCommandCodeSeq   N
+  0022,0007  SphericalLensPower                 N
+  0022,0008  CylinderLensPower                  N
+  0022,0009  CylinderAxis                       N
+  0022,000A  EmmetropicMagnification            N
+  0022,000B  IntraOcularPressure                N
+  0022,000C  HorizontalFieldOfView              N
+  0022,000D  PupilDilated                       N
+  0022,000E  DegreeOfDilation                   N
+  0022,0010  StereoBaselineAngle                N
+  0022,0011  StereoBaselineDisplacement         N
+  0022,0012  StereoHorizontalPixelOffset        N
+  0022,0013  StereoVerticalPixelOffset          N
+  0022,0014  StereoRotation                     N
+  0022,0015  AcquisitionDeviceTypeCodeSequence  N
+  0022,0016  IlluminationTypeCodeSequence       N
+  0022,0017  LightPathFilterTypeStackCodeSeq    N
+  0022,0018  ImagePathFilterTypeStackCodeSeq    N
+  0022,0019  LensesCodeSequence                 N
+  0022,001A  ChannelDescriptionCodeSequence     N
+  0022,001B  RefractiveStateSequence            N
+  0022,001C  MydriaticAgentCodeSequence         N
+  0022,001D  RelativeImagePositionCodeSequence  N
+  0022,0020  StereoPairsSequence                N
+  0022,0021  LeftImageSequence                  N
+  0022,0022  RightImageSequence                 N
+  0028,0000  ImagePresentationGroupLength       N
+  0028,0002  SamplesPerPixel                    N
+  0028,0003  SamplesPerPixelUsed                N
+  0028,0004  PhotometricInterpretation          N
+  0028,0005  ImageDimensions                    N
+  0028,0006  PlanarConfiguration                N
+  0028,0008  NumberOfFrames                     N
+  0028,0009  FrameIncrementPointer              N
+  0028,000A  FrameDimensionPointer              N
+  0028,0010  Rows                               N
+  0028,0011  Columns                            N
+  0028,0012  Planes                             N
+  0028,0014  UltrasoundColorDataPresent         N
+  0028,0030  PixelSpacing                       N
+  0028,0031  ZoomFactor                         N
+  0028,0032  ZoomCenter                         N
+  0028,0034  PixelAspectRatio                   N
+  0028,0040  ImageFormat                        N
+  0028,0050  ManipulatedImage                   N
+  0028,0051  CorrectedImage                     N
+  0028,0060  CompressionCode                    N
+  0028,0100  BitsAllocated                      N
+  0028,0101  BitsStored                         N
+  0028,0102  HighBit                            N
+  0028,0103  PixelRepresentation                N
+  0028,0104  SmallestValidPixelValue            N
+  0028,0105  LargestValidPixelValue             N
+  0028,0106  SmallestImagePixelValue            N
+  0028,0107  LargestImagePixelValue             N
+  0028,0108  SmallestPixelValueInSeries         N
+  0028,0109  LargestPixelValueInSeries          N
+  0028,0110  SmallestImagePixelValueInPlane     N
+  0028,0111  LargestImagePixelValueInPlane      N
+  0028,0120  PixelPaddingValue                  N
+  0028,0200  ImageLocation                      N
+  0028,0300  QualityControlImage                N
+  0028,0301  BurnedInAnnotation                 N
+  0028,1040  PixelIntensityRelationship         N
+  0028,1041  PixelIntensityRelationshipSign     N
+  0028,1050  WindowCenter                       N
+  0028,1051  WindowWidth                        N
+  0028,1052  RescaleIntercept                   N
+  0028,1053  RescaleSlope                       N
+  0028,1054  RescaleType                        N
+  0028,1055  WindowCenterAndWidthExplanation    N
+  0028,1080  GrayScale                          N
+  0028,1090  RecommendedViewingMode             N
+  0028,1100  GrayLookupTableDescriptor          N
+  0028,1101  RedPaletteColorTableDescriptor     N
+  0028,1102  GreenPaletteColorTableDescriptor   N
+  0028,1103  BluePaletteColorTableDescriptor    N
+  0028,1199  PaletteColorTableUID               N
+  0028,1200  GrayLookupTableData                N
+  0028,1201  RedPaletteColorTableData           N
+  0028,1202  GreenPaletteColorTableData         N
+  0028,1203  BluePaletteColorTableData          N
+  0028,1221  SegmentedRedColorTableData         N
+  0028,1222  SegmentedGreenColorTableData       N
+  0028,1223  SegmentedBlueColorTableData        N
+  0028,1300  ImplantPresent                     N
+  0028,1350  PartialView                        N
+  0028,1351  PartialViewDescription             N
+  0028,2110  LossyImageCompression              N
+  0028,2112  LossyImageCompressionRatio         N
+  0028,2114  LossyImageCompressionMethod        N
+  0028,3000  ModalityLUTSequence                N
+  0028,3002  LUTDescriptor                      N
+  0028,3003  LUTExplanation                     N
+  0028,3004  ModalityLUTType                    N
+  0028,3006  LUTData                            N
+  0028,3010  VOILUTSequence                     N
+  0028,3110  SoftcopyVOILUTSequence             N
+  0028,4000  ImagePresentationComments          N
+  0028,5000  BiPlaneAcquisitionSequence         N
+  0028,6010  RepresentativeFrameNumber          N
+  0028,6020  FrameNumbersOfInterestFOI          N
+  0028,6022  FrameOfInterestDescription         N
+  0028,6023  FrameOfInterestType                N
+  0028,6030  MaskPointers                       N
+  0028,6040  RWavePointer                       N
+  0028,6100  MaskSubtractionSequence            N
+  0028,6101  MaskOperation                      N
+  0028,6102  ApplicableFrameRange               N
+  0028,6110  MaskFrameNumbers                   N
+  0028,6112  ContrastFrameAveraging             N
+  0028,6114  MaskSubPixelShift                  N
+  0028,6120  TIDOffset                          N
+  0028,6190  MaskOperationExplanation           N
+  0028,9001  DataPointRows                      N
+  0028,9002  DataPointColumns                   N
+  0028,9003  SignalDomainColumns                N
+  0028,9099  LargestMonochromePixelValue        N
+  0028,9108  DataRepresentation                 N
+  0028,9110  PixelMeasuresSequence              N
+  0028,9132  FrameVOILUTSequence                N
+  0028,9145  PixelValueTransformationSequence   N
+  0028,9235  SignalDomainRows                   N
+  0032,0000  StudyGroupLength                   N
+  0032,000A  StudyStatusID                      N
+  0032,000C  StudyPriorityID                    N
+  0032,0012  StudyIDIssuer                      N
+  0032,0032  StudyVerifiedDate                  N
+  0032,0033  StudyVerifiedTime                  N
+  0032,0034  StudyReadDate                      N
+  0032,0035  StudyReadTime                      N
+  0032,1000  ScheduledStudyStartDate            N
+  0032,1001  ScheduledStudyStartTime            N
+  0032,1010  ScheduledStudyStopDate             N
+  0032,1011  ScheduledStudyStopTime             N
+  0032,1020  ScheduledStudyLocation             N
+  0032,1021  ScheduledStudyLocationAETitle      N
+  0032,1030  ReasonForStudy                     N
+  0032,1031  RequestingPhysicianIDSequence      N
+  0032,1032  RequestingPhysician                N
+  0032,1033  RequestingService                  N
+  0032,1040  StudyArrivalDate                   N
+  0032,1041  StudyArrivalTime                   N
+  0032,1050  StudyCompletionDate                N
+  0032,1051  StudyCompletionTime                N
+  0032,1055  StudyComponentStatusID             N
+  0032,1060  RequestedProcedureDescription      N
+  0032,1064  RequestedProcedureCodeSequence     N
+  0032,1070  RequestedContrastAgent             N
+  0032,4000  StudyComments                      N
+  0038,0004  ReferencedPatientAliasSequence     N
+  0038,0008  VisitStatusID                      N
+  0038,0010  AdmissionID                        N
+  0038,0011  IssuerOfAdmissionID                N
+  0038,0016  RouteOfAdmissions                  N
+  0038,001A  ScheduledAdmissionDate             N
+  0038,001B  ScheduledAdmissionTime             N
+  0038,001C  ScheduledDischargeDate             N
+  0038,001D  ScheduledDischargeTime             N
+  0038,001E  ScheduledPatientInstitResidence    N
+  0038,0020  AdmittingDate                      N
+  0038,0021  AdmittingTime                      N
+  0038,0030  DischargeDate                      N
+  0038,0032  DischargeTime                      N
+  0038,0040  DischargeDiagnosisDescription      N
+  0038,0044  DischargeDiagnosisCodeSequence     N
+  0038,0050  SpecialNeeds                       N
+  0038,0300  CurrentPatientLocation             N
+  0038,0400  PatientsInstitutionResidence       N
+  0038,0500  PatientState                       N
+  0038,4000  VisitComments                      N
+  003A,0004  WaveformOriginality                N
+  003A,0005  NumberOfWaveformChannels           N
+  003A,0010  NumberOfWaveformSamples            N
+  003A,001A  SamplingFrequency                  N
+  003A,0020  MultiplexGroupLabel                N
+  003A,0200  ChannelDefinitionSequence          N
+  003A,0202  WaveformChannelNumber              N
+  003A,0203  ChannelLabel                       N
+  003A,0205  ChannelStatus                      N
+  003A,0208  ChannelSourceSequence              N
+  003A,0209  ChannelSourceModifiersSequence     N
+  003A,020A  SourceWaveformSequence             N
+  003A,020C  ChannelDerivationDescription       N
+  003A,0210  ChannelSensitivity                 N
+  003A,0211  ChannelSensitivityUnitsSequence    N
+  003A,0212  ChannelSensitivityCorrectionFactor N
+  003A,0213  ChannelBaseline                    N
+  003A,0214  ChannelTimeSkew                    N
+  003A,0215  ChannelSampleSkew                  N
+  003A,0218  ChannelOffset                      N
+  003A,021A  WaveformBitsStored                 N
+  003A,0220  FilterLowFrequency                 N
+  003A,0221  FilterHighFrequency                N
+  003A,0222  NotchFilterFrequency               N
+  003A,0223  NotchFilterBandwidth               N
+  003A,0300  MultiplexAudioChannelsDescrCodeSeq N
+  003A,0301  ChannelIdentificationCode          N
+  003A,0302  ChannelMode                        N
+  0040,0001  ScheduledStationAETitle            N
+  0040,0002  ScheduledProcedureStepStartDate    N
+  0040,0003  ScheduledProcedureStepStartTime    N
+  0040,0004  ScheduledProcedureStepEndDate      N
+  0040,0005  ScheduledProcedureStepEndTime      N
+  0040,0006  ScheduledPerformingPhysiciansName  N
+  0040,0007  ScheduledProcedureStepDescription  N
+  0040,0008  ScheduledProtocolCodeSequence      N
+  0040,0009  ScheduledProcedureStepID           N
+  0040,000A  StageCodeSequence                  N
+  0040,000B  ScheduledPerformingPhysicianIDSeq  N
+  0040,0010  ScheduledStationName               N
+  0040,0011  ScheduledProcedureStepLocation     N
+  0040,0012  PreMedication                      N
+  0040,0020  ScheduledProcedureStepStatus       N
+  0040,0100  ScheduledProcedureStepSequence     N
+  0040,0220  ReferencedNonImageCompositeSOPSeq  N
+  0040,0241  PerformedStationAETitle            N
+  0040,0242  PerformedStationName               N
+  0040,0243  PerformedLocation                  N
+  0040,0244  PerformedProcedureStepStartDate    N
+  0040,0245  PerformedProcedureStepStartTime    N
+  0040,0250  PerformedProcedureStepEndDate      N
+  0040,0251  PerformedProcedureStepEndTime      N
+  0040,0252  PerformedProcedureStepStatus       N
+  0040,0253  PerformedProcedureStepID           N
+  0040,0254  PerformedProcedureStepDescription  N
+  0040,0255  PerformedProcedureTypeDescription  N
+  0040,0260  PerformedProtocolCodeSequence      N
+  0040,0270  ScheduledStepAttributesSequence    N
+  0040,0275  RequestAttributesSequence          N
+  0040,0280  CommentsOnPerformedProcedureStep   N
+  0040,0281  ProcStepDiscontinueReasonCodeSeq   N
+  0040,0293  QuantitySequence                   N
+  0040,0294  Quantity                           N
+  0040,0295  MeasuringUnitsSequence             N
+  0040,0296  BillingItemSequence                N
+  0040,0300  TotalTimeOfFluoroscopy             N
+  0040,0301  TotalNumberOfExposures             N
+  0040,0302  EntranceDose                       N
+  0040,0303  ExposedArea                        N
+  0040,0306  DistanceSourceToEntrance           N
+  0040,0307  DistanceSourceToSupport            N
+  0040,030E  ExposureDoseSequence               N
+  0040,0310  CommentsOnRadiationDose            N
+  0040,0312  XRayOutput                         N
+  0040,0314  HalfValueLayer                     N
+  0040,0316  OrganDose                          N
+  0040,0318  OrganExposed                       N
+  0040,0320  BillingProcedureStepSequence       N
+  0040,0321  FilmConsumptionSequence            N
+  0040,0324  BillingSuppliesAndDevicesSequence  N
+  0040,0330  ReferencedProcedureStepSequence    N
+  0040,0340  PerformedSeriesSequence            N
+  0040,0400  CommentsOnScheduledProcedureStep   N
+  0040,0440  ProtocolContextSequence            N
+  0040,0441  ContentItemModifierSequence        N
+  0040,050A  SpecimenAccessionNumber            N
+  0040,0550  SpecimenSequence                   N
+  0040,0551  SpecimenIdentifier                 N
+  0040,0555  AcquisitionContextSequence         N
+  0040,0556  AcquisitionContextDescription      N
+  0040,059A  SpecimenTypeCodeSequence           N
+  0040,06FA  SlideIdentifier                    N
+  0040,071A  ImageCenterPointCoordinatesSeq     N
+  0040,072A  XOffsetInSlideCoordinateSystem     N
+  0040,073A  YOffsetInSlideCoordinateSystem     N
+  0040,074A  ZOffsetInSlideCoordinateSystem     N
+  0040,08D8  PixelSpacingSequence               N
+  0040,08DA  CoordinateSystemAxisCodeSequence   N
+  0040,08EA  MeasurementUnitsCodeSequence       N
+  0040,1001  RequestedProcedureID               N
+  0040,1002  ReasonForRequestedProcedure        N
+  0040,1003  RequestedProcedurePriority         N
+  0040,1004  PatientTransportArrangements       N
+  0040,1005  RequestedProcedureLocation         N
+  0040,1006  PlacerOrderNumber-Procedure        N
+  0040,1007  FillerOrderNumber-Procedure        N
+  0040,1008  ConfidentialityCode                N
+  0040,1009  ReportingPriority                  N
+  0040,100A  ReasonForRequestedProcedureCodeSeq N
+  0040,1010  NamesOfIntendedRecipientsOfResults N
+  0040,1011  IntendedRecipientsOfResultsIDSeq   N
+  0040,1101  PersonIdentificationCodeSequence   N
+  0040,1102  PersonsAddress                     N
+  0040,1103  PersonsTelephoneNumbers            N
+  0040,1400  RequestedProcedureComments         N
+  0040,2001  ReasonForImagingServiceRequest     N
+  0040,2004  IssueDateOfImagingServiceRequest   N
+  0040,2005  IssueTimeOfImagingServiceRequest   N
+  0040,2006  PlacerOrderNum-ImagingServiceReq   N
+  0040,2007  FillerOrderNum-ImagingServiceReq   N
+  0040,2008  OrderEnteredBy                     N
+  0040,2009  OrderEnterersLocation              N
+  0040,2010  OrderCallbackPhoneNumber           N
+  0040,2016  PlacerOrderNum-ImagingServiceReq   N
+  0040,2017  FillerOrderNum-ImagingServiceReq   N
+  0040,2400  ImagingServiceRequestComments      N
+  0040,3001  ConfidentialityOnPatientDataDescr  N
+  0040,4001  GenPurposeScheduledProcStepStatus  N
+  0040,4002  GenPurposePerformedProcStepStatus  N
+  0040,4003  GenPurposeSchedProcStepPriority    N
+  0040,4004  SchedProcessingApplicationsCodeSeq N
+  0040,4005  SchedProcedureStepStartDateAndTime N
+  0040,4006  MultipleCopiesFlag                 N
+  0040,4007  PerformedProcessingAppsCodeSeq     N
+  0040,4009  HumanPerformerCodeSequence         N
+  0040,4010  SchedProcStepModificationDateTime  N
+  0040,4011  ExpectedCompletionDateAndTime      N
+  0040,4015  ResultingGenPurposePerfProcStepSeq N
+  0040,4016  RefGenPurposeSchedProcStepSeq      N
+  0040,4018  ScheduledWorkitemCodeSequence      N
+  0040,4019  PerformedWorkitemCodeSequence      N
+  0040,4020  InputAvailabilityFlag              N
+  0040,4021  InputInformationSequence           N
+  0040,4022  RelevantInformationSequence        N
+  0040,4023  RefGenPurSchedProcStepTransUID     N
+  0040,4025  ScheduledStationNameCodeSequence   N
+  0040,4026  ScheduledStationClassCodeSequence  N
+  0040,4027  SchedStationGeographicLocCodeSeq   N
+  0040,4028  PerformedStationNameCodeSequence   N
+  0040,4029  PerformedStationClassCodeSequence  N
+  0040,4030  PerformedStationGeogLocCodeSeq     N
+  0040,4031  RequestedSubsequentWorkItemCodeSeq N
+  0040,4032  NonDICOMOutputCodeSequence         N
+  0040,4033  OutputInformationSequence          N
+  0040,4034  ScheduledHumanPerformersSequence   N
+  0040,4035  ActualHumanPerformersSequence      N
+  0040,4036  HumanPerformersOrganization        N
+  0040,4037  HumanPerformersName                N
+  0040,8302  EntranceDoseInMilliGy              N
+  0040,9096  RealWorldValueMappingSequence      N
+  0040,9210  LUTLabel                           N
+  0040,9211  RealWorldValueLastValueMapped      N
+  0040,9212  RealWorldValueLUTData              N
+  0040,9216  RealWorldValueFirstValueMapped     N
+  0040,9224  RealWorldValueIntercept            N
+  0040,9225  RealWorldValueSlope                N
+  0040,A010  RelationshipType                   N
+  0040,A027  VerifyingOrganization              N
+  0040,A030  VerificationDateTime               N
+  0040,A032  ObservationDateTime                N
+  0040,A040  ValueType                          N
+  0040,A043  ConceptNameCodeSequence            N
+  0040,A050  ContinuityOfContent                N
+  0040,A073  VerifyingObserverSequence          N
+  0040,A075  VerifyingObserverName              N
+  0040,A088  VerifyingObserverIdentCodeSequence N
+  0040,A0B0  ReferencedWaveformChannels         N
+  0040,A120  DateTime                           N
+  0040,A121  Date                               N
+  0040,A122  Time                               N
+  0040,A123  PersonName                         N
+  0040,A124  UID                                N
+  0040,A130  TemporalRangeType                  N
+  0040,A132  ReferencedSamplePositions          N
+  0040,A136  ReferencedFrameNumbers             N
+  0040,A138  ReferencedTimeOffsets              N
+  0040,A13A  ReferencedDatetime                 N
+  0040,A160  TextValue                          N
+  0040,A168  ConceptCodeSequence                N
+  0040,A170  PurposeOfReferenceCodeSequence     N
+  0040,A180  AnnotationGroupNumber              N
+  0040,A195  ModifierCodeSequence               N
+  0040,A300  MeasuredValueSequence              N
+  0040,A301  NumericValueQualifierCodeSequence  N
+  0040,A30A  NumericValue                       N
+  0040,A360  PredecessorDocumentsSequence       N
+  0040,A370  ReferencedRequestSequence          N
+  0040,A372  PerformedProcedureCodeSequence     N
+  0040,A375  CurrentRequestedProcEvidenceSeq    N
+  0040,A385  PertinentOtherEvidenceSequence     N
+  0040,A491  CompletionFlag                     N
+  0040,A492  CompletionFlagDescription          N
+  0040,A493  VerificationFlag                   N
+  0040,A504  ContentTemplateSequence            N
+  0040,A525  IdenticalDocumentsSequence         N
+  0040,A730  ContentSequence                    N
+  0040,B020  AnnotationSequence                 N
+  0040,DB00  TemplateIdentifier                 N
+  0040,DB06  TemplateVersion                    N
+  0040,DB07  TemplateLocalVersion               N
+  0040,DB0B  TemplateExtensionFlag              N
+  0040,DB0C  TemplateExtensionOrganizationUID   N
+  0040,DB0D  TemplateExtensionCreatorUID        N
+  0040,DB73  ReferencedContentItemIdentifier    N
+  0050,0004  CalibrationImage                   N
+  0050,0010  DeviceSequence                     N
+  0050,0014  DeviceLength                       N
+  0050,0016  DeviceDiameter                     N
+  0050,0017  DeviceDiameterUnits                N
+  0050,0018  DeviceVolume                       N
+  0050,0019  InterMarkerDistance                N
+  0050,0020  DeviceDescription                  N
+  0054,0010  EnergyWindowVector                 N
+  0054,0011  NumberOfEnergyWindows              N
+  0054,0012  EnergyWindowInformationSequence    N
+  0054,0013  EnergyWindowRangeSequence          N
+  0054,0014  EnergyWindowLowerLimit             N
+  0054,0015  EnergyWindowUpperLimit             N
+  0054,0016  RadiopharmaceuticalInformationSeq  N
+  0054,0017  ResidualSyringeCounts              N
+  0054,0018  EnergyWindowName                   N
+  0054,0020  DetectorVector                     N
+  0054,0021  NumberOfDetectors                  N
+  0054,0022  DetectorInformationSequence        N
+  0054,0030  PhaseVector                        N
+  0054,0031  NumberOfPhases                     N
+  0054,0032  PhaseInformationSequence           N
+  0054,0033  NumberOfFramesInPhase              N
+  0054,0036  PhaseDelay                         N
+  0054,0038  PauseBetweenFrames                 N
+  0054,0039  PhaseDescription                   N
+  0054,0050  RotationVector                     N
+  0054,0051  NumberOfRotations                  N
+  0054,0052  RotationInformationSequence        N
+  0054,0053  NumberOfFramesInRotation           N
+  0054,0060  RRIntervalVector                   N
+  0054,0061  NumberOfRRIntervals                N
+  0054,0062  GatedInformationSequence           N
+  0054,0063  DataInformationSequence            N
+  0054,0070  TimeSlotVector                     N
+  0054,0071  NumberOfTimeSlots                  N
+  0054,0072  TimeSlotInformationSequence        N
+  0054,0073  TimeSlotTime                       N
+  0054,0080  SliceVector                        N
+  0054,0081  NumberOfSlices                     N
+  0054,0090  AngularViewVector                  N
+  0054,0100  TimeSliceVector                    N
+  0054,0101  NumberOfTimeSlices                 N
+  0054,0200  StartAngle                         N
+  0054,0202  TypeOfDetectorMotion               N
+  0054,0210  TriggerVector                      N
+  0054,0211  NumberOfTriggersInPhase            N
+  0054,0220  ViewCodeSequence                   N
+  0054,0222  ViewModifierCodeSequence           N
+  0054,0300  RadionuclideCodeSequence           N
+  0054,0302  AdministrationRouteCodeSequence    N
+  0054,0304  RadiopharmaceuticalCodeSequence    N
+  0054,0306  CalibrationDataSequence            N
+  0054,0308  EnergyWindowNumber                 N
+  0054,0400  ImageID                            N
+  0054,0410  PatientOrientationCodeSequence     N
+  0054,0412  PatientOrientationModifierCodeSeq  N
+  0054,0414  PatientGantryRelationshipCodeSeq   N
+  0054,0500  SliceProgressionDirection          N
+  0054,1000  SeriesType                         N
+  0054,1001  Units                              N
+  0054,1002  CountsSource                       N
+  0054,1004  ReprojectionMethod                 N
+  0054,1100  RandomsCorrectionMethod            N
+  0054,1101  AttenuationCorrectionMethod        N
+  0054,1102  DecayCorrection                    N
+  0054,1103  ReconstructionMethod               N
+  0054,1104  DetectorLinesOfResponseUsed        N
+  0054,1105  ScatterCorrectionMethod            N
+  0054,1200  AxialAcceptance                    N
+  0054,1201  AxialMash                          N
+  0054,1202  TransverseMash                     N
+  0054,1203  DetectorElementSize                N
+  0054,1210  CoincidenceWindowWidth             N
+  0054,1220  SecondaryCountsType                N
+  0054,1300  FrameReferenceTime                 N
+  0054,1310  PrimaryCountsAccumulated           N
+  0054,1311  SecondaryCountsAccumulated         N
+  0054,1320  SliceSensitivityFactor             N
+  0054,1321  DecayFactor                        N
+  0054,1322  DoseCalibrationFactor              N
+  0054,1323  ScatterFractionFactor              N
+  0054,1324  DeadTimeFactor                     N
+  0054,1330  ImageIndex                         N
+  0054,1400  CountsIncluded                     N
+  0054,1401  DeadTimeCorrectionFlag             N
+  0060,3000  HistogramSequence                  N
+  0060,3002  HistogramNumberOfBins              N
+  0060,3004  HistogramFirstBinValue             N
+  0060,3006  HistogramLastBinValue              N
+  0060,3008  HistogramBinWidth                  N
+  0060,3010  HistogramExplanation               N
+  0060,3020  HistogramData                      N
+  0070,0001  GraphicAnnotationSequence          N
+  0070,0002  GraphicLayer                       N
+  0070,0003  BoundingBoxAnnotationUnits         N
+  0070,0004  AnchorPointAnnotationUnits         N
+  0070,0005  GraphicAnnotationUnits             N
+  0070,0006  UnformattedTextValue               N
+  0070,0008  TextObjectSequence                 N
+  0070,0009  GraphicObjectSequence              N
+  0070,0010  BoundingBoxTopLeftHandCorner       N
+  0070,0011  BoundingBoxBottomRightHandCorner   N
+  0070,0012  BoundingBoxTextHorizJustification  N
+  0070,0014  AnchorPoint                        N
+  0070,0015  AnchorPointVisibility              N
+  0070,0020  GraphicDimensions                  N
+  0070,0021  NumberOfGraphicPoints              N
+  0070,0022  GraphicData                        N
+  0070,0023  GraphicType                        N
+  0070,0024  GraphicFilled                      N
+  0070,0041  ImageHorizontalFlip                N
+  0070,0042  ImageRotation                      N
+  0070,0052  DisplayedAreaTopLeftHandCorner     N
+  0070,0053  DisplayedAreaBottomRightHandCorner N
+  0070,005A  DisplayedAreaSelectionSequence     N
+  0070,0060  GraphicLayerSequence               N
+  0070,0062  GraphicLayerOrder                  N
+  0070,0066  GraphicLayerRecDisplayGraysclValue N
+  0070,0067  GraphicLayerRecDisplayRGBValue     N
+  0070,0068  GraphicLayerDescription            N
+  0070,0080  ContentLabel                       N
+  0070,0081  ContentDescription                 N
+  0070,0082  PresentationCreationDate           N
+  0070,0083  PresentationCreationTime           N
+  0070,0084  ContentCreatorsName                N
+  0070,0100  PresentationSizeMode               N
+  0070,0101  PresentationPixelSpacing           N
+  0070,0102  PresentationPixelAspectRatio       N
+  0070,0103  PresentationPixelMagRatio          N
+  0070,0306  ShapeType                          N
+  0070,0308  RegistrationSequence               N
+  0070,0309  MatrixRegistrationSequence         N
+  0070,030A  MatrixSequence                     N
+  0070,030C  FrameOfRefTransformationMatrixType N
+  0070,030D  RegistrationTypeCodeSequence       N
+  0070,030F  FiducialDescription                N
+  0070,0310  FiducialIdentifier                 N
+  0070,0311  FiducialIdentifierCodeSequence     N
+  0070,0312  ContourUncertaintyRadius           N
+  0070,0314  UsedFiducialsSequence              N
+  0070,0318  GraphicCoordinatesDataSequence     N
+  0070,031A  FiducialUID                        N
+  0070,031C  FiducialSetSequence                N
+  0070,031E  FiducialSequence                   N
+  0088,0130  StorageMediaFileSetID              N
+  0088,0140  StorageMediaFileSetUID             N
+  0088,0200  IconImageSequence                  N
+  0088,0904  TopicTitle                         N
+  0088,0906  TopicSubject                       N
+  0088,0910  TopicAuthor                        N
+  0088,0912  TopicKeyWords                      N
+  0100,0410  SOPInstanceStatus                  N
+  0100,0420  SOPAuthorizationDateAndTime        N
+  0100,0424  SOPAuthorizationComment            N
+  0100,0426  AuthorizationEquipmentCertNumber   N
+  0400,0005  MACIDNumber                        N
+  0400,0010  MACCalculationTransferSyntaxUID    N
+  0400,0015  MACAlgorithm                       N
+  0400,0020  DataElementsSigned                 N
+  0400,0100  DigitalSignatureUID                N
+  0400,0105  DigitalSignatureDateTime           N
+  0400,0110  CertificateType                    N
+  0400,0115  CertificateOfSigner                N
+  0400,0120  Signature                          N
+  0400,0305  CertifiedTimestampType             N
+  0400,0310  CertifiedTimestamp                 N
+  0400,0500  EncryptedAttributesSequence        N
+  0400,0510  EncryptedContentTransferSyntaxUID  N
+  0400,0520  EncryptedContent                   N
+  0400,0550  ModifiedAttributesSequence         N
+  2000,0010  NumberOfCopies                     N
+  2000,001E  PrinterConfigurationSequence       N
+  2000,0020  PrintPriority                      N
+  2000,0030  MediumType                         N
+  2000,0040  FilmDestination                    N
+  2000,0050  FilmSessionLabel                   N
+  2000,0060  MemoryAllocation                   N
+  2000,0061  MaximumMemoryAllocation            N
+  2000,0062  ColorImagePrintingFlag             N
+  2000,0063  CollationFlag                      N
+  2000,0065  AnnotationFlag                     N
+  2000,0067  ImageOverlayFlag                   N
+  2000,0069  PresentationLUTFlag                N
+  2000,006A  ImageBoxPresentationLUTFlag        N
+  2000,00A0  MemoryBitDepth                     N
+  2000,00A1  PrintingBitDepth                   N
+  2000,00A2  MediaInstalledSequence             N
+  2000,00A4  OtherMediaAvailableSequence        N
+  2000,00A8  SupportedImageDisplayFormatSeq     N
+  2000,0500  ReferencedFilmBoxSequence          N
+  2000,0510  ReferencedStoredPrintSequence      N
+  2010,0010  ImageDisplayFormat                 N
+  2010,0030  AnnotationDisplayFormatID          N
+  2010,0040  FilmOrientation                    N
+  2010,0050  FilmSizeID                         N
+  2010,0052  PrinterResolutionID                N
+  2010,0054  DefaultPrinterResolutionID         N
+  2010,0060  MagnificationType                  N
+  2010,0080  SmoothingType                      N
+  2010,00A6  DefaultMagnificationType           N
+  2010,00A7  OtherMagnificationTypesAvailable   N
+  2010,00A8  DefaultSmoothingType               N
+  2010,00A9  OtherSmoothingTypesAvailable       N
+  2010,0100  BorderDensity                      N
+  2010,0110  EmptyImageDensity                  N
+  2010,0120  MinDensity                         N
+  2010,0130  MaxDensity                         N
+  2010,0140  Trim                               N
+  2010,0150  ConfigurationInformation           N
+  2010,0152  ConfigurationInformationDescr      N
+  2010,0154  MaximumCollatedFilms               N
+  2010,015E  Illumination                       N
+  2010,0160  ReflectedAmbientLight              N
+  2010,0376  PrinterPixelSpacing                N
+  2010,0500  ReferencedFilmSessionSequence      N
+  2010,0510  ReferencedImageBoxSequence         N
+  2010,0520  ReferencedBasicAnnotationBoxSeq    N
+  2020,0010  ImagePosition                      N
+  2020,0020  Polarity                           N
+  2020,0030  RequestedImageSize                 N
+  2020,0040  RequestedDecimate-CropBehavior     N
+  2020,0050  RequestedResolutionID              N
+  2020,00A0  RequestedImageSizeFlag             N
+  2020,00A2  Decimate-CropResult                N
+  2020,0110  BasicGrayscaleImageSequence        N
+  2020,0111  BasicColorImageSequence            N
+  2020,0130  ReferencedImageOverlayBoxSequence  N
+  2020,0140  ReferencedVOILUTBoxSequence        N
+  2030,0010  AnnotationPosition                 N
+  2030,0020  TextString                         N
+  2040,0010  ReferencedOverlayPlaneSequence     N
+  2040,0011  ReferencedOverlayPlaneGroups       N
+  2040,0020  OverlayPixelDataSequence           N
+  2040,0060  OverlayMagnificationType           N
+  2040,0070  OverlaySmoothingType               N
+  2040,0072  OverlayOrImageMagnification        N
+  2040,0074  MagnifyToNumberOfColumns           N
+  2040,0080  OverlayForegroundDensity           N
+  2040,0082  OverlayBackgroundDensity           N
+  2040,0090  OverlayMode                        N
+  2040,0100  ThresholdDensity                   N
+  2040,0500  ReferencedImageBoxSequence         N
+  2050,0010  PresentationLUTSequence            N
+  2050,0020  PresentationLUTShape               N
+  2050,0500  ReferencedPresentationLUTSequence  N
+  2100,0010  PrintJobID                         N
+  2100,0020  ExecutionStatus                    N
+  2100,0030  ExecutionStatusInfo                N
+  2100,0040  CreationDate                       N
+  2100,0050  CreationTime                       N
+  2100,0070  Originator                         N
+  2100,0140  Destination                        N
+  2100,0160  OwnerID                            N
+  2100,0170  NumberOfFilms                      N
+  2100,0500  ReferencedPrintJobSequence         N
+  2110,0010  PrinterStatus                      N
+  2110,0020  PrinterStatusInfo                  N
+  2110,0030  PrinterName                        N
+  2110,0099  PrintQueueID                       N
+  2120,0010  QueueStatus                        N
+  2120,0050  PrintJobDescriptionSequence        N
+  2120,0070  ReferencedPrintJobSequence         N
+  2130,0010  PrintManagementCapabilitiesSeq     N
+  2130,0015  PrinterCharacteristicsSequence     N
+  2130,0030  FilmBoxContentSequence             N
+  2130,0040  ImageBoxContentSequence            N
+  2130,0050  AnnotationContentSequence          N
+  2130,0060  ImageOverlayBoxContentSequence     N
+  2130,0080  PresentationLUTContentSequence     N
+  2130,00A0  ProposedStudySequence              N
+  2130,00C0  OriginalImageSequence              N
+  2200,0001  LabelFromInfoExtractedFromInstance N
+  2200,0002  LabelText                          N
+  2200,0003  LabelStyleSelection                N
+  2200,0004  MediaDisposition                   N
+  2200,0005  BarcodeValue                       N
+  2200,0006  BarcodeSymbology                   N
+  2200,0007  AllowMediaSplitting                N
+  2200,0008  IncludeNonDICOMObjects             N
+  2200,0009  IncludeDisplayApplication          N
+  2200,000A  SaveCompInstancesAfterMediaCreate  N
+  2200,000B  TotalNumberMediaPiecesCreated      N
+  2200,000C  RequestedMediaApplicationProfile   N
+  2200,000D  ReferencedStorageMediaSequence     N
+  2200,000E  FailureAttributes                  N
+  2200,000F  AllowLossyCompression              N
+  2200,0020  RequestPriority                    N
+  3002,0002  RTImageLabel                       N
+  3002,0003  RTImageName                        N
+  3002,0004  RTImageDescription                 N
+  3002,000A  ReportedValuesOrigin               N
+  3002,000C  RTImagePlane                       N
+  3002,000D  XRayImageReceptorTranslation       N
+  3002,000E  XRayImageReceptorAngle             N
+  3002,0010  RTImageOrientation                 N
+  3002,0011  ImagePlanePixelSpacing             N
+  3002,0012  RTImagePosition                    N
+  3002,0020  RadiationMachineName               N
+  3002,0022  RadiationMachineSAD                N
+  3002,0024  RadiationMachineSSD                N
+  3002,0026  RTImageSID                         N
+  3002,0028  SourceToReferenceObjectDistance    N
+  3002,0029  FractionNumber                     N
+  3002,0030  ExposureSequence                   N
+  3002,0032  MetersetExposure                   N
+  3002,0034  DiaphragmPosition                  N
+  3002,0040  FluenceMapSequence                 N
+  3002,0041  FluenceDataSource                  N
+  3002,0042  FluenceDataScale                   N
+  3004,0001  DVHType                            N
+  3004,0002  DoseUnits                          N
+  3004,0004  DoseType                           N
+  3004,0006  DoseComment                        N
+  3004,0008  NormalizationPoint                 N
+  3004,000A  DoseSummationType                  N
+  3004,000C  GridFrameOffsetVector              N
+  3004,000E  DoseGridScaling                    N
+  3004,0010  RTDoseROISequence                  N
+  3004,0012  DoseValue                          N
+  3004,0014  TissueHeterogeneityCorrection      N
+  3004,0040  DVHNormalizationPoint              N
+  3004,0042  DVHNormalizationDoseValue          N
+  3004,0050  DVHSequence                        N
+  3004,0052  DVHDoseScaling                     N
+  3004,0054  DVHVolumeUnits                     N
+  3004,0056  DVHNumberOfBins                    N
+  3004,0058  DVHData                            N
+  3004,0060  DVHReferencedROISequence           N
+  3004,0062  DVHROIContributionType             N
+  3004,0070  DVHMinimumDose                     N
+  3004,0072  DVHMaximumDose                     N
+  3004,0074  DVHMeanDose                        N
+  3006,0002  StructureSetLabel                  N
+  3006,0004  StructureSetName                   N
+  3006,0006  StructureSetDescription            N
+  3006,0008  StructureSetDate                   N
+  3006,0009  StructureSetTime                   N
+  3006,0010  ReferencedFrameOfReferenceSequence N
+  3006,0012  RTReferencedStudySequence          N
+  3006,0014  RTReferencedSeriesSequence         N
+  3006,0016  ContourImageSequence               N
+  3006,0020  StructureSetROISequence            N
+  3006,0022  ROINumber                          N
+  3006,0024  ReferencedFrameOfReferenceUID      N
+  3006,0026  ROIName                            N
+  3006,0028  ROIDescription                     N
+  3006,002A  ROIDisplayColor                    N
+  3006,002C  ROIVolume                          N
+  3006,0030  RTRelatedROISequence               N
+  3006,0033  RTROIRelationship                  N
+  3006,0036  ROIGenerationAlgorithm             N
+  3006,0038  ROIGenerationDescription           N
+  3006,0039  ROIContourSequence                 N
+  3006,0040  ContourSequence                    N
+  3006,0042  ContourGeometricType               N
+  3006,0044  ContourSlabThickness               N
+  3006,0045  ContourOffsetVector                N
+  3006,0046  NumberOfContourPoints              N
+  3006,0048  ContourNumber                      N
+  3006,0049  AttachedContours                   N
+  3006,0050  ContourData                        N
+  3006,0080  RTROIObservationsSequence          N
+  3006,0082  ObservationNumber                  N
+  3006,0084  ReferencedROINumber                N
+  3006,0085  ROIObservationLabel                N
+  3006,0086  RTROIIdentificationCodeSequence    N
+  3006,0088  ROIObservationDescription          N
+  3006,00A0  RelatedRTROIObservationsSequence   N
+  3006,00A4  RTROIInterpretedType               N
+  3006,00A6  ROIInterpreter                     N
+  3006,00B0  ROIPhysicalPropertiesSequence      N
+  3006,00B2  ROIPhysicalProperty                N
+  3006,00B4  ROIPhysicalPropertyValue           N
+  3006,00C0  FrameOfReferenceRelationshipSeq    N
+  3006,00C2  RelatedFrameOfReferenceUID         N
+  3006,00C4  FrameOfReferenceTransformType      N
+  3006,00C6  FrameOfReferenceTransformMatrix    N
+  3006,00C8  FrameOfReferenceTransformComment   N
+  3008,0010  MeasuredDoseReferenceSequence      N
+  3008,0012  MeasuredDoseDescription            N
+  3008,0014  MeasuredDoseType                   N
+  3008,0016  MeasuredDoseValue                  N
+  3008,0020  TreatmentSessionBeamSequence       N
+  3008,0022  CurrentFractionNumber              N
+  3008,0024  TreatmentControlPointDate          N
+  3008,0025  TreatmentControlPointTime          N
+  3008,002A  TreatmentTerminationStatus         N
+  3008,002B  TreatmentTerminationCode           N
+  3008,002C  TreatmentVerificationStatus        N
+  3008,0030  ReferencedTreatmentRecordSequence  N
+  3008,0032  SpecifiedPrimaryMeterset           N
+  3008,0033  SpecifiedSecondaryMeterset         N
+  3008,0036  DeliveredPrimaryMeterset           N
+  3008,0037  DeliveredSecondaryMeterset         N
+  3008,003A  SpecifiedTreatmentTime             N
+  3008,003B  DeliveredTreatmentTime             N
+  3008,0040  ControlPointDeliverySequence       N
+  3008,0042  SpecifiedMeterset                  N
+  3008,0044  DeliveredMeterset                  N
+  3008,0048  DoseRateDelivered                  N
+  3008,0050  TreatmentSummaryCalcDoseRefSeq     N
+  3008,0052  CumulativeDoseToDoseReference      N
+  3008,0054  FirstTreatmentDate                 N
+  3008,0056  MostRecentTreatmentDate            N
+  3008,005A  NumberOfFractionsDelivered         N
+  3008,0060  OverrideSequence                   N
+  3008,0062  OverrideParameterPointer           N
+  3008,0064  MeasuredDoseReferenceNumber        N
+  3008,0066  OverrideReason                     N
+  3008,0070  CalculatedDoseReferenceSequence    N
+  3008,0072  CalculatedDoseReferenceNumber      N
+  3008,0074  CalculatedDoseReferenceDescription N
+  3008,0076  CalculatedDoseReferenceDoseValue   N
+  3008,0078  StartMeterset                      N
+  3008,007A  EndMeterset                        N
+  3008,0080  ReferencedMeasuredDoseReferenceSeq N
+  3008,0082  ReferencedMeasuredDoseReferenceNum N
+  3008,0090  ReferencedCalculatedDoseRefSeq     N
+  3008,0092  ReferencedCalculatedDoseRefNumber  N
+  3008,00A0  BeamLimitingDeviceLeafPairsSeq     N
+  3008,00B0  RecordedWedgeSequence              N
+  3008,00C0  RecordedCompensatorSequence        N
+  3008,00D0  RecordedBlockSequence              N
+  3008,00E0  TreatmentSummaryMeasuredDoseRefSeq N
+  3008,0100  RecordedSourceSequence             N
+  3008,0105  SourceSerialNumber                 N
+  3008,0110  TreatmentSessionAppSetupSeq        N
+  3008,0116  ApplicationSetupCheck              N
+  3008,0120  RecordedBrachyAccessoryDeviceSeq   N
+  3008,0122  ReferencedBrachyAccessoryDeviceNum N
+  3008,0130  RecordedChannelSequence            N
+  3008,0132  SpecifiedChannelTotalTime          N
+  3008,0134  DeliveredChannelTotalTime          N
+  3008,0136  SpecifiedNumberOfPulses            N
+  3008,0138  DeliveredNumberOfPulses            N
+  3008,013A  SpecifiedPulseRepetitionInterval   N
+  3008,013C  DeliveredPulseRepetitionInterval   N
+  3008,0140  RecordedSourceApplicatorSequence   N
+  3008,0142  ReferencedSourceApplicatorNumber   N
+  3008,0150  RecordedChannelShieldSequence      N
+  3008,0152  ReferencedChannelShieldNumber      N
+  3008,0160  BrachyControlPointDeliveredSeq     N
+  3008,0162  SafePositionExitDate               N
+  3008,0164  SafePositionExitTime               N
+  3008,0166  SafePositionReturnDate             N
+  3008,0168  SafePositionReturnTime             N
+  3008,0200  CurrentTreatmentStatus             N
+  3008,0202  TreatmentStatusComment             N
+  3008,0220  FractionGroupSummarySequence       N
+  3008,0223  ReferencedFractionNumber           N
+  3008,0224  FractionGroupType                  N
+  3008,0230  BeamStopperPosition                N
+  3008,0240  FractionStatusSummarySequence      N
+  3008,0250  TreatmentDate                      N
+  3008,0251  TreatmentTime                      N
+  300A,0002  RTPlanLabel                        N
+  300A,0003  RTPlanName                         N
+  300A,0004  RTPlanDescription                  N
+  300A,0006  RTPlanDate                         N
+  300A,0007  RTPlanTime                         N
+  300A,0009  TreatmentProtocols                 N
+  300A,000A  TreatmentIntent                    N
+  300A,000B  TreatmentSites                     N
+  300A,000C  RTPlanGeometry                     N
+  300A,000E  PrescriptionDescription            N
+  300A,0010  DoseReferenceSequence              N
+  300A,0012  DoseReferenceNumber                N
+  300A,0013  DoseReferenceUID                   N
+  300A,0014  DoseReferenceStructureType         N
+  300A,0015  NominalBeamEnergyUnit              N
+  300A,0016  DoseReferenceDescription           N
+  300A,0018  DoseReferencePointCoordinates      N
+  300A,001A  NominalPriorDose                   N
+  300A,0020  DoseReferenceType                  N
+  300A,0021  ConstraintWeight                   N
+  300A,0022  DeliveryWarningDose                N
+  300A,0023  DeliveryMaximumDose                N
+  300A,0025  TargetMinimumDose                  N
+  300A,0026  TargetPrescriptionDose             N
+  300A,0027  TargetMaximumDose                  N
+  300A,0028  TargetUnderdoseVolumeFraction      N
+  300A,002A  OrganAtRiskFullVolumeDose          N
+  300A,002B  OrganAtRiskLimitDose               N
+  300A,002C  OrganAtRiskMaximumDose             N
+  300A,002D  OrganAtRiskOverdoseVolumeFraction  N
+  300A,0040  ToleranceTableSequence             N
+  300A,0042  ToleranceTableNumber               N
+  300A,0043  ToleranceTableLabel                N
+  300A,0044  GantryAngleTolerance               N
+  300A,0046  BeamLimitingDeviceAngleTolerance   N
+  300A,0048  BeamLimitingDeviceToleranceSeq     N
+  300A,004A  BeamLimitingDevicePositionTol      N
+  300A,004C  PatientSupportAngleTolerance       N
+  300A,004E  TableTopEccentricAngleTolerance    N
+  300A,0051  TableTopVerticalPositionTolerance  N
+  300A,0052  TableTopLongitudinalPositionTol    N
+  300A,0053  TableTopLateralPositionTolerance   N
+  300A,0055  RTPlanRelationship                 N
+  300A,0070  FractionGroupSequence              N
+  300A,0071  FractionGroupNumber                N
+  300A,0072  FractionGroupDescription           N
+  300A,0078  NumberOfFractionsPlanned           N
+  300A,0079  NumberFractionPatternDigitsPerDay  N
+  300A,007A  RepeatFractionCycleLength          N
+  300A,007B  FractionPattern                    N
+  300A,0080  NumberOfBeams                      N
+  300A,0082  BeamDoseSpecificationPoint         N
+  300A,0084  BeamDose                           N
+  300A,0086  BeamMeterset                       N
+  300A,00A0  NumberOfBrachyApplicationSetups    N
+  300A,00A2  BrachyAppSetupDoseSpecPoint        N
+  300A,00A4  BrachyApplicationSetupDose         N
+  300A,00B0  BeamSequence                       N
+  300A,00B2  TreatmentMachineName               N
+  300A,00B3  PrimaryDosimeterUnit               N
+  300A,00B4  SourceAxisDistance                 N
+  300A,00B6  BeamLimitingDeviceSequence         N
+  300A,00B8  RTBeamLimitingDeviceType           N
+  300A,00BA  SourceToBeamLimitingDeviceDistance N
+  300A,00BC  NumberOfLeaf-JawPairs              N
+  300A,00BE  LeafPositionBoundaries             N
+  300A,00C0  BeamNumber                         N
+  300A,00C2  BeamName                           N
+  300A,00C3  BeamDescription                    N
+  300A,00C4  BeamType                           N
+  300A,00C6  RadiationType                      N
+  300A,00C7  HighDoseTechniqueType              N
+  300A,00C8  ReferenceImageNumber               N
+  300A,00CA  PlannedVerificationImageSequence   N
+  300A,00CC  ImagingDeviceSpecificAcqParams     N
+  300A,00CE  TreatmentDeliveryType              N
+  300A,00D0  NumberOfWedges                     N
+  300A,00D1  WedgeSequence                      N
+  300A,00D2  WedgeNumber                        N
+  300A,00D3  WedgeType                          N
+  300A,00D4  WedgeID                            N
+  300A,00D5  WedgeAngle                         N
+  300A,00D6  WedgeFactor                        N
+  300A,00D8  WedgeOrientation                   N
+  300A,00DA  SourceToWedgeTrayDistance          N
+  300A,00E0  NumberOfCompensators               N
+  300A,00E1  MaterialID                         N
+  300A,00E2  TotalCompensatorTrayFactor         N
+  300A,00E3  CompensatorSequence                N
+  300A,00E4  CompensatorNumber                  N
+  300A,00E5  CompensatorID                      N
+  300A,00E6  SourceToCompensatorTrayDistance    N
+  300A,00E7  CompensatorRows                    N
+  300A,00E8  CompensatorColumns                 N
+  300A,00E9  CompensatorPixelSpacing            N
+  300A,00EA  CompensatorPosition                N
+  300A,00EB  CompensatorTransmissionData        N
+  300A,00EC  CompensatorThicknessData           N
+  300A,00ED  NumberOfBoli                       N
+  300A,00EE  CompensatorType                    N
+  300A,00F0  NumberOfBlocks                     N
+  300A,00F2  TotalBlockTrayFactor               N
+  300A,00F4  BlockSequence                      N
+  300A,00F5  BlockTrayID                        N
+  300A,00F6  SourceToBlockTrayDistance          N
+  300A,00F8  BlockType                          N
+  300A,00F9  AccessoryCode                      N
+  300A,00FA  BlockDivergence                    N
+  300A,00FB  BlockMountingPosition              N
+  300A,00FC  BlockNumber                        N
+  300A,00FE  BlockName                          N
+  300A,0100  BlockThickness                     N
+  300A,0102  BlockTransmission                  N
+  300A,0104  BlockNumberOfPoints                N
+  300A,0106  BlockData                          N
+  300A,0107  ApplicatorSequence                 N
+  300A,0108  ApplicatorID                       N
+  300A,0109  ApplicatorType                     N
+  300A,010A  ApplicatorDescription              N
+  300A,010C  CumulativeDoseReferenceCoefficient N
+  300A,010E  FinalCumulativeMetersetWeight      N
+  300A,0110  NumberOfControlPoints              N
+  300A,0111  ControlPointSequence               N
+  300A,0112  ControlPointIndex                  N
+  300A,0114  NominalBeamEnergy                  N
+  300A,0115  DoseRateSet                        N
+  300A,0116  WedgePositionSequence              N
+  300A,0118  WedgePosition                      N
+  300A,011A  BeamLimitingDevicePositionSequence N
+  300A,011C  Leaf-JawPositions                  N
+  300A,011E  GantryAngle                        N
+  300A,011F  GantryRotationDirection            N
+  300A,0120  BeamLimitingDeviceAngle            N
+  300A,0121  BeamLimitingDeviceRotateDirection  N
+  300A,0122  PatientSupportAngle                N
+  300A,0123  PatientSupportRotationDirection    N
+  300A,0124  TableTopEccentricAxisDistance      N
+  300A,0125  TableTopEccentricAngle             N
+  300A,0126  TableTopEccentricRotateDirection   N
+  300A,0128  TableTopVerticalPosition           N
+  300A,0129  TableTopLongitudinalPosition       N
+  300A,012A  TableTopLateralPosition            N
+  300A,012C  IsocenterPosition                  N
+  300A,012E  SurfaceEntryPoint                  N
+  300A,0130  SourceToSurfaceDistance            N
+  300A,0134  CumulativeMetersetWeight           N
+  300A,0180  PatientSetupSequence               N
+  300A,0182  PatientSetupNumber                 N
+  300A,0184  PatientAdditionalPosition          N
+  300A,0190  FixationDeviceSequence             N
+  300A,0192  FixationDeviceType                 N
+  300A,0194  FixationDeviceLabel                N
+  300A,0196  FixationDeviceDescription          N
+  300A,0198  FixationDevicePosition             N
+  300A,01A0  ShieldingDeviceSequence            N
+  300A,01A2  ShieldingDeviceType                N
+  300A,01A4  ShieldingDeviceLabel               N
+  300A,01A6  ShieldingDeviceDescription         N
+  300A,01A8  ShieldingDevicePosition            N
+  300A,01B0  SetupTechnique                     N
+  300A,01B2  SetupTechniqueDescription          N
+  300A,01B4  SetupDeviceSequence                N
+  300A,01B6  SetupDeviceType                    N
+  300A,01B8  SetupDeviceLabel                   N
+  300A,01BA  SetupDeviceDescription             N
+  300A,01BC  SetupDeviceParameter               N
+  300A,01D0  SetupReferenceDescription          N
+  300A,01D2  TableTopVerticalSetupDisplacement  N
+  300A,01D4  TableTopLongitudinalSetupDisplace  N
+  300A,01D6  TableTopLateralSetupDisplacement   N
+  300A,0200  BrachyTreatmentTechnique           N
+  300A,0202  BrachyTreatmentType                N
+  300A,0206  TreatmentMachineSequence           N
+  300A,0210  SourceSequence                     N
+  300A,0212  SourceNumber                       N
+  300A,0214  SourceType                         N
+  300A,0216  SourceManufacturer                 N
+  300A,0218  ActiveSourceDiameter               N
+  300A,021A  ActiveSourceLength                 N
+  300A,0222  SourceEncapsulationNomThickness    N
+  300A,0224  SourceEncapsulationNomTransmission N
+  300A,0226  SourceIsotopeName                  N
+  300A,0228  SourceIsotopeHalfLife              N
+  300A,022A  ReferenceAirKermaRate              N
+  300A,022C  AirKermaRateReferenceDate          N
+  300A,022E  AirKermaRateReferenceTime          N
+  300A,0230  ApplicationSetupSequence           N
+  300A,0232  ApplicationSetupType               N
+  300A,0234  ApplicationSetupNumber             N
+  300A,0236  ApplicationSetupName               N
+  300A,0238  ApplicationSetupManufacturer       N
+  300A,0240  TemplateNumber                     N
+  300A,0242  TemplateType                       N
+  300A,0244  TemplateName                       N
+  300A,0250  TotalReferenceAirKerma             N
+  300A,0260  BrachyAccessoryDeviceSequence      N
+  300A,0262  BrachyAccessoryDeviceNumber        N
+  300A,0263  BrachyAccessoryDeviceID            N
+  300A,0264  BrachyAccessoryDeviceType          N
+  300A,0266  BrachyAccessoryDeviceName          N
+  300A,026A  BrachyAccessoryDeviceNomThickness  N
+  300A,026C  BrachyAccessoryDevNomTransmission  N
+  300A,0280  ChannelSequence                    N
+  300A,0282  ChannelNumber                      N
+  300A,0284  ChannelLength                      N
+  300A,0286  ChannelTotalTime                   N
+  300A,0288  SourceMovementType                 N
+  300A,028A  NumberOfPulses                     N
+  300A,028C  PulseRepetitionInterval            N
+  300A,0290  SourceApplicatorNumber             N
+  300A,0291  SourceApplicatorID                 N
+  300A,0292  SourceApplicatorType               N
+  300A,0294  SourceApplicatorName               N
+  300A,0296  SourceApplicatorLength             N
+  300A,0298  SourceApplicatorManufacturer       N
+  300A,029C  SourceApplicatorWallNomThickness   N
+  300A,029E  SourceApplicatorWallNomTrans       N
+  300A,02A0  SourceApplicatorStepSize           N
+  300A,02A2  TransferTubeNumber                 N
+  300A,02A4  TransferTubeLength                 N
+  300A,02B0  ChannelShieldSequence              N
+  300A,02B2  ChannelShieldNumber                N
+  300A,02B3  ChannelShieldID                    N
+  300A,02B4  ChannelShieldName                  N
+  300A,02B8  ChannelShieldNominalThickness      N
+  300A,02BA  ChannelShieldNominalTransmission   N
+  300A,02C8  FinalCumulativeTimeWeight          N
+  300A,02D0  BrachyControlPointSequence         N
+  300A,02D2  ControlPointRelativePosition       N
+  300A,02D4  ControlPoint3DPosition             N
+  300A,02D6  CumulativeTimeWeight               N
+  300A,02E0  CompensatorDivergence              N
+  300A,02E1  CompensatorMountingPosition        N
+  300A,02E2  SourceToCompensatorDistance        N
+  300C,0002  ReferencedRTPlanSequence           N
+  300C,0004  ReferencedBeamSequence             N
+  300C,0006  ReferencedBeamNumber               N
+  300C,0007  ReferencedReferenceImageNumber     N
+  300C,0008  StartCumulativeMetersetWeight      N
+  300C,0009  EndCumulativeMetersetWeight        N
+  300C,000A  ReferencedBrachyAppSetupSeq        N
+  300C,000C  ReferencedBrachyAppSetupNumber     N
+  300C,000E  ReferencedSourceNumber             N
+  300C,0020  ReferencedFractionGroupSequence    N
+  300C,0022  ReferencedFractionGroupNumber      N
+  300C,0040  ReferencedVerificationImageSeq     N
+  300C,0042  ReferencedReferenceImageSequence   N
+  300C,0050  ReferencedDoseReferenceSequence    N
+  300C,0051  ReferencedDoseReferenceNumber      N
+  300C,0055  BrachyReferencedDoseReferenceSeq   N
+  300C,0060  ReferencedStructureSetSequence     N
+  300C,006A  ReferencedPatientSetupNumber       N
+  300C,0080  ReferencedDoseSequence             N
+  300C,00A0  ReferencedToleranceTableNumber     N
+  300C,00B0  ReferencedBolusSequence            N
+  300C,00C0  ReferencedWedgeNumber              N
+  300C,00D0  ReferencedCompensatorNumber        N
+  300C,00E0  ReferencedBlockNumber              N
+  300C,00F0  ReferencedControlPointIndex        N
+  300E,0002  ApprovalStatus                     N
+  300E,0004  ReviewDate                         N
+  300E,0005  ReviewTime                         N
+  300E,0008  ReviewerName                       N
+  4000,0000  TextGroupLength                    N
+  4000,0010  Arbitrary                          N
+  4000,4000  TextComments                       N
+  4008,0040  ResultsID                          N
+  4008,0042  ResultsIDIssuer                    N
+  4008,0050  ReferencedInterpretationSequence   N
+  4008,0100  InterpretationRecordedDate         N
+  4008,0101  InterpretationRecordedTime         N
+  4008,0102  InterpretationRecorder             N
+  4008,0103  ReferenceToRecordedSound           N
+  4008,0108  InterpretationTranscriptionDate    N
+  4008,0109  InterpretationTranscriptionTime    N
+  4008,010A  InterpretationTranscriber          N
+  4008,010B  InterpretationText                 N
+  4008,010C  InterpretationAuthor               N
+  4008,0111  InterpretationApproverSequence     N
+  4008,0112  InterpretationApprovalDate         N
+  4008,0113  InterpretationApprovalTime         N
+  4008,0114  PhysicianApprovingInterpretation   N
+  4008,0115  InterpretationDiagnosisDescription N
+  4008,0117  InterpretationDiagnosisCodeSeq     N
+  4008,0118  ResultsDistributionListSequence    N
+  4008,0119  DistributionName                   N
+  4008,011A  DistributionAddress                N
+  4008,0200  InterpretationID                   N
+  4008,0202  InterpretationIDIssuer             N
+  4008,0210  InterpretationTypeID               N
+  4008,0212  InterpretationStatusID             N
+  4008,0300  Impressions                        N
+  4008,4000  ResultsComments                    N
+  4FFE,0001  MACParametersSequence              N
+  50xx,0005  CurveDimensions                    N
+  50xx,0010  NumberOfPoints                     N
+  50xx,0020  TypeOfData                         N
+  50xx,0022  CurveDescription                   N
+  50xx,0030  AxisUnits                          N
+  50xx,0040  AxisLabels                         N
+  50xx,0103  DataValueRepresentation            N
+  50xx,0104  MinimumCoordinateValue             N
+  50xx,0105  MaximumCoordinateValue             N
+  50xx,0106  CurveRange                         N
+  50xx,0110  CurveDataDescriptor                N
+  50xx,0112  CoordinateStartValue               N
+  50xx,0114  CoordinateStepValue                N
+  50xx,1001  CurveActivationLayer               N
+  50xx,2000  AudioType                          N
+  50xx,2002  AudioSampleFormat                  N
+  50xx,2004  NumberOfChannels                   N
+  50xx,2006  NumberOfSamples                    N
+  50xx,2008  SampleRate                         N
+  50xx,200A  TotalTime                          N
+  50xx,200C  AudioSampleData                    N
+  50xx,200E  AudioComments                      N
+  50xx,2500  CurveLabel                         N
+  50xx,2600  ReferencedOverlaySequence          N
+  50xx,2610  ReferencedOverlayGroup             N
+  50xx,3000  CurveData                          N
+  5200,9229  SharedFunctionalGroupsSequence     N
+  5200,9230  PerFrameFunctionalGroupsSequence   N
+  5400,0100  WaveformSequence                   N
+  5400,0110  ChannelMinimumValue                N
+  5400,0112  ChannelMaximumValue                N
+  5400,1004  WaveformBitsAllocated              N
+  5400,1006  WaveformSampleInterpretation       N
+  5400,100A  WaveformPaddingValue               N
+  5400,1010  WaveformData                       N
+  5600,0010  FirstOrderPhaseCorrectionAngle     N
+  5600,0020  SpectroscopyData                   N
+  6000,0000  OverlayGroupLength                 N
+  60xx,0010  OverlayRows                        N
+  60xx,0011  OverlayColumns                     N
+  60xx,0012  OverlayPlanes                      N
+  60xx,0015  NumberOfFramesInOverlay            N
+  60xx,0022  OverlayDescription                 N
+  60xx,0040  OverlayType                        N
+  60xx,0045  OverlaySubtype                     N
+  60xx,0050  OverlayOrigin                      N
+  60xx,0051  ImageFrameOrigin                   N
+  60xx,0052  OverlayPlaneOrigin                 N
+  60xx,0060  CompressionCode                    N
+  60xx,0100  OverlayBitsAllocated               N
+  60xx,0102  OverlayBitPosition                 N
+  60xx,0110  OverlayFormat                      N
+  60xx,0200  OverlayLocation                    N
+  60xx,1001  OverlayActivationLayer             N
+  60xx,1100  OverlayDescriptorGray              N
+  60xx,1101  OverlayDescriptorRed               N
+  60xx,1102  OverlayDescriptorGreen             N
+  60xx,1103  OverlayDescriptorBlue              N
+  60xx,1200  OverlaysGray                       N
+  60xx,1201  OverlaysRed                        N
+  60xx,1202  OverlaysGreen                      N
+  60xx,1203  OverlaysBlue                       N
+  60xx,1301  ROIArea                            N
+  60xx,1302  ROIMean                            N
+  60xx,1303  ROIStandardDeviation               N
+  60xx,1500  OverlayLabel                       N
+  60xx,3000  OverlayData                        N
+  60xx,4000  OverlayComments                    N
+  7FE0,0000  PixelDataGroupLength               N
+  7FE0,0010  PixelData                          N
+  FFFA,FFFA  DigitalSignaturesSequence          N
+  FFFC,FFFC  DataSetTrailingPadding             N
+  FFFE,E000  StartOfItem                        N
+  FFFE,E00D  EndOfItems                         N
+  FFFE,E0DD  EndOfSequence                      N
+
+=head2 HTML Tags
+
+Meta information extracted from the header of HTML and XHTML files.  This is
+a mix of information found in the C<META> elements and the C<TITLE> element.
+
+  Tag ID                      Tag Name          Writable
+  ------                      --------          --------
+  'abstract'                  Abstract          N
+  'author'                    Author            N
+  'classification'            Classification    N
+  'copyright'                 Copyright         N
+  'dc'                        DC                HTML dc
+  'description'               Description       N
+  'distribution'              Distribution      N
+  'doc-class'                 DocClass          N
+  'doc-rights'                DocRights         N
+  'doc-type'                  DocType           N
+  'formatter'                 Formatter         N
+  'generator'                 Generator         N
+  'googlebot'                 GoogleBot         N
+  'http-equiv'                HTTP-equiv        HTML equiv
+  'keywords'                  Keywords          N+
+  'mssmarttagspreventparsing' NoMSSmartTags     N
+  'ncc'                       NCC               HTML ncc
+  'owner'                     Owner             N
+  'prod'                      Prod              HTML prod
+  'progid'                    ProgID            N
+  'rating'                    Rating            N
+  'refresh'                   Refresh           N
+  'resource-type'             ResourceType      N
+  'revisit-after'             RevisitAfter      N
+  'robots'                    Robots            N+
+  'title'                     Title             N
+  'vw96'                      VW96              HTML vw96
+
+=head3 HTML dc Tags
+
+Dublin Core schema tags (also used in XMP).
+
+  Tag ID               Tag Name                 Writable
+  ------               --------                 --------
+  'contributor'        Contributor              N+
+  'coverage'           Coverage                 N
+  'creator'            Creator                  N+
+  'date'               Date                     N+
+  'description'        Description              N
+  'format'             Format                   N
+  'identifier'         Identifier               N
+  'language'           Language                 N+
+  'publisher'          Publisher                N+
+  'relation'           Relation                 N+
+  'rights'             Rights                   N
+  'source'             Source                   N
+  'subject'            Subject                  N+
+  'title'              Title                    N
+  'type'               Type                     N+
+
+=head3 HTML equiv Tags
+
+These tags have a family 1 group name of "HTTP-equiv".
+
+  Tag ID                Tag Name                Writable
+  ------                --------                --------
+  'cache-control'       CacheControl            N
+  'content-disposition' ContentDisposition      N
+  'content-language'    ContentLanguage         N
+  'content-script-type' ContentScriptType       N
+  'content-style-type'  ContentStyleType        N
+  'content-type'        ContentType             N
+  'default-style'       DefaultStyle            N
+  'expires'             Expires                 N
+  'ext-cache'           ExtCache                N
+  'imagetoolbar'        ImageToolbar            N
+  'lotus'               Lotus                   N
+  'page-enter'          PageEnter               N
+  'page-exit'           PageExit                N
+  'pics-label'          PicsLabel               N
+  'pragma'              Pragma                  N
+  'refresh'             Refresh                 N
+  'reply-to'            ReplyTo                 N
+  'set-cookie'          SetCookie               N
+  'site-enter'          SiteEnter               N
+  'site-exit'           SiteExit                N
+  'vary'                Vary                    N
+  'window-target'       WindowTarget            N
+
+=head3 HTML ncc Tags
+
+  Tag ID               Tag Name                 Writable
+  ------               --------                 --------
+  'charset'            Charset                  N
+  'depth'              Depth                    N
+  'files'              Files                    N
+  'footnotes'          Footnotes                N
+  'generator'          Generator                N
+  'kbytesize'          KByteSize                N
+  'maxpagenormal'      MaxPageNormal            N
+  'multimediatype'     MultimediaType           N
+  'narrator'           Narrator                 N
+  'pagefront'          PageFront                N
+  'pagenormal'         PageNormal               N
+  'pagespecial'        PageSpecial              N
+  'prodnotes'          ProdNotes                N
+  'produceddate'       ProducedDate             N
+  'producer'           Producer                 N
+  'revision'           Revision                 N
+  'revisiondate'       RevisionDate             N
+  'setinfo'            SetInfo                  N
+  'sidebars'           Sidebars                 N
+  'sourcedate'         SourceDate               N
+  'sourceedition'      SourceEdition            N
+  'sourcepublisher'    SourcePublisher          N
+  'sourcerights'       SourceRights             N
+  'sourcetitle'        SourceTitle              N
+  'tocitems'           TOCItems                 N
+  'totaltime'          Duration                 N
+
+=head3 HTML prod Tags
+
+  Tag ID               Tag Name                 Writable
+  ------               --------                 --------
+  'recengineer'        RecEngineer              N
+  'reclocation'        RecLocation              N
+
+=head3 HTML vw96 Tags
+
+  Tag ID               Tag Name                 Writable
+  ------               --------                 --------
+  'objecttype'         ObjectType               N
+
+=head2 Extra Tags
+
+The extra tags represent information found in the image but not associated
+with any other tag group.  The three writable "pseudo" tags (Filename,
+Directory and FileModifyDate) may be written without the need to rewrite the
+file since their values are not contained within the file data.
+
+  Tag Name                                      Writable
+  --------                                      --------
+  CanonVRD                                      Y!
+  Comment                                       Y
+  Directory                                     Y!
+  EXIF                                          N
+  Encryption                                    N
+  Error                                         N
+  ExifByteOrder                                 Y
+  ExifToolVersion                               N
+  FileModifyDate                                Y
+  FileName                                      Y!
+  FileSize                                      N
+  FileType                                      N
+  ICC_Profile                                   Y!
+  ImageHeight                                   N
+  ImageWidth                                    N
+  MIMEType                                      N
+  MaxVal                                        N
+  Warning                                       N
+  XMP                                           Y
+  XResolution                                   N
+  YResolution                                   N
+
+=head2 Composite Tags
+
+The values of the composite tags are derived from the values of other tags.
+These are convenience tags which are calculated after all other information
+is extracted.
+
+  Tag Name             Derived From             Writable
+  --------             ------------             --------
+  Aperture             FNumber                  N
+                       ApertureValue
+  BlueBalance          WB_RGGBLevels            N
+                       WB_RGBGLevels
+                       WB_RBGGLevels
+                       WB_GRBGLevels
+                       WB_GRGBLevels
+                       WB_RBLevels
+                       WBBlueLevel
+                       WBGreenLevel
+  CFAPattern           CFARepeatPatternDim      N
+                       CFAPattern2
+  CircleOfConfusion    ScaleFactor35efl         N
+  ConditionalFEC       FlashExposureComp        N
+                       FlashBits
+  DOF                  FocusDistance            N
+                       FocalLength
+                       Aperture
+                       CircleOfConfusion
+  DateCreated          Kodak:YearCreated        N
+                       Kodak:MonthDayCreated
+  DateTimeCreated      DateCreated              N
+                       TimeCreated
+  DateTimeOriginal     DateTimeCreated          N
+  DigitalZoom          Canon:ZoomSourceWidth    N
+                       Canon:ZoomTargetWidth
+                       Canon:DigitalZoom
+  DriveMode            ContinuousDrive          N
+                       Self-timer
+  Duration             FLAC:SampleRate          N
+                       FLAC:Channels
+                       FLAC:TotalSamples
+  Duration             FileSize                 N
+                       MPEG:AudioBitrate
+                       MPEG:VideoBitrate
+  Duration             FrameRate                N
+                       FrameCount
+  ExtenderStatus       Olympus:Extender         N
+                       Olympus:LensType
+                       MaxApertureValue
+  FlashOn              FlashBits                N
+                       Flash
+  FlashType            FlashBits                N
+  FocalLength35efl     FocalLength              N
+                       ScaleFactor35efl
+  GPSDateTime          GPSDateStamp             N
+                       GPSTimeStamp
+  GPSLatitude          GPS:GPSLatitude          N
+                       GPS:GPSLatitudeRef
+  GPSLatitudeRef       XMP:GPSLatitude          N
+  GPSLongitude         GPS:GPSLongitude         N
+                       GPS:GPSLongitudeRef
+  GPSLongitudeRef      XMP:GPSLongitude         N
+  GPSPosition          GPSLatitude              N
+                       GPSLongitude
+  HyperfocalDistance   FocalLength              N
+                       Aperture
+                       CircleOfConfusion
+  ISO                  Canon:CameraISO          N
+                       Canon:BaseISO
+                       Canon:AutoISO
+  ImageHeight          PostScript:ImageData     N
+                       PostScript:BoundingBox
+  ImageSize            ImageWidth               N
+                       ImageHeight
+  ImageWidth           PostScript:ImageData     N
+                       PostScript:BoundingBox
+  JpgFromRaw           JpgFromRawStart          Y
+                       JpgFromRawLength
+  Lens                 ShortFocal               N
+                       LongFocal
+  Lens35efl            ShortFocal               N
+                       LongFocal
+                       Lens
+                       ScaleFactor35efl
+  LensID               Nikon:LensIDNumber       N
+                       LensFStops
+                       MinFocalLength
+                       MaxFocalLength
+                       MaxApertureAtMinFocal
+                       MaxApertureAtMaxFocal
+                       MCUVersion
+                       Nikon:LensType
+  LensSpec             Nikon:Lens               N
+                       Nikon:LensType
+  LightValue           Aperture                 N
+                       ShutterSpeed
+                       ISO
+  OtherImage           OtherImageStart          N
+                       OtherImageLength
+  PreviewImage         PreviewImageStart        Y
+                       PreviewImageLength
+                       PreviewImageValid
+                       PreviewImageStart (1)
+                       PreviewImageLength (1)
+  PreviewImageSize     PreviewImageWidth        N
+                       PreviewImageHeight
+  PreviewImage         ScreenNail               N
+  RedBalance           WB_RGGBLevels            N
+                       WB_RGBGLevels
+                       WB_RBGGLevels
+                       WB_GRBGLevels
+                       WB_GRGBLevels
+                       WB_RBLevels
+                       WBRedLevel
+                       WBGreenLevel
+  RedEyeReduction      CanonFlashMode           N
+                       FlashBits
+  ScaleFactor35efl     FocalLength              N
+                       FocalLengthIn35mmFormat
+                       Composite:DigitalZoom
+                       FocalPlaneDiagonal
+                       FocalPlaneXSize
+                       FocalPlaneYSize
+                       FocalPlaneResolutionUnit
+                       FocalPlaneXResolution
+                       FocalPlaneYResolution
+                       CanonImageWidth
+                       CanonImageHeight
+                       ExifImageWidth
+                       ExifImageLength
+                       ImageWidth
+                       ImageHeight
+  ShootingMode         CanonExposureMode        N
+                       EasyMode
+  ShutterCurtainHack   FlashBits                N
+                       ShutterCurtainSync
+  ShutterSpeed         ExposureTime             N
+                       ShutterSpeedValue
+                       BulbDuration
+  SubSecDateTimeOriginal DateTimeOriginal       N
+                       SubSecTimeOriginal
+  ThumbnailImage       ThumbnailOffset          Y
+                       ThumbnailLength
+  WB_RGGBLevels        Canon:WhiteBalance       N
+                       WB_RGGBLevelsAsShot
+                       WB_RGGBLevelsAuto
+                       WB_RGGBLevelsDaylight
+                       WB_RGGBLevelsCloudy
+                       WB_RGGBLevelsTungsten
+                       WB_RGGBLevelsFluorescent
+                       WB_RGGBLevelsFlash
+                       WB_RGGBLevelsCustom
+                       WB_RGGBLevelsShade
+                       WB_RGGBLevelsKelvin
+
+=head2 Shortcuts Tags
+
+Shortcut tags are convenience tags that represent one or more other tag
+names.  They are used like regular tags to read and write the information
+for a specified set of tags.
+
+The shortcut tags below have been pre-defined, but user-defined shortcuts
+may be added via the %Image::ExifTool::Shortcuts::UserDefined lookup in the
+~/.ExifTool_config file.  See the Image::ExifTool::Shortcuts documentation
+for more details.
+
+  Tag Name             Refers To                Writable
+  --------             ---------                --------
+  AllDates             DateTimeOriginal         Y
+                       CreateDate
+                       ModifyDate
+  Canon                FileName                 Y
+                       Model
+                       DateTimeOriginal
+                       ShootingMode
+                       ShutterSpeed
+                       Aperture
+                       MeteringMode
+                       ExposureCompensation
+                       ISO
+                       Lens
+                       FocalLength
+                       ImageSize
+                       Quality
+                       FlashOn
+                       FlashType
+                       ConditionalFEC
+                       RedEyeReduction
+                       ShutterCurtainHack
+                       WhiteBalance
+                       FocusMode
+                       Contrast
+                       Sharpness
+                       Saturation
+                       ColorTone
+                       FileSize
+                       FileNumber
+                       DriveMode
+                       OwnerName
+                       SerialNumber
+  Canon20D             FileName                 Y
+                       Model
+                       DateTimeOriginal
+                       ShootingMode
+                       ShutterSpeedValue
+                       ApertureValue
+                       MeteringMode
+                       ExposureCompensation
+                       ISO
+                       Lens
+                       FocalLength
+                       ExifImageWidth
+                       ExifImageLength
+                       Quality
+                       FlashOn
+                       FlashType
+                       ConditionalFEC
+                       RedEyeReduction
+                       ShutterCurtainHack
+                       WhiteBalance
+                       FocusMode
+                       Contrast
+                       Sharpness
+                       Saturation
+                       ColorTone
+                       ColorSpace
+                       LongExposureNoiseReduction
+                       FileSize
+                       FileNumber
+                       DriveMode
+                       OwnerName
+                       SerialNumber
+  Common               FileName                 Y
+                       FileSize
+                       Model
+                       DateTimeOriginal
+                       ImageSize
+                       Quality
+                       FocalLength
+                       ShutterSpeed
+                       Aperture
+                       ISO
+                       WhiteBalance
+                       Flash
+  MakerNotes           MakerNotes               Y
+                       MakerNoteCanon
+                       MakerNoteCasio
+                       MakerNoteCasio2
+                       MakerNoteFujiFilm
+                       MakerNoteHP
+                       MakerNoteHP2
+                       MakerNoteHP4
+                       MakerNoteHP6
+                       MakerNoteJVC
+                       MakerNoteJVCText
+                       MakerNoteKodak1a
+                       MakerNoteKodak1b
+                       MakerNoteKodak2
+                       MakerNoteKodak3
+                       MakerNoteKodak4
+                       MakerNoteKodak5
+                       MakerNoteKodak6a
+                       MakerNoteKodak6b
+                       MakerNoteKodak7
+                       MakerNoteKodakIFD
+                       MakerNoteKodakUnknown
+                       MakerNoteKyocera
+                       MakerNoteMinolta
+                       MakerNoteMinolta2
+                       MakerNoteMinolta3
+                       MakerNoteNikon
+                       MakerNoteNikon2
+                       MakerNoteNikon3
+                       MakerNoteOlympus
+                       MakerNoteOlympus2
+                       MakerNoteLeica
+                       MakerNotePanasonic
+                       MakerNotePanasonic2
+                       MakerNotePentax
+                       MakerNotePentax2
+                       MakerNotePentax3
+                       MakerNotePentax4
+                       MakerNoteRicoh
+                       MakerNoteRicohText
+                       MakerNoteSanyo
+                       MakerNoteSanyoC4
+                       MakerNoteSanyoPatch
+                       MakerNoteSigma
+                       MakerNoteSony
+                       MakerNoteSonySRF
+                       MakerNoteSonySR2
+                       MakerNoteUnknown
+  Nikon                Model                    Y
+                       SubSecDateTimeOriginal
+                       ShutterCount
+                       LensSpec
+                       FocalLength
+                       ImageSize
+                       ShutterSpeed
+                       Aperture
+                       ISO
+                       NoiseReduction
+                       ExposureProgram
+                       ExposureCompensation
+                       WhiteBalance
+                       WhiteBalanceFineTune
+                       ShootingMode
+                       Quality
+                       MeteringMode
+                       FocusMode
+                       ImageOptimization
+                       ToneComp
+                       ColorHue
+                       ColorSpace
+                       HueAdjustment
+                       Saturation
+                       Sharpness
+                       Flash
+                       FlashMode
+                       FlashExposureComp
+
+=head1 NOTES
+
+This document generated automatically by
+L<Image::ExifTool::BuildTagLookup|Image::ExifTool::BuildTagLookup>.
+
+=head1 AUTHOR
+
+Copyright 2003-2007, Phil Harvey (phil at owl.phy.queensu.ca)
+
+This library is free software; you can redistribute it and/or modify it
+under the same terms as Perl itself.
+
+=head1 SEE ALSO
+
+L<Image::ExifTool(3pm)|Image::ExifTool>
+
+=cut
Index: /gsdl/trunk/perllib/cpan/Image/ExifTool/Unknown.pm
===================================================================
--- /gsdl/trunk/perllib/cpan/Image/ExifTool/Unknown.pm	(revision 16842)
+++ /gsdl/trunk/perllib/cpan/Image/ExifTool/Unknown.pm	(revision 16842)
@@ -0,0 +1,66 @@
+#------------------------------------------------------------------------------
+# File:         Unknown.pm
+#
+# Description:  Unknown EXIF maker notes tags
+#
+# Revisions:    04/07/2004  - P. Harvey Created
+#------------------------------------------------------------------------------
+
+package Image::ExifTool::Unknown;
+
+use strict;
+use vars qw($VERSION);
+use Image::ExifTool::Exif;
+
+$VERSION = '1.13';
+
+# Unknown maker notes
+%Image::ExifTool::Unknown::Main = (
+    WRITE_PROC => \&Image::ExifTool::Exif::WriteExif,
+    CHECK_PROC => \&Image::ExifTool::Exif::CheckExif,
+    GROUPS => { 0 => 'MakerNotes', 1 => 'MakerUnknown', 2 => 'Camera' },
+
+    # this seems to be a common fixture, so look for it in unknown maker notes
+    0x0e00 => {
+        Name => 'PrintIM',
+        Description => 'Print Image Matching',
+        SubDirectory => {
+            TagTable => 'Image::ExifTool::PrintIM::Main',
+        },
+    },
+);
+
+
+1;  # end
+
+__END__
+
+=head1 NAME
+
+Image::ExifTool::Unknown - Unknown EXIF maker notes tags
+
+=head1 SYNOPSIS
+
+This module is loaded automatically by Image::ExifTool when required.
+
+=head1 DESCRIPTION
+
+Image::ExifTool has definitions for the maker notes from many manufacturers,
+however information can sometimes be extracted from unknown manufacturers if
+the maker notes are in standard IFD format.  This module contains the
+definitions necessary for Image::ExifTool to read the maker notes from
+unknown manufacturers.
+
+=head1 AUTHOR
+
+Copyright 2003-2007, Phil Harvey (phil at owl.phy.queensu.ca)
+
+This library is free software; you can redistribute it and/or modify it
+under the same terms as Perl itself.
+
+=head1 SEE ALSO
+
+L<Image::ExifTool::TagNames/Unknown Tags>,
+L<Image::ExifTool(3pm)|Image::ExifTool>
+
+=cut
Index: /gsdl/trunk/perllib/cpan/Image/ExifTool/Vorbis.pm
===================================================================
--- /gsdl/trunk/perllib/cpan/Image/ExifTool/Vorbis.pm	(revision 16842)
+++ /gsdl/trunk/perllib/cpan/Image/ExifTool/Vorbis.pm	(revision 16842)
@@ -0,0 +1,353 @@
+#------------------------------------------------------------------------------
+# File:         Vorbis.pm
+#
+# Description:  Read Ogg Vorbis meta information
+#
+# Revisions:    11/10/2006 - P. Harvey Created
+#
+# References:   1) http://www.xiph.org/vorbis/doc/
+#               2) http://flac.sourceforge.net/ogg_mapping.html
+#------------------------------------------------------------------------------
+
+package Image::ExifTool::Vorbis;
+
+use strict;
+use vars qw($VERSION);
+use Image::ExifTool qw(:DataAccess :Utils);
+
+$VERSION = '1.01';
+
+my $MAX_PACKETS = 2;    # maximum packets to scan from each stream at start of file
+
+sub ProcessComments($$$);
+sub DecodeCoverArt($);
+
+# Vorbis comment tags
+%Image::ExifTool::Vorbis::Main = (
+    NOTES => q{
+        ExifTool extracts the following Vorbis information from Ogg files.  As well
+        as this, ExifTool also extracts FLAC and ID3 information from Ogg files.
+    },
+    1 => {
+        Name => 'Identification',
+        SubDirectory => { TagTable => 'Image::ExifTool::Vorbis::Identification' },
+    },
+    3 => {
+        Name => 'Comments',
+        SubDirectory => { TagTable => 'Image::ExifTool::Vorbis::Comments' },
+    },
+);
+
+%Image::ExifTool::Vorbis::Identification = (
+    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
+    GROUPS => { 2 => 'Audio' },
+    0 => {
+        Name => 'VorbisVersion',
+        Format => 'int32u',
+    },
+    4 => 'AudioChannels',
+    5 => {
+        Name => 'SampleRate',
+        Format => 'int32u',
+    },
+    9 => {
+        Name => 'MaximumBitrate',
+        Format => 'int32u',
+        RawConv => '$val || undef',
+    },
+    13 => {
+        Name => 'NominalBitrate',
+        Format => 'int32u',
+        RawConv => '$val || undef',
+    },
+    17 => {
+        Name => 'MinimumBitrate',
+        Format => 'int32u',
+        RawConv => '$val || undef',
+    },
+);
+
+%Image::ExifTool::Vorbis::Comments = (
+    PROCESS_PROC => \&ProcessComments,
+    GROUPS => { 2 => 'Audio' },
+    NOTES => q{
+        The tags below are only some common tags found in the Vorbis comments of Ogg
+        Vorbis and Ogg FLAC audio files, however ExifTool will extract values from
+        any tag found, even if not listed here.
+    },
+    vendor    => { Notes => 'from comment header' },
+    TITLE     => { Name => 'Title' },
+    VERSION   => { Name => 'Version' },
+    ALBUM     => { Name => 'Album' },
+    TRACKNUMBER=>{ Name => 'TrackNumber' },
+    ARTIST    => { Name => 'Artist',       Groups => { 2 => 'Author' }, List => 1 },
+    PERFORMER => { Name => 'Performer',    Groups => { 2 => 'Author' }, List => 1 },
+    COPYRIGHT => { Name => 'Copyright',    Groups => { 2 => 'Author' } },
+    LICENSE   => { Name => 'License',      Groups => { 2 => 'Author' } },
+    ORGANIZATION=>{Name => 'Organization', Groups => { 2 => 'Author' } },
+    DESCRIPTION=>{ Name => 'Description' },
+    GENRE     => { Name => 'Genre' },
+    DATE      => { Name => 'Date',         Groups => { 2 => 'Time' } },
+    LOCATION  => { Name => 'Location',     Groups => { 2 => 'Location' } },
+    CONTACT   => { Name => 'Contact',      Groups => { 2 => 'Author' }, List => 1 },
+    ISRC      => { Name => 'ISRCNumber' },
+    COVERARTMIME => { Name => 'CoverArtMIMEType' },
+    COVERART  => {
+        Name => 'CoverArt',
+        Notes => 'base64-encoded image',
+        ValueConv => q{
+            require Image::ExifTool::XMP;
+            Image::ExifTool::XMP::DecodeBase64($val);
+        },
+    },
+    REPLAYGAIN_TRACK_PEAK => { Name => 'ReplayGainTrackPeak' },
+    REPLAYGAIN_TRACK_GAIN => { Name => 'ReplayGainTrackGain' },
+    REPLAYGAIN_ALBUM_PEAK => { Name => 'ReplayGainAlbumPeak' },
+    REPLAYGAIN_ALBUM_GAIN => { Name => 'ReplayGainAlbumGain' },
+    # observed in "Xiph.Org libVorbis I 20020717" ogg:
+    ENCODED_USING => { Name => 'EncodedUsing' },
+    ENCODED_BY  => { Name => 'EncodedBy' },
+    COMMENT     => { Name => 'Comment' },
+);
+
+#------------------------------------------------------------------------------
+# Process Vorbis Comments
+# Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
+# Returns: 1 on success, otherwise returns 0 and sets a Warning
+sub ProcessComments($$$)
+{
+    my ($exifTool, $dirInfo, $tagTablePtr) = @_;
+    my $dataPt = $$dirInfo{DataPt};
+    my $dataPos = $$dirInfo{DataPos};
+    my $pos = $$dirInfo{DirStart} || 0;
+    my $end = $$dirInfo{DirLen} ? $pos + $$dirInfo{DirLen} : length $$dataPt;
+    my ($num, $index);
+
+    SetByteOrder('II');
+    for (;;) {
+        last if $pos + 4 > $end;
+        my $len = Get32u($dataPt, $pos);
+        last if $pos + 4 + $len > $end;
+        my $start = $pos + 4;
+        my $buff = substr($$dataPt, $start, $len);
+        $pos = $start + $len;
+        my ($tag, $val);
+        if (defined $num) {
+            $buff =~ /(.*?)=(.*)/s or last;
+            ($tag, $val) = ($1, $2);
+        } else {
+            $tag = 'vendor';
+            $val = $buff;
+            $num = ($pos + 4 < $end) ? Get32u($dataPt, $pos) : 0;
+            $exifTool->VPrint(0, "  + [Vorbis comments with $num entries]\n");
+            $pos += 4;
+        }
+        # add tag to table unless it exists already
+        unless ($$tagTablePtr{$tag}) {
+            my $name = ucfirst(lc($tag));
+            # remove invalid characters in tag name and capitalize following letters
+            $name =~ s/[^\w-]+(.?)/\U$1/sg;
+            $name =~ s/([a-z0-9])_([a-z])/$1\U$2/g;
+            Image::ExifTool::AddTagToTable($tagTablePtr, $tag, { Name => $name });
+        }
+        $exifTool->HandleTag($tagTablePtr, $tag, $val,
+            Index   => $index,
+            DataPt  => $dataPt,
+            DataPos => $dataPos,
+            Start   => $start,
+            Size    => $len,
+        );
+        # all done if this was our last tag
+        $num-- or return 1;
+        $index = (defined $index) ? $index + 1 : 0;
+    }
+    $exifTool->Warn('Format error in Vorbis comments');
+    return 0;
+}
+
+#------------------------------------------------------------------------------
+# Process Ogg packet
+# Inputs: 0) ExifTool object ref, 1) data ref, 2) tag table ref
+# Returns: 1 on success
+sub ProcessPacket($$$)
+{
+    my ($exifTool, $dataPt, $tagTablePtr) = @_;
+    if ($$dataPt =~ /^(.)vorbis/s) {
+        my $tag = ord($1);
+        my $tagInfo = $exifTool->GetTagInfo($tagTablePtr, $tag);
+        return 0 unless $tagInfo and $$tagInfo{SubDirectory};
+        my %dirInfo = (
+            DataPt => $dataPt,
+            DirName => $$tagInfo{Name},
+            DirStart => 7,
+        );
+        my $table = GetTagTable($tagInfo->{SubDirectory}->{TagTable});
+        return $exifTool->ProcessDirectory(\%dirInfo, $table);
+    }
+    return 0;
+}
+
+#------------------------------------------------------------------------------
+# Extract information from an Ogg Vorbis or Ogg FLAC file
+# Inputs: 0) ExifTool object reference, 1) dirInfo reference
+# Returns: 1 on success, 0 if this wasn't a valid Ogg Vorbis file
+sub ProcessOGG($$)
+{
+    my ($exifTool, $dirInfo) = @_;
+
+    # must first check for leading/trailing ID3 information
+    unless ($exifTool->{DONE_ID3}) {
+        require Image::ExifTool::ID3;
+        Image::ExifTool::ID3::ProcessID3($exifTool, $dirInfo) and return 1;
+    }
+    my $raf = $$dirInfo{RAF};
+    my $verbose = $exifTool->Options('Verbose');
+    my $out = $exifTool->Options('TextOut');
+    my ($success, $page, $packets, $streams) = (0,0,0,0);
+    my ($buff, $tagTablePtr, $flag, $stream, %val, $numFlac);
+
+    for (;;) {
+        # must read ahead to next page to see if it is a continuation
+        # (this code would be a lot simpler if the continuation flag
+        #  was on the leading instead of the trailing page!)
+        if ($raf and $raf->Read($buff, 28) == 28) {
+            # validate magic number
+            unless ($buff =~ /^OggS/) {
+                $success and $exifTool->Warn('Lost synchronization');
+                last;
+            }
+            unless ($success) {
+                # set file type and initialize on first page
+                $success = 1;
+                $exifTool->SetFileType();
+                SetByteOrder('II');
+                $tagTablePtr = GetTagTable('Image::ExifTool::Vorbis::Main');
+            }
+            $flag = Get8u(\$buff, 5);       # page flag
+            $stream = Get32u(\$buff, 14);   # stream serial number
+            ++$streams if $flag & 0x02;     # count start-of-stream pages
+            ++$packets unless $flag & 0x01; # keep track of packet count
+        } else {
+            # all done unless we have to process our last packet
+            last unless %val;
+            ($stream) = sort keys %val;     # take a stream
+            $flag = 0;                      # no continuation
+            undef $raf;                     # flag for done reading
+        }
+
+        if (defined $numFlac) {
+            # stop to process FLAC headers if we hit the end of file
+            last unless $raf;
+            --$numFlac; # one less header packet to read
+        } else {
+            # can finally process previous packet from this stream
+            # unless this is a continuation page
+            if (defined $val{$stream} and not $flag & 0x01) {
+                ProcessPacket($exifTool, \$val{$stream}, $tagTablePtr);
+                delete $val{$stream};
+                # only read the first $MAX_PACKETS packets from each stream
+                if ($packets > $MAX_PACKETS * $streams or not defined $raf) {
+                    last unless %val;   # all done (success!)
+                    next;               # process remaining stream(s)
+                }
+            }
+            # stop processing Ogg Vorbis if we have scanned enough packets
+            last if $packets > $MAX_PACKETS * $streams and not %val;
+        }
+
+        # continue processing the current page
+        my $pageNum = Get32u(\$buff, 18);   # page sequence number
+        my $nseg = Get8u(\$buff, 26);       # number of segments
+        # calculate total data length
+        my $dataLen = Get8u(\$buff, 27);
+        if ($nseg) {
+            $raf->Read($buff, $nseg-1) == $nseg-1 or last;
+            my @segs = unpack('C*', $buff);
+            # could check that all these (but the last) are 255...
+            foreach (@segs) { $dataLen += $_ }
+        }
+        if (defined $page) {
+            if ($page == $pageNum) {
+                ++$page;
+            } else {
+                $exifTool->Warn('Missing page(s) in Ogg file');
+                undef $page;
+            }
+        }
+        # read page data
+        $raf->Read($buff, $dataLen) == $dataLen or last;
+        if ($verbose > 1) {
+            printf $out "Page %d, stream 0x%x, flag 0x%x (%d bytes)\n",
+                   $pageNum, $stream, $flag, $dataLen;
+            $exifTool->VerboseDump(\$buff, DataPos => $raf->Tell() - $dataLen);
+        }
+        if (defined $val{$stream}) {
+            $val{$stream} .= $buff;     # add this continuation page
+        } elsif (not $flag & 0x01) {    # ignore remaining pages of a continued packet
+            # ignore the first page of any packet we aren't parsing
+            if ($buff =~ /^(.)vorbis/s and $$tagTablePtr{ord($1)}) {
+                $val{$stream} = $buff;      # save this page
+            } elsif ($buff =~ /^\x7fFLAC..(..)/s) {
+                $numFlac = unpack('n',$1);
+                $val{$stream} = substr($buff, 9);
+            }
+        }
+        if (defined $numFlac) {
+            # stop to process FLAC headers if we have them all
+            last if $numFlac <= 0;
+        } elsif (defined $val{$stream} and $flag & 0x04) {
+            # process Ogg Vorbis packet now if end-of-stream bit is set
+            ProcessPacket($exifTool, \$val{$stream}, $tagTablePtr);
+            delete $val{$stream};
+        }
+    }
+    if (defined $numFlac and defined $val{$stream}) {
+        # process FLAC headers as if it was a complete FLAC file
+        require Image::ExifTool::FLAC;
+        my %dirInfo = ( RAF => new File::RandomAccess(\$val{$stream}) );
+        Image::ExifTool::FLAC::ProcessFLAC($exifTool, \%dirInfo);
+    }
+    return $success;
+}
+
+1;  # end
+
+__END__
+
+=head1 NAME
+
+Image::ExifTool::Vorbis - Read Ogg Vorbis meta information
+
+=head1 SYNOPSIS
+
+This module is used by Image::ExifTool
+
+=head1 DESCRIPTION
+
+This module contains definitions required by Image::ExifTool to extract meta
+information from Ogg Vorbis and Ogg FLAC audio files.
+
+=head1 AUTHOR
+
+Copyright 2003-2007, Phil Harvey (phil at owl.phy.queensu.ca)
+
+This library is free software; you can redistribute it and/or modify it
+under the same terms as Perl itself.
+
+=head1 REFERENCES
+
+=over 4
+
+=item L<http://www.xiph.org/vorbis/doc/>
+
+=item L<http://flac.sourceforge.net/ogg_mapping.html>
+
+=back
+
+=head1 SEE ALSO
+
+L<Image::ExifTool::TagNames/Vorbis Tags>,
+L<Image::ExifTool(3pm)|Image::ExifTool>
+
+=cut
+
Index: /gsdl/trunk/perllib/cpan/Image/ExifTool/WriteCanonRaw.pl
===================================================================
--- /gsdl/trunk/perllib/cpan/Image/ExifTool/WriteCanonRaw.pl	(revision 16842)
+++ /gsdl/trunk/perllib/cpan/Image/ExifTool/WriteCanonRaw.pl	(revision 16842)
@@ -0,0 +1,606 @@
+#------------------------------------------------------------------------------
+# File:         WriteCanonRaw.pl
+#
+# Description:  Write Canon RAW (CRW and CR2) meta information
+#
+# Revisions:    01/25/2005 - P. Harvey Created
+#------------------------------------------------------------------------------
+package Image::ExifTool::CanonRaw;
+
+use strict;
+use vars qw($VERSION $AUTOLOAD %crwTagFormat);
+use Image::ExifTool::Fixup;
+
+# mappings to from RAW tagID to MakerNotes tagID
+# (Note: upper two bits of RawTagID are zero)
+my %mapRawTag = (
+  # RawTagID => Canon TagID
+    0x080b => 0x07, # CanonFirmwareVersion
+    0x0810 => 0x09, # OwnerName
+    0x0815 => 0x06, # CanonImageType
+    0x1028 => 0x03, # (unknown if no tag name specified)
+    0x1029 => 0x02, # FocalLength
+    0x102a => 0x04, # CanonShotInfo
+    0x102d => 0x01, # CanonCameraSettings
+    0x1033 => 0x0f, # CanonCustomFunctions (only verified for 10D)
+    0x1038 => 0x12, # CanonAFInfo
+    0x1039 => 0x13,
+    0x1093 => 0x93,
+    0x10a8 => 0xa8,
+    0x10a9 => 0xa9, # WhiteBalanceTable
+    0x10aa => 0xaa,
+    0x10ae => 0xae, # ColorTemperature
+    0x10b4 => 0xb4, # ColorSpace
+    0x10b5 => 0xb5,
+    0x10c0 => 0xc0,
+    0x10c1 => 0xc1,
+    0x180b => 0x0c, # SerialNumber
+    0x1817 => 0x08, # FileNumber
+    0x1834 => 0x10,
+    0x183b => 0x15,
+);
+# translation from Rotation to Orientation values
+my %mapRotation = (
+    0 => 1,
+    90 => 6,
+    180 => 3,
+    270 => 8,
+);
+
+
+#------------------------------------------------------------------------------
+# Initialize buffers for building MakerNotes from RAW data
+# Inputs: 0) ExifTool object reference
+sub InitMakerNotes($)
+{
+    my $exifTool = shift;
+    $exifTool->{MAKER_NOTE_INFO} = {
+        Entries => { },     # directory entries keyed by tagID
+        ValBuff => "\0\0\0\0", # value data buffer (start with zero nextIFD pointer)
+        FixupTags => { },   # flags for tags with data in value buffer
+    };
+}
+
+#------------------------------------------------------------------------------
+# Build maker notes from CanonRaw information
+# Inputs: 0) ExifTool object reference, 1) raw tag ID, 2) reference to tagInfo
+#         3) reference to value, 4) format name, 5) count
+# Notes: This will build the directory in the order the tags are found in the CRW
+# file, which isn't sequential (but Canon's version isn't sequential either...)
+sub BuildMakerNotes($$$$$$)
+{
+    my ($exifTool, $rawTag, $tagInfo, $valuePt, $formName, $count) = @_;
+
+    my $tagID = $mapRawTag{$rawTag} || return;
+    $formName or warn(sprintf "No format for tag 0x%x!\n",$rawTag), return;
+    # special case: ignore user comment because it gets saved in EXIF
+    # (and has the same raw tagID as CanonFileDescription)
+    return if $tagInfo and $$tagInfo{Name} eq 'UserComment';
+    my $tagType = ($rawTag >> 8) & 0x38;
+    my $format = $Image::ExifTool::Exif::formatNumber{$formName};
+    my $fsiz = $Image::ExifTool::Exif::formatSize[$format];
+    my $size = length($$valuePt);
+    my $value;
+    if ($count and $size != $count * $fsiz) {
+        if ($size < $count * $fsiz) {
+            warn sprintf("Value too short for raw tag 0x%x\n",$rawTag);
+            return;
+        }
+        # shorten value appropriately
+        $size = $count * $fsiz;
+        $value = substr($$valuePt, 0, $size);
+    } else {
+        $count = $size / $fsiz;
+        $value = $$valuePt;
+    }
+    my $offsetVal;
+    my $makerInfo = $exifTool->{MAKER_NOTE_INFO};
+    if ($size > 4) {
+        my $len = length $makerInfo->{ValBuff};
+        $offsetVal = Set32u($len);
+        $makerInfo->{ValBuff} .= $value;
+        # pad to an even number of bytes
+        $size & 0x01 and $makerInfo->{ValBuff} .= "\0";
+        # set flag indicating that this tag needs a fixup
+        $makerInfo->{FixupTags}->{$tagID} = 1;
+    } else {
+        $offsetVal = $value;
+        $size < 4 and $offsetVal .= "\0" x (4 - $size);
+    }
+    $makerInfo->{Entries}->{$tagID} = Set16u($tagID) . Set16u($format) .
+                                      Set32u($count) . $offsetVal;
+}
+
+#------------------------------------------------------------------------------
+# Finish building and save MakerNotes
+# Inputs: 0) ExifTool object reference
+sub SaveMakerNotes($)
+{
+    my $exifTool = shift;
+    # save maker notes
+    my $makerInfo = $exifTool->{MAKER_NOTE_INFO};
+    delete $exifTool->{MAKER_NOTE_INFO};
+    my $dirEntries = $makerInfo->{Entries};
+    my $numEntries = scalar(keys %$dirEntries);
+    my $fixup = new Image::ExifTool::Fixup;
+    return unless $numEntries;
+    # build the MakerNotes directory
+    my $makerNotes = Set16u($numEntries);
+    my $tagID;
+    # write the entries in proper tag order (even though Canon doesn't do this...)
+    foreach $tagID (sort { $a <=> $b } keys %$dirEntries) {
+        $makerNotes .= $$dirEntries{$tagID};
+        next unless $makerInfo->{FixupTags}->{$tagID};
+        # add fixup for this pointer
+        $fixup->AddFixup(length($makerNotes) - 4);
+    }
+    # save position of maker notes for pointer fixups
+    $fixup->{Shift} += length($makerNotes);
+    $exifTool->{MAKER_NOTE_FIXUP} = $fixup;
+    $exifTool->{MAKER_NOTE_BYTE_ORDER} = GetByteOrder();
+    # add value data
+    $makerNotes .= $makerInfo->{ValBuff};
+    # get MakerNotes tag info
+    my $tagTablePtr = Image::ExifTool::GetTagTable('Image::ExifTool::Exif::Main');
+    my $tagInfo = $exifTool->GetTagInfo($tagTablePtr, 0x927c);
+    # save the MakerNotes
+    $exifTool->FoundTag($tagInfo, $makerNotes);
+    # save the garbage collection some work later
+    delete $makerInfo->{Entries};
+    delete $makerInfo->{ValBuff};
+    delete $makerInfo->{FixupTags};
+    # also generate Orientation tag since Rotation isn't transferred from RAW info
+    my $rotation = $exifTool->GetValue('Rotation', 'ValueConv');
+    if (defined $rotation and defined $mapRotation{$rotation}) {
+        $tagInfo = $exifTool->GetTagInfo($tagTablePtr, 0x112);
+        $exifTool->FoundTag($tagInfo, $mapRotation{$rotation});
+    }
+}
+
+#------------------------------------------------------------------------------
+# Check CanonRaw information
+# Inputs: 0) ExifTool object reference, 1) tagInfo hash reference,
+#         2) raw value reference
+# Returns: error string or undef (and may change value) on success
+sub CheckCanonRaw($$$)
+{
+    my ($exifTool, $tagInfo, $valPtr) = @_;
+    Image::ExifTool::GenerateTagIDs($$tagInfo{Table});
+    my $tagName = $$tagInfo{Name};
+    if ($tagName eq 'JpgFromRaw' or $tagName eq 'ThumbnailImage') {
+        unless ($$valPtr =~ /^\xff\xd8/ or $exifTool->Options('IgnoreMinorErrors')) {
+            return '[minor] Not a valid image';
+        }
+    } else {
+        my $format = $$tagInfo{Format};
+        my $count = $$tagInfo{Count};
+        unless ($format) {
+            my $tagType = ($$tagInfo{TagID} >> 8) & 0x38;
+            $format = $crwTagFormat{$tagType};
+        }
+        $format and return Image::ExifTool::CheckValue($valPtr, $format, $count);
+    }
+    return undef;
+}
+
+#------------------------------------------------------------------------------
+# Write CR2 file
+# Inputs: 0) ExifTool ref, 1) dirInfo reference (must have read first 16 bytes)
+#         2) tag table reference
+# Returns: true on success
+sub WriteCR2($$$)
+{
+    my ($exifTool, $dirInfo, $tagTablePtr) = @_;
+    my $dataPt = $$dirInfo{DataPt} or return 0;
+    my $raf = $$dirInfo{RAF} or return 0;
+    my $outfile = $$dirInfo{OutFile} or return 0;
+
+    # check CR2 signature
+    return 0 if $$dataPt !~ /^.{8}CR\x02\0/s and
+        $exifTool->Error("Unsupported Canon RAW file. May cause problems if rewritten", 1);
+    # CR2 has a 16-byte header
+    $$dirInfo{NewDataPos} = 16;
+    my $newData = $exifTool->WriteDirectory($dirInfo, $tagTablePtr);
+    return 0 unless defined $newData;
+    unless ($$dirInfo{LastIFD}) {
+        $exifTool->Error("CR2 image IFD may not be deleted");
+        return 0;
+    }
+
+    if (length($newData)) {
+        # build 16 byte header for Canon RAW file
+        my $header = substr($$dataPt, 0, 16);
+        # set IFD0 pointer (may not be 16 if edited by PhotoMechanic)
+        Set32u(16, \$header, 4);
+        # last 4 bytes of header is pointer to last IFD
+        Set32u($$dirInfo{LastIFD}, \$header, 12);
+        Write($outfile, $header, $newData) or return 0;
+        undef $newData;     # free memory
+
+        # copy over image data now if necessary
+        if (ref $$dirInfo{ImageData}) {
+            $exifTool->CopyImageData($$dirInfo{ImageData}, $outfile) or return 0;
+            delete $$dirInfo{ImageData};
+        }
+    }
+    return 1;
+}
+
+#------------------------------------------------------------------------------
+# Write CanonRaw (CRW) information
+# Inputs: 0) ExifTool object reference, 1) source dirInfo reference,
+#         2) tag table reference
+# Returns: true on sucess
+# Notes: Increments ExifTool CHANGED flag for each tag changed This routine is
+# different from all of the other write routines because Canon RAW files are
+# designed well!  So it isn't necessary to buffer the data in memory before
+# writing it out.  Therefore this routine doesn't return the directory data as
+# the rest of the Write routines do.  Instead, it writes to the dirInfo
+# Outfile on the fly --> much faster, efficient, and less demanding on memory!
+sub WriteCanonRaw($$$)
+{
+    my ($exifTool, $dirInfo, $tagTablePtr) = @_;
+    $exifTool or return 1;    # allow dummy access to autoload this package
+    my $blockStart = $$dirInfo{DirStart};
+    my $blockSize = $$dirInfo{DirLen};
+    my $raf = $$dirInfo{RAF} or return 0;
+    my $outfile = $$dirInfo{OutFile} or return 0;
+    my $outPos = $$dirInfo{OutPos} or return 0;
+    my $outBase = $outPos;
+    my $verbose = $exifTool->Options('Verbose');
+    my $out = $exifTool->Options('TextOut');
+    my ($buff, $tagInfo);
+
+    # 4 bytes at end of block give directory position within block
+    $raf->Seek($blockStart+$blockSize-4, 0) or return 0;
+    $raf->Read($buff, 4) == 4 or return 0;
+    my $dirOffset = Get32u(\$buff,0) + $blockStart;
+    $raf->Seek($dirOffset, 0) or return 0;
+    $raf->Read($buff, 2) == 2 or return 0;
+    my $entries = Get16u(\$buff,0);             # get number of entries in directory
+    # read the directory (10 bytes per entry)
+    $raf->Read($buff, 10 * $entries) == 10 * $entries or return 0;
+    my $newDir = '';
+
+    # get hash of new information keyed by tagID
+    my $newTags = $exifTool->GetNewTagInfoHash($tagTablePtr);
+
+    # generate list of tags to add or delete (currently, we only allow JpgFromRaw
+    # and ThumbnailImage, to be added or deleted from the root CanonRaw directory)
+    my (@addTags, %delTag);
+    if ($$dirInfo{Nesting} == 0) {
+        my $tagID;
+        foreach $tagID (keys %$newTags) {
+            my $permanent = $newTags->{$tagID}->{Permanent};
+            push(@addTags, $tagID) if defined($permanent) and not $permanent;
+        }
+    }
+
+    my $index;
+    for ($index=0; ; ++$index) {
+        my ($pt, $tag, $size, $valuePtr, $ptr, $value);
+        if ($index<$entries) {
+            $pt = 10 * $index;
+            $tag = Get16u(\$buff, $pt);
+            $size = Get32u(\$buff, $pt+2);
+            $valuePtr = Get32u(\$buff, $pt+6);
+            $ptr = $valuePtr + $blockStart;        # all pointers relative to block start
+        }
+        # add any required new tags
+        # NOTE: can't currently add tags where value is stored in directory
+        if (@addTags and (not defined($tag) or $tag >= $addTags[0])) {
+            my $addTag = shift @addTags;
+            $tagInfo = $$newTags{$addTag};
+            my $newVal = $exifTool->GetNewValues($tagInfo);
+            if (defined $newVal) {
+                # add new directory entry
+                $newDir .= Set16u($addTag) . Set32u(length($newVal)) .
+                           Set32u($outPos - $outBase);
+                # write new value data
+                Write($outfile, $newVal) or return 0;
+                $outPos += length($newVal);     # update current position
+                $verbose > 1 and print $out "    + CanonRaw:$$tagInfo{Name}\n";
+                ++$exifTool->{CHANGED};
+            }
+            # set flag to delete this tag if found later
+            $delTag{$addTag} = 1;
+        }
+        last unless defined $tag;           # all done if no more directory entries
+        return 0 if $tag & 0x8000;      # top bit should not be set
+        my $tagID = $tag & 0x3fff;          # get tag ID
+        my $tagType = ($tag >> 8) & 0x38;   # get tag type
+        my $valueInDir = ($tag & 0x4000);   # flag for value in directory
+
+        my $tagInfo = $exifTool->GetTagInfo($tagTablePtr,$tagID);
+        my $format = $crwTagFormat{$tagType};
+        my ($count, $subdir);
+        if ($tagInfo) {
+            $subdir = $$tagInfo{SubDirectory};
+            $format = $$tagInfo{Format} if $$tagInfo{Format};
+            $count = $$tagInfo{Count};
+        }
+        if ($valueInDir) {
+            $size = 8;
+            $value = substr($buff, $pt+2, $size);
+            # set count to 1 by default for normal values in directory
+            $count = 1 if not defined $count and $format and
+                          $format ne 'string' and not $subdir;
+        } else {
+            if ($tagType==0x28 or $tagType==0x30) {
+                # this type of tag specifies a raw subdirectory
+                my $name;
+                $tagInfo and $name = $$tagInfo{Name};
+                $name or $name = sprintf("CanonRaw_0x%.4x", $tagID);
+                my %subdirInfo = (
+                    DirName  => $name,
+                    DataLen  => 0,
+                    DirStart => $ptr,
+                    DirLen   => $size,
+                    Nesting  => $$dirInfo{Nesting} + 1,
+                    RAF      => $raf,
+                    Parent   => $$dirInfo{DirName},
+                    OutFile  => $outfile,
+                    OutPos   => $outPos,
+                );
+                my $result = $exifTool->WriteDirectory(\%subdirInfo, $tagTablePtr);
+                return 0 unless $result;
+                # set size and pointer for this new directory
+                $size = $subdirInfo{OutPos} - $outPos;
+                $valuePtr = $outPos - $outBase;
+                $outPos = $subdirInfo{OutPos};
+            } else {
+                # verify that the value data is within this block
+                $valuePtr + $size <= $blockSize or return 0;
+                # read value from file
+                $raf->Seek($ptr, 0) or return 0;
+                $raf->Read($value, $size) == $size or return 0;
+            }
+        }
+        # set count from tagInfo count if necessary
+        if ($format and not $count) {
+            # set count according to format and size
+            my $fnum = $Image::ExifTool::Exif::formatNumber{$format};
+            my $fsiz = $Image::ExifTool::Exif::formatSize[$fnum];
+            $count = int($size / $fsiz);
+        }
+        # edit subdirectory if necessary
+        if ($tagInfo) {
+            if ($subdir and $$subdir{TagTable}) {
+                my $name = $$tagInfo{Name};
+                my $newTagTable = Image::ExifTool::GetTagTable($$subdir{TagTable});
+                return 0 unless $newTagTable;
+                my $subdirStart = 0;
+                #### eval Start ()
+                $subdirStart = eval $$subdir{Start} if $$subdir{Start};
+                my $dirData = \$value;
+                my %subdirInfo = (
+                    Name     => $name,
+                    DataPt   => $dirData,
+                    DataLen  => $size,
+                    DirStart => $subdirStart,
+                    DirLen   => $size - $subdirStart,
+                    Nesting  => $$dirInfo{Nesting} + 1,
+                    RAF      => $raf,
+                    Parent   => $$dirInfo{DirName},
+                );
+                #### eval Validate ($dirData, $subdirStart, $size)
+                if (defined $$subdir{Validate} and not eval $$subdir{Validate}) {
+                    $exifTool->Warn("Invalid $name data");
+                } else {
+                    $subdir = $exifTool->WriteDirectory(\%subdirInfo, $newTagTable);
+                    if (defined $subdir and length $subdir) {
+                        if ($subdirStart) {
+                            # add header before data directory
+                            $value = substr($value, 0, $subdirStart) . $subdir;
+                        } else {
+                            $value = $subdir;
+                        }
+                    }
+                }
+            } elsif ($$newTags{$tagID}) {
+                if ($delTag{$tagID}) {
+                    $verbose > 1 and print $out "    - CanonRaw:$$tagInfo{Name}\n";
+                    ++$exifTool->{CHANGED};
+                    next;   # next since we already added this tag
+                }
+                my $oldVal;
+                if ($format) {
+                    $oldVal = ReadValue(\$value, 0, $format, $count, $size);
+                } else {
+                    $oldVal = $value;
+                }
+                my $newValueHash = $exifTool->GetNewValueHash($tagInfo);
+                if (Image::ExifTool::IsOverwriting($newValueHash, $oldVal)) {
+                    my $newVal = Image::ExifTool::GetNewValues($newValueHash);
+                    my $verboseVal;
+                    $verboseVal = $newVal if $verbose > 1;
+                    # convert to specified format if necessary
+                    if (defined $newVal and $format) {
+                        $newVal = WriteValue($newVal, $format, $count);
+                    }
+                    if (defined $newVal) {
+                        $value = $newVal;
+                        ++$exifTool->{CHANGED};
+                        if ($verbose > 1) {
+                            my $oldStr = $exifTool->Printable($oldVal);
+                            my $newStr = $exifTool->Printable($verboseVal);
+                            print $out "    - CanonRaw:$$tagInfo{Name} = '$oldStr'\n";
+                            print $out "    + CanonRaw:$$tagInfo{Name} = '$newStr'\n";
+                        }
+                    }
+                }
+            }
+        }
+        if ($valueInDir) {
+            my $len = length $value;
+            if ($len < 8) {
+                # pad with original garbage in case it contained something useful
+                $value .= substr($buff, $pt+2+8-$len, 8-$len);
+            } elsif ($len > 8) {   # this shouldn't happen
+                warn "Value too long! -- trucated\n";
+                $value = substr($value, 0, 8);
+            }
+            # create new directory entry
+            $newDir .= Set16u($tag) . $value;
+            next;   # all done this entry
+        }
+        if (defined $value) {
+            # don't allow value to change length unless Writable is 'resize'
+            my $writable = $$tagInfo{Writable};
+            my $diff = length($value) - $size;
+            if ($diff) {
+                if ($writable and $writable eq 'resize') {
+                    $size += $diff; # allow size to change
+                } elsif ($diff > 0) {
+                    $value .= ("\0" x $diff);
+                } else {
+                    $value = substr($value, 0, $size);
+                }
+            }
+            # pad value if necessary to align on even-byte boundary (as per CIFF spec)
+            $value .= "\0" if $size & 0x01;
+            $valuePtr = $outPos - $outBase;
+            # write out value data
+            Write($outfile, $value) or return 0;
+            $outPos += length($value);  # update current position in outfile
+        }
+        # create new directory entry
+        $newDir .= Set16u($tag) . Set32u($size) . Set32u($valuePtr);
+    }
+    # add the directory counts and offset to the directory start,
+    $entries = length($newDir) / 10;
+    $newDir = Set16u($entries) . $newDir . Set32u($outPos - $outBase);
+    # write directory data
+    Write($outfile, $newDir) or return 0;
+
+    # update current output file position in dirInfo
+    $$dirInfo{OutPos} = $outPos + length($newDir);
+    # save outfile directory start (needed for rewriting VRD trailer)
+    $$dirInfo{OutDirStart} = $outPos - $outBase;
+
+    return 1;
+}
+
+#------------------------------------------------------------------------------
+# write Canon RAW (CRW) file
+# Inputs: 0) ExifTool object reference, 1) dirInfo reference
+# Returns: 1 on success, 0 if this wasn't a valid CRW file,
+#          or -1 if a write error occurred
+sub WriteCRW($$)
+{
+    my ($exifTool, $dirInfo) = @_;
+    my $outfile = $$dirInfo{OutFile};
+    my $raf = $$dirInfo{RAF};
+    my $rtnVal = 0;
+    my ($buff, $err, $sig);
+
+    $raf->Read($buff,2) == 2    or return 0;
+    SetByteOrder($buff)         or return 0;
+    $raf->Read($buff,4) == 4    or return 0;
+    $raf->Read($sig,8) == 8     or return 0;   # get file signature
+    $sig =~ /^HEAP(CCDR|JPGM)/  or return 0;   # validate signature
+    my $type = $1;
+    my $hlen = Get32u(\$buff, 0);   # get header length
+
+    if ($exifTool->{DEL_GROUP}->{MakerNotes}) {
+        if ($type eq 'CCDR') {
+            $exifTool->Error("Can't delete Makernotes group in CRW file");
+            return 0;
+        } else {
+            ++$exifTool->{CHANGED};
+            return 1;
+        }
+    }
+
+    # write header
+    $raf->Seek(0, 0)            or return 0;
+    $raf->Read($buff, $hlen) == $hlen or return 0;
+    Write($outfile, $buff) or $err = 1;
+
+    $raf->Seek(0, 2)            or return 0;   # seek to end of file
+    my $filesize = $raf->Tell() or return 0;
+
+    # build directory information for main raw directory
+    my %dirInfo = (
+        DataLen  => 0,
+        DirStart => $hlen,
+        DirLen   => $filesize - $hlen,
+        Nesting  => 0,
+        RAF      => $raf,
+        Parent   => 'CRW',
+        OutFile  => $outfile,
+        OutPos   => $hlen,
+    );
+    # process the raw directory
+    my $tagTablePtr = Image::ExifTool::GetTagTable('Image::ExifTool::CanonRaw::Main');
+    my $success = $exifTool->WriteDirectory(\%dirInfo, $tagTablePtr);
+
+    my $trailPt;
+    while ($success) {
+        # check to see if trailer(s) exist(s)
+        my $trailInfo = Image::ExifTool::IdentifyTrailer($raf) or last;
+        # rewrite the trailer(s)
+        $buff = '';
+        $$trailInfo{OutFile} = \$buff;
+        $success = $exifTool->ProcessTrailers($trailInfo) or last;
+        $trailPt = $$trailInfo{OutFile};
+        # nothing to write if trailers were deleted
+        undef $trailPt if length($$trailPt) < 4;
+        last;
+    }
+    if ($success) {
+        # add CanonVRD trailer if writing as a block
+        $trailPt = $exifTool->AddNewTrailers($trailPt,'CanonVRD');
+        # write trailer
+        if ($trailPt) {
+            # must append DirStart pointer to end of trailer
+            my $newDirStart = Set32u($dirInfo{OutDirStart});
+            my $len = length $$trailPt;
+            my $pad = ($len & 0x01) ? ' ' : ''; # add pad byte if necessary
+            Write($outfile, $pad, substr($$trailPt,0,$len-4), $newDirStart) or $err = 1;
+        }
+        $rtnVal = $err ? -1 : 1;
+    } else {
+        $exifTool->Error('Error rewriting CRW file');
+    }
+    return $rtnVal;
+}
+
+1; # end
+
+__END__
+
+=head1 NAME
+
+Image::ExifTool::WriteCanonRaw.pl - Write Canon RAW (CRW and CR2) information
+
+=head1 SYNOPSIS
+
+These routines are autoloaded by Image::ExifTool::CanonRaw.
+
+=head1 DESCRIPTION
+
+This file contains routines used by ExifTool to write Canon CRW and CR2
+files and metadata.
+
+=head1 NOTES
+
+The CRW format is a pleasure to work with.  All pointer offsets are relative
+to the start of the data for each directory.  If TIFF/EXIF had implemented
+pointers in this way, it would be MUCH easier to read and write TIFF/JPEG
+files, and would lead to far fewer problems with corrupted metadata.
+
+=head1 AUTHOR
+
+Copyright 2003-2007, Phil Harvey (phil at owl.phy.queensu.ca)
+
+This library is free software; you can redistribute it and/or modify it
+under the same terms as Perl itself.
+
+=head1 SEE ALSO
+
+L<Image::ExifTool::CanonRaw(3pm)|Image::ExifTool::CanonRaw>,
+L<Image::ExifTool(3pm)|Image::ExifTool>,
+L<http://owl.phy.queensu.ca/~phil/exiftool/canon_raw.html>
+
+=cut
Index: /gsdl/trunk/perllib/cpan/Image/ExifTool/WriteExif.pl
===================================================================
--- /gsdl/trunk/perllib/cpan/Image/ExifTool/WriteExif.pl	(revision 16842)
+++ /gsdl/trunk/perllib/cpan/Image/ExifTool/WriteExif.pl	(revision 16842)
@@ -0,0 +1,2488 @@
+#------------------------------------------------------------------------------
+# File:         WriteExif.pl
+#
+# Description:  Write EXIF meta information
+#
+# Revisions:    12/13/2004 - P. Harvey Created
+#------------------------------------------------------------------------------
+
+package Image::ExifTool::Exif;
+
+use strict;
+use vars qw($VERSION $AUTOLOAD @formatSize @formatName %formatNumber
+            %lightSource %compression %photometricInterpretation %orientation);
+
+use Image::ExifTool::Fixup;
+
+sub InsertWritableProperties($$;$);
+
+# some information may be stored in different IFD's with the same meaning.
+# Use this lookup to decide when we should delete information that is stored
+# in another IFD when we write it to the preferred IFD.
+my %crossDelete = (
+    ExifIFD => 'IFD0',
+    IFD0    => 'ExifIFD',
+);
+
+# mandatory tag default values
+my %mandatory = (
+    IFD0 => {
+        0x011a => 72,       # XResolution
+        0x011b => 72,       # YResolution
+        0x0128 => 2,        # Resolution unit (inches)
+        0x0213 => 1,        # YCbCrPositioning (centered)
+      # 0x8769 => ????,     # ExifOffset
+    },
+    IFD1 => {
+        0x0103 => 6,        # Compression (JPEG)
+        0x011a => 72,       # XResolution
+        0x011b => 72,       # YResolution
+        0x0128 => 2,        # Resolution unit (inches)
+    },
+    ExifIFD => {
+        0x9000 => '0220',   # ExifVersion
+        0x9101 => "\1\2\3\0", # ComponentsConfiguration
+        0xa000 => '0100',   # FlashpixVersion
+        0xa001 => 0xffff,   # ColorSpace (uncalibrated)
+      # 0xa002 => ????,     # ExifImageWidth
+      # 0xa003 => ????,     # ExifImageLength
+    },
+    GPS => {
+        0x0000 => '2 2 0 0',# GPSVersionID
+    },
+    InteropIFD => {
+        0x0002 => '0100',   # InteropVersion
+    },
+);
+
+# The main EXIF table is unique because the tags from this table may appear
+# in many different directories.  For this reason, we introduce a
+# "WriteGroup" member to the tagInfo that tells us the preferred location
+# for writing each tag.  Here is the lookup for Writable flag (format)
+# and WriteGroup for all writable tags
+# - WriteGroup is ExifIFD unless otherwise specified
+# - Protected is 1 if the tag shouldn't be copied with SetNewValuesFromFile()
+my %writeTable = (
+    0x0001 => {             # InteropIndex
+        Protected => 1,
+        Writable => 'string',
+        WriteGroup => 'InteropIFD',
+    },
+    0x0002 => {             # InteropVersion
+        Protected => 1,
+        Writable => 'undef',
+        WriteGroup => 'InteropIFD',
+    },
+    0x000b => {             # ProcessingSoftware
+        Writable => 'string',
+        WriteGroup => 'IFD0',
+    },
+    0x00fe => {             # SubfileType
+        Protected => 1,
+        Writable => 'int32u',
+        WriteGroup => 'IFD0',
+    },
+    0x00ff => {             # OldSubfileType
+        Protected => 1,
+        Writable => 'int16u',
+        WriteGroup => 'IFD0',
+    },
+    0x0100 => {             # ImageWidth
+        Protected => 1,
+        Writable => 'int32u',
+        WriteGroup => 'IFD0',
+    },
+    0x0101 => {             # ImageHeigth
+        Protected => 1,
+        Writable => 'int32u',
+        WriteGroup => 'IFD0',
+    },
+    0x0102 => {             # BitsPerSample
+        Protected => 1,
+        Writable => 'int16u',
+        WriteGroup => 'IFD0',
+        Count => -1, # can be 1 or 3: -1 means 'variable'
+    },
+    0x0103 => {             # Compression
+        Protected => 1,
+        Writable => 'int16u',
+        WriteGroup => 'IFD0',
+    },
+    0x0106 => {             # PhotometricInterpretation
+        Protected => 1,
+        Writable => 'int16u',
+        WriteGroup => 'IFD0',
+    },
+    0x0107 => {             # Thresholding
+        Protected => 1,
+        Writable => 'int16u',
+        WriteGroup => 'IFD0',
+    },
+    0x0108 => {             # CellWidth
+        Protected => 1,
+        Writable => 'int16u',
+        WriteGroup => 'IFD0',
+    },
+    0x0109 => {             # CellLength
+        Protected => 1,
+        Writable => 'int16u',
+        WriteGroup => 'IFD0',
+    },
+    0x010a => {             # FillOrder
+        Protected => 1,
+        Writable => 'int16u',
+        WriteGroup => 'IFD0',
+    },
+    0x010d => {             # DocumentName
+        Writable => 'string',
+        WriteGroup => 'IFD0',
+    },
+    0x010e => {             # ImageDescription
+        Writable => 'string',
+        WriteGroup => 'IFD0',
+    },
+    0x010f => {             # Make
+        Writable => 'string',
+        WriteGroup => 'IFD0',
+    },
+    0x0110 => {             # Model
+        Writable => 'string',
+        WriteGroup => 'IFD0',
+    },
+    0x0112 => {             # Orientation
+        Writable => 'int16u',
+        WriteGroup => 'IFD0',
+    },
+    0x0115 => {             # SamplesPerPixel
+        Protected => 1,
+        Writable => 'int16u',
+        WriteGroup => 'IFD0',
+    },
+    0x0116 => {             # RowsPerStrip
+        Protected => 1,
+        Writable => 'int32u',
+        WriteGroup => 'IFD0',
+    },
+    0x0118 => {             # MinSampleValue
+        Writable => 'int16u',
+        WriteGroup => 'IFD0',
+    },
+    0x0119 => {             # MaxSampleValue
+        Writable => 'int16u',
+        WriteGroup => 'IFD0',
+    },
+    0x011a => {             # XResolution
+        Writable => 'rational64u',
+        WriteGroup => 'IFD0',
+    },
+    0x011b => {             # YResolution
+        Writable => 'rational64u',
+        WriteGroup => 'IFD0',
+    },
+    0x011c => {             # PlanarConfiguration
+        Protected => 1,
+        Writable => 'int16u',
+        WriteGroup => 'IFD0',
+    },
+    0x011d => {             # PageName
+        Writable => 'string',
+        WriteGroup => 'IFD0',
+    },
+    0x011e => {             # XPosition
+        Writable => 'rational64u',
+        WriteGroup => 'IFD0',
+    },
+    0x011f => {             # YPosition
+        Writable => 'rational64u',
+        WriteGroup => 'IFD0',
+    },
+    0x0122 => {             # GrayResponseUnit
+        Writable => 'int16u',
+        WriteGroup => 'IFD0',
+    },
+    0x0128 => {             # ResolutionUnit
+        Writable => 'int16u',
+        WriteGroup => 'IFD0',
+    },
+    0x0129 => {             # PageNumber
+        Writable => 'int16u',
+        WriteGroup => 'IFD0',
+        Count => 2,
+    },
+    0x0131 => {             # Software
+        Writable => 'string',
+        WriteGroup => 'IFD0',
+    },
+    0x0132 => {             # ModifyDate
+        Writable => 'string',
+        Shift => 'Time',
+        WriteGroup => 'IFD0',
+        PrintConvInv => 'Image::ExifTool::Exif::ExifDateTime($val)', 
+    },
+    0x013b => {             # Artist
+        Writable => 'string',
+        WriteGroup => 'IFD0',
+    },
+    0x013c => {             # HostComputer
+        Writable => 'string',
+        WriteGroup => 'IFD0',
+    },
+    0x013d => {             # Predictor
+        Protected => 1,
+        Writable => 'int16u',
+        WriteGroup => 'IFD0',
+    },
+    0x013e => {             # WhitePoint
+        Writable => 'rational64u',
+        WriteGroup => 'IFD0',
+        Count => 2,
+    },
+    0x013f => {             # PrimaryChromaticities
+        Writable => 'rational64u',
+        WriteGroup => 'IFD0',
+        Count => 6,
+    },
+    0x0141 => {             # HalftoneHints
+        Writable => 'int16u',
+        WriteGroup => 'IFD0',
+        Count => 2,
+    },
+    0x0142 => {             # TileWidth
+        Protected => 1,
+        Writable => 'int32u',
+        WriteGroup => 'IFD0',
+    },
+    0x0143 => {             # TileLength
+        Protected => 1,
+        Writable => 'int32u',
+        WriteGroup => 'IFD0',
+    },
+    0x014c => {             # InkSet
+        Writable => 'int16u',
+        WriteGroup => 'IFD0',
+    },
+    0x0150 => {             # TargetPrinter
+        Writable => 'string',
+        WriteGroup => 'IFD0',
+    },
+    0x013c => {             # HostComputer
+        Writable => 'string',
+        WriteGroup => 'IFD0',
+    },
+    0x0211 => {             # YCbCrCoefficients
+        Protected => 1,
+        Writable => 'rational64u',
+        WriteGroup => 'IFD0',
+        Count => 3,
+    },
+    0x0212 => {             # YCbCrSubSampling
+        Protected => 1,
+        Writable => 'int16u',
+        WriteGroup => 'IFD0',
+        Count => 2,
+    },
+    0x0213 => {             # YCbCrPositioning
+        Protected => 1,
+        Writable => 'int16u',
+        WriteGroup => 'IFD0',
+    },
+    0x0214 => {             # ReferenceBlackWhite
+        Writable => 'rational64u',
+        WriteGroup => 'IFD0',
+        Count => 6,
+    },
+    0x1000 => {             # RelatedImageFileFormat
+        Protected => 1,
+        Writable => 'string',
+        WriteGroup => 'InteropIFD',
+    },
+    0x1001 => {             # RelatedImageWidth
+        Protected => 1,
+        Writable => 'int16u',
+        WriteGroup => 'InteropIFD',
+    },
+    0x1002 => {             # RelatedImageLength (is really the height)
+        Protected => 1,
+        Writable => 'int16u',
+        WriteGroup => 'InteropIFD',
+    },
+    0x4746 => {             # Rating (MicrosoftPhoto)
+        Writable => 'int16u',
+        WriteGroup => 'IFD0',
+        Avoid => 1,
+    },
+    0x4749 => {             # RatingPercent (MicrosoftPhoto)
+        Writable => 'int16u',
+        WriteGroup => 'IFD0',
+        Avoid => 1,
+    },
+    0x8298 => {             # Copyright
+        Writable => 'string',
+        WriteGroup => 'IFD0',
+    },
+#
+# Most of the tags below this belong in the ExifIFD...
+#
+    0x829a => {             # ExposureTime
+        Writable => 'rational64u',
+        PrintConvInv => 'eval $val',
+    },
+    0x829d => {             # FNumber
+        Writable => 'rational64u',
+        PrintConvInv => '$val',
+    },
+    0x83bb => {             # IPTC-NAA
+        # this should actually be written as 'undef' (see
+        # http://www.awaresystems.be/imaging/tiff/tifftags/iptc.html),
+        # but Photoshop writes it as int32u and Nikon Capture won't read
+        # anything else, so we do the same thing here...  Doh!
+        Format => 'undef',      # convert binary values as undef
+        Writable => 'int32u',   # but write int32u format code in IFD
+        WriteGroup => 'IFD0',
+    },
+    0x8546 => {             # SEMInfo
+        Writable => 'string',
+        WriteGroup => 'IFD0',
+    },
+    0x8822 => 'int16u',     # ExposureProgram
+    0x8824 => 'string',     # SpectralSensitivity
+    0x8827 => 'int16u',     # ISO
+    0x882a => {             # TimeZoneOffset
+        Writable => 'int16s',
+        Count => -1, # can be 1 or 2
+        Notes => q{
+            1 or 2 values: 1. The time zone offset of DateTimeOriginal from GMT in
+            hours, 2. If present, the time zone offset of ModifyDate
+        },
+    },
+    0x882b => 'int16u',     # SelfTimerMode
+    0x9000 => 'undef',      # ExifVersion
+    0x9003 => {             # DateTimeOriginal
+        Writable => 'string',
+        Shift => 'Time',
+        PrintConvInv => 'Image::ExifTool::Exif::ExifDateTime($val)', 
+    },
+    0x9004 => {             # CreateDate
+        Writable => 'string',
+        Shift => 'Time',
+        PrintConvInv => 'Image::ExifTool::Exif::ExifDateTime($val)', 
+    },
+    0x9101 => {             # ComponentsConfiguration
+        Writable => 'undef',
+        PrintConv => '$_=$val;s/\0.*//s;tr/\x01-\x06/YXWRGB/;s/X/Cb/g;s/W/Cr/g;$_',
+        PrintConvInv => q{
+            $_=uc($val); s/CR/W/g; s/CB/X/g;
+            return undef if /[^YXWRGB]/;
+            tr/YXWRGB/\x01-\x06/;
+            return $_ . "\0";
+        },
+    },
+    0x9102 => 'rational64u',# CompressedBitsPerPixel
+    0x9201 => {             # ShutterSpeedValue
+        Writable => 'rational64s',
+        ValueConvInv => '$val>0 ? -log($val)/log(2) : -100',
+        # do eval to convert things like '1/100'
+        PrintConvInv => 'eval $val',
+    },
+    0x9202 => {             # ApertureValue
+        Writable => 'rational64u',
+        ValueConvInv => '$val>0 ? 2*log($val)/log(2) : 0',
+        PrintConvInv => '$val',
+    },
+    0x9203 => 'rational64s',# BrightnessValue
+    0x9204 => {             # ExposureCompensation
+        Writable => 'rational64s',
+        # do eval to convert things like '+2/3'
+        PrintConvInv => 'eval $val',
+    },
+    0x9205 => {             # MaxApertureValue
+        Writable => 'rational64u',
+        ValueConvInv => '$val>0 ? 2*log($val)/log(2) : 0',
+        PrintConvInv => '$val',
+    },
+    0x9206 => {             # SubjectDistance
+        Writable => 'rational64u',
+        PrintConvInv => '$val=~s/\s*m$//;$val',
+    },
+    0x9207 => 'int16u',     # MeteringMode
+    0x9208 => 'int16u',     # LightSource
+    0x9209 => 'int16u',     # Flash
+    0x920a => {             # FocalLength
+        Writable => 'rational64u',
+        PrintConvInv => '$val=~s/\s*mm$//;$val',
+    },
+    0x9211 => 'int32u',     # ImageNumber
+    0x9212 => 'string',     # SecurityClassification
+    0x9213 => 'string',     # ImageHistory
+    0x9214 => {             # SubjectLocation
+        Writable => 'int16u',
+        Count => 4,  # write this SubjectLocation with 4 and the other with 2 values
+    },
+#    0x927c => 'undef',      # MakerNotes
+    0x9286 => {             # UserComment (string that starts with "ASCII\0\0\0")
+        Writable => 'undef',
+        PrintConvInv => 'Image::ExifTool::Exif::EncodeExifText($self,$val)',
+    },
+    0x9290 => 'string',     # SubSecTime
+    0x9291 => 'string',     # SubSecTimeOriginal
+    0x9292 => 'string',     # SubSecTimeDigitized
+    0x935c => {             # ImageSourceData
+        Writable => 'undef',
+        WriteGroup => 'IFD0',
+    },
+#    0x9928 => 'undef',      # Opto-ElectricConversionFactor
+    0x9c9b => {             # XPTitle
+        Writable => 'int8u',
+        WriteGroup => 'IFD0',
+        Notes => q{
+            tags 0x9c9b-0x9c9f are used by Windows Explorer; special characters
+            in these values are converted to UTF-8 by default, or Windows Latin1
+            with the -L option. XPTitle is ignored by Windows Explorer if
+            ImageDescription exists
+        },
+        ValueConvInv => '$self->Charset2Unicode($val,"II") . "\0\0"',
+    },
+    0x9c9c => {             # XPComment
+        Writable => 'int8u',
+        WriteGroup => 'IFD0',
+        ValueConvInv => '$self->Charset2Unicode($val,"II") . "\0\0"',
+    },
+    0x9c9d => {             # XPAuthor
+        Writable => 'int8u',
+        WriteGroup => 'IFD0',
+        Notes => 'ignored by Windows Explorer if Artist exists',
+        ValueConvInv => '$self->Charset2Unicode($val,"II") . "\0\0"',
+    },
+    0x9c9e => {             # XPKeywords
+        Writable => 'int8u',
+        WriteGroup => 'IFD0',
+        ValueConvInv => '$self->Charset2Unicode($val,"II") . "\0\0"',
+    },
+    0x9c9f => {             # XPSubject
+        Writable => 'int8u',
+        WriteGroup => 'IFD0',
+        ValueConvInv => '$self->Charset2Unicode($val,"II") . "\0\0"',
+    },
+    0xa000 => 'undef',      # FlashpixVersion
+    0xa001 => 'int16u',     # ColorSpace
+    0xa002 => 'int16u',     # ExifImageWidth (could also be int32u)
+    0xa003 => 'int16u',     # ExifImageLength (could also be int32u)
+    0xa004 => 'string',     # RelatedSoundFile
+    0xa20b => {             # FlashEnergy
+        Writable => 'rational64u',
+        Count => -1, # 1 or 2 (ref 12)
+    },
+#    0xa20c => 'undef',      # SpatialFrequencyResponse
+    0xa20e => 'rational64u',# FocalPlaneXResolution
+    0xa20f => 'rational64u',# FocalPlaneYResolution
+    0xa210 => 'int16u',     # FocalPlaneResolutionUnit
+    0xa214 => {             # SubjectLocation
+        Writable => 'int16u',
+        Count => 2,
+    },
+    0xa215 => 'rational64u',# ExposureIndex
+    0xa217 => 'int16u',     # SensingMethod
+    0xa300 => {             # FileSource
+        Writable => 'undef',
+        ValueConvInv => 'chr($val)',
+        PrintConvInv => 3,
+    },
+    0xa301 => {             # SceneType
+        Writable => 'undef',
+        ValueConvInv => 'chr($val)',
+    },
+    0xa302 => {             # CFAPattern
+        Writable => 'undef',
+        PrintConvInv => 'Image::ExifTool::Exif::GetCFAPattern($val)',
+    },
+    0xa401 => 'int16u',     # CustomRendered
+    0xa402 => 'int16u',     # ExposureMode
+    0xa403 => 'int16u',     # WhiteBalance
+    0xa404 => 'rational64u',# DigitalZoomRatio
+    0xa405 => {             # FocalLengthIn35mmFormat
+        Writable => 'int16u',
+        PrintConvInv => '$val=~s/\s*mm$//;$val',
+    },
+    0xa406 => 'int16u',     # SceneCaptureType
+    0xa407 => 'int16u',     # GainControl
+    0xa408 => {             # Contrast
+        Writable => 'int16u',
+        PrintConvInv => 'Image::ExifTool::Exif::ConvertParameter($val)',
+    },
+    0xa409 => {             # Saturation
+        Writable => 'int16u',
+        PrintConvInv => 'Image::ExifTool::Exif::ConvertParameter($val)',
+    },
+    0xa40a => {             # Sharpness
+        Writable => 'int16u',
+        PrintConvInv => 'Image::ExifTool::Exif::ConvertParameter($val)',
+    },
+#    0xa40b => 'undef',      # DeviceSettingDescription
+    0xa40c => 'int16u',     # SubjectDistanceRange
+    0xa420 => 'string',     # ImageUniqueID
+    0xa500 => 'rational64u',# Gamma
+    0xc4a5 => {             # PrintIM
+        Writable => 'undef',
+        WriteGroup => 'IFD0',
+    },
+#
+# DNG stuff (back in IFD0)
+#
+    0xc612 => {             # DNGVersion
+        Writable => 'int8u',
+        WriteGroup => 'IFD0',
+        Count => 4,
+    },
+    0xc613 => {             # DNGBackwardVersion
+        Writable => 'int8u',
+        WriteGroup => 'IFD0',
+        Count => 4,
+    },
+    0xc614 => {             # UniqueCameraModel
+        Writable => 'string',
+        WriteGroup => 'IFD0',
+    },
+    0xc615 => {             # LocalizedCameraModel
+        Writable => 'string',
+        WriteGroup => 'IFD0',
+        PrintConvInv => '$val',
+    },
+    0xc61e => {             # DefaultScale
+        Writable => 'rational64u',
+        WriteGroup => 'SubIFD',
+        Count => 2,
+    },
+    0xc61f => {             # DefaultCropOrigin
+        Writable => 'int32u',
+        WriteGroup => 'SubIFD',
+        Count => 2,
+    },
+    0xc620 => {             # DefaultCropSize
+        Writable => 'int32u',
+        WriteGroup => 'SubIFD',
+        Count => 2,
+    },
+    0xc629 => {             # AsShotWhiteXY
+        Writable => 'rational64u',
+        WriteGroup => 'IFD0',
+        Count => 2,
+    },
+    0xc62a => {             # BaselineExposure
+        Writable => 'rational64s',
+        WriteGroup => 'IFD0',
+    },
+    0xc62b => {             # BaselineNoise
+        Writable => 'rational64u',
+        WriteGroup => 'IFD0',
+    },
+    0xc62c => {             # BaselineSharpness
+        Writable => 'rational64u',
+        WriteGroup => 'IFD0',
+    },
+    0xc62d => {             # BayerGreenSplit
+        Writable => 'int32u',
+        WriteGroup => 'SubIFD',
+    },
+    0xc62e => {             # LinearResponseLimit
+        Writable => 'rational64u',
+        WriteGroup => 'IFD0',
+    },
+    0xc62f => {             # CameraSerialNumber
+        Writable => 'string',
+        WriteGroup => 'IFD0',
+    },
+    0xc630 => {             # DNGLensInfo
+        Writable => 'rational64u',
+        WriteGroup => 'IFD0',
+        Count => 4,
+        PrintConvInv => '$_=$val;s/(-|mm f)/ /g;$_',
+    },
+    0xc631 => {             # ChromaBlurRadius
+        Writable => 'rational64u',
+        WriteGroup => 'SubIFD',
+    },
+    0xc632 => {             # AntiAliasStrength
+        Writable => 'rational64u',
+        WriteGroup => 'SubIFD',
+    },
+    0xc633 => {             # ShadowScale
+        Writable => 'rational64u',
+        WriteGroup => 'IFD0',
+    },
+    0xc635 => {             # MakerNoteSafety
+        Writable => 'int16u',
+        WriteGroup => 'IFD0',
+    },
+    0xc65a => {             # CalibrationIlluminant1
+        Writable => 'int16u',
+        WriteGroup => 'IFD0',
+    },
+    0xc65b => {             # CalibrationIlluminant2
+        Writable => 'int16u',
+        WriteGroup => 'IFD0',
+    },
+    0xc65c => {             # BestQualityScale
+        Writable => 'rational64u',
+        WriteGroup => 'SubIFD',
+    },
+    0xc65d => {             # RawDataUniqueID
+        Writable => 'int8u',
+        WriteGroup => 'IFD0',
+        Count => 16,
+        ValueConvInv => 'pack("H*",$val)',
+    },
+    0xc68b => {             # OriginalRawFileName
+        Writable => 'string',
+        WriteGroup => 'IFD0',
+    },
+    0xc68c => {             # OriginalRawFileData (a writable directory)
+        Writable => 'undef',
+        WriteGroup => 'IFD0',
+    },
+    0xc68d => {             # ActiveArea
+        Writable => 'int32u',
+        WriteGroup => 'SubIFD',
+        Count => 4,
+    },
+    0xc68e => {             # MaskedAreas
+        Writable => 'int32u',
+        WriteGroup => 'SubIFD',
+        Count => 4,
+    },
+    0xc68f => {             # AsShotICCProfile
+        Writable => 'undef',
+        WriteGroup => 'IFD0',
+        Protected => 1,
+        WriteCheck => q{
+            require Image::ExifTool::ICC_Profile;
+            return Image::ExifTool::ICC_Profile::ValidateICC(\$val);
+        },
+    },
+    0xc690 => {             # AsShotPreProfileMatrix
+        Writable => 'rational64s',
+        Count => -1,
+        WriteGroup => 'IFD0',
+        Protected => 1,
+    },
+    0xc691 => {             # CurrentICCProfile
+        Writable => 'undef',
+        WriteGroup => 'IFD0',
+        Protected => 1,
+        WriteCheck => q{
+            require Image::ExifTool::ICC_Profile;
+            return Image::ExifTool::ICC_Profile::ValidateICC(\$val);
+        },
+    },
+    0xc692 => {             # CurrentPreProfileMatrix
+        Writable => 'rational64s',
+        Count => -1,
+        WriteGroup => 'IFD0',
+        Protected => 1,
+    },
+    0xea1d => {             # OffsetSchema
+        Writable => 'int32s',
+    },
+    # tags produced by Photoshop Camera RAW
+    # (avoid creating these tags unless there is no other option)
+    0xfde8 => {
+        Name => 'OwnerName',
+        Avoid => 1,
+        Writable => 'string',
+        ValueConv => '$val=~s/.*: //;$val',
+        ValueConvInv => q{"Owner's Name: $val"},
+        Notes => q{
+            tags 0xfde8-0xfe58 are generated by Photoshop Camera RAW -- some
+            names are the same as other EXIF tags, but ExifTool will avoid
+            writing these unless they already exist in the file
+        },
+    },
+    0xfde9 => {
+        Name => 'SerialNumber',
+        Avoid => 1,
+        Writable => 'string',
+        ValueConv => '$val=~s/.*: //;$val',
+        ValueConvInv => q{"Serial Number: $val"},
+    },
+    0xfdea => {
+        Name => 'Lens',
+        Avoid => 1,
+        Writable => 'string',
+        ValueConv => '$val=~s/.*: //;$val',
+        ValueConvInv => q{"Lens: $val"},
+    },
+    0xfe4c => {
+        Name => 'RawFile',
+        Avoid => 1,
+        Writable => 'string',
+        ValueConv => '$val=~s/.*: //;$val',
+        ValueConvInv => q{"Raw File: $val"},
+    },
+    0xfe4d => {
+        Name => 'Converter',
+        Avoid => 1,
+        Writable => 'string',
+        ValueConv => '$val=~s/.*: //;$val',
+        ValueConvInv => q{"Converter: $val"},
+    },
+    0xfe4e => {
+        Name => 'WhiteBalance',
+        Avoid => 1,
+        Writable => 'string',
+        ValueConv => '$val=~s/.*: //;$val',
+        ValueConvInv => q{"White Balance: $val"},
+    },
+    0xfe51 => {
+        Name => 'Exposure',
+        Avoid => 1,
+        Writable => 'string',
+        ValueConv => '$val=~s/.*: //;$val',
+        ValueConvInv => q{"Exposure: $val"},
+    },
+    0xfe52 => {
+        Name => 'Shadows',
+        Avoid => 1,
+        Writable => 'string',
+        ValueConv => '$val=~s/.*: //;$val',
+        ValueConvInv => q{"Shadows: $val"},
+    },
+    0xfe53 => {
+        Name => 'Brightness',
+        Avoid => 1,
+        Writable => 'string',
+        ValueConv => '$val=~s/.*: //;$val',
+        ValueConvInv => q{"Brightness: $val"},
+    },
+    0xfe54 => {
+        Name => 'Contrast',
+        Avoid => 1,
+        Writable => 'string',
+        ValueConv => '$val=~s/.*: //;$val',
+        ValueConvInv => q{"Contrast: $val"},
+    },
+    0xfe55 => {
+        Name => 'Saturation',
+        Avoid => 1,
+        Writable => 'string',
+        ValueConv => '$val=~s/.*: //;$val',
+        ValueConvInv => q{"Saturation: $val"},
+    },
+    0xfe56 => {
+        Name => 'Sharpness',
+        Avoid => 1,
+        Writable => 'string',
+        ValueConv => '$val=~s/.*: //;$val',
+        ValueConvInv => q{"Sharpness: $val"},
+    },
+    0xfe57 => {
+        Name => 'Smoothness',
+        Avoid => 1,
+        Writable => 'string',
+        ValueConv => '$val=~s/.*: //;$val',
+        ValueConvInv => q{"Smoothness: $val"},
+    },
+    0xfe58 => {
+        Name => 'MoireFilter',
+        Avoid => 1,
+        Writable => 'string',
+        ValueConv => '$val=~s/.*: //;$val',
+        ValueConvInv => q{"Moire Filter: $val"},
+    },
+);
+
+# insert our writable properties into main EXIF tag table
+InsertWritableProperties('Image::ExifTool::Exif::Main', \%writeTable, \&CheckExif);
+
+#------------------------------------------------------------------------------
+# Change date/time string to standard EXIF formatting
+# Inputs: 0) Date/Time string
+# Returns: formatted date/time string (or undef and issues warning on error)
+sub ExifDateTime($)
+{
+    my $val = shift;
+    my $rtnVal;
+    if ($val =~ /(\d{4})/g) {           # get YYYY
+        my $yr = $1;
+        my @a = ($val =~ /\d{2}/g);     # get MM, DD, and maybe hh, mm, ss
+        if (@a >= 2) {
+            push @a, '00' while @a < 5; # add hh, mm, ss if not given
+            # construct properly formatted date/time string
+            $rtnVal = "$yr:$a[0]:$a[1] $a[2]:$a[3]:$a[4]";
+        }
+    }
+    $rtnVal or warn "Improperly formatted date/time\n";
+    return $rtnVal;
+}
+
+#------------------------------------------------------------------------------
+# Get binary CFA Pattern from a text string
+# Inputs: CFA pattern string (ie. '[Blue,Green][Green,Red]')
+# Returns: Binary CFA data or prints warning and returns undef on error
+sub GetCFAPattern($)
+{
+    my $val = shift;
+    my @rows = split /\]\s*\[/, $val;
+    @rows or warn("Rows not properly bracketed by '[]'\n"), return undef;
+    my @cols = split /,/, $rows[0];
+    @cols or warn("Colors not separated by ','\n"), return undef;
+    my $ny = @cols;
+    my $rtnVal = Set16u(scalar(@rows)) . Set16u(scalar(@cols));
+    my %cfaLookup = (red=>0, green=>1, blue=>2, cyan=>3, magenta=>4, yellow=>5, white=>6);
+    my $row;
+    foreach $row (@rows) {
+        @cols = split /,/, $row;
+        @cols == $ny or warn("Inconsistent number of colors in each row\n"), return undef;
+        foreach (@cols) {
+            tr/ \]\[//d;    # remove remaining brackets and any spaces
+            my $c = $cfaLookup{lc($_)};
+            defined $c or warn("Unknown color '$_'\n"), return undef;
+            $rtnVal .= Set8u($c);
+        }
+    }
+    return $rtnVal;
+}
+
+#------------------------------------------------------------------------------
+# validate raw values for writing
+# Inputs: 0) ExifTool object reference, 1) tagInfo hash reference,
+#         2) raw value reference
+# Returns: error string or undef (and possibly changes value) on success
+sub CheckExif($$$)
+{
+    my ($exifTool, $tagInfo, $valPtr) = @_;
+    my $format = $$tagInfo{Format} || $$tagInfo{Writable} || $tagInfo->{Table}->{WRITABLE};
+    if (not $format or $format eq '1') {
+        if ($tagInfo->{Groups}->{0} eq 'MakerNotes') {
+            return undef;   # OK to have no format for makernotes
+        } else {
+            return 'No writable format';
+        }
+    }
+    return Image::ExifTool::CheckValue($valPtr, $format, $$tagInfo{Count});
+}
+
+#------------------------------------------------------------------------------
+# encode exif ASCII/Unicode text from UTF8 or Latin
+# Inputs: 0) ExifTool ref, 1) text string
+# Returns: encoded string
+sub EncodeExifText($$)
+{
+    my ($exifTool, $val) = @_;
+    # does the string contain special characters?
+    if ($val =~ /[\x80-\xff]/) {
+        return "UNICODE\0" . $exifTool->Charset2Unicode($val);
+    } else {
+        return "ASCII\0\0\0$val";
+    }
+}
+
+#------------------------------------------------------------------------------
+# insert writable properties into main tag table
+# Inputs: 0) tag table name, 1) reference to writable properties
+#         2) [optional] CHECK_PROC reference
+sub InsertWritableProperties($$;$)
+{
+    my ($tableName, $writeTablePtr, $checkProc) = @_;
+    my $tag;
+    my $tagTablePtr = GetTagTable($tableName);
+    $checkProc and $tagTablePtr->{CHECK_PROC} = $checkProc;
+    foreach $tag (keys %$writeTablePtr) {
+        my $writeInfo = $$writeTablePtr{$tag};
+        my @infoList = GetTagInfoList($tagTablePtr, $tag);
+        if (@infoList) {
+            my $tagInfo;
+            foreach $tagInfo (@infoList) {
+                if (ref $writeInfo) {
+                    my $key;
+                    foreach $key (%$writeInfo) {
+                        $$tagInfo{$key} = $$writeInfo{$key};
+                    }
+                } else {
+                    $$tagInfo{Writable} = $writeInfo;
+                }
+            }
+        } else {
+            Image::ExifTool::AddTagToTable($tagTablePtr, $tag, $writeInfo);
+        }
+    }
+}
+
+#------------------------------------------------------------------------------
+# rebuild maker notes to properly contain all value data
+# (some manufacturers put value data outside maker notes!!)
+# Inputs: 0) ExifTool object reference, 1) tag table reference,
+#         2) dirInfo reference
+# Returns: new maker note data (and creates MAKER_NOTE_FIXUP), or undef on error
+sub RebuildMakerNotes($$$)
+{
+    my ($exifTool, $tagTablePtr, $dirInfo) = @_;
+    my $dirStart = $$dirInfo{DirStart};
+    my $dirLen = $$dirInfo{DirLen};
+    my $dataPt = $$dirInfo{DataPt};
+    my $dataPos = $$dirInfo{DataPos} || 0;
+    my $rtnValue;
+    my %subdirInfo = %$dirInfo;
+
+    delete $exifTool->{MAKER_NOTE_FIXUP};
+
+    # don't need to rebuild text or binary-data maker notes
+    my $tagInfo = $$dirInfo{TagInfo};
+    my $subdir = $$tagInfo{SubDirectory};
+    my $proc = $$subdir{ProcessProc} || $$tagTablePtr{PROCESS_PROC} || \&ProcessExif;
+    if (($proc ne \&ProcessExif and $$tagInfo{Name} =~ /Text/) or
+         $proc eq \&Image::ExifTool::ProcessBinaryData)
+    {
+        return substr($$dataPt, $dirStart, $dirLen);
+    }
+    my $saveOrder = GetByteOrder();
+    my $loc = Image::ExifTool::MakerNotes::LocateIFD($exifTool,\%subdirInfo);
+    if (defined $loc) {
+        my $makerFixup = $subdirInfo{Fixup} = new Image::ExifTool::Fixup;
+        # create new exiftool object to rewrite the directory without changing it
+        my $newTool = new Image::ExifTool;
+        # don't copy over preview image
+        $newTool->SetNewValue(PreviewImage => '');
+        # copy all transient members over in case they are used for writing
+        # (CameraMake, CameraModel, etc)
+        foreach (grep /[a-z]/, keys %$exifTool) {
+            $$newTool{$_} = $$exifTool{$_};
+        }
+        # fix base offsets if specified
+        $newTool->Options(FixBase => $exifTool->Options('FixBase'));
+        # set FILE_TYPE to JPEG so PREVIEW_INFO will be generated
+        $newTool->{FILE_TYPE} = 'JPEG';
+        # drop any large tags
+        $newTool->{DROP_TAGS} = 1;
+        # rewrite maker notes
+        $rtnValue = $newTool->WriteDirectory(\%subdirInfo, $tagTablePtr);
+        if (defined $rtnValue and length $rtnValue) {
+            # add the dummy preview image if necessary
+            if ($newTool->{PREVIEW_INFO}) {
+                $makerFixup->SetMarkerPointers(\$rtnValue, 'PreviewImage', length($rtnValue));
+                $rtnValue .= $newTool->{PREVIEW_INFO}->{Data};
+                delete $newTool->{PREVIEW_INFO};
+            }
+            # add makernote header
+            $loc and $rtnValue = substr($$dataPt, $dirStart, $loc) . $rtnValue;
+            # adjust fixup for shift in start position
+            $makerFixup->{Start} += $loc;
+            # shift offsets according to original position of maker notes,
+            # and relative to the makernotes Base
+            $makerFixup->{Shift} += $dataPos + $dirStart +
+                                    $$dirInfo{Base} - $subdirInfo{Base};
+            # repair incorrect offsets if offsets were fixed
+            $makerFixup->{Shift} += $subdirInfo{FixedBy} || 0;
+            # fix up pointers to the specified offset
+            $makerFixup->ApplyFixup(\$rtnValue);
+        }
+        # save fixup information unless offsets were relative
+        unless ($subdirInfo{Relative}) {
+            # set shift so offsets are all relative to start of maker notes
+            $makerFixup->{Shift} -= $dataPos + $dirStart;
+            $exifTool->{MAKER_NOTE_FIXUP} = $makerFixup;    # save fixup for later
+        }
+    }
+    SetByteOrder($saveOrder);
+
+    return $rtnValue;
+}
+
+#------------------------------------------------------------------------------
+# Sort IFD directory entries
+# Inputs: 0) data reference, 1) directory start, 2) number of entries
+sub SortIFD($$$)
+{
+    my ($dataPt, $dirStart, $numEntries) = @_;
+    my ($index, %entries);
+    # split the directory into separate entries
+    my ($padding, $newDir) = ('','');
+    for ($index=0; $index<$numEntries; ++$index) {
+        my $entry = $dirStart + 2 + 12 * $index;
+        my $tagID = Get16u($dataPt, $entry);
+        my $entryData = substr($$dataPt, $entry, 12);
+        # silly software can pad directories with zero entries -- put these at the end
+        $tagID = 0x10000 unless $tagID or $index == 0;
+        # add new entry (allow for duplicate tag ID's, which shouldn't normally happen)
+        $entries{$tagID} or $entries{$tagID} = '';
+        $entries{$tagID} .= $entryData;
+    }
+    # sort the directory entries
+    my @sortedTags = sort { $a <=> $b } keys %entries;
+    foreach (@sortedTags) {
+        $newDir .= $entries{$_};
+    }
+    # replace original directory with new, sorted one
+    substr($$dataPt, $dirStart + 2, 12 * $numEntries) = $newDir . $padding;
+}
+
+#------------------------------------------------------------------------------
+# Validate IFD entries (strict validation to test possible chained IFD's)
+# Inputs: 0) dirInfo ref (must have RAF set), 1) optional DirStart
+# Returns: true if IFD looks OK
+sub ValidateIFD($;$)
+{
+    my ($dirInfo, $dirStart) = @_;
+    my $raf = $$dirInfo{RAF} or return 0;
+    my $base = $$dirInfo{Base};
+    $dirStart = $$dirInfo{DirStart} || 0 unless defined $dirStart;
+    my $offset = $dirStart + ($$dirInfo{DataPos} || 0);
+    my ($buff, $index);
+    $raf->Seek($offset + $base, 0) and $raf->Read($buff,2) == 2 or return 0;
+    my $numEntries = Get16u(\$buff,0);
+    $numEntries > 1 and $numEntries < 64 or return 0;
+    my $len = 12 * $numEntries;
+    $raf->Read($buff, $len) == $len or return 0;
+    my $lastID = -1;
+    for ($index=0; $index<$numEntries; ++$index) {
+        my $entry = 12 * $index;
+        my $tagID = Get16u(\$buff, $entry);
+        $tagID > $lastID or return 0;
+        my $format = Get16u(\$buff, $entry+2);
+        $format > 0 and $format <= 13 or return 0;
+        my $count = Get32u(\$buff, $entry+4);
+        $count > 0 or return 0;
+        $lastID = $tagID;
+    }
+    return 1;
+}
+
+#------------------------------------------------------------------------------
+# Update TIFF_END member if defined
+# Inputs: 0) ExifTool ref, 1) end of valid TIFF data
+sub UpdateTiffEnd($$)
+{
+    my ($exifTool, $end) = @_;
+    if (defined $exifTool->{TIFF_END} and
+        $exifTool->{TIFF_END} < $end)
+    {
+        $exifTool->{TIFF_END} = $end;
+    }
+}
+
+#------------------------------------------------------------------------------
+# Handle error while writing EXIF
+# Inputs: 0) ExifTool ref, 1) error string, 2) flag set for minor error
+# Returns: undef on fatal error, or '' if minor error is ignored
+sub ExifErr($$$)
+{
+    my ($exifTool, $errStr, $minor) = @_;
+    return undef if $exifTool->Error($errStr, $minor);
+    return '';
+}
+
+#------------------------------------------------------------------------------
+# Write EXIF directory
+# Inputs: 0) ExifTool object reference, 1) source dirInfo reference,
+#         2) tag table reference
+# Returns: Exif data block (may be empty if no Exif data) or undef on error
+# Notes: Increments ExifTool CHANGED flag for each tag changed.  Also updates
+#        TIFF_END if defined with location of end of original TIFF image.
+# Returns IFD data in the following order:
+#   1. IFD0 directory followed by its data
+#   2. SubIFD directory followed by its data, thumbnail and image
+#   3. GlobalParameters, EXIF, GPS, Interop IFD's each with their data
+#   4. IFD1,IFD2,... directories each followed by their data
+#   5. Thumbnail and/or image data for each IFD, with IFD0 image last
+sub WriteExif($$$)
+{
+    my ($exifTool, $dirInfo, $tagTablePtr) = @_;
+    $exifTool or return 1;    # allow dummy access to autoload this package
+    my $dataPt = $$dirInfo{DataPt};
+    unless ($dataPt) {
+        my $emptyData = '';
+        $dataPt = \$emptyData;
+    }
+    my $dataPos = $$dirInfo{DataPos} || 0;
+    my $dirStart = $$dirInfo{DirStart} || 0;
+    my $dataLen = $$dirInfo{DataLen} || length($$dataPt);
+    my $dirLen = $$dirInfo{DirLen} || ($dataLen - $dirStart);
+    my $base = $$dirInfo{Base} || 0;
+    my $firstBase = $base;
+    my $raf = $$dirInfo{RAF};
+    my $dirName = $$dirInfo{DirName} || 'unknown';
+    my $fixup = $$dirInfo{Fixup} || new Image::ExifTool::Fixup;
+    my $verbose = $exifTool->Options('Verbose');
+    my $out = $exifTool->Options('TextOut');
+    my (@offsetInfo, %xDelete);
+    my $newData = '';   # initialize buffer to receive new directory data
+    my ($nextIfdPos, %offsetData, $inMakerNotes);
+    my $deleteAll = 0;
+
+    # allow multiple IFD's in IFD0-IFD1-IFD2... chain
+    $$dirInfo{Multi} = 1 if $dirName eq 'IFD0' or $dirName eq 'SubIFD';
+    $inMakerNotes = 1 if $tagTablePtr->{GROUPS}->{0} eq 'MakerNotes';
+    my $ifd;
+#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+# loop through each IFD
+#
+    for ($ifd=0; ; ++$ifd) {  # loop through multiple IFD's
+
+        # loop through new values and accumulate all information for this IFD
+        my (%set, $tagInfo);
+        my $tableGroup = $tagTablePtr->{GROUPS}->{0};
+        my $wrongDir = $crossDelete{$dirName};
+        foreach $tagInfo ($exifTool->GetNewTagInfoList($tagTablePtr)) {
+            my $tagID = $$tagInfo{TagID};
+            # evaluate conditional lists now if necessary
+            if (ref $tagTablePtr->{$tagID} eq 'ARRAY' or $$tagInfo{Condition}) {
+                my $curInfo = $exifTool->GetTagInfo($tagTablePtr, $tagID);
+                if (defined $curInfo and not $curInfo) {
+                    # need value to evaluate the condition
+                    my ($val) = $exifTool->GetNewValues($tagInfo);
+                    # must convert to binary for evaluating in Condition
+                    if ($$tagInfo{Format} and defined $val) {
+                        $val = WriteValue($val, $$tagInfo{Format}, $$tagInfo{Count});
+                    }
+                    if (defined $val) {
+                        $curInfo = $exifTool->GetTagInfo($tagTablePtr, $tagID, \$val,
+                                        $$tagInfo{Format} || 'undef', $$tagInfo{Count} || 1);
+                    }
+                }
+                # don't set this tag unless valid for the current condition
+                next unless defined $curInfo and $curInfo eq $tagInfo;
+            }
+            if ($$tagInfo{WriteCondition}) {
+                my $self = $exifTool;   # set $self to be used in eval
+                #### eval WriteCondition ($self)
+                unless (eval $$tagInfo{WriteCondition}) {
+                    $@ and warn $@;
+                    next;
+                }
+            }
+            my $newValueHash = $exifTool->GetNewValueHash($tagInfo, $dirName);
+            unless ($newValueHash) {
+                next unless $wrongDir;
+                # delete stuff from the wrong directory if setting somewhere else
+                $newValueHash = $exifTool->GetNewValueHash($tagInfo, $wrongDir);
+                next unless Image::ExifTool::IsOverwriting($newValueHash);
+                # don't cross delete if specifically deleting from the other directory
+                my $val = Image::ExifTool::GetNewValues($newValueHash);
+                next if not defined $val and $newValueHash->{WantGroup} and
+                        lc($newValueHash->{WantGroup}) eq lc($wrongDir);
+                # remove this tag if found in this IFD
+                $xDelete{$tagID} = 1;
+            }
+            $set{$tagID} = $tagInfo;
+        }
+        # save pointer to start of this IFD within the newData
+        my $newStart = length($newData);
+        my @subdirs;    # list of subdirectory data and tag table pointers
+        # determine if directory is contained within our data
+        my $mustRead;
+        if ($dirStart < 0 or $dirStart > $dataLen-2) {
+            $mustRead = 1;
+        } elsif ($dirLen > 2) {
+            my $len = 2 + 12 * Get16u($dataPt, $dirStart);
+            $mustRead = 1 if $dirStart + $len > $dataLen;
+        }
+        # read IFD from file if necessary
+        if ($raf and $mustRead) {
+            # read the count of entries in this IFD
+            my $offset = $dirStart + $dataPos;
+            my ($buff, $buf2);
+            unless ($raf->Seek($offset + $base, 0) and $raf->Read($buff,2) == 2) {
+                return ExifErr($exifTool, "Bad IFD or truncated file in $dirName", $inMakerNotes);
+            }
+            my $len = 12 * Get16u(\$buff,0);
+            # (also read next IFD pointer if available)
+            unless ($raf->Read($buf2, $len+4) >= $len) {
+                return ExifErr($exifTool, "Error reading $dirName", $inMakerNotes);
+            }
+            $buff .= $buf2;
+            # make copy of dirInfo since we're going to modify it
+            my %newDirInfo = %$dirInfo;
+            $dirInfo = \%newDirInfo;
+            # update directory parameters for the newly loaded IFD
+            $dataPt = $$dirInfo{DataPt} = \$buff;
+            $dirStart = $$dirInfo{DirStart} = 0;
+            $dataPos = $$dirInfo{DataPos} = $offset;
+            $dataLen = $$dirInfo{DataLen} = length $buff;
+            $dirLen = $$dirInfo{DirLen} = $dataLen;
+            # only account for nextIFD pointer if we are going to use it
+            $len += 4 if $dataLen==$len+6 and ($$dirInfo{Multi} or $buff =~ /\0{4}$/);
+            UpdateTiffEnd($exifTool, $offset+$base+2+$len);
+        }
+        my ($len, $numEntries);
+        if ($dirStart + 4 < $dataLen) {
+            $numEntries = Get16u($dataPt, $dirStart);
+            $len = 2 + 12 * $numEntries;
+            if ($dirStart + $len > $dataLen) {
+                return ExifErr($exifTool, "Truncated $dirName directory", $inMakerNotes);
+            }
+            # sort entries if necessary (but not in maker notes IFDs)
+            unless ($inMakerNotes) {
+                my $index;
+                my $lastID = -1;
+                for ($index=0; $index<$numEntries; ++$index) {
+                    my $tagID = Get16u($dataPt, $dirStart + 2 + 12 * $index);
+                    # check for proper sequence (but ignore null entries at end)
+                    if ($tagID < $lastID and $tagID) {
+                        SortIFD($dataPt, $dirStart, $numEntries);
+                        $exifTool->Warn("Entries in $dirName were out of sequence. Fixed.");
+                        last;
+                    }
+                    $lastID = $tagID;
+                }
+            }
+        } else {
+            $numEntries = $len = 0;
+        }
+
+        # fix base offsets
+        if ($dirName eq 'MakerNotes' and $$dirInfo{Parent} eq 'ExifIFD' and
+            Image::ExifTool::MakerNotes::FixBase($exifTool, $dirInfo))
+        {
+            # update local variables from fixed values
+            $base = $$dirInfo{Base};
+            $dataPos = $$dirInfo{DataPos};
+        }
+
+        # initialize variables to handle mandatory tags
+        my $mandatory = $mandatory{$dirName};
+        my $allMandatory;
+        if ($mandatory) {
+            # use X/Y resolution values from JFIF if available
+            if ($dirName eq 'IFD0' and defined $$exifTool{JFIFYResolution}) {
+                my %ifd0Vals = %$mandatory;
+                $ifd0Vals{0x011a} = $$exifTool{JFIFXResolution};
+                $ifd0Vals{0x011b} = $$exifTool{JFIFYResolution};
+                $ifd0Vals{0x0128} = $$exifTool{JFIFResolutionUnit} + 1;
+                $mandatory = \%ifd0Vals;
+            }
+            $allMandatory = 0;  # initialize to zero
+            # add mandatory tags if creating a new directory
+            unless ($numEntries) {
+                foreach (keys %$mandatory) {
+                    $set{$_} or $set{$_} = $$tagTablePtr{$_};
+                }
+            }
+        } else {
+            undef $deleteAll;   # don't remove directory (no mandatory entries)
+        }
+        my ($addDirs, @newTags);
+        if ($inMakerNotes) {
+            $addDirs = { };
+        } else {
+            # get a hash of directories we will be writing in this one
+            $addDirs = $exifTool->GetAddDirHash($tagTablePtr, $dirName);
+            # make a union of tags & dirs (can set whole dirs, like MakerNotes)
+            my %allTags = %set;
+            foreach (keys %$addDirs) {
+                $allTags{$_} = $$addDirs{$_};
+            }
+            # make sorted list of new tags to be added
+            @newTags = sort { $a <=> $b } keys(%allTags);
+        }
+        my $dirBuff = '';   # buffer for directory data
+        my $valBuff = '';   # buffer for value data
+        my @valFixups;      # list of fixups for offsets in valBuff
+        # fixup for offsets in dirBuff
+        my $dirFixup = new Image::ExifTool::Fixup;
+        my $entryBasedFixup;
+        my $index = 0;
+        my $lastTagID = -1;
+        my ($oldInfo, $oldFormat, $oldFormName, $oldCount, $oldSize, $oldValue);
+        my ($readFormat, $readFormName, $readCount); # format for reading old value(s)
+        my ($entry, $valueDataPt, $valueDataPos, $valueDataLen, $valuePtr, $valEnd);
+        my $oldID = -1;
+        my $newID = -1;
+#..............................................................................
+# loop through entries in new directory
+#
+Entry:  for (;;) {
+
+            if (defined $oldID and $oldID == $newID) {
+#
+# read next entry from existing directory
+#
+                if ($index < $numEntries) {
+                    $entry = $dirStart + 2 + 12 * $index;
+                    $oldID = Get16u($dataPt, $entry);
+                    $readFormat = $oldFormat = Get16u($dataPt, $entry+2);
+                    $readCount = $oldCount = Get32u($dataPt, $entry+4);
+                    if ($oldFormat < 1 or $oldFormat > 13) {
+                        # don't write out null directory entry
+                        unless ($oldFormat or $oldCount or not $index) {
+                            ++$index;
+                            $newID = $oldID;    # pretend we wrote this
+                            # must keep same directory size to avoid messing up our fixed offsets
+                            $dirBuff .= ("\0" x 12) if $$dirInfo{FixBase};
+                            next;
+                        }
+                        return ExifErr($exifTool, "Bad format ($oldFormat) for $dirName entry $index", $inMakerNotes);
+                    }
+                    $readFormName = $oldFormName = $formatName[$oldFormat];
+                    $valueDataPt = $dataPt;
+                    $valueDataPos = $dataPos;
+                    $valueDataLen = $dataLen;
+                    $valuePtr = $entry + 8;
+                    $oldSize = $oldCount * $formatSize[$oldFormat];
+                    # must try direct method first so we will get unknown tags too
+                    # (this is necessary so we don't miss a tag we want to Drop)
+                    $oldInfo = $$tagTablePtr{$oldID};
+                    if (ref $oldInfo ne 'HASH' or $$oldInfo{Condition}) {
+                        $oldInfo = $exifTool->GetTagInfo($tagTablePtr, $oldID);
+                    }
+                    my $readFromFile;
+                    if ($oldSize > 4) {
+                        $valuePtr = Get32u($dataPt, $valuePtr);
+                        # fix valuePtr if necessary
+                        if ($$dirInfo{FixOffsets}) {
+                            $valEnd or $valEnd = $dataPos + $dirStart + 2 + 12 * $numEntries + 4;
+                            my ($tagID, $size, $wFlag) = ($oldID, $oldSize, 1);
+                            #### eval FixOffsets($valuePtr, $valEnd, $size, $tagID, $wFlag)
+                            eval $$dirInfo{FixOffsets};
+                            unless (defined $valuePtr) {
+                                unless ($$exifTool{DROP_TAGS}) {
+                                    my $tagStr = $oldInfo ? $$oldInfo{Name} : sprintf("tag 0x%x",$oldID);
+                                    return undef if $exifTool->Error("Bad $dirName directory pointer for $tagStr", $inMakerNotes);
+                                }
+                                goto DropTag;
+                            }
+                        }
+                        # convert offset to pointer in $$dataPt
+                        if ($$dirInfo{EntryBased} or (ref $$tagTablePtr{$oldID} eq 'HASH' and
+                            $tagTablePtr->{$oldID}->{EntryBased}))
+                        {
+                            $valuePtr += $entry;
+                        } else {
+                            $valuePtr -= $dataPos;
+                        }
+                        # get value by seeking in file if we are allowed
+                        if ($valuePtr < 0 or $valuePtr+$oldSize > $dataLen) {
+                            my ($pos, $tagStr, $invalidPreview);
+                            if ($oldInfo) {
+                                $tagStr = $$oldInfo{Name};
+                            } elsif (defined $oldInfo) {
+                                my $tmpInfo = $exifTool->GetTagInfo($tagTablePtr, $oldID, \ '', $oldFormName, $oldCount);
+                                $tagStr = $$tmpInfo{Name} if $tmpInfo;
+                            }
+                            $tagStr or $tagStr = sprintf("tag 0x%x",$oldID);
+                            # allow PreviewImage to run outside EXIF segment
+                            if (not $raf and $tagStr eq 'PreviewImage') {
+                                $raf = $exifTool->{RAF};
+                                if ($raf) {
+                                    $pos = $raf->Tell();
+                                } else {
+                                    $invalidPreview = 1;
+                                }
+                            }
+                            if ($raf) {
+                                my $success = ($raf->Seek($base + $valuePtr + $dataPos, 0) and
+                                               $raf->Read($oldValue, $oldSize) == $oldSize);
+                                if (defined $pos) {
+                                    $raf->Seek($pos, 0);
+                                    undef $raf;
+                                    unless ($success and $oldValue =~ /^(\xff\xd8\xff|.\xd8\xff\xdb)/s) {
+                                        $exifTool->Error("Bad PreviewImage pointer in $dirName", 1);
+                                        $invalidPreview = 1;
+                                        $success = 1;   # continue writing directory
+                                    }
+                                }
+                                unless ($success) {
+                                    return undef if $exifTool->Error("Error reading value for $dirName entry $index", $inMakerNotes);
+                                    goto DropTag;
+                                }
+                            } elsif (not $invalidPreview) {
+                                return undef if $exifTool->Error("Bad $dirName directory pointer for $tagStr", $inMakerNotes);
+                                goto DropTag;
+                            }
+                            if ($invalidPreview) {
+                                $oldValue = 'none';     # flag for missing preview
+                                $oldSize = length $oldValue;
+                                $valuePtr = 0;
+                            } else {
+                                UpdateTiffEnd($exifTool, $base+$valuePtr+$dataPos+$oldSize);
+                            }
+                            # update pointers for value just read from file
+                            $valueDataPt = \$oldValue;
+                            $valueDataPos = $valuePtr + $dataPos;
+                            $valueDataLen = $oldSize;
+                            $valuePtr = 0;
+                            $readFromFile = 1;
+                        }
+                    }
+                    # read value if we haven't already
+                    $oldValue = substr($$valueDataPt, $valuePtr, $oldSize) unless $readFromFile;
+                    # get tagInfo using value if necessary
+                    if (defined $oldInfo and not $oldInfo) {
+                        $oldInfo = $exifTool->GetTagInfo($tagTablePtr, $oldID, \$oldValue, $oldFormName, $oldCount);
+                    }
+                    # override format we use to read the value if specified
+                    if ($oldInfo) {
+                        if ($$oldInfo{Drop} and $$exifTool{DROP_TAGS}) {
+                            # don't rewrite this tag
+DropTag:                    ++$index;
+                            $oldID = $newID;
+                            next;
+                        }
+                        if ($$oldInfo{Format}) {
+                            $readFormName = $$oldInfo{Format};
+                            $readFormat = $formatNumber{$readFormName};
+                            unless ($readFormat) {
+                                # we aren't reading in a standard EXIF format, so rewrite in old format
+                                $readFormName = $oldFormName;
+                                $readFormat = $oldFormat;
+                            }
+                            # adjust number of items to read if format size changed
+                            $readCount = $oldSize / $formatSize[$readFormat];
+                        }
+                    }
+                    if ($oldID <= $lastTagID and not $inMakerNotes) {
+                        my $str = $oldInfo ? "$$oldInfo{Name} tag" : sprintf('tag 0x%x',$oldID);
+                        if ($oldID == $lastTagID) {
+                            $exifTool->Warn("Duplicate $str in $dirName");;
+                        } else {
+                            $exifTool->Warn("\u$str out of sequence in $dirName");
+                        }
+                    }
+                    $lastTagID = $oldID;
+                    ++$index;               # increment index for next time
+                } else {
+                    undef $oldID;           # no more existing entries
+                }
+            }
+#
+# write out all new tags, up to and including this one
+#
+            $newID = $newTags[0];
+            my $isNew;  # -1=tag is old, 0=tag same as existing, 1=tag is new
+            if (not defined $oldID) {
+                last unless defined $newID;
+                $isNew = 1;
+            } elsif (not defined $newID) {
+                # maker notes will have no new tags defined
+                if ($set{$oldID}) {
+                    $newID = $oldID;
+                    $isNew = 0;
+                } else {
+                    $isNew = -1;
+                }
+            } else {
+                $isNew = $oldID <=> $newID;
+            }
+            my $newInfo = $oldInfo;
+            my $newFormat = $oldFormat;
+            my $newFormName = $oldFormName;
+            my $newCount = $oldCount;
+            my $ifdFormName;
+            my $newValue;
+            my $newValuePt = $isNew >= 0 ? \$newValue : \$oldValue;
+            my $isOverwriting;
+
+            if ($isNew >= 0) {
+                # add, edit or delete this tag
+                shift @newTags; # remove from list
+                if ($set{$newID}) {
+#
+# set the new tag value (or 'next' if deleting tag)
+#
+                    $newInfo = $set{$newID};
+                    $newCount = $$newInfo{Count};
+                    my ($val, $newVal);
+                    my $newValueHash = $exifTool->GetNewValueHash($newInfo, $dirName);
+                    if ($isNew > 0) {
+                        # don't create new entry unless requested
+                        if ($newValueHash) {
+                            next unless Image::ExifTool::IsCreating($newValueHash);
+                            $isOverwriting = Image::ExifTool::IsOverwriting($newValueHash);
+                        } else {
+                            next if $xDelete{$newID};       # don't create if cross deleting
+                            $newVal = $$mandatory{$newID};  # get value for mandatory tag
+                            $isOverwriting = 1;
+                        }
+                        # convert using new format
+                        if ($$newInfo{Format}) {
+                            $newFormName = $$newInfo{Format};
+                            # use Writable flag to specify IFD format code
+                            $ifdFormName = $$newInfo{Writable};
+                        } else {
+                            $newFormName = $$newInfo{Writable};
+                            unless ($newFormName) {
+                                warn("No format for $dirName $$newInfo{Name}\n");
+                                next;
+                            }
+                        }
+                        $newFormat = $formatNumber{$newFormName};
+                    } elsif ($newValueHash or $xDelete{$newID}) {
+                        unless ($newValueHash) {
+                            $newValueHash = $exifTool->GetNewValueHash($newInfo, $wrongDir);
+                        }
+                        # read value
+                        $val = ReadValue(\$oldValue, 0, $readFormName, $readCount, $oldSize);
+                        if ($$newInfo{Format}) {
+                            $newFormName = $$newInfo{Format};
+                            # override existing format if necessary
+                            $ifdFormName = $$newInfo{Writable};
+                            $ifdFormName = $oldFormName unless $ifdFormName and $ifdFormName ne '1';
+                            $newFormat = $formatNumber{$newFormName};
+                        }
+                        if ($inMakerNotes and $readFormName ne 'string' and $readFormName ne 'undef') {
+                            # keep same size in maker notes unless string or binary
+                            $newCount = $oldCount * $formatSize[$oldFormat] / $formatSize[$newFormat];
+                        }
+                        $isOverwriting = Image::ExifTool::IsOverwriting($newValueHash, $val);
+                    }
+                    if ($isOverwriting) {
+                        $newVal = Image::ExifTool::GetNewValues($newValueHash) unless defined $newVal;
+                        # value undefined if deleting this tag
+                        # (also delete tag if cross-deleting and this isn't a date/time shift)
+                        if (not defined $newVal or ($xDelete{$newID} and not defined $$newValueHash{Shift})) {
+                            if ($$newInfo{RawConvInv} and defined $$newValueHash{Value}) {
+                                goto NoOverwrite;   # error in RawConvInv, so rewrite existing tag
+                            }
+                            unless ($isNew) {
+                                ++$exifTool->{CHANGED};
+                                $val = $exifTool->Printable($val);
+                                $verbose > 1 and print $out "    - $dirName:$$newInfo{Name} = '$val'\n";
+                            }
+                            next;
+                        }
+                        if (length $newVal) {
+                            if ($newCount and $newCount < 0) {
+                                # set count to number of values if variable
+                                my @vals = split ' ',$newVal;
+                                $newCount = @vals;
+                            }
+                            # convert to binary format
+                            $newValue = WriteValue($newVal, $newFormName, $newCount);
+                            unless (defined $newValue) {
+                                $exifTool->Warn("Error writing $dirName:$$newInfo{Name}");
+                                goto NoOverwrite;
+                            }
+                        } else {
+                            $exifTool->Warn("Can't write zero length $$newInfo{Name} in $tagTablePtr->{GROUPS}->{1}");
+                            goto NoOverwrite;
+                        }
+                        if ($isNew >= 0) {
+                            $newCount = length($newValue) / $formatSize[$newFormat];
+                            ++$exifTool->{CHANGED};
+                            if ($verbose > 1) {
+                                $val = $exifTool->Printable($val);
+                                $newVal = $exifTool->Printable($newVal);
+                                print $out "    - $dirName:$$newInfo{Name} = '$val'\n" unless $isNew;
+                                my $str = $newValueHash ? '' : ' (mandatory)';
+                                print $out "    + $dirName:$$newInfo{Name} = '$newVal'$str\n";
+                            }
+                        }
+                    } else {
+NoOverwrite:            next if $isNew > 0;
+                        $isNew = -1;        # rewrite existing tag
+                    }
+                    # set format for EXIF IFD if different than conversion format
+                    if ($ifdFormName) {
+                        $newFormName = $ifdFormName;
+                        $newFormat = $formatNumber{$newFormName};
+                    }
+
+                } elsif ($isNew > 0) {
+#
+# create new subdirectory
+#
+                    $newInfo = $$addDirs{$newID} or warn('internal error'), next;
+                    # make sure we don't try to generate a new MakerNotes directory
+                    # or a SubIFD
+                    next if $$newInfo{MakerNotes} or $$newInfo{Name} eq 'SubIFD';
+                    my $subTable;
+                    if ($newInfo->{SubDirectory}->{TagTable}) {
+                        $subTable = GetTagTable($newInfo->{SubDirectory}->{TagTable});
+                    } else {
+                        $subTable = $tagTablePtr;
+                    }
+                    # create empty source directory
+                    my %sourceDir = (
+                        Parent => $dirName,
+                        Fixup => new Image::ExifTool::Fixup,
+                    );
+                    $sourceDir{DirName} = $newInfo->{Groups}->{1} if $$newInfo{SubIFD};
+                    $newValue = $exifTool->WriteDirectory(\%sourceDir, $subTable);
+                    # only add new directory if it isn't empty
+                    next unless defined $newValue and length($newValue);
+                    # set the fixup start location
+                    if ($$newInfo{SubIFD}) {
+                        # subdirectory is referenced by an offset in value buffer
+                        my $subdir = $newValue;
+                        $newValue = Set32u(0xfeedf00d);
+                        push @subdirs, {
+                            DataPt => \$subdir,
+                            Table => $subTable,
+                            Fixup => $sourceDir{Fixup},
+                            Offset => length($dirBuff) + 8,
+                            Where => 'dirBuff',
+                        };
+                        $newFormName = 'int32u';
+                        $newFormat = $formatNumber{$newFormName};
+                    } else {
+                        # subdirectory goes directly into value buffer
+                        $sourceDir{Fixup}->{Start} += length($valBuff);
+                        # use Writable to set format, otherwise 'undef'
+                        $newFormName = $$newInfo{Writable};
+                        unless ($newFormName and $formatNumber{$newFormName}) {
+                            $newFormName = 'undef';
+                        }
+                        $newFormat = $formatNumber{$newFormName};
+                        push @valFixups, $sourceDir{Fixup};
+                    }
+                } elsif ($$newInfo{Format} and $$newInfo{Writable} and $$newInfo{Writable} ne '1') {
+                    # use specified write format
+                    $newFormName = $$newInfo{Writable};
+                    $newFormat = $formatNumber{$newFormName};
+                }
+            }
+            if ($isNew < 0) {
+                # just rewrite existing tag
+                $newID = $oldID;
+                $newValue = $oldValue;
+                $newFormat = $oldFormat; # (just in case it changed)
+                $newFormName = $oldFormName;
+            }
+            if ($newInfo) {
+#
+# load necessary data for this tag (thumbnail image, etc)
+#
+                if ($$newInfo{DataTag} and $isNew >= 0) {
+                    my $dataTag = $$newInfo{DataTag};
+                    # load data for this tag
+                    unless (defined $offsetData{$dataTag}) {
+                        $offsetData{$dataTag} = $exifTool->GetNewValues($dataTag);
+                        my $err;
+                        if (defined $offsetData{$dataTag}) {
+                            if ($exifTool->{FILE_TYPE} eq 'JPEG' and
+                                $dataTag ne 'PreviewImage' and length($offsetData{$dataTag}) > 60000)
+                            {
+                                delete $offsetData{$dataTag};
+                                $err = "$dataTag not written (too large for JPEG segment)";
+                            }
+                        } else {
+                            $err = "$dataTag not found";
+                        }
+                        if ($err) {
+                            $exifTool->Warn($err) if $$newInfo{IsOffset};
+                            delete $set{$newID};    # remove from list of tags we are setting
+                            next;
+                        }
+                    }
+                }
+#
+# write maker notes
+#
+                if ($$newInfo{MakerNotes}) {
+                    # don't write new makernotes if we are deleting this group
+                    if ($exifTool->{DEL_GROUP}->{MakerNotes} and
+                        ($exifTool->{DEL_GROUP}->{MakerNotes} != 2 or $isNew <= 0))
+                    {
+                        if ($isNew <= 0) {
+                            ++$exifTool->{CHANGED};
+                            $verbose and print $out "  Deleting MakerNotes\n";
+                        }
+                        next;
+                    }
+                    my $saveOrder = GetByteOrder();
+                    if ($isNew >= 0 and $set{$newID}) {
+                        # we are writing a whole new maker note block
+                        # --> add fixup information if necessary
+                        my $newValueHash = $exifTool->GetNewValueHash($newInfo, $dirName);
+                        if ($newValueHash and $newValueHash->{MAKER_NOTE_FIXUP}) {
+                            # must clone fixup because we will be shifting it
+                            my $makerFixup = $newValueHash->{MAKER_NOTE_FIXUP}->Clone();
+                            my $valLen = length($valBuff);
+                            $makerFixup->{Start} += $valLen;
+                            push @valFixups, $makerFixup;
+                        }
+                    } else {
+                        # update maker notes if possible
+                        my %subdirInfo = (
+                            Base => $base,
+                            DataPt => $valueDataPt,
+                            DataPos => $valueDataPos,
+                            DataLen => $valueDataLen,
+                            DirStart => $valuePtr,
+                            DirLen => $oldSize,
+                            DirName => 'MakerNotes',
+                            Parent => $dirName,
+                            TagInfo => $newInfo,
+                            RAF => $raf,
+                        );
+                        if ($$newInfo{SubDirectory}) {
+                            my $sub = $$newInfo{SubDirectory};
+                            $subdirInfo{FixBase} = 1 if $$sub{FixBase};
+                            $subdirInfo{FixOffsets} = $$sub{FixOffsets};
+                            $subdirInfo{EntryBased} = $$sub{EntryBased};
+                            $subdirInfo{NoFixBase} = 1 if $$sub{Base};
+                        }
+                        # get the proper tag table for these maker notes
+                        my $subTable;
+                        if ($oldInfo and $oldInfo->{SubDirectory}) {
+                            $subTable = $oldInfo->{SubDirectory}->{TagTable};
+                            $subTable and $subTable = Image::ExifTool::GetTagTable($subTable);
+                        } else {
+                            $exifTool->Warn('Internal problem getting maker notes tag table');
+                        }
+                        $subTable or $subTable = $tagTablePtr;
+                        my $subdir;
+                        # look for IFD-style maker notes
+                        my $loc = Image::ExifTool::MakerNotes::LocateIFD($exifTool,\%subdirInfo);
+                        if (defined $loc) {
+                            # we need fixup data for this subdirectory
+                            $subdirInfo{Fixup} = new Image::ExifTool::Fixup;
+                            # rewrite maker notes
+                            $subdir = $exifTool->WriteDirectory(\%subdirInfo, $subTable);
+                        } elsif ($$subTable{PROCESS_PROC} and
+                                 $$subTable{PROCESS_PROC} eq \&Image::ExifTool::ProcessBinaryData)
+                        {
+                            my $sub = $oldInfo->{SubDirectory};
+                            if (defined $$sub{Start}) {
+                                #### eval Start ($valuePtr)
+                                my $start = eval $$sub{Start};
+                                $loc = $start - $valuePtr;
+                                $subdirInfo{DirStart} = $start;
+                                $subdirInfo{DirLen} -= $loc;
+                            } else {
+                                $loc = 0;
+                            }
+                            # rewrite maker notes
+                            $subdir = $exifTool->WriteDirectory(\%subdirInfo, $subTable);
+                        } else {
+                            $exifTool->Warn('Maker notes could not be parsed',1);
+                        }
+                        if (defined $subdir) {
+                            next unless length $subdir;
+                            my $valLen = length($valBuff);
+                            # restore existing header and substitute the new
+                            # maker notes for the old value
+                            $newValue = substr($oldValue, 0, $loc) . $subdir;
+                            my $makerFixup = $subdirInfo{Fixup};
+                            my $previewInfo = $exifTool->{PREVIEW_INFO};
+                            if ($subdirInfo{Relative}) {
+                                # apply a one-time fixup to $loc since offsets are relative
+                                $makerFixup->{Start} += $loc;
+                                # shift all offsets to be relative to new base
+                                my $baseShift = $valueDataPos + $valuePtr + $base - $subdirInfo{Base};
+                                $makerFixup->{Shift} += $baseShift;
+                                $makerFixup->ApplyFixup(\$newValue);
+                                if ($previewInfo) {
+                                    # remove all but PreviewImage fixup (since others shouldn't change)
+                                    foreach (keys %{$makerFixup->{Pointers}}) {
+                                        /_PreviewImage$/ or delete $makerFixup->{Pointers}->{$_};
+                                    }
+                                    # zero pointer so we can see how it gets shifted later
+                                    $makerFixup->SetMarkerPointers(\$newValue, 'PreviewImage', 0);
+                                    # set the pointer to the start of the EXIF information
+                                    # add preview image fixup to list of value fixups
+                                    $makerFixup->{Start} += $valLen;
+                                    push @valFixups, $makerFixup;
+                                    $previewInfo->{BaseShift} = $baseShift;
+                                    $previewInfo->{Relative} = 1;
+                                }
+                            } elsif (not defined $subdirInfo{Relative}) {
+                                # don't shift anything if relative flag set to zero (Pentax patch)
+                                my $baseShift = $base - $subdirInfo{Base};
+                                $makerFixup->{Start} += $valLen + $loc;
+                                $makerFixup->{Shift} += $baseShift;
+                                push @valFixups, $makerFixup;
+                                if ($previewInfo and not $previewInfo->{NoBaseShift}) {
+                                    $previewInfo->{BaseShift} = $baseShift;
+                                }
+                            }
+                            $newValuePt = \$newValue;   # write new value
+                        }
+                    }
+                    SetByteOrder($saveOrder);
+
+                # process existing subdirectory unless we are overwriting it entirely
+                } elsif ($$newInfo{SubDirectory} and $isNew <= 0 and not $isOverwriting
+                    # don't edit directory if Writable is set to 0
+                    and (not defined $$newInfo{Writable} or $$newInfo{Writable}))
+                {
+
+                    my $subdir = $$newInfo{SubDirectory};
+                    if ($$newInfo{SubIFD}) {
+#
+# rewrite existing sub IFD's
+#
+                        my $subdirName = $newInfo->{Groups}->{1} || $$newInfo{Name};
+                        # must handle sub-IFD's specially since the values
+                        # are actually offsets to subdirectories
+                        unless ($readCount) {   # can't have zero count
+                            return undef if $exifTool->Error("$dirName entry $index has zero count", 1);
+                            next;
+                        }
+                        my $writeCount = 0;
+                        my $i;
+                        $newValue = '';    # reset value because we regenerate it below
+                        for ($i=0; $i<$readCount; ++$i) {
+                            my $off = $i * $formatSize[$readFormat];
+                            my $pt = Image::ExifTool::ReadValue($valueDataPt, $valuePtr + $off,
+                                            $readFormName, 1, $oldSize - $off);
+                            my $subdirStart = $pt - $dataPos;
+                            my $subdirBase = $base;
+                            if ($$subdir{Base}) {
+                                my $start = $subdirStart + $valueDataPos;
+                                #### eval Base ($start)
+                                $subdirBase += eval $$subdir{Base};
+                            }
+                            my %subdirInfo = (
+                                Base => $subdirBase,
+                                DataPt => $dataPt,
+                                DataPos => $dataPos - $subdirBase + $base,
+                                DataLen => $dataLen,
+                                DirStart => $subdirStart,
+                                DirName => $subdirName . ($i ? $i : ''),
+                                Parent => $dirName,
+                                Fixup => new Image::ExifTool::Fixup,
+                                RAF => $raf,
+                            );
+                            # read IFD from file if necessary
+                            if ($subdirStart < 0 or $subdirStart + 2 > $dataLen) {
+                                my ($buff, $buf2, $subSize);
+                                unless ($raf and $raf->Seek($pt + $base, 0) and
+                                        $raf->Read($buff,2) == 2 and
+                                        $subSize = 12 * Get16u(\$buff, 0) and
+                                        $raf->Read($buf2,$subSize+4) >= $subSize)
+                                {
+                                    if (defined $subSize and not $subSize) {
+                                        return undef if $exifTool->Error("$subdirName IFD has zero entries", 1);
+                                        next Entry;
+                                    }
+                                    return undef if $exifTool->Error("Can't read $subdirName data", $inMakerNotes);
+                                    next Entry;
+                                }
+                                $buff .= $buf2;
+                                # change subdirectory information to data we just read
+                                $subdirInfo{DataPt} = \$buff;
+                                $subdirInfo{DirStart} = 0;
+                                $subdirInfo{DataPos} = $pt;
+                                $subdirInfo{DataLen} = length $buff;
+                                # only account for nextIFD pointer if we will use it
+                                $subSize += 4 if length($buff)==$subSize+6 and 
+                                    ($$newInfo{Name} eq 'SubIFD' or $buff =~ /\0{4}$/);
+                                UpdateTiffEnd($exifTool, $pt+$base+2+$subSize);
+                            }
+                            my $subTable = $tagTablePtr;
+                            if ($$subdir{TagTable}) {
+                                $subTable = GetTagTable($$subdir{TagTable});
+                            }
+                            my $subdirData = $exifTool->WriteDirectory(\%subdirInfo, $subTable);
+                            return undef unless defined $subdirData;
+                            next unless length($subdirData);
+                            # temporarily set value to subdirectory index
+                            # (will set to actual offset later when we know what it is)
+                            $newValue .= Set32u(0xfeedf00d);
+                            my ($offset, $where);
+                            if ($readCount > 1) {
+                                $offset = length($valBuff) + $i * 4;
+                                $where = 'valBuff';
+                            } else {
+                                $offset = length($dirBuff) + 8;
+                                $where = 'dirBuff';
+                            }
+                            # add to list of subdirectories we will append later
+                            push @subdirs, {
+                                DataPt => \$subdirData,
+                                Table => $subTable,
+                                Fixup => $subdirInfo{Fixup},
+                                Offset => $offset,
+                                Where => $where,
+                            };
+                            ++$writeCount;  # count number of subdirs written
+                        }
+                        next unless length $newValue;
+                        # must change location of subdir offset if we deleted
+                        # a directory and only one remains
+                        if ($writeCount < $readCount and $writeCount == 1) {
+                            $subdirs[-1]->{Where} = 'dirBuff';
+                            $subdirs[-1]->{Offset} = length($dirBuff) + 8;
+                        }
+                        # set new format to int32u for IFD
+                        $newFormName = $$newInfo{FixFormat} || 'int32u';
+                        $newFormat = $formatNumber{$newFormName};
+                        $newValuePt = \$newValue;
+
+                    } elsif ((not defined $$subdir{Start} or
+                             $$subdir{Start} =~ /\$valuePtr/) and
+                             $$subdir{TagTable})
+                    {
+#
+# rewrite other existing subdirectories ('$valuePtr' type only)
+#
+                        # set subdirectory Start and Base
+                        my $subdirStart = $valuePtr;
+                        if ($$subdir{Start}) {
+                            #### eval Start ($valuePtr)
+                            $subdirStart = eval $$subdir{Start};
+                            # must adjust directory size if start changed
+                            $oldSize -= $subdirStart - $valuePtr;
+                        }
+                        my $subdirBase = $base;
+                        if ($$subdir{Base}) {
+                            my $start = $subdirStart + $valueDataPos;
+                            #### eval Base ($start)
+                            $subdirBase += eval $$subdir{Base};
+                        }
+                        my $subFixup = new Image::ExifTool::Fixup;
+                        my %subdirInfo = (
+                            Base => $subdirBase,
+                            DataPt => $valueDataPt,
+                            DataPos => $valueDataPos - $subdirBase + $base,
+                            DataLen => $valueDataLen,
+                            DirStart => $subdirStart,
+                            DirName => $$subdir{DirName},
+                            DirLen => $oldSize,
+                            Parent => $dirName,
+                            Fixup => $subFixup,
+                            RAF => $raf,
+                        );
+                        my $subTable = GetTagTable($$subdir{TagTable});
+                        $newValue = $exifTool->WriteDirectory(\%subdirInfo, $subTable);
+                        if (defined $newValue) {
+                            my $hdrLen = $subdirStart - $valuePtr;
+                            if ($hdrLen) {
+                                $newValue = substr($$valueDataPt, $valuePtr, $hdrLen) . $newValue;
+                                $subFixup->{Start} += $hdrLen;
+                            }
+                            $newValuePt = \$newValue;
+                        }
+                        unless (defined $$newValuePt) {
+                            $exifTool->Error("Error writing $dirName:$$newInfo{Name}");
+                            return undef;
+                        }
+                        next unless length $$newValuePt;
+                        if ($subFixup->{Pointers} and $subdirInfo{Base} == $base) {
+                            $subFixup->{Start} += length $valBuff;
+                            push @valFixups, $subFixup;
+                        } else {
+                            # apply fixup in case we added a header ($hdrLen above)
+                            $subFixup->ApplyFixup(\$newValue);
+                        }
+                    }
+
+                } elsif ($$newInfo{OffsetPair}) {
+#
+# keep track of offsets
+#
+                    my $dataTag = $$newInfo{DataTag} || '';
+                    if ($dataTag eq 'CanonVRD') {
+                        # must decide now if we will write CanonVRD information
+                        my $hasVRD;
+                        if ($exifTool->{NEW_VALUE}->{$Image::ExifTool::Extra{CanonVRD}}) {
+                            # adding or deleting as a block
+                            $hasVRD = $exifTool->GetNewValues('CanonVRD') ? 1 : 0;
+                        } elsif ($exifTool->{DEL_GROUP}->{CanonVRD} or
+                                 $exifTool->{DEL_GROUP}->{Trailer})
+                        {
+                            $hasVRD = 0;    # deleting as a group
+                        } else {
+                            $hasVRD = ($$newValuePt ne "\0\0\0\0");
+                        }
+                        if ($hasVRD) {
+                            # add a fixup, and set this offset later
+                            $dirFixup->AddFixup(length($dirBuff) + 8, $dataTag);
+                        } else {
+                            # there is (or will soon be) no VRD information, so set pointer to zero
+                            $newValue = "\0" x length($$newValuePt);
+                            $newValuePt = \$newValue;
+                        }
+                    } else {
+                        my $offsetInfo = $offsetInfo[$ifd];
+                        # save original values (for updating TIFF_END later)
+                        my @vals;
+                        if ($isNew <= 0) {
+                            @vals = ReadValue(\$oldValue, 0, $readFormName, $readCount, $oldSize);
+                        }
+                        # only support int32 pointers (for now)
+                        if ($formatSize[$newFormat] != 4 and $$newInfo{IsOffset}) {
+                            die "Internal error (Offset not int32)" if $isNew > 0;
+                            die "Wrong count!" if $newCount != $readCount;
+                            # change to int32
+                            $newFormName = 'int32u';
+                            $newFormat = $formatNumber{$newFormName};
+                            $newValue = WriteValue(join(' ',@vals), $newFormName, $newCount);
+                            die "Internal error writing offsets\n" unless defined $newValue;
+                        }
+                        $offsetInfo or $offsetInfo = $offsetInfo[$ifd] = { };
+                        # save location of valuePtr in new directory
+                        # (notice we add 10 instead of 8 for valuePtr because
+                        # we will put a 2-byte count at start of directory later)
+                        my $ptr = $newStart + length($dirBuff) + 10;
+                        $newCount or $newCount = 1; # make sure count is set for offsetInfo
+                        # save value pointer and value count for each tag
+                        $offsetInfo->{$newID} = [$newInfo, $ptr, $newCount, \@vals, $newFormat];
+                    }
+
+                } elsif ($$newInfo{DataMember}) {
+
+                    # save any necessary data members (CameraMake, CameraModel)
+                    $exifTool->{$$newInfo{DataMember}} = $$newValuePt;
+                }
+            }
+#
+# write out the directory entry
+#
+            my $newSize = length($$newValuePt);
+            my $fsize = $formatSize[$newFormat];
+            my $offsetVal;
+            $newCount = int(($newSize + $fsize - 1) / $fsize);  # set proper count
+            if ($newSize > 4) {
+                # zero-pad to an even number of bytes (required by EXIF standard)
+                # and make sure we are a multiple of the format size
+                while ($newSize & 0x01 or $newSize < $newCount * $fsize) {
+                    $$newValuePt .= "\0";
+                    ++$newSize;
+                }
+                my $entryBased;
+                if ($$dirInfo{EntryBased} or ($newInfo and $$newInfo{EntryBased})) {
+                    $entryBased = 1;
+                    $offsetVal = Set32u(length($valBuff) - length($dirBuff));
+                } else {
+                    $offsetVal = Set32u(length $valBuff);
+                }
+                my $dataTag;
+                if ($newInfo and $$newInfo{DataTag}) {
+                    $dataTag = $$newInfo{DataTag};
+                    if ($dataTag eq 'PreviewImage' and $exifTool->{FILE_TYPE} eq 'JPEG') {
+                        # hold onto the PreviewImage until we can determine if it fits
+                        $exifTool->{PREVIEW_INFO} or $exifTool->{PREVIEW_INFO} = { };
+                        $exifTool->{PREVIEW_INFO}->{Data} = $$newValuePt;
+                        if ($$newInfo{Name} eq 'PreviewImage') {
+                            $exifTool->{PREVIEW_INFO}->{IsValue} = 1;
+                        }
+                        if ($$newInfo{IsOffset} and $$newInfo{IsOffset} eq '2') {
+                            $exifTool->{PREVIEW_INFO}->{NoBaseShift} = 1;
+                        }
+                        $$newValuePt = '';
+                    }
+                }
+                $valBuff .= $$newValuePt;       # add value data to buffer
+                # must save a fixup pointer for every pointer in the directory
+                if ($entryBased) {
+                    $entryBasedFixup or $entryBasedFixup = new Image::ExifTool::Fixup;
+                    $entryBasedFixup->AddFixup(length($dirBuff) + 8, $dataTag);
+                } else {
+                    $dirFixup->AddFixup(length($dirBuff) + 8, $dataTag);
+                }
+            } else {
+                $offsetVal = $$newValuePt;      # save value in offset if 4 bytes or less
+                # must pad value with zeros if less than 4 bytes
+                $newSize < 4 and $offsetVal .= "\0" x (4 - $newSize);
+            }
+            # write the directory entry
+            $dirBuff .= Set16u($newID) . Set16u($newFormat) .
+                        Set32u($newCount) . $offsetVal;
+            # update flag to keep track of mandatory tags
+            while (defined $allMandatory) {
+                if (defined $$mandatory{$newID}) {
+                    # values must correspond to mandatory values
+                    my $mandVal = WriteValue($$mandatory{$newID}, $newFormName, $newCount);
+                    if (defined $mandVal and $mandVal eq $$newValuePt) {
+                        ++$allMandatory;        # count mandatory tags
+                        last;
+                    }
+                }
+                undef $deleteAll;
+                undef $allMandatory;
+            }
+        }
+#..............................................................................
+# write directory counts and nextIFD pointer and add value data to end of IFD
+#
+        # calculate number of entries in new directory
+        my $newEntries = length($dirBuff) / 12;
+        # delete entire directory if only mandatory tags remain
+        if ($newEntries < $numEntries and $allMandatory) {
+            $newEntries = 0;
+            $dirBuff = '';
+            $valBuff = '';
+            undef $dirFixup;    # no fixups in this directory
+            ++$deleteAll if defined $deleteAll;
+            $verbose > 1 and print $out "    - $allMandatory mandatory tag(s)\n";
+        }
+        if ($ifd and not $newEntries) {
+            $verbose and print $out "  Deleting IFD1\n";
+            last;   # don't write IFD1 if empty
+        }
+        # apply one-time fixup for entry-based offsets
+        if ($entryBasedFixup) {
+            $entryBasedFixup->{Shift} = length($dirBuff) + 4;
+            $entryBasedFixup->ApplyFixup(\$dirBuff);
+            undef $entryBasedFixup;
+        }
+        # add directory entry count to start of IFD and next IFD pointer to end
+        # (temporarily set next IFD pointer to zero)
+        $newData .= Set16u($newEntries) . $dirBuff . Set32u(0);
+        # get position of value data in newData
+        my $valPos = length($newData);
+        # go back now and set next IFD pointer if this isn't the first IFD
+        if ($nextIfdPos) {
+            # set offset to next IFD
+            Set32u($newStart, \$newData, $nextIfdPos);
+            $fixup->AddFixup($nextIfdPos,'NextIFD');    # add fixup for this offset in newData
+        }
+        # remember position of 'next IFD' pointer so we can set it next time around
+        $nextIfdPos = $valPos - 4;
+        # add value data after IFD
+        $newData .= $valBuff;
+#
+# add any subdirectories, adding fixup information
+#
+        if (@subdirs) {
+            my $subdir;
+            foreach $subdir (@subdirs) {
+                my $pos = length($newData);    # position of subdirectory in data
+                my $subdirFixup = $subdir->{Fixup};
+                $subdirFixup->{Start} += $pos;
+                $fixup->AddFixup($subdirFixup);
+                $newData .= ${$subdir->{DataPt}};   # add subdirectory to our data
+                undef ${$subdir->{DataPt}};         # free memory now
+                # set the pointer
+                my $offset = $subdir->{Offset};
+                # if offset is in valBuff, it was added to the end of dirBuff
+                # (plus 4 bytes for nextIFD pointer)
+                $offset += length($dirBuff) + 4 if $subdir->{Where} eq 'valBuff';
+                $offset += $newStart + 2;           # get offset in newData
+                # check to be sure we got the right offset
+                unless (Get32u(\$newData, $offset) == 0xfeedf00d) {
+                    $exifTool->Error("Internal error while rewriting $dirName");
+                    return undef;
+                }
+                # set the offset to the subdirectory data
+                Set32u($pos, \$newData, $offset);
+                $fixup->AddFixup($offset);  # add fixup for this offset in newData
+            }
+        }
+        # add fixup for all offsets in directory according to value data position
+        # (which is at the end of this directory)
+        if ($dirFixup) {
+            $dirFixup->{Start} = $newStart + 2;
+            $dirFixup->{Shift} = $valPos - $dirFixup->{Start};
+            $fixup->AddFixup($dirFixup);
+        }
+        # add valueData fixups, adjusting for position of value data
+        my $valFixup;
+        foreach $valFixup (@valFixups) {
+            $valFixup->{Start} += $valPos;
+            $fixup->AddFixup($valFixup);
+        }
+        # stop if no next IFD pointer
+        last unless $$dirInfo{Multi};  # stop unless scanning for multiple IFD's
+        my $offset;
+        if ($dirStart + $len + 4 <= $dataLen) {
+            $offset = Get32u($dataPt, $dirStart + $len);
+        } else {
+            $offset = 0;
+        }
+        if ($offset) {
+            # continue with next IFD
+            $dirStart = $offset - $dataPos;
+        } else {
+            # create IFD1 if necessary
+            last unless $dirName eq 'IFD0' and $exifTool->{ADD_DIRS}->{'IFD1'};
+            $verbose and print $out "  Creating IFD1\n";
+            my $ifd1 = "\0" x 2;  # empty IFD1 data (zero entry count)
+            $dataPt = \$ifd1;
+            $dirStart = 0;
+            $dirLen = $dataLen = 2;
+        }
+        # increment IFD name
+        $dirName =~ s/(\d+)$//;
+        $dirName .= ($1 || 0) + 1;
+        $exifTool->{DIR_NAME} = $dirName;
+        next unless $offset;
+
+        # guard against writing the same directory twice
+        my $addr = $offset + $base;
+        if ($exifTool->{PROCESSED}->{$addr}) {
+            $exifTool->Error("$dirName pointer references previous $exifTool->{PROCESSED}->{$addr} directory", 1);
+            last;
+        }
+        $exifTool->{PROCESSED}->{$addr} = $dirName;
+
+        if ($dirName eq 'SubIFD1' and not ValidateIFD($dirInfo, $dirStart)) {
+            if ($exifTool->{TIFF_TYPE} eq 'TIFF') {
+                $exifTool->Error('Ignored bad IFD linked from SubIFD', 1);
+            } elsif ($verbose) {
+                $exifTool->Warn('Ignored bad IFD linked from SubIFD');
+            }
+            last;   # don't write bad IFD
+        }
+        if ($exifTool->{DEL_GROUP}->{$dirName}) {
+            $verbose and print $out "  Deleting $dirName\n";
+            $raf and $exifTool->Error("Deleting $dirName also deletes subsequent" .
+                                      " IFD's and possibly image data", 1);
+            ++$exifTool->{CHANGED};
+            if ($exifTool->{DEL_GROUP}->{$dirName} == 2 and
+                $exifTool->{ADD_DIRS}->{$dirName})
+            {
+                my $emptyIFD = "\0" x 2;    # start with empty IFD
+                $dataPt = \$emptyIFD;
+                $dirStart = 0;
+                $dirLen = $dataLen = 2;
+            } else {
+                last;   # don't write this IFD (or any subsequent IFD)
+            }
+        } else {
+            $verbose and print $out "  Rewriting $dirName\n";
+        }
+    }
+#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+    # do our fixups now so we can more easily calculate offsets below
+    $fixup->ApplyFixup(\$newData);
+#
+# copy over image data for IFD's, starting with the last IFD first
+#
+    my @imageData; # image data blocks if requested
+    my $blockSize = 0;  # total size of blocks to copy later
+    if (@offsetInfo) {
+        my @writeLater;  # write image data last
+        for ($ifd=$#offsetInfo; $ifd>=-1; --$ifd) {
+            # build list of offsets to process
+            my @offsetList;
+            if ($ifd >= 0) {
+                my $offsetInfo = $offsetInfo[$ifd] or next;
+                my $tagID;
+                # loop through all tags in reverse order so we save thumbnail
+                # data before main image data if both exist in the same IFD
+                foreach $tagID (reverse sort keys %$offsetInfo) {
+                    my $tagInfo = $offsetInfo->{$tagID}->[0];
+                    next unless $$tagInfo{IsOffset}; # handle byte counts with offsets
+                    my $sizeInfo = $offsetInfo->{$$tagInfo{OffsetPair}};
+                    $sizeInfo or $exifTool->Error("No size tag for $dirName:$$tagInfo{Name}"), next;
+                    my $dataTag = $$tagInfo{DataTag};
+                    # write TIFF image data (strips or tiles) later if requested
+                    if ($raf and defined $_[1]->{ImageData} and
+                        ($tagID == 0x111 or $tagID == 0x144) and
+                        (not defined $dataTag or not defined $offsetData{$dataTag}))
+                    {
+                        push @writeLater, [ $offsetInfo->{$tagID}, $sizeInfo ];
+                    } else {
+                        push @offsetList, [ $offsetInfo->{$tagID}, $sizeInfo ];
+                    }
+                }
+            } else {
+                last unless @writeLater;
+                @offsetList = @writeLater;
+            }
+            my $offsetPair;
+            foreach $offsetPair (@offsetList) {
+                my ($tagInfo, $offsets, $count, $oldOffset) = @{$$offsetPair[0]};
+                my ($cntInfo, $byteCounts, $count2, $oldSize, $format) = @{$$offsetPair[1]};
+                # must be the same number of offset and byte count values
+                unless ($count == $count2) {
+                    $exifTool->Error("Offset/byteCounts disagree on count for $$tagInfo{Name}");
+                    return undef;
+                }
+                my $formatStr = $formatName[$format];
+                # follow pointer to value data if necessary
+                $count > 1 and $offsets = Get32u(\$newData, $offsets);
+                my $n = $count * $formatSize[$format];
+                $n > 4 and $byteCounts = Get32u(\$newData, $byteCounts);
+                if ($byteCounts < 0 or $byteCounts + $n > length($newData)) {
+                    $exifTool->Error("Error reading $$tagInfo{Name} byte counts");
+                    return undef;
+                }
+                # get offset base and data pos (abnormal for some preview images)
+                my ($dbase, $dpos);
+                if ($$tagInfo{IsOffset} eq '2') {
+                    $dbase = $firstBase;
+                    $dpos = $dataPos + $base - $firstBase;
+                } else {
+                    $dbase = $base;
+                    $dpos = $dataPos;
+                }
+                # transfer the data referenced by all offsets of this tag
+                for ($n=0; $n<$count; ++$n) {
+                    my $oldEnd;
+                    if (@$oldOffset and @$oldSize) {
+                        # calculate end offset of this block
+                        $oldEnd = $$oldOffset[$n] + $$oldSize[$n];
+                        # update TIFF_END as if we read this data from file
+                        UpdateTiffEnd($exifTool, $oldEnd + $dbase);
+                    }
+                    my $offsetPos = $offsets + $n * 4;
+                    my $byteCountPos = $byteCounts + $n * $formatSize[$format];
+                    my $size = ReadValue(\$newData, $byteCountPos, $formatStr, 1, 4);
+                    my $offset = Get32u(\$newData, $offsetPos) - $dpos;
+                    my $buff;
+                    # look for 'feed' code to use our new data
+                    if ($size == 0xfeedfeed) {
+                        my $dataTag = $$tagInfo{DataTag};
+                        unless (defined $dataTag) {
+                            $exifTool->Error("No DataTag defined for $$tagInfo{Name}");
+                            return undef;
+                        }
+                        unless (defined $offsetData{$dataTag}) {
+                            $exifTool->Error("Internal error (no $dataTag)");
+                            return undef;
+                        }
+                        if ($count > 1) {
+                            $exifTool->Error("Can't modify $$tagInfo{Name} with count $count");
+                            return undef;
+                        }
+                        $buff = $offsetData{$dataTag};
+                        if ($formatSize[$format] != 4) {
+                            $exifTool->Error("$$cntInfo{Name} is not int32");
+                            return undef;
+                        }
+                        # set the data size
+                        $size = length($buff);
+                        Set32u($size, \$newData, $byteCountPos);
+                    } elsif ($ifd < 0) {
+                        # pad if necessary (but don't pad contiguous image blocks)
+                        my $pad = 0;
+                        ++$pad if $size & 0x01 and ($n+1 >= $count or not $oldEnd or
+                                  $oldEnd != $$oldOffset[$n+1]);
+                        # preserve original image padding if specified
+                        if ($_[1]->{PreserveImagePadding} and $n+1 < $count and
+                            $oldEnd and $$oldOffset[$n+1] > $oldEnd)
+                        {
+                            $pad = $$oldOffset[$n+1] - $oldEnd;
+                        }
+                        # copy data later
+                        push @imageData, [$offset+$dbase+$dpos, $size, $pad];
+                        $size += $pad; # account for pad byte if necessary
+                        # return ImageData list
+                        $_[1]->{ImageData} = \@imageData;
+                    } elsif ($offset >= 0 and $offset+$size <= $dataLen) {
+                        # take data from old dir data buffer
+                        $buff = substr($$dataPt, $offset, $size);
+                    } elsif ($raf and $raf->Seek($offset+$dbase+$dpos,0) and
+                             $raf->Read($buff,$size) == $size)
+                    {
+                        # data read OK
+                    } elsif ($$tagInfo{Name} eq 'ThumbnailOffset' and $offset>=0 and $offset<$dataLen) {
+                        # Grrr.  The Canon 350D writes the thumbnail with an incorrect byte count
+                        my $diff = $offset + $size - $dataLen;
+                        $exifTool->Warn("ThumbnailImage runs outside EXIF data by $diff bytes (truncated)",1);
+                        # set the size to the available data
+                        $size -= $diff;
+                        unless (WriteValue($size, $formatStr, 1, \$newData, $byteCountPos)) {
+                            warn 'Internal error writing thumbnail size';
+                        }
+                        # get the truncated image
+                        $buff = substr($$dataPt, $offset, $size);
+                    } elsif ($$tagInfo{Name} eq 'PreviewImageStart' and $exifTool->{FILE_TYPE} eq 'JPEG') {
+                        # try to load the preview image using the specified offset
+                        undef $buff;
+                        my $r = $exifTool->{RAF};
+                        if ($r and not $raf) {
+                            my $tell = $r->Tell();
+                            # read and validate
+                            undef $buff unless $r->Seek($offset+$base+$dataPos,0) and
+                                               $r->Read($buff,$size) == $size and
+                                               $buff =~ /^.\xd8\xff[\xc4\xdb\xe0-\xef]/;
+                            $r->Seek($tell, 0) or $exifTool->Error('Seek error'), return undef;
+                        }
+                        $buff = 'LOAD' unless defined $buff;    # flag indicating we must load PreviewImage
+                    } else {
+                        my $dataName = $$tagInfo{DataTag} || $$tagInfo{Name};
+                        return undef if $exifTool->Error("Error reading $dataName data in $dirName", $inMakerNotes);
+                        $buff = '';
+                    }
+                    if ($$tagInfo{Name} eq 'PreviewImageStart' and $exifTool->{FILE_TYPE} eq 'JPEG') {
+                        # hold onto the PreviewImage until we can determine if it fits
+                        $exifTool->{PREVIEW_INFO} or $exifTool->{PREVIEW_INFO} = { };
+                        $exifTool->{PREVIEW_INFO}->{Data} = $buff;
+                        if ($$tagInfo{IsOffset} and $$tagInfo{IsOffset} eq '2') {
+                            $exifTool->{PREVIEW_INFO}->{NoBaseShift} = 1;
+                        }
+                        $buff = '';
+                    }
+                    # update offset accordingly and add to end of new data
+                    Set32u(length($newData)+$blockSize, \$newData, $offsetPos);
+                    # add a pointer to fix up this offset value (marked with DataTag name)
+                    $fixup->AddFixup($offsetPos, $$tagInfo{DataTag});
+                    if ($ifd >= 0) {
+                        $buff .= "\0" if $size & 0x01;  # must be even size
+                        $newData .= $buff;      # add this strip to the data
+                    } else {
+                        $blockSize += $size;    # keep track of total size
+                    }
+                }
+            }
+        }
+    }
+#
+# apply final shift to new data position if this is the top level IFD
+#
+    unless ($$dirInfo{Fixup}) {
+        my $newDataPos = $$dirInfo{NewDataPos} || 0;
+        # adjust CanonVRD offset to point to end of regular TIFF if necessary
+        # (NOTE: This will be incorrect if multiple trailers exist,
+        #  but it is unlikely that it could ever be correct in this case anyway)
+        $fixup->SetMarkerPointers(\$newData, 'CanonVRD', length($newData) + $blockSize);
+        if ($newDataPos) {
+            $fixup->{Shift} += $newDataPos;
+            $fixup->ApplyFixup(\$newData);
+        }
+        # save fixup for PreviewImage in JPEG file if necessary
+        my $previewInfo = $exifTool->{PREVIEW_INFO};
+        if ($previewInfo) {
+            my $pt = \$previewInfo->{Data}; # image data or 'LOAD' flag
+            # now that we know the size of the EXIF data, first test to see if our new image fits
+            # inside the EXIF segment (remember about the TIFF and EXIF headers: 8+6 bytes)
+            if (($$pt ne 'LOAD' and length($$pt) + length($newData) + 14 <= 0xfffd) or
+                $previewInfo->{IsShort}) # must fit in this segment if using short pointers
+            {
+                # It fits! (or must exist in EXIF segment), so fixup the
+                # PreviewImage pointers and stuff the preview image in here
+                my $newPos = length($newData) + ($newDataPos || 0);
+                $newPos += ($previewInfo->{BaseShift} || 0);
+                if ($previewInfo->{Relative}) {
+                    # calculate our base by looking at how far the pointer got shifted
+                    $newPos -= $fixup->GetMarkerPointers(\$newData, 'PreviewImage');
+                }
+                $fixup->SetMarkerPointers(\$newData, 'PreviewImage', $newPos);
+                $newData .= $$pt;
+                delete $exifTool->{PREVIEW_INFO};   # done with our preview data
+                $exifTool->{DEL_PREVIEW} = 1;       # set flag to delete old preview
+            } else {
+                # Doesn't fit, or we still don't know, so save fixup information
+                # and put the preview at the end of the file
+                $previewInfo->{Fixup} or $previewInfo->{Fixup} = new Image::ExifTool::Fixup;
+                $previewInfo->{Fixup}->AddFixup($fixup);
+            }
+        } else {
+            # delete both IFD0 and IFD1 if only mandatory tags remain
+            $newData = '' if defined $newData and $deleteAll;
+        }
+        # save location of last IFD for use in Canon RAW header
+        if ($newDataPos == 16) {
+            my @ifdPos = $fixup->GetMarkerPointers(\$newData,'NextIFD');
+            $_[1]->{LastIFD} = pop @ifdPos;
+        }
+    }
+    # return empty string if no entries in directory
+    # (could be up to 10 bytes and still be empty)
+    $newData = '' if defined $newData and length($newData) < 12;
+
+    return $newData;    # return our directory data
+}
+
+1; # end
+
+__END__
+
+=head1 NAME
+
+Image::ExifTool::WriteExif.pl - Write EXIF meta information
+
+=head1 SYNOPSIS
+
+This file is autoloaded by Image::ExifTool::Exif.
+
+=head1 DESCRIPTION
+
+This file contains routines to write EXIF metadata.
+
+=head1 AUTHOR
+
+Copyright 2003-2007, Phil Harvey (phil at owl.phy.queensu.ca)
+
+This library is free software; you can redistribute it and/or modify it
+under the same terms as Perl itself.
+
+=head1 SEE ALSO
+
+L<Image::ExifTool::Exif(3pm)|Image::ExifTool::Exif>,
+L<Image::ExifTool(3pm)|Image::ExifTool>
+
+=cut
Index: /gsdl/trunk/perllib/cpan/Image/ExifTool/WriteIPTC.pl
===================================================================
--- /gsdl/trunk/perllib/cpan/Image/ExifTool/WriteIPTC.pl	(revision 16842)
+++ /gsdl/trunk/perllib/cpan/Image/ExifTool/WriteIPTC.pl	(revision 16842)
@@ -0,0 +1,564 @@
+#------------------------------------------------------------------------------
+# File:         WriteIPTC.pl
+#
+# Description:  Write IPTC meta information
+#
+# Revisions:    12/15/2004 - P. Harvey Created
+#------------------------------------------------------------------------------
+
+package Image::ExifTool::IPTC;
+
+use strict;
+
+# mandatory IPTC tags for each record
+my %mandatory = (
+    1 => {
+        0  => 4,        # EnvelopeRecordVersion
+    },
+    2 => {
+        0  => 4,        # ApplicationRecordVersion
+    },
+    3 => {
+        0  => 4,        # NewsPhotoVersion
+    },
+);
+
+# manufacturer strings for IPTCPictureNumber
+my %manufacturer = (
+    1 => 'Associated Press, USA',
+    2 => 'Eastman Kodak Co, USA',
+    3 => 'Hasselblad Electronic Imaging, Sweden',
+    4 => 'Tecnavia SA, Switzerland',
+    5 => 'Nikon Corporation, Japan',
+    6 => 'Coatsworth Communications Inc, Canada',
+    7 => 'Agence France Presse, France',
+    8 => 'T/One Inc, USA',
+    9 => 'Associated Newspapers, UK',
+    10 => 'Reuters London',
+    11 => 'Sandia Imaging Systems Inc, USA',
+    12 => 'Visualize, Spain',
+);
+
+my %iptcCharsetInv = ( 'UTF8' => "\x1b%G" );
+
+# ISO 2022 Character Coding Notes
+# -------------------------------
+# Character set designation: (0x1b I F, or 0x1b I I F)
+# Initial character 0x1b (ESC)
+# Intermediate character I:
+#   0x28 ('(') - G0, 94 chars
+#   0x29 (')') - G1, 94 chars
+#   0x2a ('*') - G2, 94 chars
+#   0x2b ('+') - G3, 94 chars
+#   0x2c (',') - G1, 96 chars
+#   0x2d ('-') - G2, 96 chars
+#   0x2e ('.') - G3, 96 chars
+#   0x24 I ('$I') - multiple byte graphic sets (I from above)
+#   I 0x20 ('I ') - dynamically redefinable character sets
+# Final character:
+#   0x30 - 0x3f = private character set
+#   0x40 - 0x7f = standardized character set
+# Character set invocation:
+#   G0 : SI = 0x15
+#   G1 : SO = 0x14,             LS1R = 0x1b 0x7e ('~')
+#   G2 : LS2 = 0x1b 0x6e ('n'), LS2R = 0x1b 0x7d ('}')
+#   G3 : LS3 = 0x1b 0x6f ('o'), LS3R = 0x1b 0x7c ('|')
+#   (the locking shift "R" codes shift into 0x80-0xff space)
+# Single character invocation:
+#   G2 : SS2 = 0x1b 0x8e (or 0x4e in 7-bit)
+#   G3 : SS3 = 0x1b 0x8f (or 0x4f in 7-bit)
+# Control chars (designated and invoked)
+#   C0 : 0x1b 0x21 F (0x21 = '!')
+#   C1 : 0x1b 0x22 F (0x22 = '"')
+# Complete codes (control+graphics, designated and involked)
+#   0x1b 0x25 F   (0x25 = '%')
+#   0x1b 0x25 I F
+#   0x1b 0x25 0x47 ("\x1b%G") - UTF-8
+#   0x1b 0x25 0x40 ("\x1b%@") - return to ISO 2022
+# -------------------------------
+
+#------------------------------------------------------------------------------
+# Inverse print conversion for CodedCharacterSet
+# Inputs: 0) value
+sub PrintInvCodedCharset($)
+{
+    my $val = shift;
+    my $code = $iptcCharsetInv{uc($val)};
+    unless ($code) {
+        if (($code = $val) =~ s/ESC /\x1b/g) {  # translate ESC chars
+            $code =~ s/, \x1b/\x1b/g;   # remove comma separators
+            $code =~ tr/ //d;           # remove spaces
+        } else {
+            warn "Bad syntax (use 'UTF8' or 'ESC X Y[, ...]')\n";
+        }
+    }
+    return $code;
+}
+
+#------------------------------------------------------------------------------
+# validate raw values for writing
+# Inputs: 0) ExifTool object reference, 1) tagInfo hash reference,
+#         2) raw value reference
+# Returns: error string or undef (and possibly changes value) on success
+sub CheckIPTC($$$)
+{
+    my ($exifTool, $tagInfo, $valPtr) = @_;
+    my $format = $$tagInfo{Format} || $tagInfo->{Table}->{FORMAT} || '';
+    if ($format =~ /^int(\d+)/) {
+        my $bytes = int(($1 || 0) / 8);
+        if ($bytes ne 1 and $bytes ne 2 and $bytes ne 4) {
+            return "Can't write $bytes-byte integer";
+        }
+        my $val = $$valPtr;
+        unless (Image::ExifTool::IsInt($val)) {
+            return 'Not an integer' unless Image::ExifTool::IsHex($val);
+            $val = $$valPtr = hex($val);
+        }
+        my $n;
+        for ($n=0; $n<$bytes; ++$n) { $val >>= 8; }
+        return "Value too large for $bytes-byte format" if $val;
+    } elsif ($format =~ /^(string|digits)\[?(\d+),?(\d*)\]?$/) {
+        my ($fmt, $minlen, $maxlen) = ($1, $2, $3);
+        my $len = length $$valPtr;
+        if ($fmt eq 'digits') {
+            return 'Non-numeric characters in value' unless $$valPtr =~ /^\d+$/;
+            # left pad with zeros if necessary
+            $$valPtr = ('0' x ($len - $minlen)) . $$valPtr if $len < $minlen;
+        }
+        if ($minlen) {
+            $maxlen or $maxlen = $minlen;
+            return "String too short (minlen is $minlen)" if $len < $minlen;
+            return "String too long (maxlen is $maxlen)" if $len > $maxlen;
+        }
+    } else {
+        return "Bad IPTC Format ($format)";
+    }
+    return undef;
+}
+
+#------------------------------------------------------------------------------
+# format IPTC data for writing
+# Inputs: 0) ExifTool object ref, 1) tagInfo pointer,
+#         2) value reference (changed if necessary),
+#         3) reference to character set for translation (changed if necessary)
+#         4) record number, 5) flag set to read value (instead of write)
+sub FormatIPTC($$$$$;$)
+{
+    my ($exifTool, $tagInfo, $valPtr, $xlatPtr, $rec, $read) = @_;
+    return unless $$tagInfo{Format};
+    if ($$tagInfo{Format} =~ /^int(\d+)/) {
+        if ($read) {
+            my $len = length($$valPtr);
+            if ($len <= 8) {    # limit integer conversion to 8 bytes long
+                my $val = 0;
+                my $i;
+                for ($i=0; $i<$len; ++$i) {
+                    $val = $val * 256 + ord(substr($$valPtr, $i, 1));
+                }
+                $$valPtr = $val;
+            }
+        } else {
+            my $len = int(($1 || 0) / 8);
+            if ($len == 1) {        # 1 byte
+                $$valPtr = chr($$valPtr);
+            } elsif ($len == 2) {   # 2-byte integer
+                $$valPtr = pack('n', $$valPtr);
+            } else {                # 4-byte integer
+                $$valPtr = pack('N', $$valPtr);
+            }
+        }
+    } elsif ($$tagInfo{Format} =~ /^string/) {
+        if ($rec == 1) {
+            if ($$tagInfo{Name} eq 'CodedCharacterSet') {
+                $$xlatPtr = HandleCodedCharset($exifTool, $$valPtr);
+            }
+        } elsif ($$xlatPtr and $rec < 7 and $$valPtr =~ /[\x80-\xff]/) {
+            TranslateCodedString($exifTool, $valPtr, $xlatPtr, $read);
+        }
+    }
+}
+
+#------------------------------------------------------------------------------
+# generate IPTC-format date
+# Inputs: 0) EXIF-format date string (YYYY:MM:DD) or date/time string
+# Returns: IPTC-format date string (YYYYMMDD), or undef on error
+sub IptcDate($)
+{
+    my $val = shift;
+    $val =~ s/.*(\d{4}):?(\d{2}):?(\d{2}).*/$1$2$3/ or undef $val;
+    return $val;
+}
+
+#------------------------------------------------------------------------------
+# generate IPTC-format time
+# Inputs: 0) EXIF-format time string (HH:MM:SS[+/-HH:MM]) or date/time string
+# Returns: IPTC-format time string (HHMMSS+HHMM), or undef on error
+sub IptcTime($)
+{
+    my $val = shift;
+    if ($val =~ /\s*\b(\d{1,2}):?(\d{1,2}):?(\d{1,2})(\S*)\s*$/) {
+        $val = sprintf("%.2d%.2d%.2d",$1,$2,$3);
+        my $tz = $4;
+        if ($tz =~ /([+-]\d{1,2}):?(\d{1,2})/) {
+            $val .= sprintf("%+.2d%.2d",$1,$2);
+        } else {
+            $val .= '+0000';    # don't know the time zone
+        }
+    } else {
+        undef $val;     # time format error
+    }
+    return $val;
+}
+
+#------------------------------------------------------------------------------
+# Convert picture number
+# Inputs: 0) value
+# Returns: Converted value
+sub ConvertPictureNumber($)
+{
+    my $val = shift;
+    if ($val eq "\0" x 16) {
+        $val = 'Unknown';
+    } elsif (length $val >= 16) {
+        my @vals = unpack('nNA8n', $val);
+        $val = $vals[0];
+        my $manu = $manufacturer{$val};
+        $val .= " ($manu)" if $manu;
+        $val .= ', equip ' . $vals[1];
+        $vals[2] =~ s/(\d{4})(\d{2})(\d{2})/$1:$2:$3/;
+        $val .= ", $vals[2], no. $vals[3]";
+    } else {
+        $val = '<format error>'
+    }
+    return $val;
+}
+
+#------------------------------------------------------------------------------
+# Inverse picture number conversion
+# Inputs: 0) value
+# Returns: Converted value (or undef on error)
+sub InvConvertPictureNumber($)
+{
+    my $val = shift;
+    $val =~ s/\(.*\)//g;    # remove manufacturer description
+    $val =~ tr/://d;        # remove date separators
+    $val =~ tr/0-9/ /c;     # turn remaining non-numbers to spaces
+    my @vals = split ' ', $val;
+    if (@vals >= 4) {
+        $val = pack('nNA8n', @vals);
+    } elsif ($val =~ /unknown/i) {
+        $val = "\0" x 16;
+    } else {
+        undef $val;
+    }
+    return $val;
+}
+
+#------------------------------------------------------------------------------
+# Write IPTC data record
+# Inputs: 0) ExifTool object reference, 1) source dirInfo reference,
+#         2) tag table reference
+# Returns: IPTC data block (may be empty if no IPTC data)
+# Notes: Increments ExifTool CHANGED flag for each tag changed
+sub WriteIPTC($$$)
+{
+    my ($exifTool, $dirInfo, $tagTablePtr) = @_;
+    $exifTool or return 1;    # allow dummy access to autoload this package
+    my $dataPt = $$dirInfo{DataPt};
+    unless ($dataPt) {
+        my $emptyData = '';
+        $dataPt = \$emptyData;
+    }
+    my $start = $$dirInfo{DirStart} || 0;
+    my $dirLen = $$dirInfo{DirLen};
+    my $verbose = $exifTool->Options('Verbose');
+    my $out = $exifTool->Options('TextOut');
+    my ($tagInfo, %iptcInfo, $tag);
+
+    # begin by assuming IPTC is coded in Latin unless otherwise specified
+    my $xlat = $exifTool->Options('Charset');
+    undef $xlat if $xlat eq 'Latin';
+
+    # make sure our dataLen is defined (note: allow zero length directory)
+    unless (defined $dirLen) {
+        my $dataLen = $$dirInfo{DataLen};
+        $dataLen = length($$dataPt) unless defined $dataLen;
+        $dirLen = $dataLen - $start;
+    }
+    # quick check for improperly byte-swapped IPTC
+    if ($dirLen >= 4 and substr($$dataPt, $start, 1) ne "\x1c" and
+                         substr($$dataPt, $start + 3, 1) eq "\x1c")
+    {
+        $exifTool->Warn('IPTC data was improperly byte-swapped');
+        my $newData = pack('N*', unpack('V*', substr($$dataPt, $start, $dirLen) . "\0\0\0"));
+        $dataPt = \$newData;
+        $start = 0;
+        # NOTE: MUST NOT access $dirInfo DataPt, DirStart or DataLen after this!
+    }
+    # generate lookup so we can find the record numbers
+    my %recordNum;
+    foreach $tag (Image::ExifTool::TagTableKeys($tagTablePtr)) {
+        $tagInfo = $tagTablePtr->{$tag};
+        $$tagInfo{SubDirectory} or next;
+        my $table = $tagInfo->{SubDirectory}->{TagTable} or next;
+        my $subTablePtr = Image::ExifTool::GetTagTable($table);
+        $recordNum{$subTablePtr} = $tag;
+        Image::ExifTool::GenerateTagIDs($subTablePtr);
+    }
+
+    # loop through new values and accumulate all IPTC information
+    # into lists based on their IPTC record type
+    foreach $tagInfo ($exifTool->GetNewTagInfoList()) {
+        my $table = $$tagInfo{Table};
+        my $record = $recordNum{$table};
+        # ignore tags we aren't writing to this directory
+        next unless defined $record;
+        $iptcInfo{$record} = [] unless defined $iptcInfo{$record};
+        push @{$iptcInfo{$record}}, $tagInfo;
+    }
+
+    # get sorted list of records used.  Might as well be organized and
+    # write our records in order of record number first, then tag number
+    my @recordList = sort { $a <=> $b } keys %iptcInfo;
+    my ($record, %set);
+    foreach $record (@recordList) {
+        # sort tagInfo lists by tagID
+        @{$iptcInfo{$record}} = sort { $$a{TagID} <=> $$b{TagID} } @{$iptcInfo{$record}};
+        # build hash of all tagIDs to set
+        foreach $tagInfo (@{$iptcInfo{$record}}) {
+            $set{$record}->{$$tagInfo{TagID}} = $tagInfo;
+        }
+    }
+    # run through the old IPTC data, inserting our records in
+    # sequence and deleting existing records where necessary
+    # (the IPTC specification states that records must occur in
+    # numerical order, but tags within records need not be ordered)
+    my $pos = $start;
+    my $tail = $pos;        # old data written up to this point
+    my $dirEnd = $start + $dirLen;
+    my $newData = '';
+    my $lastRec = -1;
+    my $lastRecPos = 0;
+    my $allMandatory = 0;
+    my %foundRec;           # found flags: 0x01-existed before, 0x02-deleted, 0x04-created
+
+    for (;;$tail=$pos) {
+        # get next IPTC record from input directory
+        my ($id, $rec, $tag, $len, $valuePtr);
+        if ($pos + 5 <= $dirEnd) {
+            my $buff = substr($$dataPt, $pos, 5);
+            ($id, $rec, $tag, $len) = unpack("CCCn", $buff);
+            if ($id == 0x1c) {
+                if ($rec < $lastRec) {
+                    return undef if $exifTool->Warn("IPTC doesn't conform to spec: Records out of sequence", 1);
+                }
+                # handle extended IPTC entry if necessary
+                $pos += 5;      # step to after field header
+                if ($len & 0x8000) {
+                    my $n = $len & 0x7fff;  # get num bytes in length field
+                    if ($pos + $n <= $dirEnd and $n <= 8) {
+                        # determine length (a big-endian, variable sized int)
+                        for ($len = 0; $n; ++$pos, --$n) {
+                            $len = $len * 256 + ord(substr($$dataPt, $pos, 1));
+                        }
+                    } else {
+                        $len = $dirEnd;     # invalid length
+                    }
+                }
+                $valuePtr = $pos;
+                $pos += $len;   # step $pos to next entry
+                # make sure we don't go past the end of data
+                # (this can only happen if original data is bad)
+                $pos = $dirEnd if $pos > $dirEnd;
+            } else {
+                undef $rec;
+            }
+        }
+        if (not defined $rec or $rec != $lastRec) {
+            # write out all our records that come before this one
+            for (;;) {
+                my $newRec = $recordList[0];
+                if (not defined $newRec or $newRec != $lastRec) {
+                    # handle mandatory tags in last record unless it was empty
+                    if (length $newData > $lastRecPos) {
+                        if ($allMandatory > 1) {
+                            # entire lastRec contained mandatory tags, and at least one tag
+                            # was deleted, so delete entire record unless we specifically
+                            # added a mandatory tag
+                            my $num = 0;
+                            foreach (keys %{$foundRec{$lastRec}}) {
+                                my $code = $foundRec{$lastRec}->{$_};
+                                $num = 0, last if $code & 0x04;
+                                ++$num if ($code & 0x03) == 0x01;
+                            }
+                            if ($num) {
+                                $newData = substr($newData, 0, $lastRecPos);
+                                $verbose > 1 and print $out "    - $num mandatory tags\n";
+                            }
+                        } elsif ($mandatory{$lastRec} and
+                                 $tagTablePtr eq \%Image::ExifTool::IPTC::Main)
+                        {
+                            # add required mandatory tags
+                            my $mandatory = $mandatory{$lastRec};
+                            my ($mandTag, $subTablePtr);
+                            foreach $mandTag (sort { $a <=> $b } keys %$mandatory) {
+                                next if $foundRec{$lastRec}->{$mandTag};
+                                unless ($subTablePtr) {
+                                    $tagInfo = $tagTablePtr->{$lastRec};
+                                    $tagInfo and $$tagInfo{SubDirectory} or warn("WriteIPTC: Internal error 1\n"), next;
+                                    $tagInfo->{SubDirectory}->{TagTable} or next;
+                                    $subTablePtr = Image::ExifTool::GetTagTable($tagInfo->{SubDirectory}->{TagTable});
+                                }
+                                $tagInfo = $subTablePtr->{$mandTag} or warn("WriteIPTC: Internal error 2\n"), next;
+                                my $value = $mandatory->{$mandTag};
+                                $verbose > 1 and print $out "    + IPTC:$$tagInfo{Name} = '$value' (mandatory)\n";
+                                # apply necessary format conversions
+                                FormatIPTC($exifTool, $tagInfo, \$value, \$xlat, $lastRec);
+                                $len = length $value;
+                                # generate our new entry
+                                my $entry = pack("CCCn", 0x1c, $lastRec, $mandTag, length($value));
+                                $newData .= $entry . $value;    # add entry to new IPTC data
+                                ++$exifTool->{CHANGED};
+                            }
+                        }
+                    }
+                    last unless defined $newRec;
+                    $lastRec = $newRec;
+                    $lastRecPos = length $newData;
+                    $allMandatory = 1;
+                }
+                $tagInfo = ${$iptcInfo{$newRec}}[0];
+                my $newTag = $$tagInfo{TagID};
+                # compare current entry with entry next in line to write out
+                # (write out our tags in numerical order even though
+                # this isn't required by the IPTC spec)
+                last if defined $rec and $rec <= $newRec;
+                my $newValueHash = $exifTool->GetNewValueHash($tagInfo);
+                # only add new values if...
+                my ($doSet, @values);
+                my $found = $foundRec{$newRec}->{$newTag} || 0;
+                if ($found & 0x02) {
+                    # ...tag existed before and was deleted
+                    $doSet = 1;
+                } elsif ($$tagInfo{List}) {
+                    # ...tag is List and it existed before or we are creating it
+                    $doSet = 1 if $found or Image::ExifTool::IsCreating($newValueHash);
+                } else {
+                    # ...tag didn't exist before and we are creating it
+                    $doSet = 1 if not $found and Image::ExifTool::IsCreating($newValueHash);
+                }
+                if ($doSet) {
+                    @values = Image::ExifTool::GetNewValues($newValueHash);
+                    @values and $foundRec{$newRec}->{$newTag} = $found | 0x04;
+                    # write tags for each value in list
+                    my $value;
+                    foreach $value (@values) {
+                        $verbose > 1 and print $out "    + IPTC:$$tagInfo{Name} = '$value'\n";
+                        # reset allMandatory flag if a non-mandatory tag is written
+                        if ($allMandatory) {
+                            my $mandatory = $mandatory{$newRec};
+                            $allMandatory = 0 unless $mandatory and $mandatory->{$newTag};
+                        }
+                        # apply necessary format conversions
+                        FormatIPTC($exifTool, $tagInfo, \$value, \$xlat, $newRec);
+                        # (note: IPTC string values are NOT null terminated)
+                        $len = length $value;
+                        # generate our new entry
+                        my $entry = pack("CCC", 0x1c, $newRec, $newTag);
+                        if ($len <= 0x7fff) {
+                            $entry .= pack("n", $len);
+                        } else {
+                            # extended dataset tag
+                            $entry .= pack("nN", 0x8004, $len);
+                        }
+                        $newData .= $entry . $value;    # add entry to new IPTC data
+                        ++$exifTool->{CHANGED};
+                    }
+                }
+                # remove this tagID from the sorted write list
+                shift @{$iptcInfo{$newRec}};
+                shift @recordList unless @{$iptcInfo{$newRec}};
+            }
+            # all done if no more records to write
+            last unless defined $rec;
+            # update last record variables
+            $lastRec = $rec;
+            $lastRecPos = length $newData;
+            $allMandatory = 1;
+        }
+        # set flag indicating we found this tag
+        $foundRec{$rec}->{$tag} = 1;
+        # write out this record unless we are setting it with a new value
+        $tagInfo = $set{$rec}->{$tag};
+        if ($tagInfo) {
+            my $newValueHash = $exifTool->GetNewValueHash($tagInfo);
+            $len = $pos - $valuePtr;
+            my $val = substr($$dataPt, $valuePtr, $len);
+            my $oldXlat = $xlat;
+            FormatIPTC($exifTool, $tagInfo, \$val, \$xlat, $rec, 1);
+            if (Image::ExifTool::IsOverwriting($newValueHash, $val)) {
+                $xlat = $oldXlat;   # don't change translation (not writing this value)
+                $verbose > 1 and print $out "    - IPTC:$$tagInfo{Name} = '$val'\n";
+                ++$exifTool->{CHANGED};
+                # set deleted flag to indicate we found and deleted this tag
+                $foundRec{$rec}->{$tag} |= 0x02;
+                # set allMandatory flag to 2 indicating a tag was removed
+                $allMandatory and ++$allMandatory;
+                next;
+            }
+        } elsif ($rec == 1 and $tag == 90) {
+            # handle CodedCharacterSet tag
+            my $val = substr($$dataPt, $valuePtr, $pos - $valuePtr);
+            $xlat = HandleCodedCharset($exifTool, $val);
+        }
+        # reset allMandatory flag if a non-mandatory tag is written
+        if ($allMandatory) {
+            my $mandatory = $mandatory{$rec};
+            unless ($mandatory and $mandatory->{$tag}) {
+                $allMandatory = 0;
+            }
+        }
+        # write out the record
+        $newData .= substr($$dataPt, $tail, $pos-$tail);
+    }
+    # make sure the rest of the data is zero
+    if ($tail < $dirEnd) {
+        my $trailer = substr($$dataPt, $tail, $dirEnd-$tail);
+        if ($trailer =~ /[^\0]/) {
+            return undef if $exifTool->Warn('Unrecognized data in IPTC trailer', 1);
+        }
+    }
+    return $newData;
+}
+
+
+1; # end
+
+__END__
+
+=head1 NAME
+
+Image::ExifTool::WriteIPTC.pl - Write IPTC meta information
+
+=head1 SYNOPSIS
+
+This file is autoloaded by Image::ExifTool::IPTC.
+
+=head1 DESCRIPTION
+
+This file contains routines to write IPTC metadata, plus a few other
+seldom-used routines.
+
+=head1 AUTHOR
+
+Copyright 2003-2007, Phil Harvey (phil at owl.phy.queensu.ca)
+
+This library is free software; you can redistribute it and/or modify it
+under the same terms as Perl itself.
+
+=head1 SEE ALSO
+
+L<Image::ExifTool::IPTC(3pm)|Image::ExifTool::IPTC>,
+L<Image::ExifTool(3pm)|Image::ExifTool>
+
+=cut
Index: /gsdl/trunk/perllib/cpan/Image/ExifTool/WritePNG.pl
===================================================================
--- /gsdl/trunk/perllib/cpan/Image/ExifTool/WritePNG.pl	(revision 16842)
+++ /gsdl/trunk/perllib/cpan/Image/ExifTool/WritePNG.pl	(revision 16842)
@@ -0,0 +1,294 @@
+#------------------------------------------------------------------------------
+# File:         WritePNG.pl
+#
+# Description:  Write PNG meta information
+#
+# Revisions:    09/16/2005 - P. Harvey Created
+#
+# References:   1) http://www.libpng.org/pub/png/spec/1.2/
+#------------------------------------------------------------------------------
+package Image::ExifTool::PNG;
+
+use strict;
+
+#------------------------------------------------------------------------------
+# Calculate CRC or update running CRC (ref 1)
+# Inputs: 0) data reference, 1) running crc to update (undef intially)
+#         2) data position (undef for 0), 3) data length (undef for all data),
+# Returns: updated CRC
+my @crcTable;
+sub CalculateCRC($;$$$)
+{
+    my ($dataPt, $crc, $pos, $len) = @_;
+    $crc = 0 unless defined $crc;
+    $pos = 0 unless defined $pos;
+    $len = length($$dataPt) - $pos unless defined $len;
+    $crc ^= 0xffffffff;         # undo 1's complement
+    # build lookup table unless done already
+    unless (@crcTable) {
+        my ($c, $n, $k);
+        for ($n=0; $n<256; ++$n) {
+            for ($k=0, $c=$n; $k<8; ++$k) {
+                $c = ($c & 1) ? 0xedb88320 ^ ($c >> 1) : $c >> 1;
+            }
+            $crcTable[$n] = $c;
+        }
+    }
+    # calculate the CRC
+    foreach (unpack("x${pos}C$len", $$dataPt)) {
+        $crc = $crcTable[($crc^$_) & 0xff] ^ ($crc >> 8);
+    }
+    return $crc ^ 0xffffffff;   # return 1's complement
+}
+
+#------------------------------------------------------------------------------
+# Encode data in ASCII Hex
+# Inputs: 0) input data reference
+# Returns: Hex-encoded data (max 72 chars per line)
+sub HexEncode($)
+{
+    my $dataPt = shift;
+    my $len = length($$dataPt);
+    my $hex = '';
+    my $pos;
+    for ($pos = 0; $pos < $len; $pos += 36) {
+        my $n = $len - $pos;
+        $n > 36 and $n = 36;
+        $hex .= unpack('H*',substr($$dataPt,$pos,$n)) . "\n";
+    }
+    return $hex;
+}
+
+#------------------------------------------------------------------------------
+# Write profile to tEXt or zTXt chunk (zTXt if Zlib is available)
+# Inputs: 0) outfile, 1) Raw profile type, 2) data ref
+#         3) profile header type (undef if not a text profile)
+# Returns: 1 on success
+sub WriteProfile($$$;$)
+{
+    my ($outfile, $rawType, $dataPt, $profile) = @_;
+    my ($buff, $prefix, $chunk, $deflate);
+    if (eval 'require Compress::Zlib') {
+        $deflate = Compress::Zlib::deflateInit();
+    }
+    if (not defined $profile) {
+        # write ICC profile as compressed iCCP chunk if possible
+        return 0 unless $deflate;
+        $buff = $deflate->deflate($$dataPt);
+        return 0 unless defined $buff;
+        $buff .= $deflate->flush();
+        my %rawTypeChunk = ( icm => 'iCCP' );
+        $chunk = $rawTypeChunk{$rawType} or return 0;
+        $prefix = "$rawType\0\0";
+        $dataPt = \$buff;
+    } else {
+        # write as ASCII-hex encoded profile in tEXt or zTXt chunk
+        my $txtHdr = sprintf("\n$profile profile\n%8d\n", length($$dataPt));
+        $buff = $txtHdr . HexEncode($dataPt);
+        $chunk = 'tEXt';         # write as tEXt if deflate not available
+        $prefix = "Raw profile type $rawType\0";
+        $dataPt = \$buff;
+        # write profile as zTXt chunk if possible
+        if ($deflate) {
+            my $buf2 = $deflate->deflate($buff);
+            if (defined $buf2) {
+                $dataPt = \$buf2;
+                $buf2 .= $deflate->flush();
+                $chunk = 'zTXt';
+                $prefix .= "\0";    # compression type byte (0=deflate)
+            }
+        }
+    }
+    my $hdr = pack('Na4', length($prefix) + length($$dataPt), $chunk) . $prefix;
+    my $crc = CalculateCRC(\$hdr, undef, 4);
+    $crc = CalculateCRC($dataPt, $crc);
+    return Write($outfile, $hdr, $$dataPt, pack('N',$crc));
+}
+
+#------------------------------------------------------------------------------
+# Add iCCP to the PNG image if necessary (must come before PLTE and IDAT)
+# Inputs: 0) ExifTool object ref, 1) output file or scalar ref
+# Returns: true on success
+sub Add_iCCP($$)
+{
+    my ($exifTool, $outfile) = @_;
+    if ($exifTool->{ADD_DIRS}->{ICC_Profile}) {
+        # write new ICC data
+        my $tagTablePtr = GetTagTable('Image::ExifTool::ICC_Profile::Main');
+        my %dirInfo = ( Parent => 'PNG', DirName => 'ICC_Profile' );
+        my $buff = $exifTool->WriteDirectory(\%dirInfo, $tagTablePtr);
+        if (defined $buff and length $buff and WriteProfile($outfile, 'icm', \$buff)) {
+            $exifTool->VPrint(0, "Created ICC profile\n");
+            delete $exifTool->{ADD_DIRS}->{ICC_Profile}; # don't add it again
+        }
+    }
+    return 1;
+}
+
+#------------------------------------------------------------------------------
+# Add any outstanding new chunks to the PNG image
+# Inputs: 0) ExifTool object ref, 1) output file or scalar ref
+# Returns: true on success
+sub AddChunks($$)
+{
+    my ($exifTool, $outfile) = @_;
+    # write any outstanding PNG tags
+    my $addTags = $exifTool->{ADD_PNG};
+    delete $exifTool->{ADD_PNG};
+    my ($tag, $dir, $err, $tagTablePtr);
+
+    foreach $tag (sort keys %$addTags) {
+        my $tagInfo = $$addTags{$tag};
+        my $newValueHash = $exifTool->GetNewValueHash($tagInfo);
+        # (always create native PNG information, so don't check IsCreating())
+        next unless Image::ExifTool::IsOverwriting($newValueHash) > 0;
+        my $val = Image::ExifTool::GetNewValues($newValueHash);
+        if (defined $val) {
+            my $data;
+            if ($$tagInfo{Table} eq \%Image::ExifTool::PNG::TextualData) {
+                $data = "tEXt$tag\0$val";
+            } else {
+                $data = "$tag$val";
+            }
+            # write as compressed zTXt if specified
+            if ($exifTool->Options('Compress')) {
+                my $warn;
+                if (eval 'require Compress::Zlib') {
+                    my $buff;
+                    my $deflate = Compress::Zlib::deflateInit();
+                    $buff = $deflate->deflate($val) if $deflate;
+                    if (defined $buff) {
+                        $buff .= $deflate->flush();
+                        # only write as zTXt if it actually saves space
+                        if (length($buff) < length($val) - 1) {
+                            $data = "zTXt$tag\0\0$buff";
+                        } else {
+                            $warn = 'uncompressed data is smaller';
+                        }
+                    } else {
+                        $warn = 'deflate error';
+                    }
+                } else {
+                    $warn = 'Compress::Zlib not available'; 
+                }
+                $warn and $exifTool->Warn("PNG:$$tagInfo{Name} not compressed ($warn)", 1);
+            }
+            my $hdr = pack('N', length($data) - 4);
+            my $cbuf = pack('N', CalculateCRC(\$data, undef));
+            Write($outfile, $hdr, $data, $cbuf) or $err = 1;
+            $exifTool->VPrint(1, "    + PNG:$$tagInfo{Name} = '",$exifTool->Printable($val),"'\n");
+            ++$exifTool->{CHANGED};
+        }
+    }
+    $addTags = { };     # prevent from adding tags again
+    # create any necessary directories
+    foreach $dir (sort keys %{$exifTool->{ADD_DIRS}}) {
+        my $buff;
+        my %dirInfo = (
+            Parent => 'PNG',
+            DirName => $dir,
+        );
+        if ($dir eq 'IFD0') {
+            $exifTool->VPrint(0, "Creating EXIF profile:\n");
+            $exifTool->{TIFF_TYPE} = 'APP1';
+            $tagTablePtr = GetTagTable('Image::ExifTool::Exif::Main');
+            # use specified byte ordering or ordering from maker notes if set
+            my $byteOrder = $exifTool->Options('ByteOrder') ||
+                $exifTool->GetNewValues('ExifByteOrder') || $exifTool->{MAKER_NOTE_BYTE_ORDER} || 'MM';
+            unless (SetByteOrder($byteOrder)) {
+                warn "Invalid byte order '$byteOrder'\n";
+                $byteOrder = $exifTool->{MAKER_NOTE_BYTE_ORDER} || 'MM';
+                SetByteOrder($byteOrder);
+            }
+            $dirInfo{NewDataPos} = 8,   # new data will come after TIFF header
+            $buff = $exifTool->WriteDirectory(\%dirInfo, $tagTablePtr);
+            if (defined $buff and length $buff) {
+                my $tiffHdr = $byteOrder . Set16u(42) . Set32u(8);
+                $buff = $Image::ExifTool::exifAPP1hdr . $tiffHdr . $buff;
+                WriteProfile($outfile, 'APP1', \$buff, 'generic') or $err = 1;
+            }
+        } elsif ($dir eq 'XMP') {
+            $exifTool->VPrint(0, "Creating XMP iTXt chunk:\n");
+            $tagTablePtr = GetTagTable('Image::ExifTool::XMP::Main');
+            $dirInfo{ReadOnly} = 1;
+            $buff = $exifTool->WriteDirectory(\%dirInfo, $tagTablePtr);
+            if (defined $buff and length $buff and
+                # the packet is read-only (because of CRC)
+                Image::ExifTool::XMP::ValidateXMP(\$buff, 'r'))
+            {
+                # (previously, XMP was created as a non-standard XMP profile chunk)
+                # $buff = $Image::ExifTool::xmpAPP1hdr . $buff;
+                # WriteProfile($outfile, 'APP1', \$buff, 'generic') or $err = 1;
+                # (but now write XMP iTXt chunk according to XMP specification)
+                $buff = "iTXtXML:com.adobe.xmp\0\0\0\0\0" . $buff;
+                my $hdr = pack('N', length($buff) - 4);
+                my $cbuf = pack('N', CalculateCRC(\$buff, undef));
+                Write($outfile, $hdr, $buff, $cbuf) or $err = 1;
+            }
+        } elsif ($dir eq 'IPTC') {
+            $exifTool->VPrint(0, "Creating IPTC profile:\n");
+            # write new IPTC data
+            $tagTablePtr = GetTagTable('Image::ExifTool::Photoshop::Main');
+            $buff = $exifTool->WriteDirectory(\%dirInfo, $tagTablePtr);
+            if (defined $buff and length $buff) {
+                WriteProfile($outfile, 'iptc', \$buff, 'IPTC') or $err = 1;
+            }
+        } elsif ($dir eq 'ICC_Profile') {
+            $exifTool->VPrint(0, "Creating ICC profile:\n");
+            # write new ICC data (only done if we couldn't create iCCP chunk)
+            $tagTablePtr = GetTagTable('Image::ExifTool::ICC_Profile::Main');
+            $buff = $exifTool->WriteDirectory(\%dirInfo, $tagTablePtr);
+            if (defined $buff and length $buff) {
+                WriteProfile($outfile, 'icm', \$buff, 'ICC') or $err = 1;
+                $exifTool->Warn('Wrote ICC as generic profile (no Compress::Zlib)');
+            }
+        }
+    }
+    $exifTool->{ADD_DIRS} = { };    # prevent from adding dirs again
+    return not $err;
+}
+
+
+1; # end
+
+__END__
+
+=head1 NAME
+
+Image::ExifTool::WritePNG.pl - Write PNG meta information
+
+=head1 SYNOPSIS
+
+These routines are autoloaded by Image::ExifTool::PNG.
+
+=head1 DESCRIPTION
+
+This file contains routines to write PNG metadata.
+
+=head1 NOTES
+
+Compress::Zlib is required to write compressed text.
+
+Existing text tags are always rewritten in their original form (compressed
+zTXt, uncompressed tEXt or internation iTXt), so pre-existing compressed
+information can only be modified if Compress::Zlib is available.
+
+Newly created textual information is written in uncompressed tEXt form by
+default, or as compressed zTXt if the Compress option is used and
+Compress::Zlib is available (but only if the resulting compressed data is
+smaller than the original text, which isn't always the case for short text
+strings).
+
+=head1 AUTHOR
+
+Copyright 2003-2007, Phil Harvey (phil at owl.phy.queensu.ca)
+
+This library is free software; you can redistribute it and/or modify it
+under the same terms as Perl itself.
+
+=head1 SEE ALSO
+
+L<Image::ExifTool::PNG(3pm)|Image::ExifTool::PNG>,
+L<Image::ExifTool(3pm)|Image::ExifTool>
+
+=cut
Index: /gsdl/trunk/perllib/cpan/Image/ExifTool/WritePhotoshop.pl
===================================================================
--- /gsdl/trunk/perllib/cpan/Image/ExifTool/WritePhotoshop.pl	(revision 16842)
+++ /gsdl/trunk/perllib/cpan/Image/ExifTool/WritePhotoshop.pl	(revision 16842)
@@ -0,0 +1,239 @@
+#------------------------------------------------------------------------------
+# File:         WritePhotoshop.pl
+#
+# Description:  Write Photoshop IRB meta information
+#
+# Revisions:    12/17/2004 - P. Harvey Created
+#------------------------------------------------------------------------------
+
+package Image::ExifTool::Photoshop;
+
+use strict;
+
+#------------------------------------------------------------------------------
+# Strip resource name from value prepare resource name for writing into IRB
+# Inputs: 0) tagInfo ref, 1) resource name (padded pascal string), 2) new value ref
+# Returns: none (updates name and value if necessary)
+sub SetResourceName($$$)
+{
+    my ($tagInfo, $name, $valPt) = @_;
+    my $setName = $$tagInfo{SetResourceName};
+    if (defined $setName) {
+        # extract resource name from value
+        if ($$valPt =~ m{.*/#(.{0,255})#/$}s) {
+            $name = $1;
+            # strip name from value
+            $$valPt = substr($$valPt, 0, -4 - length($name));
+        } elsif ($setName eq '1') {
+            return;     # use old name
+        } else {
+            $name = $setName;
+        }
+        # convert to padded pascal string
+        $name = chr(length $name) . $name;
+        $name .= "\0" if length($name) & 0x01;
+        $_[1] = $name;  # return new name
+    }
+}
+
+#------------------------------------------------------------------------------
+# Write Photoshop IRB resource
+# Inputs: 0) ExifTool object reference, 1) source dirInfo reference,
+#         2) tag table reference
+# Returns: IRB resource data (may be empty if no Photoshop data)
+# Notes: Increments ExifTool CHANGED flag for each tag changed
+sub WritePhotoshop($$$)
+{
+    my ($exifTool, $dirInfo, $tagTablePtr) = @_;
+    $exifTool or return 1;    # allow dummy access to autoload this package
+    my $dataPt = $$dirInfo{DataPt};
+    unless ($dataPt) {
+        my $emptyData = '';
+        $dataPt = \$emptyData;
+    }
+    my $start = $$dirInfo{DirStart} || 0;
+    my $dirLen = $$dirInfo{DirLen} || (length($$dataPt) - $start);
+    my $dirEnd = $start + $dirLen;
+    my $verbose = $exifTool->Options('Verbose');
+    my $out = $exifTool->Options('TextOut');
+    my $newData = '';
+
+    # make a hash of new tag info, keyed on tagID
+    my $newTags = $exifTool->GetNewTagInfoHash($tagTablePtr);
+
+    my ($addDirs, $editDirs) = $exifTool->GetAddDirHash($tagTablePtr);
+
+    SetByteOrder('MM');     # Photoshop is always big-endian
+#
+# rewrite existing tags in the old directory, deleting ones as necessary
+# (the Photoshop directory entries aren't in any particular order)
+#
+    # Format: 0) Type, 4 bytes - '8BIM' (or the rare 'PHUT', 'DCSR' or 'AgHg')
+    #         1) TagID,2 bytes
+    #         2) Name, pascal string padded to even no. bytes
+    #         3) Size, 4 bytes - N
+    #         4) Data, N bytes
+    my ($pos, $value, $size, $tagInfo, $tagID);
+    for ($pos=$start; $pos+8<$dirEnd; $pos+=$size) {
+        # each entry must be on same even byte boundary as directory start
+        ++$pos if ($pos ^ $start) & 0x01;
+        my $type = substr($$dataPt, $pos, 4);
+        if ($type !~ /^(8BIM|PHUT|DCSR|AgHg)$/) {
+            $exifTool->Error("Bad Photoshop IRB resource");
+            undef $newData;
+            last;
+        }
+        $tagID = Get16u($dataPt, $pos + 4);
+        # get resource block name (pascal string padded to an even # of bytes)
+        my $namelen = 1 + Get8u($dataPt, $pos + 6);
+        ++$namelen if $namelen & 0x01;
+        if ($pos + $namelen + 10 > $dirEnd) {
+            $exifTool->Error("Bad APP13 resource block");
+            undef $newData;
+            last;
+        }
+        my $name = substr($$dataPt, $pos + 6, $namelen);
+        $size = Get32u($dataPt, $pos + 6 + $namelen);
+        $pos += $namelen + 10;
+        if ($size + $pos > $dirEnd) {
+            $exifTool->Error("Bad APP13 resource data size $size");
+            undef $newData;
+            last;
+        }
+        if ($$newTags{$tagID} and $type eq '8BIM') {
+            $tagInfo = $$newTags{$tagID};
+            delete $$newTags{$tagID};
+            my $newValueHash = $exifTool->GetNewValueHash($tagInfo);
+            # check to see if we are overwriting this tag
+            $value = substr($$dataPt, $pos, $size);
+            if (Image::ExifTool::IsOverwriting($newValueHash, $value)) {
+                $verbose > 1 and print $out "    - Photoshop:$$tagInfo{Name} = '$value'\n";
+                $value = Image::ExifTool::GetNewValues($newValueHash);
+                ++$exifTool->{CHANGED};
+                next unless defined $value;     # next if tag is being deleted
+                # set resource name if necessary
+                SetResourceName($tagInfo, $name, \$value);
+                $verbose > 1 and print $out "    + Photoshop:$$tagInfo{Name} = '$value'\n";
+            }
+        } else {
+            if ($type eq '8BIM') {
+                $tagInfo = $$editDirs{$tagID};
+                unless ($tagInfo) {
+                    # process subdirectory anyway if writable (except EXIF to avoid recursion)
+                    # --> this allows IPTC to be processed if found here in TIFF images
+                    my $tmpInfo = $exifTool->GetTagInfo($tagTablePtr, $tagID);
+                    if ($tmpInfo and $$tmpInfo{SubDirectory} and
+                        $tmpInfo->{SubDirectory}->{TagTable} ne 'Image::ExifTool::Exif::Main')
+                    {
+                        my $table = Image::ExifTool::GetTagTable($tmpInfo->{SubDirectory}->{TagTable});
+                        $tagInfo = $tmpInfo if $$table{WRITE_PROC};
+                    }
+                }
+            }
+            if ($tagInfo) {
+                $$addDirs{$tagID} and delete $$addDirs{$tagID};
+                my %subdirInfo = (
+                    DataPt => $dataPt,
+                    DirStart => $pos,
+                    DataLen => $dirLen,
+                    DirLen => $size,
+                    Parent => $$dirInfo{DirName},
+                );
+                my $subTable = Image::ExifTool::GetTagTable($tagInfo->{SubDirectory}->{TagTable});
+                my $writeProc = $tagInfo->{SubDirectory}->{WriteProc};
+                my $newValue = $exifTool->WriteDirectory(\%subdirInfo, $subTable, $writeProc);
+                if (defined $newValue) {
+                    next unless length $newValue;   # remove subdirectory entry
+                    $value = $newValue;
+                    SetResourceName($tagInfo, $name, \$value);
+                } else {
+                    $value = substr($$dataPt, $pos, $size); # rewrite old directory
+                }
+            } else {
+                $value = substr($$dataPt, $pos, $size);
+            }
+        }
+        my $newSize = length $value;
+        # write this directory entry
+        $newData .= $type . Set16u($tagID) . $name . Set32u($newSize) . $value;
+        $newData .= "\0" if $newSize & 0x01;    # must null pad to even byte
+    }
+#
+# write any remaining entries we didn't find in the old directory
+# (might as well write them in numerical tag order)
+#
+    my @tagsLeft = sort { $a <=> $b } keys(%$newTags), keys(%$addDirs);
+    foreach $tagID (@tagsLeft) {
+        my $name = "\0\0";
+        if ($$newTags{$tagID}) {
+            $tagInfo = $$newTags{$tagID};
+            my $newValueHash = $exifTool->GetNewValueHash($tagInfo);
+            $value = Image::ExifTool::GetNewValues($newValueHash);
+            next unless defined $value;     # next if tag is being deleted
+            # don't add this tag unless specified
+            next unless Image::ExifTool::IsCreating($newValueHash);
+            $verbose > 1 and print $out "    + Photoshop:$$tagInfo{Name} = '$value'\n";
+            ++$exifTool->{CHANGED};
+        } else {
+            $tagInfo = $$addDirs{$tagID};
+            # create new directory
+            my %subdirInfo = (
+                Parent => $$dirInfo{DirName},
+            );
+            my $subTable = Image::ExifTool::GetTagTable($tagInfo->{SubDirectory}->{TagTable});
+            my $writeProc = $tagInfo->{SubDirectory}->{WriteProc};
+            $value = $exifTool->WriteDirectory(\%subdirInfo, $subTable, $writeProc);
+            next unless $value;
+        }
+        # set resource name if necessary
+        SetResourceName($tagInfo, $name, \$value);
+        $size = length($value);
+        # write the new directory entry
+        $newData .= '8BIM' . Set16u($tagID) . $name . Set32u($size) . $value;
+        $newData .= "\0" if $size & 0x01;   # must null pad to even numbered byte
+        ++$exifTool->{CHANGED};
+    }
+    return $newData;
+}
+
+
+1; # end
+
+__END__
+
+=head1 NAME
+
+Image::ExifTool::WritePhotoshop.pl - Write Photoshop IRB meta information
+
+=head1 SYNOPSIS
+
+This file is autoloaded by Image::ExifTool::Photoshop.
+
+=head1 DESCRIPTION
+
+This file contains routines to write Photoshop metadata.
+
+=head1 NOTES
+
+Photoshop IRB blocks may have an associated resource name.  By default, the
+existing name is preserved when rewriting a resource, and an empty name is
+used when creating a new resource.  However, a different resource name may
+be specified by defining a C<SetResourceName> entry in the tag information
+hash.  With this defined, a new resource name may be appended to the value
+in the form "VALUE/#NAME#/" (the slashes and hashes are literal).  If
+C<SetResourceName> is anything other than '1', the value is used as a
+default resource name, and applied if no appended name is provided.
+
+=head1 AUTHOR
+
+Copyright 2003-2007, Phil Harvey (phil at owl.phy.queensu.ca)
+
+This library is free software; you can redistribute it and/or modify it
+under the same terms as Perl itself.
+
+=head1 SEE ALSO
+
+L<Image::ExifTool::Photoshop(3pm)|Image::ExifTool::Photoshop>,
+L<Image::ExifTool(3pm)|Image::ExifTool>
+
+=cut
Index: /gsdl/trunk/perllib/cpan/Image/ExifTool/WritePostScript.pl
===================================================================
--- /gsdl/trunk/perllib/cpan/Image/ExifTool/WritePostScript.pl	(revision 16842)
+++ /gsdl/trunk/perllib/cpan/Image/ExifTool/WritePostScript.pl	(revision 16842)
@@ -0,0 +1,743 @@
+#------------------------------------------------------------------------------
+# File:         WritePostScript.pl
+#
+# Description:  Write PostScript meta information
+#
+# Revisions:    03/03/2006 - P. Harvey Created
+#
+# References:   (see references in PostScript.pm, plus:)
+#               1) http://www.adobe.com/products/postscript/pdfs/PLRM.pdf
+#               2) http://www-cdf.fnal.gov/offline/PostScript/PLRM2.pdf
+#               3) http://partners.adobe.com/public/developer/en/acrobat/sdk/pdf/pdf_creation_apis_and_specs/pdfmarkReference.pdf
+#               4) http://www.npes.org/standards/Tools/DCS20Spec.pdf
+#
+# Notes:        (see NOTES in POD doc below)
+#------------------------------------------------------------------------------
+
+package Image::ExifTool::PostScript;
+
+use strict;
+
+# Structure of a DSC PS/EPS document:
+#
+#   %!PS-Adobe-3.0     [plus " EPSF-3.0" for EPS]
+#     <comments>
+#   %%EndComments      [optional]
+#   %%BeginXxxx
+#     <stuff to ignore>
+#   %%EndXxxx
+#   %%BeginProlog
+#     <prolog stuff>
+#   %%EndProlog
+#   %%BeginSetup
+#     <setup stuff>
+#   %%EndSetup
+#   %ImageData x x x x  [written by Photoshop]
+#   %BeginPhotoshop: xxxx
+#     <ascii-hex IRB information>
+#   %EndPhotosop
+#   %%BeginICCProfile: (name) <num> <type>
+#     <ICC Profile info>
+#   %%EndICCProfile
+#   %begin_xml_code
+#     <postscript code to define and read the XMP stream object>
+#   %begin_xml_packet: xxxx
+#     <XMP data>
+#   %end_xml_packet
+#     <postscript code to add XMP stream to dictionary>
+#   %end_xml_code
+#   %%Page: x x         [PS only (optional?)]
+#     <graphics commands>
+#   %%PageTrailer
+#   %%Trailer
+#     <a bit more code to bracket EPS content for distiller>
+#   %%EOF
+
+# map of where information is stored in PS image
+my %psMap = (
+    XMP          => 'PostScript',
+    Photoshop    => 'PostScript',
+    IPTC         => 'Photoshop',
+    EXIFInfo     => 'Photoshop',
+    IFD0         => 'EXIFInfo',
+    IFD1         => 'IFD0',
+    ICC_Profile  => 'PostScript',
+    ExifIFD      => 'IFD0',
+    GPS          => 'IFD0',
+    SubIFD       => 'IFD0',
+    GlobParamIFD => 'IFD0',
+    PrintIM      => 'IFD0',
+    InteropIFD   => 'ExifIFD',
+    MakerNotes   => 'ExifIFD',
+);
+
+
+#------------------------------------------------------------------------------
+# Check PS tag value
+# Inputs: 0) ExifTool object ref, 1) tag info ref, 2) value ref
+# Returns: undef on success, or error string
+sub CheckPS($$$)
+{
+    my ($exifTool, $tagInfo, $valPt) = @_;
+    # parentheses must be balanced (or escaped)
+    my $n = 0;
+    pos($$valPt) = 0;
+    while ($$valPt =~ /(\(|\))/g) {
+        $n += ($1 eq '(') ? 1 : -1;
+        last if $n < 0;
+    }
+    return 'Unmatched parentheses' unless $n == 0;
+    return undef;   # success
+}
+
+#------------------------------------------------------------------------------
+# Write XMP directory to file, with begin/end tokens if necessary
+# Inputs: 0) outfile ref, 1) flags hash ref, 2-N) data to write
+# Returns: true on success
+sub WriteXMPDir($$@)
+{
+    my $outfile = shift;
+    my $flags = shift;
+    my $success = 1;
+    Write($outfile, "%begin_xml_code$/") or $success = 0 unless $$flags{WROTE_BEGIN};
+    Write($outfile, @_) or $success = 0;
+    Write($outfile, "%end_xml_code$/") or $success = 0 unless $$flags{WROTE_BEGIN};
+    return $success;
+}
+
+#------------------------------------------------------------------------------
+# Write a directory inside a PS document
+# Inputs: 0) ExifTool object ref, 1) output file reference,
+#         2) Directory name, 3) data reference, 4) flags hash ref
+# Returns: 0=error, 1=nothing written, 2=dir written ok
+sub WritePSDirectory($$$$$)
+{
+    my ($exifTool, $outfile, $dirName, $dataPt, $flags) = @_;
+    my $success = 2;
+    my $len = $dataPt ? length($$dataPt) : 0;
+    my $create = $len ? 0 : 1;
+    my %dirInfo = (
+        DataPt => $dataPt,
+        DataLen => $len,
+        DirStart => 0,
+        DirLen => $len,
+        DirName => $dirName,
+        Parent => 'PostScript',
+    );
+    # Note: $$flags{WROTE_BEGIN} may be 1 for XMP (it is always 0 for
+    # other dirs, but if 1, the begin/end markers were already written)
+#
+# prepare necessary postscript code to support embedded XMP
+#
+    my ($beforeXMP, $afterXMP, $reportedLen);
+    if ($dirName eq 'XMP' and $len) {
+        # isolate the XMP
+        pos($$dataPt) = 0;
+        unless ($$dataPt =~ /(.*)(<\?xpacket begin=.{7,13}W5M0MpCehiHzreSzNTczkc9d)/sg) {
+            $exifTool->Warn('No XMP packet start');
+            return WriteXMPDir($outfile, $flags, $$dataPt);
+        }
+        $beforeXMP = $1;
+        my $xmp = $2;
+        my $p1 = pos($$dataPt);
+        unless ($$dataPt =~ m{<\?xpacket end=.(w|r).\?>}sg) {
+            $exifTool->Warn('No XMP packet end');
+            return WriteXMPDir($outfile, $flags, $$dataPt);
+        }
+        my $p2 = pos($$dataPt);
+        $xmp .= substr($$dataPt, $p1, $p2-$p1);
+        $afterXMP = substr($$dataPt, $p2);
+        # determine if we can adjust the XMP size
+        if ($beforeXMP =~ /%begin_xml_packet: (\d+)/s) {
+            $reportedLen = $1;
+            my @matches= ($beforeXMP =~ /\b$reportedLen\b/sg);
+            undef $reportedLen unless @matches == 2;
+        }
+        # must edit in place if we can't reliably change the XMP length
+        $dirInfo{InPlace} = 1 unless $reportedLen;
+        # process XMP only
+        $dirInfo{DataLen} = $dirInfo{DirLen} = length $xmp;
+        $dirInfo{DataPt} = \$xmp;
+    }
+    my $tagTablePtr = GetTagTable("Image::ExifTool::${dirName}::Main");
+    my $val = $exifTool->WriteDirectory(\%dirInfo, $tagTablePtr);
+    if (defined $val) {
+        $dataPt = \$val;    # use modified directory
+        $len = length $val;
+    } elsif ($dirName eq 'XMP') {
+        return 1 unless $len;
+        # just write the original XMP
+        return WriteXMPDir($outfile, $flags, $$dataPt);
+    }
+    unless ($len) {
+        return 1 if $create or $dirName ne 'XMP';   # nothing to create
+        # it would be really difficult to delete the XMP,
+        # so instead we write a blank XMP record
+        $val = <<EMPTY_XMP;
+<?xpacket begin='ï»¿' id='W5M0MpCehiHzreSzNTczkc9d'?>
+<x:xmpmeta xmlns:x='adobe:ns:meta/' x:xmptk='Image::ExifTool $Image::ExifTool::VERSION'>
+</x:xmpmeta>
+EMPTY_XMP
+        $val .= ((' ' x 100) . "\n") x 24 unless $exifTool->Options('Compact');
+        $val .= q{<?xpacket end='w'?>};
+        $dataPt = \$val;
+        $len = length $val;
+    }
+#
+# write XMP directory
+#
+    if ($dirName eq 'XMP') {
+        if ($create) {
+            # create necessary PS/EPS code to support XMP
+            $beforeXMP = <<HDR_END;
+/pdfmark where {pop true} {false} ifelse
+/currentdistillerparams where {pop currentdistillerparams
+/CoreDistVersion get 5000 ge } {false} ifelse
+and not {userdict /pdfmark /cleartomark load put} if
+[/NamespacePush pdfmark
+[/_objdef {exiftool_metadata_stream} /type /stream /OBJ pdfmark
+[{exiftool_metadata_stream} 2 dict begin /Type /Metadata def
+  /Subtype /XML def currentdict end /PUT pdfmark
+/MetadataString $len string def % exact length of metadata
+/TempString 100 string def
+/ConsumeMetadata {
+currentfile TempString readline pop pop
+currentfile MetadataString readstring pop pop
+} bind def
+ConsumeMetadata
+%begin_xml_packet: $len
+HDR_END
+            # note: use q() to get necessary linefeed before %end_xml_packet
+            $afterXMP = q(
+%end_xml_packet
+[{exiftool_metadata_stream} MetadataString /PUT pdfmark
+);
+            if ($$flags{EPS}) {
+                $afterXMP .= <<EPS_AFTER;
+[/Document 1 dict begin
+  /Metadata {exiftool_metadata_stream} def currentdict end /BDC pdfmark
+[/NamespacePop pdfmark
+EPS_AFTER
+                # write this at end of file
+                $$flags{TRAILER} = "[/EMC pdfmark$/";
+            } else { # PS
+                $afterXMP .= <<PS_AFTER;
+[{Catalog} {exiftool_metadata_stream} /Metadata pdfmark
+[/NamespacePop pdfmark
+PS_AFTER
+            }
+            $beforeXMP =~ s{\n}{$/}sg;  # use proper newline characters
+            $afterXMP =~ s{\n}{$/}sg;
+        } else {
+            # replace xmp size in PS code
+            $reportedLen and $beforeXMP =~ s/\b$reportedLen\b/$len/sg;
+        }
+        WriteXMPDir($outfile, $flags, $beforeXMP, $$dataPt, $afterXMP) or $success = 0;
+#
+# Write Photoshop or ICC_Profile directory
+#
+    } elsif ($dirName eq 'Photoshop' or $dirName eq 'ICC_Profile') {
+        my ($startToken, $endToken);
+        if ($dirName eq 'Photoshop') {
+            $startToken = "%BeginPhotoshop: $len";
+            $endToken = '%EndPhotoshop';
+        } else {
+            $startToken = '%%BeginICCProfile: (Photoshop Profile) -1 Hex';
+            $endToken = '%%EndICCProfile';
+        }
+        Write($outfile, $startToken, $/) or $success = 0;
+        # write as an ASCII-hex comment
+        my $i;
+        my $wid = 32;
+        for ($i=0; $i<$len; $i+=$wid) {
+            $wid > $len-$i and $wid = $len-$i;
+            my $dat = substr($$dataPt, $i, $wid);
+            Write($outfile, "% ", uc(unpack('H*',$dat)), $/) or $success = 0;
+        }
+        Write($outfile, $endToken, $/) or $success = 0;
+    } else {
+        $exifTool->Warn("Can't write PS directory $dirName");
+    }
+    undef $val;
+    return $success;
+}
+
+#------------------------------------------------------------------------------
+# Write new tags information in comments section
+# Inputs: 0) ExifTool object ref, 1) output file ref, 2) reference to new tag hash
+# Returns: true on success
+sub WriteNewTags($$$)
+{
+    my ($exifTool, $outfile, $newTags) = @_;
+    my $success = 1;
+    my $tag;
+
+    # get XMP hint and remove from tags hash
+    my $xmpHint = $$newTags{XMP_HINT};
+    delete $$newTags{XMP_HINT};
+
+    foreach $tag (sort keys %$newTags) {
+        my $tagInfo = $$newTags{$tag};
+        my $newValueHash = $exifTool->GetNewValueHash($tagInfo);
+        next unless Image::ExifTool::IsCreating($newValueHash);
+        my $val = Image::ExifTool::GetNewValues($newValueHash);
+        if ($exifTool->Options('Verbose') > 1) {
+            my $out = $exifTool->Options('TextOut');
+            print $out "    + PostScript:$$tagInfo{Name} = '$val'\n";
+        }
+        $val =~ /^\d+$/ or $val = "($val)"; # add brackets around strings
+        my $buff = "%%$tag: $val$/";
+        if (length $buff > 255) {
+            $exifTool->Warn("Value for too long for $tag");
+        } else {
+            Write($outfile, $buff) or $success = 0;
+            ++$exifTool->{CHANGED};
+        }
+    }
+    # write XMP hint if necessary
+    Write($outfile, "%ADO_ContainsXMP: MainFirst$/") or $success = 0 if $xmpHint;
+
+    %$newTags = ();     # all done with new tags
+    return $success;
+}
+
+#------------------------------------------------------------------------------
+# check to be sure we haven't read past end of PS data in DOS-style file
+# Inputs: 0) RAF ref, 1) pointer to end of PS, 2) data
+# - modifies data and sets RAF to EOF if end of PS is reached
+sub CheckPSEnd($$$)
+{
+    my $pos = $_[0]->Tell();
+    if ($pos >= $_[1]) {
+        $_[0]->Seek(0, 2);   # seek to end of file so we can't read any more
+        $_[2] = substr($_[2], 0, length($_[2]) - $pos + $_[1]) if $pos > $_[1];
+    }
+}
+
+#------------------------------------------------------------------------------
+# Split into lines ending in any CR, LF or CR+LF combination
+# (this is annoying, and could be avoided if EPS files didn't mix linefeeds!)
+# Inputs: 0) data pointer, 1) reference to lines array
+# Notes: Updates data to contain next line and fills list with remaining lines
+sub SplitLine($$)
+{
+    my ($dataPt, $lines) = @_;
+    for (;;) {
+        my $endl;
+        # find the position of the first LF (\x0a)
+        $endl = pos($$dataPt), pos($$dataPt) = 0 if $$dataPt =~ /\x0a/g;
+        if ($$dataPt =~ /\x0d/g) { # find the first CR (\x0d)
+            if (defined $endl) {
+                # (remember, CR+LF is a DOS newline...)
+                $endl = pos($$dataPt) if pos($$dataPt) < $endl - 1;
+            } else {
+                $endl = pos($$dataPt);
+            }
+        } elsif (not defined $endl) {
+            push @$lines, $$dataPt;
+            last;
+        }
+        # split into separate lines
+        if (length $$dataPt == $endl) {
+            push @$lines, $$dataPt;
+            last;
+        } else {
+            push @$lines, substr($$dataPt, 0, $endl);
+            $$dataPt = substr($$dataPt, $endl);
+        }
+    }
+    $$dataPt = shift @$lines;   # set $$dataPt to first line
+}
+
+#------------------------------------------------------------------------------
+# Write PS file
+# Inputs: 0) ExifTool object reference, 1) source dirInfo reference
+# Returns: 1 on success, 0 if this wasn't a valid PS file,
+#          or -1 if a write error occurred
+sub WritePS($$)
+{
+    my ($exifTool, $dirInfo) = @_;
+    $exifTool or return 1;    # allow dummy access to autoload this package
+    my $tagTablePtr = Image::ExifTool::GetTagTable('Image::ExifTool::PostScript::Main');
+    my $raf = $$dirInfo{RAF};
+    my $outfile = $$dirInfo{OutFile};
+    my $verbose = $exifTool->Options('Verbose');
+    my $out = $exifTool->Options('TextOut');
+    my ($data, $buff, %flags, $err, $mode, $endToken);
+    my ($dos, $psStart, $psEnd, $psNewStart, $xmpHint);
+
+    $raf->Read($data, 4) == 4 or return 0;
+    return 0 unless $data =~ /^(%!PS|%!Ad|\xc5\xd0\xd3\xc6)/;
+
+    if ($data =~ /^%!Ad/) {
+        # I've seen PS files start with "%!Adobe-PS"...
+        return 0 unless $raf->Read($buff, 6) == 6 and $buff eq "obe-PS";
+        $data .= $buff;
+
+    } elsif ($data =~ /^\xc5\xd0\xd3\xc6/) {
+#
+# process DOS binary PS files
+#
+        # save DOS header then seek ahead and check PS header
+        $raf->Read($dos, 26) == 26 or return 0;
+        $dos = $data . $dos;
+        SetByteOrder('II');
+        $psStart = Get32u(\$dos, 4);
+        unless ($raf->Seek($psStart, 0) and
+                $raf->Read($data, 4) == 4 and $data eq '%!PS')
+        {
+            $exifTool->Error('Invalid PS header');
+            return 1;
+        }
+        $psEnd = $psStart + Get32u(\$dos, 8);
+        my $base = Get32u(\$dos, 20);
+        Set16u(0xffff, \$dos, 28);  # ignore checksum
+        if ($base) {
+            my %dirInfo = (
+                Parent => 'PS',
+                RAF => $raf,
+                Base => $base,
+                NoTiffEnd => 1, # no end-of-TIFF check
+            );
+            $buff = $exifTool->WriteTIFF(\%dirInfo);
+            SetByteOrder('II'); # (WriteTIFF may change this)
+            if ($buff) {
+                $buff = substr($buff, $base);   # remove header written by WriteTIFF()
+            } else {
+                # error rewriting TIFF, so just copy over original data
+                my $len = Get32u(\$dos, 24);
+                unless ($raf->Seek($base, 0) and $raf->Read($buff, $len) == $len) {
+                    $exifTool->Error('Error reading embedded TIFF');
+                    return 1;
+                }
+                $exifTool->Warn('Bad embedded TIFF');
+            }
+            Set32u(0, \$dos, 12);                   # zero metafile pointer
+            Set32u(0, \$dos, 16);                   # zero metafile length
+            Set32u(length($dos), \$dos, 20);        # set TIFF pointer
+            Set32u(length($buff), \$dos, 24);       # set TIFF length
+        } elsif (($base = Get32u(\$dos, 12)) != 0) {
+            # copy over metafile section
+            my $len = Get32u(\$dos, 16);
+            unless ($raf->Seek($base, 0) and $raf->Read($buff, $len) == $len) {
+                $exifTool->Error('Error reading metafile section');
+                return 1;
+            }
+            Set32u(length($dos), \$dos, 12);        # set metafile pointer
+        } else {
+            $buff = '';
+        }
+        $psNewStart = length($dos) + length($buff);
+        Set32u($psNewStart, \$dos, 4);  # set pointer to start of PS
+        Write($outfile, $dos, $buff) or $err = 1;
+        $raf->Seek($psStart + 4, 0);    # seek back to where we were
+    }
+#
+# rewrite PostScript data
+#
+    my $oldsep = SetInputRecordSeparator($raf);
+    unless ($oldsep and $raf->ReadLine($buff)) {
+        $exifTool->Error('Invalid PostScript data');
+        return 1;
+    }
+    $data .= $buff;
+    unless ($data =~ /^%!PS-Adobe-3.(0|1)/) {
+        if ($exifTool->Error("Document does not conform to DSC spec. Metadata may be unreadable by other apps", 1)) {
+            return 1;
+        }
+    }
+    Write($outfile, $data) or $err = 1;
+    $flags{EPS} = 1 if $data =~ /EPSF/;
+
+    # get hash of new information keyed by tagID and directories to add/edit
+    my $newTags = $exifTool->GetNewTagInfoHash($tagTablePtr);
+
+    # figure out which directories we need to write (PostScript takes priority)
+    $exifTool->InitWriteDirs(\%psMap, 'PostScript');
+    my $addDirs = $exifTool->{ADD_DIRS};
+    my $editDirs = $exifTool->{EDIT_DIRS};
+    my %doneDir;
+
+    # set XMP hint flag (1 for adding, 0 for deleting, undef for no change)
+    $xmpHint = 1 if $$addDirs{XMP};
+    $xmpHint = 0 if $exifTool->{DEL_GROUP}->{XMP};
+    $$newTags{XMP_HINT} = $xmpHint if $xmpHint;  # add special tag to newTags list
+
+    my @lines;
+    my $altnl = ($/ eq "\x0d") ? "\x0a" : "\x0d";
+
+    for (;;) {
+        if (@lines) {
+            $data = shift @lines;
+        } else {
+            $raf->ReadLine($data) or last;
+            $dos and CheckPSEnd($raf, $psEnd, $data);
+            # split line if it contains other newline sequences
+            SplitLine(\$data, \@lines) if $data =~ /$altnl/;
+        }
+        if ($endToken) {
+            # look for end token
+            if ($data =~ m/^$endToken\s*$/is) {
+                undef $endToken;
+                # found end: process this information
+                if ($mode) {
+                    $doneDir{$mode} and $exifTool->Error("Multiple $mode directories", 1);
+                    $doneDir{$mode} = 1;
+                    WritePSDirectory($exifTool, $outfile, $mode, \$buff, \%flags) or $err = 1;
+                    # write end token if we wrote the begin token
+                    Write($outfile, $data) or $err = 1 if $flags{WROTE_BEGIN};
+                    undef $buff;
+                } else {
+                    Write($outfile, $data) or $err = 1;
+                }
+            } else {
+                # buffer data in current begin/end block
+                if (not defined $mode) {
+                    # pick up XMP in unrecognized blocks for editing in place
+                    if ($data =~ /^<\?xpacket begin=.{7,13}W5M0MpCehiHzreSzNTczkc9d/ and
+                        $$editDirs{XMP})
+                    {
+                        $buff = $data;
+                        $mode = 'XMP';
+                    } else {
+                        Write($outfile, $data) or $err = 1;
+                    }
+                } elsif ($mode eq 'XMP') {
+                    $buff .= $data;
+                } else {
+                    # data is ASCII-hex encoded
+                    $data =~ tr/0-9A-Fa-f//dc;  # remove all but hex characters
+                    $buff .= pack('H*', $data); # translate from hex
+                }
+            }
+            next;
+        } elsif ($data =~ m{^(%{1,2})(Begin)(?!Object:)(.*?)[:\x0d\x0a]}i) {
+            # comments section is over... write any new tags now
+            WriteNewTags($exifTool, $outfile, $newTags) or $err = 1 if %$newTags;
+            undef $xmpHint;
+            # the beginning of a data block (can only write XMP and Photoshop)
+            my %modeLookup = (
+                _xml_code => 'XMP',
+                photoshop => 'Photoshop',
+                iccprofile => 'ICC_Profile',
+            );
+            $verbose > 1 and print $out "$2$3\n";
+            $endToken = $1 . ($2 eq 'begin' ? 'end' : 'End') . $3;
+            $mode = $modeLookup{lc($3)};
+            if ($mode and $$editDirs{$mode}) {
+                $buff = '';     # initialize buffer for this block
+                $flags{WROTE_BEGIN} = 0;
+            } else {
+                undef $mode;    # not editing this directory
+                Write($outfile, $data) or $err = 1;
+                $flags{WROTE_BEGIN} = 1;
+            }
+            next;
+        } elsif ($data =~ /^%%(?!Page:|PlateFile:|BeginObject:)(\w+): ?(.*)/s) {
+            # rewrite information from PostScript tags in comments
+            my ($tag, $val) = ($1, $2);
+            if ($$newTags{$tag}) {
+                my $tagInfo = $$newTags{$tag};
+                next unless ref $tagInfo;
+                delete $$newTags{$tag}; # write it then forget it
+                $val =~ s/\x0d*\x0a*$//;        # remove trailing CR, LF or CR/LF
+                if ($val =~ s/^\((.*)\)$/$1/) { # remove brackets if necessary
+                    $val =~ s/\) \(/, /g;       # convert contained brackets too
+                }
+                my $newValueHash = $exifTool->GetNewValueHash($tagInfo);
+                if (Image::ExifTool::IsOverwriting($newValueHash, $val)) {
+                    $verbose > 1 and print $out "    - PostScript:$$tagInfo{Name} = '$val'\n";
+                    $val = Image::ExifTool::GetNewValues($newValueHash);
+                    ++$exifTool->{CHANGED};
+                    next unless defined $val;   # next if tag is being deleted
+                    $verbose > 1 and print $out "    + PostScript:$$tagInfo{Name} = '$val'\n";
+                    $val =~ /^\d+$/ or $val = "($val)"; # add brackets around strings
+                    $buff = "%%$tag: $val$/";
+                    if (length $buff > 255) {
+                        # lines in PS documents must be less than 256 characters
+                        # (don't yet support continuation with %%+ comment)
+                        $exifTool->Warn("Value for too long for $tag");
+                    } else {
+                        $data = $buff;  # write the new value
+                    }
+                }
+            }
+        # (note: Adobe InDesign doesn't put colon after %ADO_ContainsXMP -- doh!)
+        } elsif (defined $xmpHint and $data =~ m{^%ADO_ContainsXMP:? ?(.+?)[\x0d\x0a]*$}s) {
+            # change the XMP hint if necessary
+            if ($xmpHint) {
+                $data = "%ADO_ContainsXMP: MainFirst$/" if $1 eq 'NoMain';
+            } else {
+                $data = "%ADO_ContainsXMP: NoMain$/";
+            }
+            # delete XMP hint flags
+            delete $$newTags{XMP_HINT};
+            undef $xmpHint;
+        } else {
+            # look for end of comments section
+            if (%$newTags and ($data !~ /^%\S/ or
+                $data =~ /^%(%EndComments|%Page:|%PlateFile:|%BeginObject:|.*BeginLayer)/))
+            {
+                # write new tags at end of comments section
+                WriteNewTags($exifTool, $outfile, $newTags) or $err = 1;
+                undef $xmpHint;
+            }
+            # look for start of drawing commands (AI uses "%AI5_BeginLayer",
+            # and Helios uses "%%BeginObject:")
+            if ($data =~ /^%(%Page:|%PlateFile:|%BeginObject:|.*BeginLayer)/ or
+                $data !~ m{^(%.*|\s*)$}s)
+            {
+                # we have reached the first page or drawing command, so create necessary
+                # directories and copy the rest of the file, then all done
+                my $dir;
+                my $plateFile = ($data =~ /^%%PlateFile:/);
+                # create Photoshop first, then XMP if necessary
+                foreach $dir (qw{Photoshop ICC_Profile XMP}) {
+                    next unless $$editDirs{$dir} and not $doneDir{$dir};
+                    if ($plateFile) {
+                        # PlateFile comments may contain offsets so we can't edit these files!
+                        $exifTool->Warn("Can only edit PostScript information DCS Plate files");
+                        last;
+                    }
+                    next unless $$addDirs{$dir} or $dir eq 'XMP';
+                    $flags{WROTE_BEGIN} = 0;
+                    WritePSDirectory($exifTool, $outfile, $dir, undef, \%flags) or $err = 1;
+                    $doneDir{$dir} = 1;
+                }
+                # copy rest of file
+                if ($flags{TRAILER}) {
+                    # write trailer before %%EOF
+                    for (;;) {
+                        Write($outfile, $data) or $err = 1;
+                        if (@lines) {
+                            $data = shift @lines;
+                        } else {
+                            $raf->ReadLine($data) or undef($data), last;
+                            $dos and CheckPSEnd($raf, $psEnd, $data);
+                            if ($data =~ /[\x0d\x0a]%%EOF\b/g) {
+                                # split data before "%%EOF"
+                                # (necessary if data contains other newline sequences)
+                                my $pos = pos($data) - 5;
+                                push @lines, substr($data, $pos);
+                                $data = substr($data, 0, $pos);
+                            }
+                        }
+                        last if $data =~ /^%%EOF\b/;
+                    }
+                    Write($outfile, $flags{TRAILER}) or $err = 1;
+                }
+                # simply copy the rest of the file if any data is left
+                if (defined $data) {
+                    Write($outfile, $data) or $err = 1;
+                    Write($outfile, @lines) or $err = 1 if @lines;
+                    while ($raf->Read($data, 65536)) {
+                        $dos and CheckPSEnd($raf, $psEnd, $data);
+                        Write($outfile, $data) or $err = 1;
+                    }
+                }
+                last;   # all done!
+            }
+        }
+        # write new information or copy existing line
+        Write($outfile, $data) or $err = 1;
+    }
+    if ($dos and not $err) {
+        # must go back and set length of PS section in DOS header (very dumb design)
+        if (ref $outfile eq 'SCALAR') {
+            Set32u(length($$outfile) - $psNewStart, $outfile, 8);
+        } else {
+            my $pos = tell $outfile;
+            unless (seek($outfile, 8, 0) and
+                    print $outfile Set32u($pos - $psNewStart) and
+                    seek($outfile, $pos, 0))
+            {
+                $exifTool->Error("Can't write DOS-style PS files in non-seekable stream");
+                $err = 1;
+            }
+        }
+    }
+    # issue warning if we couldn't write any information
+    unless ($err) {
+        my (@notDone, $dir);
+        delete $$newTags{XMP_HINT};
+        push @notDone, 'PostScript' if %$newTags;
+        foreach $dir (qw{Photoshop ICC_Profile XMP}) {
+            push @notDone, $dir if $$editDirs{$dir} and not $doneDir{$dir};
+        }
+        @notDone and $exifTool->Warn("Couldn't write ".join('/',@notDone).' information');
+    }
+    $endToken and $exifTool->Error("File missing $endToken");
+    return $err ? -1 : 1;
+}
+
+
+1; # end
+
+__END__
+
+=head1 NAME
+
+Image::ExifTool::WritePostScript.pl - Write PostScript meta information
+
+=head1 SYNOPSIS
+
+This file is autoloaded by Image::ExifTool::PostScript.
+
+=head1 DESCRIPTION
+
+This file contains routines to write meta information in PostScript
+documents.  Six forms of meta information may be written:
+
+    1) PostScript comments (Adobe DSC specification)
+    2) XMP information embedded in a document-level XMP stream
+    3) EXIF information embedded in a Photoshop record
+    4) IPTC information embedded in a PhotoShop record
+    5) ICC_Profile information embedded in an ICCProfile record
+    6) TIFF information embedded in DOS-style binary header
+
+=head1 NOTES
+
+Currently, information is written only in the outter-level document.
+
+Photoshop will discard meta information in a PostScript document if it has
+to rasterize the image, and it will rasterize anything that doesn't contain
+the Photoshop-specific 'ImageData' tag.  So don't expect Photoshop to read
+any meta information added to EPS images that it didn't create.
+
+The following two acronyms may be confusing since they are so similar and
+have different meanings with respect to PostScript documents:
+
+    DSC = Document Structuring Conventions
+    DCS = Desktop Color Separation
+
+=head1 REFERENCES
+
+See references in L<PostScript.pm|Image::ExifTool::PostScript>, plus:
+
+=over 4
+
+=item L<http://www.adobe.com/products/postscript/pdfs/PLRM.pdf>
+
+=item L<http://www-cdf.fnal.gov/offline/PostScript/PLRM2.pdf>
+
+=item L<http://partners.adobe.com/public/developer/en/acrobat/sdk/pdf/pdf_creation_apis_and_specs/pdfmarkReference.pdf>
+
+=back
+
+=head1 ACKNOWLEDGEMENTS
+
+Thanks to Tim Kordick for his help testing the EPS writer.
+
+=head1 AUTHOR
+
+Copyright 2003-2007, Phil Harvey (phil at owl.phy.queensu.ca)
+
+This library is free software; you can redistribute it and/or modify it
+under the same terms as Perl itself.
+
+=head1 SEE ALSO
+
+L<Image::ExifTool::PostScript(3pm)|Image::ExifTool::PostScript>,
+L<Image::ExifTool(3pm)|Image::ExifTool>
+
+=cut
Index: /gsdl/trunk/perllib/cpan/Image/ExifTool/WriteXMP.pl
===================================================================
--- /gsdl/trunk/perllib/cpan/Image/ExifTool/WriteXMP.pl	(revision 16842)
+++ /gsdl/trunk/perllib/cpan/Image/ExifTool/WriteXMP.pl	(revision 16842)
@@ -0,0 +1,1026 @@
+#------------------------------------------------------------------------------
+# File:         WriteXMP.pl
+#
+# Description:  Write XMP meta information
+#
+# Revisions:    12/19/2004 - P. Harvey Created
+#------------------------------------------------------------------------------
+package Image::ExifTool::XMP;
+
+use strict;
+use Image::ExifTool qw(:DataAccess :Utils);
+
+sub CheckXMP($$$);
+sub SetPropertyPath($$;$$);
+sub CaptureXMP($$$;$);
+
+my $debug = 0;
+
+# XMP structures (each structure is similar to a tag table so we can
+# recurse through them in SetPropertyPath() as if they were tag tables)
+my %xmpStruct = (
+    ResourceRef => {
+        NAMESPACE => 'stRef',
+        InstanceID      => { },
+        DocumentID      => { },
+        VersionID       => { },
+        RenditionClass  => { },
+        RenditionParams => { },
+        Manager         => { },
+        ManagerVariant  => { },
+        ManageTo        => { },
+        ManageUI        => { },
+    },
+    ResourceEvent => {
+        NAMESPACE => 'stEvt',
+        action          => { },
+        instanceID      => { },
+        parameters      => { },
+        softwareAgent   => { },
+        when            => { },
+    },
+    JobRef => {
+        NAMESPACE => 'stJob',
+        name        => { },
+        id          => { },
+        url         => { },
+    },
+    Version => {
+        NAMESPACE => 'stVer',
+        comments    => { },
+        event       => { Struct => 'ResourceEvent' },
+        modifyDate  => { },
+        modifier    => { },
+        version     => { },
+    },
+    Thumbnail => {
+        NAMESPACE => 'xapGImg',
+        height      => { },
+        width       => { },
+       'format'     => { },
+        image       => { },
+    },
+    IdentifierScheme => {
+        NAMESPACE => 'xmpidq',
+        Scheme      => { }, # qualifier for xmp:Identifier only
+    },
+    Dimensions => {
+        NAMESPACE => 'stDim',
+        w           => { },
+        h           => { },
+        unit        => { },
+    },
+    Colorant => {
+        NAMESPACE => 'xapG',
+        swatchName  => { },
+        mode        => { },
+        type        => { },
+        cyan        => { },
+        magenta     => { },
+        yellow      => { },
+        black       => { },
+        red         => { },
+        green       => { },
+        blue        => { },
+        L           => { },
+        A           => { },
+        B           => { },
+    },
+    Font => {
+        NAMESPACE => 'stFnt',
+        fontName    => { },
+        fontFamily  => { },
+        fontFace    => { },
+        fontType    => { },
+        versionString => { },
+        composite   => { },
+        fontFileName=> { },
+        childFontFiles=> { List => 'Seq' },
+    },
+    # the following stuctures are different:  They don't have
+    # their own namespaces -- instead they use the parent namespace
+    Flash => {
+        NAMESPACE => 'exif',
+        Fired       => { },
+        Return      => { },
+        Mode        => { },
+        Function    => { },
+        RedEyeMode  => { },
+    },
+    OECF => {
+        NAMESPACE => 'exif',
+        Columns     => { },
+        Rows        => { },
+        Names       => { },
+        Values      => { },
+    },
+    CFAPattern => {
+        NAMESPACE => 'exif',
+        Columns     => { },
+        Rows        => { },
+        Values      => { },
+    },
+    DeviceSettings => {
+        NAMESPACE => 'exif',
+        Columns     => { },
+        Rows        => { },
+        Settings    => { },
+    },
+    # Iptc4xmpCore structures
+    ContactInfo => {
+        NAMESPACE => 'Iptc4xmpCore',
+        CiAdrCity   => { },
+        CiAdrCtry   => { },
+        CiAdrExtadr => { },
+        CiAdrPcode  => { },
+        CiAdrRegion => { },
+        CiEmailWork => { },
+        CiTelWork   => { },
+        CiUrlWork   => { },
+    },
+    # Dynamic Media structures
+    BeatSpliceStretch => {
+        NAMESPACE => 'xmpDM',
+        useFileBeatsMarker  => { },
+        riseInDecibel       => { },
+        riseInTimeDuration  => { },
+    },
+    Marker => {
+        NAMESPACE => 'xmpDM',
+        startTime   => { },
+        duration    => { },
+        comment     => { },
+        name        => { },
+        location    => { },
+        target      => { },
+        type        => { },
+    },
+    Media => {
+        NAMESPACE => 'xmpDM',
+        path        => { },
+        track       => { },
+        startTime   => { },
+        duration    => { },
+        managed     => { },
+        webStatement=> { },
+    },
+    ProjectLink => {
+        NAMESPACE => 'xmpDM',
+        type        => { },
+        path        => { },
+    },
+    ResampleStretch => {
+        NAMESPACE => 'xmpDM',
+        quality     => { },
+    },
+    Time => {
+        NAMESPACE => 'xmpDM',
+        value       => { },
+        scale       => { },
+    },
+    Timecode => {
+        NAMESPACE => 'xmpDM',
+        timeValue   => { },
+        timeFormat  => { },
+    },
+    TimeScaleStretch => {
+        NAMESPACE => 'xmpDM',
+        quality     => { },
+        frameSize   => { },
+        frameOverlappingPercentage => { },
+    },
+);
+
+# Lookup to translate our namespace prefixes into URI's.  This list need
+# not be complete, but it must contain an entry for each namespace prefix
+# (NAMESPACE) for writable tags in the XMP tables or the table above
+my %nsURI = (
+    aux       => 'http://ns.adobe.com/exif/1.0/aux/',
+    cc        => 'http://web.resource.org/cc/',
+    crs       => 'http://ns.adobe.com/camera-raw-settings/1.0/',
+    crss      => 'http://ns.adobe.com/camera-raw-saved-settings/1.0/',
+    dc        => 'http://purl.org/dc/elements/1.1/',
+    exif      => 'http://ns.adobe.com/exif/1.0/',
+    iX        => 'http://ns.adobe.com/iX/1.0/',
+    pdf       => 'http://ns.adobe.com/pdf/1.3/',
+    pdfx      => 'http://ns.adobe.com/pdfx/1.3/',
+    photoshop => 'http://ns.adobe.com/photoshop/1.0/',
+    rdf       => 'http://www.w3.org/1999/02/22-rdf-syntax-ns#',
+    rdfs      => 'http://www.w3.org/2000/01/rdf-schema#',
+    stDim     => 'http://ns.adobe.com/xap/1.0/sType/Dimensions#',
+    stEvt     => 'http://ns.adobe.com/xap/1.0/sType/ResourceEvent#',
+    stFnt     => 'http://ns.adobe.com/xap/1.0/sType/Font#',
+    stJob     => 'http://ns.adobe.com/xap/1.0/sType/Job#',
+    stRef     => 'http://ns.adobe.com/xap/1.0/sType/ResourceRef#',
+    stVer     => 'http://ns.adobe.com/xap/1.0/sType/Version#',
+    tiff      => 'http://ns.adobe.com/tiff/1.0/',
+   'x'        => 'adobe:ns:meta/',
+    xapG      => 'http://ns.adobe.com/xap/1.0/g/',
+    xapGImg   => 'http://ns.adobe.com/xap/1.0/g/img/',
+    xmp       => 'http://ns.adobe.com/xap/1.0/',
+    xmpBJ     => 'http://ns.adobe.com/xap/1.0/bj/',
+    xmpDM     => 'http://ns.adobe.com/xmp/1.0/DynamicMedia/',
+    xmpMM     => 'http://ns.adobe.com/xap/1.0/mm/',
+    xmpRights => 'http://ns.adobe.com/xap/1.0/rights/',
+    xmpTPg    => 'http://ns.adobe.com/xap/1.0/t/pg/',
+    xmpidq    => 'http://ns.adobe.com/xmp/Identifier/qual/1.0/',
+    xmpPLUS   => 'http://ns.adobe.com/xap/1.0/PLUS/',
+    dex       => 'http://ns.optimasc.com/dex/1.0/',
+    mediapro  => 'http://ns.iview-multimedia.com/mediapro/1.0/',
+    Iptc4xmpCore => 'http://iptc.org/std/Iptc4xmpCore/1.0/xmlns/',
+    MicrosoftPhoto => 'http://ns.microsoft.com/photo/1.0',
+    lr        => 'http://ns.adobe.com/lightroom/1.0/',
+    DICOM     => 'http://ns.adobe.com/DICOM/',
+);
+
+my $x_toolkit = "x:xmptk='Image::ExifTool $Image::ExifTool::VERSION'";
+my $rdfDesc = 'rdf:Description';
+#
+# packet/xmp/rdf headers and trailers
+#
+my $pktOpen = "<?xpacket begin='\xef\xbb\xbf' id='W5M0MpCehiHzreSzNTczkc9d'?>\n";
+my $xmlOpen = "<?xml version='1.0' encoding='UTF-8'?>\n";
+my $xmpOpen = "<x:xmpmeta xmlns:x='$nsURI{x}' $x_toolkit>\n";
+my $rdfOpen = "<rdf:RDF xmlns:rdf='$nsURI{rdf}'>\n";
+my $rdfClose = "</rdf:RDF>\n";
+my $xmpClose = "</x:xmpmeta>\n";
+my $pktCloseW =  "<?xpacket end='w'?>"; # writable by default
+my $pktCloseR =  "<?xpacket end='r'?>";
+
+# Update XMP tag tables when this library is loaded:
+# - generate all TagID's (required when writing)
+# - generate PropertyPath for structure elements
+# - add necessary inverse conversion routines
+{
+    my $mainTable = GetTagTable('Image::ExifTool::XMP::Main');
+    GenerateTagIDs($mainTable);
+    my ($mainTag, $ns, $tag);
+    foreach $mainTag (keys %$mainTable) {
+        my $mainInfo = $mainTable->{$mainTag};
+        next unless ref $mainInfo eq 'HASH' and $mainInfo->{SubDirectory};
+        my $table = GetTagTable($mainInfo->{SubDirectory}->{TagTable});
+        $$table{WRITE_PROC} = \&WriteXMP;   # set WRITE_PROC for all tables
+        GenerateTagIDs($table);
+        $table->{CHECK_PROC} = \&CheckXMP;  # add our write check routine
+        foreach $tag (TagTableKeys($table)) {
+            my $tagInfo = $$table{$tag};
+            next unless ref $tagInfo eq 'HASH';
+            # must set PropertyPath now for all tags that are Struct elements
+            # (normal tags will get set later if they are actually written)
+            SetPropertyPath($table, $tag) if $$tagInfo{Struct};
+            my $format = $$tagInfo{Writable};
+            next unless $format and $format eq 'date';
+            # add dummy conversion for dates (for now...)
+            $$tagInfo{PrintConvInv} = '$val' unless $$tagInfo{PrintConvInv};
+            $$tagInfo{ValueConvInv} = '$val' unless $$tagInfo{ValueConvInv};
+        }
+        # add new namespace if NAMESPACE is ns/uri pair
+        next unless ref $$table{NAMESPACE};
+        my $nsRef = $$table{NAMESPACE};
+        # recognize as either a list or hash
+        if (ref $nsRef eq 'ARRAY') {
+            $ns = $$nsRef[0];
+            $nsURI{$ns} = $$nsRef[1];
+        } else { # must be a hash
+            ($ns) = keys %$nsRef;
+            $nsURI{$ns} = $$nsRef{$ns};
+        }
+        $$table{NAMESPACE} = $ns;
+    }
+}
+
+#------------------------------------------------------------------------------
+# Validate XMP packet and set read or read/write mode
+# Inputs: 0) XMP data reference, 1) 'r' = read only, 'w' or undef = read/write
+# Returns: true if XMP is good (and adds packet header/trailer if necessary)
+sub ValidateXMP($;$)
+{
+    my ($xmpPt, $mode) = @_;
+    unless ($$xmpPt =~ /^\0*<\0*\?\0*x\0*p\0*a\0*c\0*k\0*e\0*t/) {
+        return '' unless $$xmpPt =~ /^<x(mp)?:xmpmeta/;
+        # add required xpacket header/trailer
+        $$xmpPt = $pktOpen . $$xmpPt . $pktCloseW;
+    }
+    $mode = 'w' unless $mode;
+    my $end = substr($$xmpPt, -32, 32);
+    # check for proper xpacket trailer and set r/w mode if necessary
+    return '' unless $end =~ s/(e\0*n\0*d\0*=\0*['"]\0*)([rw])(\0*['"]\0*\?\0*>)/$1$mode$3/;
+    substr($$xmpPt, -32, 32) = $end if $2 ne $mode;
+    return 1;
+}
+
+#------------------------------------------------------------------------------
+# Check XMP values for validity and format accordingly
+# Inputs: 0) ExifTool object reference, 1) tagInfo hash reference,
+#         2) raw value reference
+# Returns: error string or undef (and may change value) on success
+sub CheckXMP($$$)
+{
+    my ($exifTool, $tagInfo, $valPtr) = @_;
+    # convert value from Latin if necessary
+    if ($exifTool->{OPTIONS}->{Charset} eq 'Latin' and $$valPtr =~ /[\x80-\xff]/) {
+        # convert from Latin to UTF-8
+        my $val = Image::ExifTool::Latin2Unicode($$valPtr,'n');
+        $$valPtr = Image::ExifTool::Unicode2UTF8($val,'n');
+    }
+    my $format = $tagInfo->{Writable};
+    # if no format specified, value is a simple string
+    return undef unless $format and $format ne 'string';
+    if ($format eq 'rational' or $format eq 'real') {
+        # make sure the value is a valid floating point number
+        Image::ExifTool::IsFloat($$valPtr) or return 'Not a floating point number';
+        if ($format eq 'rational') {
+            $$valPtr = join('/', Image::ExifTool::Rationalize($$valPtr));
+        }
+    } elsif ($format eq 'integer') {
+        # make sure the value is integer
+        if (Image::ExifTool::IsInt($$valPtr)) {
+            # no conversion required (converting to 'int' would remove leading '+')
+        } elsif (Image::ExifTool::IsHex($$valPtr)) {
+            $$valPtr = hex($$valPtr);
+        } else {
+            return 'Not an integer';
+        }
+    } elsif ($format eq 'date') {
+        if ($$valPtr =~ /(\d{4}):(\d{2}):(\d{2}) (\d{2}:\d{2}(?::\d{2}(?:\.\d*)?)?)(.*)/) {
+            my ($y, $m, $d, $t, $tz) = ($1, $2, $3, $4, $5);
+            # use 'Z' for timezone unless otherwise specified
+            $tz = 'Z' unless $tz and $tz =~ /([+-]\d{2}:\d{2})/;
+            $$valPtr = "$y-$m-${d}T$t$tz";
+        } elsif ($$valPtr =~ /^\s*\d{4}(:\d{2}){0,2}\s*$/) {
+            # this is just a date (YYYY, YYYY-MM or YYYY-MM-DD)
+            $$valPtr =~ tr/:/-/;
+        } elsif ($$valPtr =~ /^\s*(\d{2}:\d{2}:\d{2})(.*)\s*$/) {
+            # this is just a time
+            my ($t, $tz) = ($1, $2);
+            $tz = 'Z' unless $tz and $tz =~ /([+-]\d{2}:\d{2})/;
+            $$valPtr = "$t$tz";
+        } else {
+            return "Invalid date or time format (should be YYYY:MM:DD HH:MM:SS[+/-HH:MM])";
+        }
+    } elsif ($format eq 'lang-alt') {
+        # nothing to do
+    } elsif ($format eq 'boolean') {
+        if (not $$valPtr or $$valPtr =~ /false/i or $$valPtr =~ /^no$/i) {
+            $$valPtr = 'False';
+        } else {
+            $$valPtr = 'True';
+        }
+    } elsif ($format eq '1') {
+        # this is the entire XMP data block
+        return 'Invalid XMP data' unless ValidateXMP($valPtr);
+    } else {
+        return "Unknown XMP format: $format";
+    }
+    return undef;   # success!
+}
+
+#------------------------------------------------------------------------------
+# Get PropertyPath for specified tagInfo
+# Inputs: 0) tagInfo reference
+# Returns: PropertyPath string
+sub GetPropertyPath($)
+{
+    my $tagInfo = shift;
+    unless ($$tagInfo{PropertyPath}) {
+        SetPropertyPath($$tagInfo{Table}, $$tagInfo{TagID});
+    }
+    return $$tagInfo{PropertyPath};
+}
+
+#------------------------------------------------------------------------------
+# Set PropertyPath for specified tag (also for any structure elements)
+# Inputs: 0) tagTable reference, 1) tagID, 2) structure reference (or undef),
+#         3) property list up to this point (or undef), 4) true if tag is a list
+sub SetPropertyPath($$;$$)
+{
+    my ($tagTablePtr, $tagID, $structPtr, $propList) = @_;
+    my $table = $structPtr || $tagTablePtr;
+    my $tagInfo = $$table{$tagID};
+    my $ns = $$table{NAMESPACE};
+    # don't override existing main table entry if already set by a Struct
+    return if not $structPtr and $$tagInfo{PropertyPath};
+    $ns or warn("No namespace for $tagID\n"), return;
+    my (@propList, $listType);
+    $propList and @propList = @$propList;
+    push @propList, "$ns:$tagID";
+    # lang-alt lists are handled specially, signified by Writable='lang-alt'
+    if ($$tagInfo{Writable} and $$tagInfo{Writable} eq 'lang-alt') {
+        $listType = 'Alt';
+        # remove language code from property path if it exists
+        $propList[-1] =~ s/-$$tagInfo{LangCode}$// if $$tagInfo{LangCode};
+    } else {
+        $listType = $$tagInfo{List};
+    }
+    # add required properties if this is a list
+    push @propList, "rdf:$listType", 'rdf:li 000' if $listType and $listType ne '1';
+    # set PropertyPath for all elements of this structure if necessary
+    if ($$tagInfo{Struct}) {
+        my $struct = $xmpStruct{$$tagInfo{Struct}};
+        $struct or warn("No XMP $$tagInfo{Struct} structure!\n"), return;
+        my $tag;
+        foreach $tag (keys %$struct) {
+            next if $tag eq 'NAMESPACE';
+            SetPropertyPath($tagTablePtr, $tag, $struct, \@propList);
+        }
+    }
+    # use tagInfo for combined tag name if this was a Struct
+    if ($structPtr) {
+        my $tagName = GetXMPTagID(\@propList);
+        $$tagTablePtr{$tagName} or warn("Tag $tagName not found!\n"), return;
+        $tagInfo = $$tagTablePtr{$tagName};
+        # must check again for List's at this level
+        if ($$tagInfo{Writable} and $$tagInfo{Writable} eq 'lang-alt') {
+            $listType = 'Alt';
+        } else {
+            $listType = $$tagInfo{List};
+        }
+        push @propList, "rdf:$listType", 'rdf:li 000' if $listType and $listType ne '1';
+    }
+    # set property path for tagInfo in main table
+    $$tagInfo{PropertyPath} = join '/', @propList;
+}
+
+#------------------------------------------------------------------------------
+# Save XMP property name/value for rewriting
+# Inputs: 0) ExifTool object reference
+#         1) reference to array of XMP property path (last is current property)
+#         2) property value, 3) optional reference to hash of property attributes
+sub CaptureXMP($$$;$)
+{
+    my ($exifTool, $propList, $val, $attrs) = @_;
+    return unless defined $val and @$propList > 2;
+    if ($$propList[0] =~ /^x:x(a|m)pmeta$/ and
+        $$propList[1] eq 'rdf:RDF' and
+        $$propList[2] =~ /$rdfDesc( |$)/)
+    {
+        # no properties to save yet if this is just the description
+        return unless @$propList > 3;
+        # save information about this property
+        my $capture = $exifTool->{XMP_CAPTURE};
+        my $path = join('/', @$propList[3..$#$propList]);
+        if (defined $$capture{$path}) {
+            $exifTool->{XMP_ERROR} = "Duplicate XMP property: $path";
+        } else {
+            $$capture{$path} = [$val, $attrs || { }];
+        }
+    } else {
+        $exifTool->{XMP_ERROR} = 'Improperly enclosed XMP property: ' . join('/',@$propList);
+    }
+}
+
+#------------------------------------------------------------------------------
+# Save information about resource containing blank node with nodeID
+# Inputs: 0) reference to blank node information hash
+#         1) reference to property list
+#         2) property value
+#         3) [optional] reference to attribute hash
+# Notes: This routine and ProcessBlankInfo() are also used for reading information, but
+#        are uncommon so are put in this file to reduce compile time for the common case
+sub SaveBlankInfo($$$;$)
+{
+    my ($blankInfo, $propListPt, $val, $attrs) = @_;
+
+    my $propPath = join '/', @$propListPt;
+    my @ids = ($propPath =~ m{ #([^ /]*)}g);
+    my $id;
+    # split the property path at each nodeID
+    foreach $id (@ids) {
+        my ($pre, $prop, $post) = ($propPath =~ m{^(.*?)/([^/]*) #$id((/.*)?)$});
+        defined $pre or warn("internal error parsing nodeID's"), next;
+        # the element with the nodeID should be in the path prefix for subject
+        # nodes and the path suffix for object nodes
+        unless ($prop eq $rdfDesc) {
+            if ($post) {
+                $post = "/$prop$post";
+            } else {
+                $pre = "$pre/$prop";
+            }
+        }
+        $blankInfo->{Prop}->{$id}->{Pre}->{$pre} = 1;
+        if ((defined $post and length $post) or (defined $val and length $val)) {
+            # save the property value and attributes for each unique path suffix
+            $blankInfo->{Prop}->{$id}->{Post}->{$post} = [ $val, $attrs, $propPath ];
+        }
+    }
+}
+
+#------------------------------------------------------------------------------
+# Process blank-node information
+# Inputs: 0) ExifTool object ref, 1) tag table ref,
+#         2) blank node information hash ref, 3) flag set for writing
+sub ProcessBlankInfo($$$;$)
+{
+    my ($exifTool, $tagTablePtr, $blankInfo, $isWriting) = @_;
+    $exifTool->VPrint(1, "  [Elements with nodeID set:]\n") unless $isWriting;
+    my ($id, $pre, $post);
+    # handle each nodeID separately
+    foreach $id (sort keys %{$$blankInfo{Prop}}) {
+        my $path = $blankInfo->{Prop}->{$id};
+        # flag all resource names so we can warn later if some are unused
+        my %unused;
+        foreach $post (keys %{$path->{Post}}) {
+            $unused{$post} = 1;
+        }
+        # combine property paths for all possible paths through this node
+        foreach $pre (sort keys %{$path->{Pre}}) {
+            # there will be no description for the object of a blank node
+            next unless $pre =~ m{/$rdfDesc/};
+            foreach $post (sort keys %{$path->{Post}}) {
+                my @propList = split m{/}, "$pre$post";
+                my ($val, $attrs) = @{$path->{Post}->{$post}};
+                if ($isWriting) {
+                    CaptureXMP($exifTool, \@propList, $val, $attrs);
+                } else {
+                    FoundXMP($exifTool, $tagTablePtr, \@propList, $val);
+                }
+                delete $unused{$post};
+            }
+        }
+        # save information from unused properties (if RDF is malformed like f-spot output)
+        if (%unused) {
+            $exifTool->Options('Verbose') and $exifTool->Warn('An XMP resource is about nothing');
+            foreach $post (sort keys %unused) {
+                my ($val, $attrs, $propPath) = @{$path->{Post}->{$post}};
+                my @propList = split m{/}, $propPath;
+                if ($isWriting) {
+                    CaptureXMP($exifTool, \@propList, $val, $attrs);
+                } else {
+                    FoundXMP($exifTool, $tagTablePtr, \@propList, $val);
+                }
+            }
+        }
+    }
+}
+
+#------------------------------------------------------------------------------
+# Convert path to namespace used in file (this is a pain, but the XMP
+# spec only suggests 'preferred' namespace prefixes...)
+# Inputs: 0) ExifTool object reference, 1) property path
+# Returns: conforming property path
+sub ConformPathToNamespace($$)
+{
+    my ($exifTool, $path) = @_;
+    my @propList = split('/',$path);
+    my ($prop, $newKey);
+    my $nsUsed = $exifTool->{XMP_NS};
+    foreach $prop (@propList) {
+        my ($ns, $tag) = $prop =~ /(.+?):(.*)/;
+        next if $$nsUsed{$ns};
+        my $uri = $nsURI{$ns};
+        unless ($uri) {
+            warn "No URI for namepace prefix $ns!\n";
+            next;
+        }
+        my $ns2;
+        foreach $ns2 (keys %$nsUsed) {
+            next unless $$nsUsed{$ns2} eq $uri;
+            # use the existing namespace prefix instead of ours
+            $prop = "$ns2:$tag";
+            last;
+        }
+    }
+    return join('/',@propList);
+}
+
+#------------------------------------------------------------------------------
+# sort tagInfo hash references by tag name
+sub ByTagName
+{
+    return $$a{Name} cmp $$b{Name};
+}
+
+#------------------------------------------------------------------------------
+# Write XMP information
+# Inputs: 0) ExifTool object reference, 1) source dirInfo reference,
+#         2) [optional] tag table reference
+# Returns: with tag table: new XMP data (may be empty if no XMP data) or undef on error
+#          without tag table: 1 on success, 0 if not valid XMP file, -1 on write error
+# Notes: May set dirInfo InPlace flag to rewrite with specified DirLen
+#        May set dirInfo ReadOnly flag to write as read-only XMP ('r' mode and no padding)
+sub WriteXMP($$;$)
+{
+    my ($exifTool, $dirInfo, $tagTablePtr) = @_;
+    $exifTool or return 1;    # allow dummy access to autoload this package
+    my $dataPt = $$dirInfo{DataPt};
+    my $dirStart = $$dirInfo{DirStart} || 0;
+    my (%capture, %nsUsed, $xmpErr, $uuid);
+    my $changed = 0;
+    my $xmpFile = (not $tagTablePtr);   # this is an XMP data file if no $tagTablePtr
+    my $preferred = $xmpFile;   # write XMP as preferred if this is an XMP file
+    my $verbose = $exifTool->Options('Verbose');
+#
+# extract existing XMP information into %capture hash
+#
+    # define hash in ExifTool object to capture XMP information (also causes
+    # CaptureXMP() instead of FoundXMP() to be called from ParseXMPElement())
+    #
+    # The %capture hash is keyed on the complete property path beginning after
+    # rdf:RDF/rdf:Description/.  The values are array references with the
+    # following entries: 0) value, 1) attribute hash reference.
+    $exifTool->{XMP_CAPTURE} = \%capture;
+    $exifTool->{XMP_NS} = \%nsUsed;
+
+    if ($dataPt or $xmpFile) {
+        delete $exifTool->{XMP_ERROR};
+        delete $exifTool->{XMP_UUID};
+        # extract all existing XMP information (to the XMP_CAPTURE hash)
+        my $success = ProcessXMP($exifTool, $dirInfo, $tagTablePtr);
+        # don't continue if there is nothing to parse or if we had a parsing error
+        unless ($success and not $exifTool->{XMP_ERROR}) {
+            my $err = $exifTool->{XMP_ERROR} || 'Error parsing XMP';
+            # may ignore this error only if we were successful
+            if ($xmpFile) {
+                my $raf = $$dirInfo{RAF};
+                # allow empty XMP data so we can create something from nothing
+                if ($success or not $raf->Seek(0,2) or $raf->Tell()) {
+                    # no error message if not an XMP file
+                    return 0 unless $exifTool->{XMP_ERROR};
+                    if ($exifTool->Error($err, $success)) {
+                        delete $exifTool->{XMP_CAPTURE};
+                        return 0;
+                    }
+                }
+            } else {
+                if ($exifTool->Warn($err, $success)) {
+                    delete $exifTool->{XMP_CAPTURE};
+                    return undef;
+                }
+            }
+        }
+        $uuid = $exifTool->{XMP_UUID} || '';
+        delete $exifTool->{XMP_ERROR};
+        delete $exifTool->{XMP_UUID};
+    } else {
+        $uuid = '';
+    }
+#
+# add, delete or change information as specified
+#
+    # get hash of all information we want to change
+    # (sorted by tag name so alternate languages come last)
+    my @tagInfoList = sort ByTagName $exifTool->GetNewTagInfoList();
+    my $tagInfo;
+    foreach $tagInfo (@tagInfoList) {
+        next unless $exifTool->GetGroup($tagInfo, 0) eq 'XMP';
+        my $tag = $tagInfo->{TagID};
+        my $path = GetPropertyPath($tagInfo);
+        unless ($path) {
+            $exifTool->Warn("Can't write XMP:$tag (namespace unknown)");
+            next;
+        }
+        # change our property path namespace prefixes to conform
+        # to the ones used in this file
+        $path = ConformPathToNamespace($exifTool, $path);
+        # find existing property
+        my $capList = $capture{$path};
+        # MicrosoftPhoto screws up the case of some tags, so test for this
+        unless ($capList) {
+            my ($path2) = grep /^\Q$path\E$/i, keys %capture;
+            $path2 and $capList = $capture{$path = $path2};
+        }
+        my $newValueHash = $exifTool->GetNewValueHash($tagInfo);
+        my $overwrite = Image::ExifTool::IsOverwriting($newValueHash);
+        my $writable = $$tagInfo{Writable} || '';
+        my (%attrs, $deleted, $added);
+        # delete existing entry if necessary
+        if ($capList) {
+            # take attributes from old values if they exist
+            %attrs = %{$capList->[1]};
+            if ($overwrite) {
+                my ($delPath, @matchingPaths, $oldLang, $delLang);
+                # check to see if this is an indexed list item
+                if ($path =~ / /) {
+                    my $pathPattern;
+                    ($pathPattern = $path) =~ s/ 000/ \\d\{3\}/g;
+                    @matchingPaths = sort grep(/^$pathPattern$/, keys %capture);
+                } else {
+                    push @matchingPaths, $path;
+                }
+                foreach $path (@matchingPaths) {
+                    my ($val, $attrs) = @{$capture{$path}};
+                    if ($overwrite < 0) {
+                        # only overwrite specific values
+                        next unless Image::ExifTool::IsOverwriting($newValueHash, $val);
+                    }
+                    if ($writable eq 'lang-alt') {
+                        # get original language code (lc for comparisons)
+                        $oldLang = lc($$attrs{'xml:lang'} || 'x-default');
+                        # delete all if deleting "x-default" or writing with no LangCode
+                        # (XMP spec requires x-default language exist and be first in list)
+                        if ($oldLang eq 'x-default' and not ($newValueHash->{Value} or
+                            ($$tagInfo{LangCode} and $$tagInfo{LangCode} ne 'x-default')))
+                        {
+                            $delLang = 1;   # delete all languages
+                            $overwrite = 1; # force overwrite
+                        }
+                        if ($$tagInfo{LangCode} and not $delLang) {
+                            # only overwrite specified language
+                            next unless lc($$tagInfo{LangCode}) eq $oldLang;
+                        }
+                    }
+                    if ($verbose > 1) {
+                        my $grp = $exifTool->GetGroup($tagInfo, 1);
+                        my $tagName = $$tagInfo{Name};
+                        $tagName =~ s/-$$tagInfo{LangCode}$// if $$tagInfo{LangCode};
+                        $tagName .= '-' . $$attrs{'xml:lang'} if $$attrs{'xml:lang'};
+                        $exifTool->VPrint(1, "    - $grp:$tagName = '$val'\n");
+                    }
+                    # save attributes and path from first deleted property
+                    # so we can replace it exactly
+                    unless ($delPath) {
+                        %attrs = %$attrs;
+                        $delPath = $path;
+                    }
+                    delete $capture{$path};
+                    ++$changed;
+                }
+                next unless $delPath or $$tagInfo{List} or $oldLang;
+                if ($delPath) {
+                    $path = $delPath;
+                    $deleted = 1;
+                } else {
+                    # don't change tag if we couldn't delete old copy
+                    # unless this is a list or an lang-alt tag
+                    next unless $$tagInfo{List} or $oldLang;
+                    $path =~ m/ (\d{3})/g or warn "Internal error: no list index!\n", next;
+                    $added = $1;
+                }
+            } elsif ($path =~ m/ (\d{3})/g) {
+                $added = $1;
+            }
+            if (defined $added) {
+                # add to end of list
+                my $pos = pos($path) - 3;
+                for (;;) {
+                    substr($path, $pos, 3) = ++$added;
+                    last unless $capture{$path};
+                }
+            }
+        }
+        # check to see if we want to create this tag
+        # (create non-avoided tags in XMP data files by default)
+        my $isCreating = (Image::ExifTool::IsCreating($newValueHash) or
+                          ($preferred and not $$tagInfo{Avoid} and
+                            not defined $$newValueHash{Shift}));
+
+        # don't add new values unless...
+            # ...tag existed before and was deleted, or we added it to a list
+        next unless $deleted or defined $added or
+            # ...tag didn't exist before and we are creating it
+            (not $capList and $isCreating);
+
+        # get list of new values (all done if no new values specified)
+        my @newValues = Image::ExifTool::GetNewValues($newValueHash) or next;
+
+        # set language attribute for lang-alt lists
+        if ($writable eq 'lang-alt') {
+            $attrs{'xml:lang'} = $$tagInfo{LangCode} || 'x-default';
+            # must generate x-default entry as first entry if it didn't exist
+            unless ($capList or lc($attrs{'xml:lang'}) eq 'x-default') {
+                my $newValue = EscapeXML($newValues[0]);
+                $capture{$path} = [ $newValue, { %attrs, 'xml:lang' => 'x-default' } ];
+                if ($verbose > 1) {
+                    my $tagName = $$tagInfo{Name};
+                    $tagName =~ s/-$$tagInfo{LangCode}$/-x-default/;
+                    my $grp = $exifTool->GetGroup($tagInfo, 1);
+                    $exifTool->VPrint(1, "    + $grp:$tagName = '$newValue'\n");
+                }
+                $path =~ s/ 000/ 001/ or warn "Internal error: no list index!\n", next;
+            }
+        }
+        
+        # add new value(s) to %capture hash
+        for (;;) {
+            my $newValue = EscapeXML(shift @newValues);
+            $capture{$path} = [ $newValue, \%attrs ];
+            if ($verbose > 1) {
+                my $grp = $exifTool->GetGroup($tagInfo, 1);
+                $exifTool->VPrint(1, "    + $grp:$$tagInfo{Name} = '$newValue'\n");
+            }
+            ++$changed;
+            last unless @newValues;
+            $path =~ m/ (\d{3})/g or warn("Internal error: no list index!\n"), next;
+            my $listIndex = $1;
+            my $pos = pos($path) - 3;
+            for (;;) {
+                substr($path, $pos, 3) = ++$listIndex;
+                last unless $capture{$path};
+            }
+            $capture{$path} and warn("Too many entries in XMP list!\n"), next;
+        }
+    }
+    # remove the ExifTool members we created
+    delete $exifTool->{XMP_CAPTURE};
+    delete $exifTool->{XMP_NS};
+
+    # return now if we didn't change anything
+    unless ($changed) {
+        return undef unless $xmpFile;   # just rewrite original XMP
+        # get DataPt again because it may have been set by ProcessXMP
+        $dataPt = $$dirInfo{DataPt};
+        unless (defined $dataPt) {
+            $exifTool->Error("Nothing to write");
+            return 1;
+        }
+        return 1 if Write($$dirInfo{OutFile}, $$dataPt);
+        return -1;
+    }
+#
+# write out the new XMP information
+#
+    # start writing the XMP data
+    my $newData = '';
+    if ($$exifTool{XMP_NO_XPACKET}) {
+        # write BOM if flag is set
+        $newData .= "\xef\xbb\xbf" if $$exifTool{XMP_NO_XPACKET} == 2;
+    } else {
+        $newData .= $pktOpen;
+    }
+    $newData .= $xmlOpen if $$exifTool{XMP_IS_XML};
+    $newData .= $xmpOpen . $rdfOpen;
+
+    # initialize current property path list
+    my @curPropList;
+    my (%nsCur, $prop, $n);
+    my @pathList = sort keys %capture;
+
+    while (@pathList) {
+        my $path = shift @pathList;
+        my @propList = split('/',$path); # get property list
+        # must open/close rdf:Description too
+        unshift @propList, $rdfDesc;
+        # make sure we have defined all necessary namespaces
+        my (%nsNew, $newDesc);
+        foreach $prop (@propList) {
+            $prop =~ /(.*):/ or next;
+            $1 eq 'rdf' and next;   # rdf namespace already defined
+            my $nsNew = $nsUsed{$1};
+            unless ($nsNew) {
+                $nsNew = $nsURI{$1}; # we must have added a namespace
+                unless ($nsNew) {
+                    $xmpErr = "Undefined XMP namespace: $1";
+                    next;
+                }
+            }
+            $nsNew{$1} = $nsNew;
+            # need a new description if any new namespaces
+            $newDesc = 1 unless $nsCur{$1};
+        }
+        my $closeTo = 0;
+        if ($newDesc) {
+            # look forward to see if we will want to also open other namespaces
+            # (this is necessary to keep lists from being broken if a property
+            #  introduces a new namespace; plus it improves formatting)
+            my ($path2, $ns2);
+            foreach $path2 (@pathList) {
+                my @ns2s = ($path2 =~ m{(?:^|/)([^/]+?):}g);
+                my $opening = 0;
+                foreach $ns2 (@ns2s) {
+                    next if $ns2 eq 'rdf';
+                    $nsNew{$ns2} and ++$opening, next;
+                    last unless $opening and $nsURI{$ns2};
+                    # also open this namespace
+                    $nsNew{$ns2} = $nsURI{$ns2};
+                }
+                last unless $opening;
+            }
+        } else {
+            # find first property where the current path differs from the new path
+            for ($closeTo=0; $closeTo<@curPropList; ++$closeTo) {
+                last unless $closeTo < @propList;
+                last unless $propList[$closeTo] eq $curPropList[$closeTo];
+            }
+        }
+        # close out properties down to the common base path
+        while (@curPropList > $closeTo) {
+            ($prop = pop @curPropList) =~ s/ .*//;
+            $newData .= (' ' x scalar(@curPropList)) . " </$prop>\n";
+        }
+        if ($newDesc) {
+            # open the new description
+            $prop = $rdfDesc;
+            %nsCur = %nsNew;            # save current namespaces
+            $newData .= "\n <$prop rdf:about='$uuid'";
+            foreach (sort keys %nsCur) {
+                $newData .= "\n  xmlns:$_='$nsCur{$_}'";
+            }
+            $newData .= ">\n";
+            push @curPropList, $prop;
+        }
+        # loop over all values for this new property
+        my $capList = $capture{$path};
+        my ($val, $attrs) = @$capList;
+        $debug and print "$path = $val\n";
+        # open new properties
+        my $attr;
+        for ($n=@curPropList; $n<$#propList; ++$n) {
+            $prop = $propList[$n];
+            push @curPropList, $prop;
+            # remove list index if it exists
+            $prop =~ s/ .*//;
+            $attr = '';
+            if ($prop ne $rdfDesc and $propList[$n+1] !~ /^rdf:/) {
+                # need parseType='Resource' to avoid new 'rdf:Description'
+                $attr = " rdf:parseType='Resource'";
+            }
+            $newData .= (' ' x scalar(@curPropList)) . "<$prop$attr>\n";
+        }
+        my $prop2 = pop @propList;   # get new property name
+        $prop2 =~ s/ .*//;  # remove list index if it exists
+        $newData .= (' ' x scalar(@curPropList)) . " <$prop2";
+        # print out attributes
+        foreach $attr (sort keys %$attrs) {
+            my $attrVal = $$attrs{$attr};
+            my $quot = ($attrVal =~ /'/) ? '"' : "'";
+            $newData .= " $attr=$quot$attrVal$quot";
+        }
+        $newData .= ">$val</$prop2>\n";
+    }
+    # close off any open elements
+    while ($prop = pop @curPropList) {
+        $prop =~ s/ .*//;   # remove list index if it exists
+        $newData .= (' ' x scalar(@curPropList)) . " </$prop>\n";
+    }
+#
+# clean up, close out the XMP, and return our data
+#
+    # remove the ExifTool members we created
+    delete $exifTool->{XMP_CAPTURE};
+    delete $exifTool->{XMP_NS};
+
+    $newData .= $rdfClose . $xmpClose;
+
+    # (the XMP standard recommends writing 2k-4k of white space before the
+    # packet trailer, with a newline every 100 characters)
+    unless ($$exifTool{XMP_NO_XPACKET}) {
+        my $pad = (' ' x 100) . "\n";
+        if ($$dirInfo{InPlace}) {
+            # pad to specified DirLen
+            my $dirLen = $$dirInfo{DirLen} || length $$dataPt;
+            my $len = length($newData) + length($pktCloseW);
+            if ($len > $dirLen) {
+                $exifTool->Warn('Not enough room to edit XMP in place');
+                return undef;
+            }
+            my $num = int(($dirLen - $len) / length($pad));
+            if ($num) {
+                $newData .= $pad x $num;
+                $len += length($pad) * $num;
+            }
+            $len < $dirLen and $newData .= (' ' x ($dirLen - $len - 1)) . "\n";
+        } elsif (not $exifTool->Options('Compact') and
+                 not $xmpFile and not $$dirInfo{ReadOnly})
+        {
+            $newData .= $pad x 24;
+        }
+        $newData .= ($$dirInfo{ReadOnly} ? $pktCloseR : $pktCloseW);
+    }
+    # return empty data if no properties exist
+    $newData = '' unless %capture or $$dirInfo{InPlace};
+
+    if ($xmpErr) {
+        if ($xmpFile) {
+            $exifTool->Error($xmpErr);
+            return -1;
+        }
+        $exifTool->Warn($xmpErr);
+        return undef;
+    }
+    $exifTool->{CHANGED} += $changed;
+    $debug > 1 and $newData and print $newData,"\n";
+    return $newData unless $xmpFile;
+    return 1 if Write($$dirInfo{OutFile}, $newData);
+    return -1;
+}
+
+
+1; # end
+
+__END__
+
+=head1 NAME
+
+Image::ExifTool::WriteXMP.pl - Write XMP meta information
+
+=head1 SYNOPSIS
+
+These routines are autoloaded by Image::ExifTool::XMP.
+
+=head1 DESCRIPTION
+
+This file contains routines to write XMP metadata.
+
+=head1 AUTHOR
+
+Copyright 2003-2007, Phil Harvey (phil at owl.phy.queensu.ca)
+
+This library is free software; you can redistribute it and/or modify it
+under the same terms as Perl itself.
+
+=head1 SEE ALSO
+
+L<Image::ExifTool::XMP(3pm)|Image::ExifTool::XMP>,
+L<Image::ExifTool(3pm)|Image::ExifTool>
+
+=cut
Index: /gsdl/trunk/perllib/cpan/Image/ExifTool/Writer.pl
===================================================================
--- /gsdl/trunk/perllib/cpan/Image/ExifTool/Writer.pl	(revision 16842)
+++ /gsdl/trunk/perllib/cpan/Image/ExifTool/Writer.pl	(revision 16842)
@@ -0,0 +1,4506 @@
+#------------------------------------------------------------------------------
+# File:         Writer.pl
+#
+# Description:  ExifTool write routines
+#
+# Notes:        Also contains some less used ExifTool functions
+#
+# URL:          http://owl.phy.queensu.ca/~phil/exiftool/
+#
+# Revisions:    12/16/2004 - P. Harvey Created
+#------------------------------------------------------------------------------
+
+package Image::ExifTool;
+
+use strict;
+
+use Image::ExifTool::TagLookup qw(FindTagInfo TagExists);
+use Image::ExifTool::Fixup;
+
+sub AssembleRational($$@);
+sub LastInList($);
+sub CreateDirectory($);
+sub RemoveNewValueHash($$$);
+sub RemoveNewValuesForGroup($$);
+
+my $loadedAllTables;    # flag indicating we loaded all tables
+
+# the following is a road map of where we write each directory
+# in the different types of files.
+my %tiffMap = (
+    IFD0         => 'TIFF',
+    IFD1         => 'IFD0',
+    XMP          => 'IFD0',
+    ICC_Profile  => 'IFD0',
+    ExifIFD      => 'IFD0',
+    GPS          => 'IFD0',
+    SubIFD       => 'IFD0',
+    GlobParamIFD => 'IFD0',
+    PrintIM      => 'IFD0',
+    IPTC         => 'IFD0',
+    Photoshop    => 'IFD0',
+    InteropIFD   => 'ExifIFD',
+    MakerNotes   => 'ExifIFD',
+);
+my %jpegMap = (
+    JFIF         => 'APP0',
+    CIFF         => 'APP0',
+    IFD0         => 'APP1',
+    XMP          => 'APP1',
+    ICC_Profile  => 'APP2',
+    FlashPix     => 'APP2',
+    Meta         => 'APP3',
+    MetaIFD      => 'Meta',
+    RMETA        => 'APP5',
+    Ducky        => 'APP12',
+    Photoshop    => 'APP13',
+    IFD1         => 'IFD0',
+    EXIF         => 'IFD0', # to write EXIF as a block
+    ExifIFD      => 'IFD0',
+    GPS          => 'IFD0',
+    SubIFD       => 'IFD0',
+    GlobParamIFD => 'IFD0',
+    PrintIM      => 'IFD0',
+    IPTC         => 'Photoshop',
+    InteropIFD   => 'ExifIFD',
+    MakerNotes   => ['ExifIFD', 'CIFF'], # (first parent is the default)
+    Comment      => 'COM',
+);
+my %dirMap = (
+    JPEG => \%jpegMap,
+    TIFF => \%tiffMap,
+    ORF  => \%tiffMap,
+    RAW  => \%tiffMap,
+);
+
+# groups we are allowed to delete
+# Notes:
+# 1) these names must either exist in %dirMap, or be translated in InitWriteDirs())
+# 2) any dependencies must be added to %excludeGroups
+my @delGroups = qw(
+    AFCP CanonVRD CIFF Ducky EXIF ExifIFD File FlashPix FotoStation GlobParamIFD
+    GPS ICC_Profile IFD0 IFD1 InteropIFD IPTC JFIF MakerNotes Meta MetaIFD MIE
+    PhotoMechanic Photoshop PNG PrintIM RMETA SubIFD Trailer XMP
+);
+# other group names of new tag values to remove when deleting an entire group
+my %removeGroups = (
+    IFD0    => [ 'EXIF', 'MakerNotes' ],
+    EXIF    => [ 'MakerNotes' ],
+    ExifIFD => [ 'MakerNotes', 'InteropIFD' ],
+    Trailer => [ 'CanonVRD' ], #(because we can add back CanonVRD as a block)
+);
+# related family 0/1 groups in @delGroups (and not already in %jpegMap)
+# that must be removed from delete list when excluding a group
+my %excludeGroups = (
+    EXIF         => [ qw(IFD0 IFD1 ExifIFD GPS MakerNotes GlobParamIFD InteropIFD PrintIM SubIFD) ],
+    IFD0         => [ 'EXIF' ],
+    IFD1         => [ 'EXIF' ],
+    ExifIFD      => [ 'EXIF' ],
+    GPS          => [ 'EXIF' ],
+    MakerNotes   => [ 'EXIF' ],
+    InteropIFD   => [ 'EXIF' ],
+    GlobParamIFD => [ 'EXIF' ],
+    PrintIM      => [ 'EXIF' ],
+    CIFF         => [ 'MakerNotes' ],
+    # technically correct, but very uncommon and not a good reason to avoid deleting trailer
+  # IPTC         => [ qw(AFCP FotoStation Trailer) ],
+    AFCP         => [ 'Trailer' ],
+    FotoStation  => [ 'Trailer' ],
+    CanonVRD     => [ 'Trailer' ],
+    PhotoMechanic=> [ 'Trailer' ],
+    MIE          => [ 'Trailer' ],
+);
+# group names to translate for writing
+my %translateWriteGroup = (
+    EXIF => 'ExifIFD',
+    File => 'Comment',
+    MIE  => 'MIE',
+);
+# names of valid EXIF directories:
+my %exifDirs = (
+    gps          => 'GPS',
+    exififd      => 'ExifIFD',
+    subifd       => 'SubIFD',
+    globparamifd => 'GlobParamIFD',
+    interopifd   => 'InteropIFD',
+    makernotes   => 'MakerNotes',
+);
+# min/max values for integer formats
+my %intRange = (
+    'int8u'  => [0, 0xff],
+    'int8s'  => [-0x80, 0x7f],
+    'int16u' => [0, 0xffff],
+    'int16s' => [-0x8000, 0x7fff],
+    'int32u' => [0, 0xffffffff],
+    'int32s' => [-0x80000000, 0x7fffffff],
+);
+my $maxSegmentLen = 0xfffd; # maximum length of data in a JPEG segment
+
+# value separators when conversion list is used (in SetNewValue)
+my %listSep = ( PrintConv => '; ?', ValueConv => ' ' );
+
+#------------------------------------------------------------------------------
+# Set tag value
+# Inputs: 0) ExifTool object reference
+#         1) tag key, tag name, or '*' (optionally prefixed by group name),
+#            or undef to reset all previous SetNewValue() calls
+#         2) new value (scalar, scalar ref or list ref), or undef to delete tag
+#         3-N) Options:
+#           Type => PrintConv, ValueConv or Raw - specifies value type
+#           AddValue => true to add to list of existing values instead of overwriting
+#           DelValue => true to delete this existing value value from a list
+#           Group => family 0 or 1 group name (case insensitive)
+#           Replace => 0, 1 or 2 - overwrite previous new values (2=reset)
+#           Protected => bitmask to write tags with specified protections
+#           Shift => undef, 0, +1 or -1 - shift value if possible
+#           NoShortcut => true to prevent looking up shortcut tags
+#           CreateGroups => [internal use] createGroups hash ref from related tags
+# Returns: number of tags set (plus error string in list context)
+# Notes: For tag lists (like Keywords), call repeatedly with the same tag name for
+#        each value in the list.  Internally, the new information is stored in
+#        the following members of the $self->{NEW_VALUE}->{$tagInfo} hash:
+#           TagInfo - tag info ref
+#           DelValue - list ref for values to delete
+#           Value - list ref for values to add
+#           IsCreating - must be set for the tag to be added.  otherwise just
+#                        changed if it already exists
+#           CreateGroups - hash of all family 0 group names where tag may be created
+#           WriteGroup - group name where information is being written (correct case)
+#           WantGroup - group name as specified in call to function (case insensitive)
+#           Next - pointer to next newValueHash (if more than one)
+#           Self - ExifTool object reference
+#           Shift - shift value
+#           MAKER_NOTE_FIXUP - pointer to fixup if necessary for a maker note value
+sub SetNewValue($;$$%)
+{
+    local $_;
+    my ($self, $tag, $value, %options) = @_;
+    my ($err, $tagInfo);
+    my $verbose = $self->Options('Verbose');
+    my $out = $self->Options('TextOut');
+    my $protected = $options{Protected} || 0;
+    my $numSet = 0;
+    unless (defined $tag) {
+        # remove any existing set values
+        delete $self->{NEW_VALUE};
+        $self->{DEL_GROUP} = { };
+        $verbose > 1 and print $out "Cleared new values\n";
+        return 1;
+    }
+    # allow value to be scalar or list reference
+    if (ref $value) {
+        if (ref $value eq 'ARRAY') {
+            foreach (@$value) {
+                my ($n, $e) = SetNewValue($self, $tag, $_, %options);
+                $err = $e if $e;
+                $numSet += $n;
+                delete $options{Replace}; # don't replace earlier values in list
+            }
+ReturnNow:  return ($numSet, $err) if wantarray;
+            $err and warn "$err\n";
+            return $numSet;
+        } elsif (ref $value eq 'SCALAR') {
+            $value = $$value;
+        }
+    }
+    # make sure the Perl UTF-8 flag is OFF for the value if perl 5.6 or greater
+    # (otherwise our byte manipulations get corrupted!!)
+    if ($] >= 5.006 and defined $value) {
+        if (eval 'require Encode; Encode::is_utf8($value)' or $@) {
+            $value = pack('C*', unpack('C*', $value));
+        }
+    }
+    # set group name in options if specified
+    if ($tag =~ /(.+?):(.+)/) {
+        $options{Group} = $1 if $1 ne '*' and lc($1) ne 'all';
+        $tag = $2;
+    }
+    # ignore leading family number if 0 or 1 specified
+    if ($options{Group} and $options{Group} =~ /^(\d+)(.*)/ and $1 < 2) {
+        $options{Group} = $2;
+    }
+#
+# get list of tags we want to set
+#
+    my $wantGroup = $options{Group};
+    $tag =~ s/ .*//;    # convert from tag key to tag name if necessary
+    my @matchingTags = FindTagInfo($tag);
+    until (@matchingTags) {
+        if ($tag eq '*' or lc($tag) eq 'all') {
+            # set groups to delete
+            if (defined $value) {
+                $err = "Can't set value for all tags";
+            } else {
+                my (@del, $grp);
+                my $remove = ($options{Replace} and $options{Replace} > 1);
+                if ($wantGroup) {
+                    @del = grep /^$wantGroup$/i, @delGroups;
+                    # remove associated groups when excluding from mass delete
+                    if (@del and $remove) {
+                        # remove associated groups in other family
+                        push @del, @{$excludeGroups{$del[0]}} if $excludeGroups{$del[0]};
+                        # remove upstream groups according to JPEG map
+                        my $dirName = $del[0];
+                        my @dirNames;
+                        for (;;) {
+                            my $parent = $jpegMap{$dirName};
+                            if (ref $parent) {
+                                push @dirNames, @$parent;
+                                $parent = pop @dirNames;
+                            }
+                            $dirName = $parent || shift @dirNames or last;
+                            push @del, $dirName;    # exclude this too
+                        }
+                    }
+                    # allow MIE groups to be deleted by number
+                    push @del, uc($wantGroup) if $wantGroup =~ /^MIE\d+$/i;
+                } else {
+                    # push all groups plus '*', except IFD1 and a few others
+                    push @del, (grep !/^(IFD1|SubIFD|InteropIFD|GlobParamIFD)$/, @delGroups), '*';
+                }
+                if (@del) {
+                    ++$numSet;
+                    my @donegrps;
+                    foreach $grp (@del) {
+                        if ($remove) {
+                            exists $self->{DEL_GROUP}->{$grp} or next;
+                            delete $self->{DEL_GROUP}->{$grp};
+                        } else {
+                            $self->{DEL_GROUP}->{$grp} = 1;
+                            # remove all of this group from previous new values
+                            $self->RemoveNewValuesForGroup($grp);
+                        }
+                        push @donegrps, $grp;
+                    }
+                    if ($verbose > 1 and @donegrps) {
+                        @donegrps = sort @donegrps;
+                        my $msg = $remove ? 'Excluding from deletion' : 'Deleting tags in';
+                        print $out "  $msg: @donegrps\n";
+                    }
+                } else {
+                    $err = "Not a deletable group: $wantGroup";
+                }
+            }
+        } else {
+            my $origTag = $tag;
+            my $langCode;
+            # allow language suffix of form "-en_CA" or "-<rfc3066>" on tag name
+            if ($tag =~ /^(\w+)-([a-z]{2})(_[a-z]{2})$/i or # MIE
+                $tag =~ /^(\w+)-([a-z]{2,3}|[xi])(-[a-z\d]{2,8}(-[a-z\d]{1,8})*)?$/i) # XMP
+            {
+                $tag = $1;
+                $langCode = lc($2);
+                $langCode .= (length($3) == 3 ? uc($3) : lc($3)) if $3;
+                my @newMatches = FindTagInfo($tag);
+                foreach $tagInfo (@newMatches) {
+                    # only allow language codes in tables which support them
+                    next unless $$tagInfo{Table};
+                    my $langInfoProc = $tagInfo->{Table}->{LANG_INFO} or next;
+                    my $langInfo = &$langInfoProc($tagInfo, $langCode);
+                    push @matchingTags, $langInfo if $langInfo;
+                }
+                last if @matchingTags;
+            } else {
+                # look for a shortcut or alias
+                require Image::ExifTool::Shortcuts;
+                my ($match) = grep /^\Q$tag\E$/i, keys %Image::ExifTool::Shortcuts::Main;
+                undef $err;
+                if ($match and not $options{NoShortcut}) {
+                    if (@{$Image::ExifTool::Shortcuts::Main{$match}} == 1) {
+                        $tag = $Image::ExifTool::Shortcuts::Main{$match}->[0];
+                        @matchingTags = FindTagInfo($tag);
+                        last if @matchingTags;
+                    } else {
+                        $options{NoShortcut} = 1;
+                        foreach $tag (@{$Image::ExifTool::Shortcuts::Main{$match}}) {
+                            my ($n, $e) = $self->SetNewValue($tag, $value, %options);
+                            $numSet += $n;
+                            $e and $err = $e;
+                        }
+                        undef $err if $numSet;  # no error if any set successfully
+                        goto ReturnNow; # all done
+                    }
+                }
+            }
+            if (not TagExists($tag)) {
+                $err = "Tag '$origTag' does not exist";
+                $err .= ' or has a bad language code' if $origTag =~ /-/;
+            } elsif ($langCode) {
+                $err = "Tag '$tag' does not support alternate languages";
+            } elsif ($wantGroup) {
+                $err = "Sorry, $wantGroup:$origTag doesn't exist or isn't writable";
+            } else {
+                $err = "Sorry, $origTag is not writable";
+            }
+            $verbose > 2 and print $out "$err\n";
+        }
+        goto ReturnNow; # all done
+    }
+    # get group name that we're looking for
+    my $foundMatch = 0;
+    my ($ifdName, $mieGroup);
+    if ($wantGroup) {
+        # set $ifdName if this group is a valid IFD or SubIFD name
+        if ($wantGroup =~ /^IFD(\d+)$/i) {
+            $ifdName = "IFD$1";
+        } elsif ($wantGroup =~ /^SubIFD(\d+)$/i) {
+            $ifdName = "SubIFD$1";
+        } elsif ($wantGroup =~ /^MIE(\d*-?)(\w+)$/i) {
+            $mieGroup = "MIE$1" . ucfirst(lc($2));
+        } else {
+            $ifdName = $exifDirs{lc($wantGroup)};
+            if ($wantGroup =~ /^XMP\b/i) {
+                # must load XMP table to set group1 names
+                my $table = GetTagTable('Image::ExifTool::XMP::Main');
+                my $writeProc = $table->{WRITE_PROC};
+                $writeProc and &$writeProc();
+            }
+        }
+    }
+#
+# determine the groups for all tags found, and the tag with
+# the highest priority group
+#
+    my (@tagInfoList, %writeGroup, %preferred, %tagPriority, $avoid, $wasProtected);
+    my $highestPriority = -1;
+    foreach $tagInfo (@matchingTags) {
+        $tag = $tagInfo->{Name};    # set tag so warnings will use proper case
+        my ($writeGroup, $priority);
+        if ($wantGroup) {
+            my $lcWant = lc($wantGroup);
+            # only set tag in specified group
+            $writeGroup = $self->GetGroup($tagInfo, 0);
+            unless (lc($writeGroup) eq $lcWant) {
+                if ($writeGroup eq 'EXIF') {
+                    next unless $ifdName;
+                    $writeGroup = $ifdName;  # write to the specified IFD
+                } elsif ($writeGroup eq 'MIE') {
+                    next unless $mieGroup;
+                    $writeGroup = $mieGroup; # write to specific MIE group
+                    # set specific write group with document number if specified
+                    if ($writeGroup =~ /^MIE\d+$/ and $tagInfo->{Table}->{WRITE_GROUP}) {
+                        $writeGroup = $tagInfo->{Table}->{WRITE_GROUP};
+                        $writeGroup =~ s/^MIE/$mieGroup/;
+                    }
+                } else {
+                    # allow group1 name to be specified
+                    my $grp1 = $self->GetGroup($tagInfo, 1);
+                    unless ($grp1 and lc($grp1) eq $lcWant) {
+                        # must also check group1 name directly in case it is different
+                        $grp1 = $tagInfo->{Groups}->{1};
+                        next unless $grp1 and lc($grp1) eq $lcWant;
+                    }
+                }
+            }
+            $priority = 1000;   # highest priority since group was specified
+        }
+        ++$foundMatch;
+        # must do a dummy call to the write proc to autoload write package
+        # before checking Writable flag
+        my $table = $tagInfo->{Table};
+        my $writeProc = $table->{WRITE_PROC};
+        # load parent table if this was a user-defined table
+        if ($table->{PARENT}) {
+            my $parent = GetTagTable($table->{PARENT});
+            $writeProc = $parent->{WRITE_PROC} unless $writeProc;
+        }
+        next unless $writeProc and &$writeProc();
+        # must still check writable flags in case of UserDefined tags
+        my $writable = $tagInfo->{Writable};
+        next unless $writable or ($table->{WRITABLE} and
+            not defined $writable and not $$tagInfo{SubDirectory});
+        # don't write tag if protected
+        if ($tagInfo->{Protected}) {
+            my $prot = $tagInfo->{Protected} & ~$protected;
+            if ($prot) {
+                my %lkup = ( 1=>'unsafe', 2=>'protected', 3=>'unsafe and protected');
+                $wasProtected = $lkup{$prot};
+                next;
+            }
+        }
+        # set specific write group (if we didn't already)
+        if (not $writeGroup or $translateWriteGroup{$writeGroup}) {
+            # use default write group
+            $writeGroup = $tagInfo->{WriteGroup} || $tagInfo->{Table}->{WRITE_GROUP};
+            # use group 0 name if no WriteGroup specified
+            my $group0 = $self->GetGroup($tagInfo, 0);
+            $writeGroup or $writeGroup = $group0;
+            # get priority for this group
+            unless ($priority) {
+                $priority = $self->{WRITE_PRIORITY}->{lc($writeGroup)};
+                unless ($priority) {
+                    $priority = $self->{WRITE_PRIORITY}->{lc($group0)} || 0;
+                }
+            }
+        }
+        $tagPriority{$tagInfo} = $priority;
+        if ($priority > $highestPriority) {
+            $highestPriority = $priority;
+            %preferred = ( $tagInfo => 1 );
+            $avoid = 0;
+            ++$avoid if $$tagInfo{Avoid};
+        } elsif ($priority == $highestPriority) {
+            # create all tags with highest priority
+            $preferred{$tagInfo} = 1;
+            ++$avoid if $$tagInfo{Avoid};
+        }
+        push @tagInfoList, $tagInfo;
+        $writeGroup{$tagInfo} = $writeGroup;
+    }
+    # don't create tags with priority 0 if group priorities are set
+    if ($highestPriority == 0 and %{$self->{WRITE_PRIORITY}}) {
+        undef %preferred;
+    }
+    # avoid creating tags with 'Avoid' flag set if there are other alternatives
+    if ($avoid and %preferred) {
+        if ($avoid < scalar(keys %preferred)) {
+            # just remove the 'Avoid' tags since there are other preferred tags
+            foreach $tagInfo (@tagInfoList) {
+                delete $preferred{$tagInfo} if $$tagInfo{Avoid};
+            }
+        } elsif ($highestPriority < 1000) {
+            # look for another priority tag to create instead
+            my $nextHighest = 0;
+            my @nextBestTags;
+            foreach $tagInfo (@tagInfoList) {
+                my $priority = $tagPriority{$tagInfo} or next;
+                next if $priority == $highestPriority;
+                next if $priority < $nextHighest;
+                next if $$tagInfo{Avoid} or $$tagInfo{Permanent};
+                next if $writeGroup{$tagInfo} eq 'MakerNotes';
+                if ($nextHighest < $priority) {
+                    $nextHighest = $priority;
+                    undef @nextBestTags;
+                }
+                push @nextBestTags, $tagInfo;
+            }
+            if (@nextBestTags) {
+                # change our preferred tags to the next best tags
+                undef %preferred;
+                foreach $tagInfo (@nextBestTags) {
+                    $preferred{$tagInfo} = 1;
+                }
+            }
+        }
+    }
+#
+# generate new value hash for each tag
+#
+    my ($prioritySet, $createGroups);
+    # sort tag info list in reverse order of priority (higest number last)
+    # so we get the highest priority error message in the end
+    @tagInfoList = sort { $tagPriority{$a} <=> $tagPriority{$b} } @tagInfoList;
+    # loop through all valid tags to find the one(s) to write
+    SetTagLoop: foreach $tagInfo (@tagInfoList) {
+        my $writeGroup = $writeGroup{$tagInfo};
+        my $permanent = $$tagInfo{Permanent};
+        $writeGroup eq 'MakerNotes' and $permanent = 1 unless defined $permanent;
+        my $wgrp1;
+        if ($writeGroup eq 'MakerNotes' or $writeGroup eq 'XMP') {
+            $wgrp1 = $self->GetGroup($tagInfo, 1);
+        } else {
+            $wgrp1 = $writeGroup;
+        }
+        $tag = $tagInfo->{Name};    # get proper case for tag name
+        my $shift = $options{Shift};
+        if (defined $shift) {
+            if ($tagInfo->{Shift}) {
+                unless ($shift) {
+                    # set shift according to AddValue/DelValue
+                    $shift = 1 if $options{AddValue};
+                    $shift = -1 if $options{DelValue};
+                }
+            } elsif ($shift) {
+                $err = "$wgrp1:$tag is not shiftable";
+                $verbose > 2 and print $out "$err\n";
+                next;
+            }
+        }
+        my $val = $value;
+        if (defined $val) {
+            # check to make sure this is a List or Shift tag if adding
+            if ($options{AddValue} and not ($shift or $tagInfo->{List})) {
+                $err = "Can't add $wgrp1:$tag (not a List type)";
+                $verbose > 2 and print $out "$err\n";
+                next;
+            }
+        } else {
+            # can't delete permanent tags, so set value to empty string instead
+            $val = '' if $permanent;
+        }
+        # convert the value
+        my $type;
+        if ($shift) {
+            # add '+' or '-' prefix to indicate shift direction
+            $val = ($shift > 0 ? '+' : '-') . $val;
+            # check the shift for validity
+            require 'Image/ExifTool/Shift.pl';
+            my $err2 = CheckShift($tagInfo->{Shift}, $val);
+            if ($err2) {
+                $err = "$err2 for $wgrp1:$tag";
+                $verbose > 2 and print $out "$err\n";
+                next;
+            }
+        } else {
+            $type = $options{Type};
+            $type or $type = $self->Options('PrintConv') ? 'PrintConv' : 'ValueConv';
+        }
+Conv:   while (defined $val and not $shift) {
+            my $conv = $tagInfo->{$type};
+            my $convInv = $tagInfo->{"${type}Inv"};
+            my (@valList, $index, $convList, $convInvList);
+            # handle conversion lists
+            if (ref $conv eq 'ARRAY' or ref $convInv eq 'ARRAY') {
+                @valList = split /$listSep{$type}/, $val;
+                $val = $valList[$index = 0];
+                if (ref $conv eq 'ARRAY') {
+                    $convList = $conv;
+                    $conv = $$conv[0];
+                }
+                if (ref $convInv eq 'ARRAY') {
+                    $convInvList = $convInv;
+                    $convInv = $$convInv[0];
+                }
+            }
+            # loop through multiple values if necessary
+            for (;;) {
+                if ($convInv) {
+                    # capture eval warnings too
+                    local $SIG{'__WARN__'} = \&SetWarning;
+                    undef $evalWarning;
+                    if (ref($convInv) eq 'CODE') {
+                        $val = &$convInv($val, $self);
+                    } else {
+                        #### eval PrintConvInv/ValueConvInv ($val, $self)
+                        $val = eval $convInv;
+                        $@ and $evalWarning = $@;
+                    }
+                    if ($evalWarning) {
+                        $err = CleanWarning() . " in $wgrp1:$tag (${type}Inv)";
+                        $verbose > 2 and print $out "$err\n";
+                        undef $val;
+                        last Conv;
+                    } elsif (not defined $val) {
+                        $err = "Error converting value for $wgrp1:$tag (${type}Inv)";
+                        $verbose > 2 and print $out "$err\n";
+                        last Conv;
+                    }
+                } elsif ($conv) {
+                    if (ref $conv eq 'HASH') {
+                        my $multi;
+                        if ($$conv{BITMASK}) {
+                            my $lookupBits = $$conv{BITMASK};
+                            my ($val2, $err2) = EncodeBits($val, $lookupBits);
+                            if ($err2) {
+                                # ok, try matching a straight value
+                                ($val, $multi) = ReverseLookup($val, $conv);
+                                unless (defined $val) {
+                                    $err = "Can't encode $wgrp1:$tag ($err2)";
+                                    $verbose > 2 and print $out "$err\n";
+                                    last Conv;
+                                }
+                            } elsif (defined $val2) {
+                                $val = $val2;
+                            } else {
+                                delete $$conv{BITMASK};
+                                ($val, $multi) = ReverseLookup($val, $conv);
+                                $$conv{BITMASK} = $lookupBits;
+                            }
+                        } else {
+                            ($val, $multi) = ReverseLookup($val, $conv);
+                        }
+                        unless (defined $val) {
+                            $err = "Can't convert $wgrp1:$tag (" .
+                                   ($multi ? 'matches more than one' : 'not in') . " $type)";
+                            $verbose > 2 and print $out "$err\n";
+                            last Conv;
+                        }
+                    } elsif (not $$tagInfo{WriteAlso}) {
+                        $err = "Can't convert value for $wgrp1:$tag (no ${type}Inv)";
+                        $verbose > 2 and print $out "$err\n";
+                        undef $val;
+                        last Conv;
+                    }
+                }
+                last unless @valList;
+                $valList[$index] = $val;
+                if (++$index >= @valList) {
+                    $val = join ' ', @valList;
+                    last;
+                }
+                $conv = $$convList[$index] if $convList;
+                $convInv = $$convInvList[$index] if $convInvList;
+                $val = $valList[$index];
+            }
+            # cycle through PrintConv, ValueConv
+            if ($type eq 'PrintConv') {
+                $type = 'ValueConv';
+            } else {
+                # validate the value with WriteCheck and CHECK_PROC if they exist
+                my $err2;
+                if ($tagInfo->{WriteCheck}) {
+                    #### eval WriteCheck ($self, $tagInfo, $val)
+                    $err2 = eval $tagInfo->{WriteCheck};
+                    $@ and warn($@), $err2 = 'Error evaluating WriteCheck';
+                }
+                unless ($err2) {
+                    my $table = $tagInfo->{Table};
+                    if ($table and $table->{CHECK_PROC} and not $$tagInfo{RawConvInv}) {
+                        my $checkProc = $table->{CHECK_PROC};
+                        $err2 = &$checkProc($self, $tagInfo, \$val);
+                    }
+                }
+                if ($err2) {
+                    $err = "$err2 for $wgrp1:$tag";
+                    $verbose > 2 and print $out "$err\n";
+                    undef $val; # value was invalid
+                }
+                last;
+            }
+        }
+        if (not defined $val and defined $value) {
+            # if value conversion failed, we must still add a NEW_VALUE
+            # entry for this tag it it was a DelValue
+            next unless $options{DelValue};
+            $val = 'xxx never delete xxx';
+        }
+        $self->{NEW_VALUE} or $self->{NEW_VALUE} = { };
+        if ($options{Replace}) {
+            # delete the previous new value
+            $self->GetNewValueHash($tagInfo, $writeGroup, 'delete');
+            # also delete related tag previous new values
+            if ($$tagInfo{WriteAlso}) {
+                my $wtag;
+                foreach $wtag (keys %{$$tagInfo{WriteAlso}}) {
+                    my ($n,$e) = $self->SetNewValue($wtag, undef, Replace=>2);
+                    $numSet += $n;
+                }
+            }
+            $options{Replace} == 2 and ++$numSet, next;
+        }
+
+        # set value in NEW_VALUE hash
+        if (defined $val) {
+            # we are editing this tag, so create a NEW_VALUE hash entry
+            my $newValueHash = $self->GetNewValueHash($tagInfo, $writeGroup, 'create');
+            $newValueHash->{WantGroup} = $wantGroup;
+            # save maker note information if writing maker notes
+            if ($$tagInfo{MakerNotes}) {
+                $newValueHash->{MAKER_NOTE_FIXUP} = $self->{MAKER_NOTE_FIXUP};
+            }
+            if ($options{DelValue} or $options{AddValue} or $shift) {
+                # flag any AddValue or DelValue by creating the DelValue list
+                $newValueHash->{DelValue} or $newValueHash->{DelValue} = [ ];
+                if ($shift) {
+                    # add shift value to list
+                    $newValueHash->{Shift} = $val;
+                } elsif ($options{DelValue}) {
+                    # don't create if we are replacing a specific value
+                    $newValueHash->{IsCreating} = 0 unless $val eq '';
+                    # add delete value to list
+                    push @{$newValueHash->{DelValue}}, $val;
+                    if ($verbose > 1) {
+                        my $verb = $permanent ? 'Replacing' : 'Deleting';
+                        my $fromList = $tagInfo->{List} ? ' from list' : '';
+                        print $out "$verb $wgrp1:$tag$fromList if value is '$val'\n";
+                    }
+                }
+            }
+            # set priority flag to add only the high priority info
+            # (will only create the priority tag if it doesn't exist,
+            #  others get changed only if they already exist)
+            if ($preferred{$tagInfo} or $tagInfo->{Table}->{PREFERRED}) {
+                if ($permanent or $shift) {
+                    # don't create permanent or Shift-ed tag but define IsCreating
+                    # so we know that it is the preferred tag
+                    $newValueHash->{IsCreating} = 0;
+                } elsif (not ($newValueHash->{DelValue} and @{$newValueHash->{DelValue}}) or
+                         # also create tag if any DelValue value is empty ('')
+                         grep(/^$/,@{$newValueHash->{DelValue}}))
+                {
+                    $newValueHash->{IsCreating} = 1;
+                    # add to hash of groups where this tag is being created
+                    $createGroups or $createGroups = $options{CreateGroups} || { };
+                    $$createGroups{$self->GetGroup($tagInfo, 0)} = 1;
+                    $newValueHash->{CreateGroups} = $createGroups;
+                }
+            }
+            if (%{$self->{DEL_GROUP}} and $newValueHash->{IsCreating}) {
+                my ($grp, @grps);
+                foreach $grp (keys %{$self->{DEL_GROUP}}) {
+                    next if $self->{DEL_GROUP}->{$grp} == 2;
+                    # set flag indicating tags were written after this group was deleted
+                    $self->{DEL_GROUP}->{$grp} = 2;
+                    push @grps, $grp;
+                }
+                if ($verbose > 1 and @grps) {
+                    @grps = sort @grps;
+                    print $out "  Writing new tags after deleting groups: @grps\n";
+                }
+            }
+            if ($shift or not $options{DelValue}) {
+                $newValueHash->{Value} or $newValueHash->{Value} = [ ];
+                if ($tagInfo->{List}) {
+                    # we can write a list of entries
+                    push @{$newValueHash->{Value}}, $val;
+                } else {
+                    # not a List tag -- overwrite existing value
+                    $newValueHash->{Value}->[0] = $val;
+                }
+                if ($verbose > 1) {
+                    my $ifExists = $newValueHash->{IsCreating} ? '' : ' if tag exists';
+                    my $verb = ($shift ? 'Shifting' : ($options{AddValue} ? 'Adding' : 'Writing'));
+                    print $out "$verb $wgrp1:$tag$ifExists\n";
+                }
+            }
+        } elsif ($permanent) {
+            $err = "Can't delete $tag";
+            $verbose > 1 and print $out "$err\n";
+            next;
+        } elsif ($options{AddValue} or $options{DelValue}) {
+            $verbose > 1 and print $out "Adding/Deleting nothing does nothing\n";
+            next;
+        } else {
+            # create empty new value hash entry to delete this tag
+            $self->GetNewValueHash($tagInfo, $writeGroup, 'delete');
+            my $newValueHash = $self->GetNewValueHash($tagInfo, $writeGroup, 'create');
+            $newValueHash->{WantGroup} = $wantGroup;
+            $verbose > 1 and print $out "Deleting $wgrp1:$tag\n";
+        }
+        ++$numSet;
+        $prioritySet = 1 if $preferred{$tagInfo};
+        # also write related tags
+        my $writeAlso = $$tagInfo{WriteAlso};
+        if ($writeAlso) {
+            my $wtag;
+            foreach $wtag (keys %$writeAlso) {
+                #### eval WriteAlso ($val)
+                my $v = eval $writeAlso->{$wtag};
+                $@ and warn($@), next;
+                my ($n,$e) = $self->SetNewValue($wtag, $v, Type => 'ValueConv',
+                                                Protected => $protected | 0x02,
+                                                CreateGroups => $createGroups);
+                print $out "Not writing $wtag ($e)\n" if $e and $verbose > 1;
+                $numSet += $n;
+            }
+        }
+    }
+    # print warning if we couldn't set our priority tag
+    if ($err and not $prioritySet) {
+        warn "$err\n" unless wantarray;
+    } elsif (not $numSet) {
+        my $pre = $wantGroup ? "$wantGroup:" : '';
+        if ($wasProtected) {
+            $err = "Tag '$pre$tag' is $wasProtected for writing";
+        } elsif ($foundMatch) {
+            $err = "Sorry, $pre$tag is not writable";
+        } else {
+            $err = "Tag '$pre$tag' does not exist";
+        }
+        $verbose > 2 and print $out "$err\n";
+        warn "$err\n" unless wantarray;
+    } elsif ($err and not $verbose) {
+        undef $err;
+    }
+    return ($numSet, $err) if wantarray;
+    return $numSet;
+}
+
+#------------------------------------------------------------------------------
+# set new values from information in specified file
+# Inputs: 0) ExifTool object reference, 1) source file name or reference, etc
+#         2) List of tags to set (or all if none specified)
+# Returns: Hash of information set successfully (includes Warning or Error messages)
+# Notes: Tag names may contain group prefix and/or leading '-' to exclude from copy,
+#        and the tag name '*' may be used to represent all tags in a group.
+#        Also, a tag name may end with '>DSTTAG' to copy the information to a
+#        different tag, or a tag with a specified group.  (Also allow 'DSTTAG<TAG'.)
+sub SetNewValuesFromFile($$;@)
+{
+    my ($self, $srcFile, @setTags) = @_;
+
+    # expand shortcuts
+    @setTags and ExpandShortcuts(\@setTags);
+    my $srcExifTool = new Image::ExifTool;
+    my $options = $self->{OPTIONS};
+    # set options for our extraction tool
+    $srcExifTool->Options(
+        MakerNotes  => 1,
+        Binary      => 1,
+        Duplicates  => 1,
+        List        => 1,
+        StrictDate  => 1,
+        FixBase     => $$options{FixBase},
+        DateFormat  => $$options{DateFormat},
+        PrintConv   => $$options{PrintConv},
+        Composite   => $$options{Composite},
+        MissingTagValue   => $$options{MissingTagValue},
+        IgnoreMinorErrors => $$options{IgnoreMinorErrors},
+    );
+    # get all tags from source file (including MakerNotes block)
+    my $info = $srcExifTool->ImageInfo($srcFile);
+    return $info if $info->{Error} and $info->{Error} eq 'Error opening file';
+    delete $srcExifTool->{VALUE}->{Error};  # delete so we can check this later
+
+    # sort tags in reverse order so we get priority tag last
+    my @tags = reverse sort keys %$info;
+    my $tag;
+#
+# simply transfer all tags from source image if no tags specified
+#
+    unless (@setTags) {
+        # transfer maker note information to this object
+        $self->{MAKER_NOTE_FIXUP} = $srcExifTool->{MAKER_NOTE_FIXUP};
+        $self->{MAKER_NOTE_BYTE_ORDER} = $srcExifTool->{MAKER_NOTE_BYTE_ORDER};
+        foreach $tag (@tags) {
+            # don't try to set Warning's or Error's
+            next if $tag =~ /^Warning\b/ or $tag =~ /^Error\b/;
+            # set value for this tag
+            my ($n, $e) = $self->SetNewValue($tag, $info->{$tag}, Replace => 1);
+            # delete this tag if we could't set it
+            $n or delete $info->{$tag};
+        }
+        return $info;
+    }
+#
+# transfer specified tags in the proper order
+#
+    # 1) loop through input list of tags to set, and build @setList
+    my (@setList, $set, %setMatches);
+    foreach (@setTags) {
+        $tag = lc($_);  # change tag name to all lower case
+        my ($fam, $grp, $dst, $dstGrp, $dstTag, $expr, $opt);
+        # handle redirection to another tag
+        if ($tag =~ /(.+?)\s*(>|<)\s*(.+)/) {
+            $dstGrp = '';
+            if ($2 eq '>') {
+                ($tag, $dstTag) = ($1, $3);
+                # flag add and delete (ie. '+<' and '-<') redirections
+                $opt = $1 if $tag =~ s/\s*([-+])$// or $dstTag =~ s/^([-+])\s*//;
+            } else {
+                ($tag, $dstTag) = ($3, $1);
+                $opt = $1 if $dstTag =~ s/\s*([-+])$//;
+                # handle expressions
+                if ($tag =~ /\$/) {
+                    $tag = $_;  # restore original case
+                    # recover leading whitespace (except for initial single space)
+                    # and flag expression by starting with '@'
+                    $tag =~ s/(.+?)\s*(>|<) ?/\@/;
+                    $expr = 1;  # flag this expression
+                } else {
+                    $opt = $1 if $tag =~ s/^([-+])\s*//;
+                }
+            }
+            # translate '+' and '-' to appropriate SetNewValue option
+            $opt and $opt = { '+' => 'AddValue', '-' => 'DelValue' }->{$opt};
+            $dstTag =~ s/\ball\b/\*/g;       # replace 'all' with '*'
+            ($dstGrp, $dstTag) = ($1, $2) if $dstTag =~ /(.+?):(.+)/;
+            # ignore leading family number
+            $dstGrp = $2 if $dstGrp =~ /^(\d+)(.*)/ and $1 < 2;
+        }
+        $tag =~ s/\ball\b/\*/g unless $expr; # replace 'all' with '*'
+        my $isExclude = ($tag =~ s/^-//);
+        if ($tag =~ /^([\w-]+?|\*):(.+)/) {
+            ($grp, $tag) = ($1, $2);
+            # separate leading family number
+            ($fam, $grp) = ($1, $2) if $grp =~ /^(\d+)(.+)/ and $1 < 2;
+        } else {
+            $grp = '';  # flag for don't care about group
+        }
+        # redirect, exclude or set this tag (Note: $grp is '' if we don't care)
+        if ($dstTag) {
+            # redirect this tag
+            $isExclude and return { Error => "Can't redirect excluded tag" };
+            if ($tag eq '*' and $dstTag ne '*') {
+                return { Error => "Can't redirect from all tags to one tag" };
+            }
+            # set destination group the same as source if necessary
+            $dstGrp = $grp if $dstGrp eq '*' and $grp;
+            # write to specified destination group/tag
+            $dst = [ $dstGrp, $dstTag ];
+        } elsif ($isExclude) {
+            # implicitly assume '*' if first entry is an exclusion
+            unshift @setList, [ undef, '*', '*', [ '', '*' ] ] unless @setList;
+            # exclude this tag by leaving $dst undefined
+        } else {
+            # copy to same group/tag
+            $dst = [ $grp, $tag ];
+        }
+        $grp or $grp = '*';     # use '*' for any group
+        # save in reverse order so we don't set tags before an exclude
+        unshift @setList, [ $fam, $grp, $tag, $dst, $opt ];
+    }
+    # 2) initialize lists of matching tags for each condition
+    foreach $set (@setList) {
+        $$set[3] and $setMatches{$$set[3]} = [ ];
+    }
+    # 3) loop through all tags in source image and save tags matching each condition
+    my %rtnInfo;
+    foreach $tag (@tags) {
+        # don't try to set Warning's or Error's
+        if ($tag =~ /^Warning\b/ or $tag =~ /^Error\b/) {
+            $rtnInfo{$tag} = $info->{$tag};
+            next;
+        }
+        my @dstList;
+        # only set specified tags
+        my $lcTag = lc(GetTagName($tag));
+        my @grp;
+        foreach $set (@setList) {
+            # check first for matching tag
+            next unless $$set[2] eq $lcTag or $$set[2] eq '*';
+            # then check for matching group
+            unless ($$set[1] eq '*') {
+                # get lower case group names if not done already
+                @grp or @grp = map(lc, $srcExifTool->GetGroup($tag));
+                # handle leading family number
+                if (defined $$set[0]) {
+                    next unless $$set[1] eq $grp[$$set[0]];
+                } else {
+                    next unless $$set[1] eq $grp[0] or $$set[1] eq $grp[1];
+                }
+            }
+            last unless $$set[3];   # all done if we hit an exclude
+            # add to the list of tags matching this condition
+            push @{$setMatches{$set}}, $tag;
+        }
+    }
+    # 4) loop through each condition in original order, setting new tag values
+    foreach $set (reverse @setList) {
+        # handle expressions
+        if ($$set[2] =~ s/^\@//) {
+            my $val = $srcExifTool->InsertTagValues(\@tags, $$set[2], 'Error');
+            unless (defined $val) {
+                # return warning if one of the tags didn't exist
+                $tag = NextTagKey(\%rtnInfo, 'Warning');
+                $rtnInfo{$tag} = $srcExifTool->GetValue('Error');
+                delete $srcExifTool->{VALUE}->{Error};
+                next;
+            }
+            my ($dstGrp, $dstTag) = @{$$set[3]};
+            my %opts = ( Protected => 1, Replace => 1 );
+            # add or delete value if specified
+            $opts{$$set[4]} = 1 if $$set[4];
+            $opts{Group} = $dstGrp if $dstGrp;
+            my @rtnVals = $self->SetNewValue($dstTag, $val, %opts);
+            $rtnInfo{$dstTag} = $val if $rtnVals[0]; # tag was set successfully
+            next;
+        }
+        foreach $tag (@{$setMatches{$set}}) {
+            my (@values, %opts, $val, $noWarn);
+            # get all values for this tag
+            if (ref $info->{$tag} eq 'ARRAY') {
+                @values = @{$info->{$tag}};
+            } elsif (ref $info->{$tag} eq 'SCALAR') {
+                @values = ( ${$info->{$tag}} );
+            } else {
+                @values = ( $info->{$tag} );
+            }
+            my ($dstGrp, $dstTag) = @{$$set[3]};
+            if ($dstGrp) {
+                if ($dstGrp eq '*') {
+                    $dstGrp = $srcExifTool->GetGroup($tag, 1);
+                    $noWarn = 1;    # don't warn on wildcard destinations
+                }
+                $opts{Group} = $dstGrp;
+            }
+            # transfer maker note information if setting this tag
+            if ($srcExifTool->{TAG_INFO}->{$tag}->{MakerNotes}) {
+                $self->{MAKER_NOTE_FIXUP} = $srcExifTool->{MAKER_NOTE_FIXUP};
+                $self->{MAKER_NOTE_BYTE_ORDER} = $srcExifTool->{MAKER_NOTE_BYTE_ORDER};
+            }
+            if ($dstTag eq '*') {
+                $dstTag = $tag;
+                $noWarn = 1;
+            }
+            # add or delete value if specified
+            $opts{$$set[4]} = 1 if $$set[4];
+            # allow protected tags to be copied if specified explicitly
+            $opts{Protected} = 1 unless $$set[2] eq '*';
+            $opts{Replace} = 1;     # replace the first value found
+            # set all values for this tag
+            foreach $val (@values) {
+                my ($rtn, $wrn) = $self->SetNewValue($dstTag, $val, %opts);
+                if ($wrn and not $noWarn) {
+                    # return this warning
+                    $rtnInfo{NextTagKey(\%rtnInfo, 'Warning')} = $wrn;
+                    $noWarn = 1;
+                }
+                last unless $rtn;
+                $rtnInfo{$tag} = $info->{$tag}; # tag was set successfully
+                $opts{Replace} = 0;
+            }
+        }
+    }
+    return \%rtnInfo;   # return information that we set
+}
+
+#------------------------------------------------------------------------------
+# Get new value(s) for tag
+# Inputs: 0) ExifTool object reference, 1) tag key, tag name, or tagInfo hash ref
+#         2) optional pointer to return new value hash reference (not part of public API)
+#    or   0) new value hash reference (not part of public API)
+# Returns: List of new Raw values (list may be empty if tag is being deleted)
+# Notes: 1) Preferentially returns new value from Extra table if writable Extra tag exists
+# 2) Must call AFTER IsOverwriting() returns 1 to get proper value for shifted times
+sub GetNewValues($;$$)
+{
+    local $_;
+    my $newValueHash;
+    if (ref $_[0] eq 'HASH') {
+        $newValueHash = shift;
+    } else {
+        my ($self, $tag, $newValueHashPt) = @_;
+        if ($self->{NEW_VALUE}) {
+            my $tagInfo;
+            if (ref $tag) {
+                $newValueHash = $self->GetNewValueHash($tag);
+            } elsif (defined($tagInfo = $Image::ExifTool::Extra{$tag}) and
+                     $$tagInfo{Writable})
+            {
+                $newValueHash = $self->GetNewValueHash($tagInfo);
+            } else {
+                my @tagInfoList = FindTagInfo($tag);
+                # choose the one that we are creating
+                foreach $tagInfo (@tagInfoList) {
+                    my $nvh = $self->GetNewValueHash($tagInfo) or next;
+                    $newValueHash = $nvh;
+                    last if defined $newValueHash->{IsCreating};
+                }
+            }
+        }
+        # return new value hash if requested
+        $newValueHashPt and $$newValueHashPt = $newValueHash;
+    }
+    if ($newValueHash and $newValueHash->{Value}) {
+        my $vals = $newValueHash->{Value};
+        # do inverse raw conversion if necessary
+        if ($newValueHash->{TagInfo}->{RawConvInv}) {
+            my $tagInfo = $$newValueHash{TagInfo};
+            my $conv = $$tagInfo{RawConvInv};
+            my $self = $newValueHash->{Self};
+            my ($val, $checkProc);
+            my $table = $tagInfo->{Table};
+            $checkProc = $$table{CHECK_PROC} if $table;
+            local $SIG{'__WARN__'} = \&SetWarning;
+            undef $evalWarning;
+            foreach $val (@$vals) {
+                if (ref($conv) eq 'CODE') {
+                    $val = &$conv($val, $self);
+                } else {
+                    #### eval RawConvInv ($self, $val)
+                    $val = eval $conv;
+                    $@ and $evalWarning = $@;
+                }
+                if ($evalWarning) {
+                    my $err = CleanWarning() . " in $$tagInfo{Name} (RawConvInv)";
+                    $self->Warn($err);
+                    @$vals = ();
+                    last;
+                }
+                # must check value now
+                next unless $checkProc;
+                my $err = &$checkProc($self, $tagInfo, \$val);
+                if ($err or not defined $val) {
+                    $err or $err = 'Error generating raw value';
+                    $self->Warn("$err for $$tagInfo{Name}");
+                    @$vals = ();
+                    last;
+                }
+            }
+        }
+        # return our value(s)
+        if (wantarray) {
+            return @$vals;
+        } else {
+            return $$vals[0];
+        }
+    }
+    return () if wantarray;  # return empty list
+    return undef;
+}
+
+#------------------------------------------------------------------------------
+# Return the total number of new values set
+# Inputs: 0) ExifTool object reference
+# Returns: Scalar context) Number of new values that have been set
+#          List context) Number of new values, number of "pseudo" values
+# ("pseudo" values are those which don't require rewriting the file to change)
+sub CountNewValues($)
+{
+    my $self = shift;
+    my $newVal = $self->{NEW_VALUE};
+    my $num = 0;
+    $num += scalar keys %$newVal if $newVal;
+    $num += scalar keys %{$self->{DEL_GROUP}};
+    return $num unless wantarray;
+    my $pseudo = 0;
+    if ($newVal) {
+        my $tag;
+        # (Note: all "pseudo" tags must be found in Extra table)
+        foreach $tag (qw{FileName Directory FileModifyDate}) {
+            ++$pseudo if defined $$newVal{$Image::ExifTool::Extra{$tag}};
+        }
+    }
+    return ($num, $pseudo);
+}
+
+#------------------------------------------------------------------------------
+# Save new values for subsequent restore
+# Inputs: 0) ExifTool object reference
+sub SaveNewValues($)
+{
+    my $self = shift;
+    my $newValues = $self->{NEW_VALUE};
+    my $key;
+    foreach $key (keys %$newValues) {
+        my $newValueHash = $$newValues{$key};
+        while ($newValueHash) {
+            $newValueHash->{Save} = 1;  # set Save flag
+            $newValueHash = $newValueHash->{Next};
+        }
+    }
+    # initialize hash for saving overwritten new values
+    $self->{SAVE_NEW_VALUE} = { };
+}
+
+#------------------------------------------------------------------------------
+# Restore new values to last saved state
+# Inputs: 0) ExifTool object reference
+# Notes: Restores saved new values, but currently doesn't restore them in the
+# orginal order, so there may be some minor side-effects when restoring tags
+# with overlapping groups. ie) XMP:Identifier, XMP-dc:Identifier
+sub RestoreNewValues($)
+{
+    my $self = shift;
+    my $newValues = $self->{NEW_VALUE};
+    my $savedValues = $self->{SAVE_NEW_VALUE};
+    my $key;
+    # 1) remove any new values which don't have the Save flag set
+    if ($newValues) {
+        my @keys = keys %$newValues;
+        foreach $key (@keys) {
+            my $lastHash;
+            my $newValueHash = $$newValues{$key};
+            while ($newValueHash) {
+                if ($newValueHash->{Save}) {
+                    $lastHash = $newValueHash;
+                } else {
+                    # remove this entry from the list
+                    if ($lastHash) {
+                        $lastHash->{Next} = $newValueHash->{Next};
+                    } elsif ($newValueHash->{Next}) {
+                        $$newValues{$key} = $newValueHash->{Next};
+                    } else {
+                        delete $$newValues{$key};
+                    }
+                }
+                $newValueHash = $newValueHash->{Next};
+            }
+        }
+    }
+    # 2) restore saved new values
+    if ($savedValues) {
+        $newValues or $newValues = $self->{NEW_VALUE} = { };
+        foreach $key (keys %$savedValues) {
+            if ($$newValues{$key}) {
+                # add saved values to end of list
+                my $newValueHash = LastInList($$newValues{$key});
+                $newValueHash->{Next} = $$savedValues{$key};
+            } else {
+                $$newValues{$key} = $$savedValues{$key};
+            }
+        }
+        $self->{SAVE_NEW_VALUE} = { };  # reset saved new values
+    }
+}
+
+#------------------------------------------------------------------------------
+# Set file modification time from FileModifyDate tag
+# Inputs: 0) ExifTool object reference, 1) file name or file ref
+#         2) modify time (-M) of original file (needed for time shift)
+# Returns: 1=time changed OK, 0=nothing done, -1=error setting time
+#          (and increments CHANGED flag if time was changed)
+sub SetFileModifyDate($$;$)
+{
+    my ($self, $file, $originalTime) = @_;
+    my $newValueHash;
+    my $val = $self->GetNewValues('FileModifyDate', \$newValueHash);
+    return 0 unless defined $val;
+    my $isOverwriting = IsOverwriting($newValueHash);
+    return 0 unless $isOverwriting;
+    if ($isOverwriting < 0) {  # are we shifting time?
+        # use original time of this file if not specified
+        $originalTime = -M $file unless defined $originalTime;
+        return 0 unless defined $originalTime;
+        return 0 unless IsOverwriting($newValueHash, $^T - $originalTime*(24*3600));
+        $val = $newValueHash->{Value}->[0]; # get shifted value
+    }
+    unless (utime($val, $val, $file)) {
+        $self->Warn('Error setting FileModifyDate');
+        return -1;
+    }
+    ++$self->{CHANGED};
+    $self->VPrint(1, "    + FileModifyDate = '$val'\n");
+    return 1;
+}
+
+#------------------------------------------------------------------------------
+# Change file name and/or directory from FileName and Directory tags
+# Inputs: 0) ExifTool object reference, 1) current file name (including path)
+#         2) New name (or undef to build from FileName and Directory tags)
+# Returns: 1=name changed OK, 0=nothing changed, -1=error changing name
+#          (and increments CHANGED flag if filename changed)
+# Notes: Will not overwrite existing file.  Creates directories as necessary.
+sub SetFileName($$;$)
+{
+    my ($self, $file, $newName) = @_;
+    my ($newValueHash, $doName, $doDir);
+    # determine the new file name
+    unless (defined $newName) {
+        my $filename = $self->GetNewValues('FileName', \$newValueHash);
+        $doName = 1 if defined $filename and IsOverwriting($newValueHash, $file);
+        my $dir = $self->GetNewValues('Directory', \$newValueHash);
+        $doDir = 1 if defined $dir and IsOverwriting($newValueHash, $file);
+        return 0 unless $doName or $doDir;  # nothing to do
+        if ($doName) {
+            $newName = GetNewFileName($file, $filename);
+            $newName = GetNewFileName($newName, $dir) if $doDir;
+        } else {
+            $newName = GetNewFileName($file, $dir);
+        }
+    }
+    if (-e $newName) {
+        # don't replace existing file
+        $self->Warn("File '$newName' already exists");
+        return -1;
+    }
+    # create directory for new file if necessary
+    my $result;
+    if (($result = CreateDirectory($newName)) != 0) {
+        if ($result < 0) {
+            $self->Warn("Error creating directory for '$newName'");
+            return -1;
+        }
+        $self->VPrint(0, "Created directory for '$newName'");
+    }
+    # attempt to rename the file
+    unless (rename $file, $newName) {
+        # renaming didn't work, so copy the file instead
+        unless (open EXIFTOOL_SFN_IN, $file) {
+            $self->Warn("Error opening '$file'");
+            return -1;
+        }
+        unless (open EXIFTOOL_SFN_OUT, ">$newName") {
+            close EXIFTOOL_SFN_IN;
+            $self->Warn("Error creating '$newName'");
+            return -1;
+        }
+        binmode EXIFTOOL_SFN_IN;
+        binmode EXIFTOOL_SFN_OUT;
+        my ($buff, $err);
+        while (read EXIFTOOL_SFN_IN, $buff, 65536) {
+            print EXIFTOOL_SFN_OUT $buff or $err = 1;
+        }
+        close EXIFTOOL_SFN_OUT or $err = 1;
+        close EXIFTOOL_SFN_IN;
+        if ($err) {
+            unlink $newName;    # erase bad output file
+            $self->Warn("Error writing '$newName'");
+            return -1;
+        }
+        # preserve modification time
+        my $modTime = $^T - (-M $file) * (24 * 3600);
+        my $accTime = $^T - (-A $file) * (24 * 3600);
+        utime($accTime, $modTime, $newName);
+        # remove the original file
+        unlink $file or $self->Warn('Error removing old file');
+    }
+    ++$self->{CHANGED};
+    $self->VPrint(1, "    + FileName = '$newName'\n");
+    return 1;
+}
+
+#------------------------------------------------------------------------------
+# Write information back to file
+# Inputs: 0) ExifTool object reference,
+#         1) input filename, file ref, or scalar ref (or '' or undef to create from scratch)
+#         2) output filename, file ref, or scalar ref (or undef to edit in place)
+#         3) optional output file type (required only if input file is not specified
+#            and output file is a reference)
+# Returns: 1=file written OK, 2=file written but no changes made, 0=file write error
+sub WriteInfo($$;$$)
+{
+    local $_;
+    my ($self, $infile, $outfile, $outType) = @_;
+    my (@fileTypeList, $fileType, $tiffType);
+    my ($inRef, $outRef, $closeIn, $closeOut, $outPos, $outBuff, $eraseIn);
+    my $oldRaf = $self->{RAF};
+    my $rtnVal = 1;
+
+    # initialize member variables
+    $self->Init();
+
+    # first, save original file modify date if necessary
+    # (do this now in case we are modifying file in place and shifting date)
+    my ($newValueHash, $originalTime);
+    my $fileModifyDate =  $self->GetNewValues('FileModifyDate', \$newValueHash);
+    if (defined $fileModifyDate and IsOverwriting($newValueHash) < 0 and
+        defined $infile and ref $infile ne 'SCALAR')
+    {
+        $originalTime = -M $infile;
+    }
+#
+# do quick in-place change of file dir/name or date if that is all we are doing
+#
+    if (not defined $outfile and defined $infile) {
+        my $newFileName =  $self->GetNewValues('FileName', \$newValueHash);
+        my ($numNew, $numPseudo) = $self->CountNewValues();
+        if ($numNew == $numPseudo) {
+            $rtnVal = 2;
+            if (defined $fileModifyDate and (not ref $infile or UNIVERSAL::isa($infile,'GLOB'))) {
+                $self->SetFileModifyDate($infile) > 0 and $rtnVal = 1;
+            }
+            if (defined $newFileName and not ref $infile) {
+                $self->SetFileName($infile) > 0 and $rtnVal = 1;
+            }
+            return $rtnVal;
+        } elsif (defined $newFileName and length $newFileName) {
+            # can't simply rename file, so just set the output name if new FileName
+            # --> in this case, must erase original copy
+            if (ref $infile) {
+                $outfile = $newFileName;
+                # can't delete original
+            } elsif (IsOverwriting($newValueHash, $infile)) {
+                $outfile = GetNewFileName($infile, $newFileName);
+                $eraseIn = 1; # delete original                
+            }
+        }
+    }
+#
+# set up input file
+#
+    if (ref $infile) {
+        $inRef = $infile;
+        if (UNIVERSAL::isa($inRef,'GLOB')) {
+            seek($inRef, 0, 0); # make sure we are at the start of the file
+        } elsif (eval 'require Encode; Encode::is_utf8($$inRef)') {
+            # convert image data from UTF-8 to character stream if necessary
+            if (defined $outfile) {
+                my $buff = pack('C*', unpack('U0U*', $$inRef));
+                $inRef = \$buff;
+            } else {
+                $$inRef = pack('C*', unpack('U0U*', $$inRef));
+            }
+        }
+    } elsif (defined $infile and $infile ne '') {
+        if (open(EXIFTOOL_FILE2, defined $outfile ? $infile : "+<$infile")) {
+            $fileType = GetFileType($infile);
+            $tiffType = GetFileExtension($infile);
+            $self->VPrint(0, "Rewriting $infile...\n");
+            $inRef = \*EXIFTOOL_FILE2;
+            $closeIn = 1;   # we must close the file since we opened it
+        } else {
+            my $forUpdate = (defined $outfile ? '' : ' for update');
+            $self->Error("Error opening file$forUpdate");
+            return 0;
+        }
+    } elsif (not defined $outfile) {
+        $self->Error("WriteInfo(): Must specify infile or outfile\n");
+        return 0;
+    } else {
+        # create file from scratch
+        $outType = GetFileType($outfile) unless $outType or ref $outfile;
+        if (CanCreate($outType)) {
+            $fileType = $tiffType = $outType;   # use output file type if no input file
+            $infile = "$fileType file";         # make bogus file name
+            $self->VPrint(0, "Creating $infile...\n");
+            $inRef = \ '';      # set $inRef to reference to empty data
+        } elsif ($outType) {
+            $self->Error("Can't create $outType files");
+            return 0;
+        } else {
+            $self->Error("Can't create file (unknown type)");
+            return 0;
+        }
+    }
+    if ($fileType) {
+        @fileTypeList = ( $fileType );
+    } else {
+        @fileTypeList = @fileTypes;
+        $tiffType = 'TIFF';
+    }
+#
+# set up output file
+#
+    if (ref $outfile) {
+        $outRef = $outfile;
+        if (UNIVERSAL::isa($outRef,'GLOB')) {
+            binmode($outRef);
+            $outPos = tell($outRef);
+        } else {
+            # initialize our output buffer if necessary
+            defined $$outRef or $$outRef = '';
+            $outPos = length($$outRef);
+        }
+    } elsif (not defined $outfile) {
+        # editing in place, so write to memory first
+        $outBuff = '';
+        $outRef = \$outBuff;
+        $outPos = 0;
+    } elsif (-e $outfile) {
+        $self->Error("File already exists: $outfile");
+        $rtnVal = 0;
+    } elsif (open(EXIFTOOL_OUTFILE, ">$outfile")) {
+        $outRef = \*EXIFTOOL_OUTFILE;
+        $closeOut = 1;  # we must close $outRef
+        binmode($outRef);
+        $outPos = 0;
+    } else {
+        $self->Error("Error creating file: $outfile");
+        $rtnVal = 0;
+    }
+#
+# write the file
+#
+    if ($rtnVal) {
+        # create random access file object
+        my $raf = new File::RandomAccess($inRef);
+        # patch for Windows command shell pipe
+        $raf->{TESTED} = -1 if not ref $infile and ($infile eq '-' or $infile =~ /\|$/);
+       # $raf->Debug() and warn "  RAF debugging enabled!\n";
+        my $inPos = $raf->Tell();
+        $raf->BinMode();
+        $self->{RAF} = $raf;
+        my %dirInfo = (
+            RAF => $raf,
+            OutFile => $outRef,
+        );
+        for (;;) {
+            my $type = shift @fileTypeList;
+            # save file type in member variable
+            $dirInfo{Parent} = $self->{FILE_TYPE} = $type;
+            # determine which directories we must write for this file type
+            $self->InitWriteDirs($type);
+            if ($type eq 'JPEG') {
+                $rtnVal = $self->WriteJPEG(\%dirInfo);
+            } elsif ($type eq 'TIFF') {
+                # disallow writing of some TIFF-based RAW images:
+                if ($tiffType =~ /^(SRF|SR2|ARW|RAW|DCR|K25)$/) {
+                    $fileType = $tiffType;
+                    undef $rtnVal;
+                } else {
+                    $dirInfo{Parent} = $tiffType;
+                    $rtnVal = $self->ProcessTIFF(\%dirInfo);
+                }
+            } elsif ($type eq 'GIF') {
+                require Image::ExifTool::GIF;
+                $rtnVal = Image::ExifTool::GIF::ProcessGIF($self,\%dirInfo);
+            } elsif ($type eq 'CRW') {
+                require Image::ExifTool::CanonRaw;
+                $rtnVal = Image::ExifTool::CanonRaw::WriteCRW($self, \%dirInfo);
+            } elsif ($type eq 'MRW') {
+                require Image::ExifTool::MinoltaRaw;
+                $rtnVal = Image::ExifTool::MinoltaRaw::ProcessMRW($self, \%dirInfo);
+            } elsif ($type eq 'ORF') {
+                $rtnVal = $self->ProcessTIFF(\%dirInfo);
+            } elsif ($type eq 'PNG') {
+                require Image::ExifTool::PNG;
+                $rtnVal = Image::ExifTool::PNG::ProcessPNG($self, \%dirInfo);
+            } elsif ($type eq 'MIE') {
+                require Image::ExifTool::MIE;
+                $rtnVal = Image::ExifTool::MIE::ProcessMIE($self, \%dirInfo);
+            } elsif ($type eq 'XMP') {
+                require Image::ExifTool::XMP;
+                $rtnVal = Image::ExifTool::XMP::WriteXMP($self, \%dirInfo);
+            } elsif ($type eq 'PPM') {
+                require Image::ExifTool::PPM;
+                $rtnVal = Image::ExifTool::PPM::ProcessPPM($self, \%dirInfo);
+            } elsif ($type eq 'PSD') {
+                require Image::ExifTool::Photoshop;
+                $rtnVal = Image::ExifTool::Photoshop::ProcessPSD($self, \%dirInfo);
+            } elsif ($type eq 'EPS' or $type eq 'PS') {
+                require Image::ExifTool::PostScript;
+                $rtnVal = Image::ExifTool::PostScript::WritePS($self, \%dirInfo);
+            } elsif ($type eq 'ICC') {
+                require Image::ExifTool::ICC_Profile;
+                $rtnVal = Image::ExifTool::ICC_Profile::WriteICC($self, \%dirInfo);
+            } elsif ($type eq 'VRD') {
+                require Image::ExifTool::CanonVRD;
+                $rtnVal = Image::ExifTool::CanonVRD::ProcessVRD($self, \%dirInfo);
+            } elsif ($type eq 'JP2') {
+                require Image::ExifTool::Jpeg2000;
+                $rtnVal = Image::ExifTool::Jpeg2000::ProcessJP2($self, \%dirInfo);
+            } elsif ($type eq 'RAW') {
+                $rtnVal = $self->ProcessTIFF(\%dirInfo);
+            } else {
+                undef $rtnVal;  # flag that we don't write this type of file
+            }
+            # all done unless we got the wrong type
+            last if $rtnVal;
+            last unless @fileTypeList;
+            # seek back to original position in files for next try
+            unless ($raf->Seek($inPos, 0)) {
+                $self->Error('Error seeking in file');
+                last;
+            }
+            if (UNIVERSAL::isa($outRef,'GLOB')) {
+                seek($outRef, 0, $outPos);
+            } else {
+                $$outRef = substr($$outRef, 0, $outPos);
+            }
+        }
+        # print file format errors
+        unless ($rtnVal) {
+            if ($fileType and defined $rtnVal) {
+                $self->{VALUE}->{Error} or $self->Error('Format error in file');
+            } elsif ($fileType) {
+                $self->Error("ExifTool does not yet support writing of $fileType files");
+            } else {
+                $self->Error('ExifTool does not support writing of this type of file');
+            }
+            $rtnVal = 0;
+        }
+       # $raf->Close(); # only used to force debug output
+    }
+    # don't return success code if any error occurred
+    $rtnVal = 0 if $rtnVal > 0 and $self->{VALUE}->{Error};
+
+    # rewrite original file in place if required
+    if (defined $outBuff) {
+        if ($rtnVal <= 0 or not $self->{CHANGED}) {
+            # nothing changed, so no need to write $outBuff
+        } elsif (UNIVERSAL::isa($inRef,'GLOB')) {
+            my $len = length($outBuff);
+            my $size;
+            $rtnVal = -1 unless
+                seek($inRef, 0, 2) and          # seek to the end of file
+                ($size = tell $inRef) >= 0 and  # get the file size
+                seek($inRef, 0, 0) and          # seek back to the start
+                print $inRef $outBuff and       # write the new data
+                ($len >= $size or               # if necessary:
+                eval 'truncate($inRef, $len)'); #  shorten output file
+        } else {
+            $$inRef = $outBuff;                 # replace original data
+        }
+        $outBuff = '';  # free memory but leave $outBuff defined
+    }
+    # close input file if we opened it
+    if ($closeIn) {
+        # errors on input file are significant if we edited the file in place
+        $rtnVal and $rtnVal = -1 unless close($inRef) or not defined $outBuff;
+        if ($eraseIn and $rtnVal > 0) {
+            # erase input file if renaming while editing information in place
+            unlink $infile or $self->Warn('Error erasing original file');
+        }
+    }
+    # close output file if we created it
+    if ($closeOut) {
+        # close file and set $rtnVal to -1 if there was an error
+        $rtnVal and $rtnVal = -1 unless close($outRef);
+        # erase the output file if we weren't successful
+        $rtnVal > 0 or unlink $outfile;
+    }
+    # set FileModifyDate if requested (and if possible!)
+    if (defined $fileModifyDate and $rtnVal > 0 and
+        ($closeOut or ($closeIn and defined $outBuff)))
+    {
+        $self->SetFileModifyDate($closeOut ? $outfile : $infile, $originalTime);
+    }
+    # check for write error and set appropriate error message and return value
+    if ($rtnVal < 0) {
+        $self->Error('Error writing output file');
+        $rtnVal = 0;    # return 0 on failure
+    } elsif ($rtnVal > 0) {
+        ++$rtnVal unless $self->{CHANGED};
+    }
+    # set things back to the way they were
+    $self->{RAF} = $oldRaf;
+
+    return $rtnVal;
+}
+
+#------------------------------------------------------------------------------
+# Get list of all available tags for specified group
+# Inputs: 0) optional group name
+# Returns: tag list (sorted alphabetically)
+# Notes: Can't get tags for specific IFD
+sub GetAllTags(;$)
+{
+    local $_;
+    my $group = shift;
+    my (%allTags, $exifTool);
+
+    $group and $exifTool = new Image::ExifTool;
+    LoadAllTables();    # first load all our tables
+    my @tableNames = ( keys %allTables );
+
+    # loop through all tables and save tag names to %allTags hash
+    while (@tableNames) {
+        my $table = GetTagTable(pop @tableNames);
+        my $tagID;
+        foreach $tagID (TagTableKeys($table)) {
+            my @infoArray = GetTagInfoList($table,$tagID);
+            my $tagInfo;
+            foreach $tagInfo (@infoArray) {
+                my $tag = $$tagInfo{Name} || die "no name for tag!\n";
+                # don't list subdirectories unless they are writable
+                next unless $$tagInfo{Writable} or not $$tagInfo{SubDirectory};
+                if ($group) {
+                    my @groups = $exifTool->GetGroup($tagInfo);
+                    next unless grep /^$group$/i, @groups;
+                }
+                $allTags{$tag} = 1;
+            }
+        }
+    }
+    return sort keys %allTags;
+}
+
+#------------------------------------------------------------------------------
+# Get list of all writable tags
+# Inputs: 0) optional group name
+# Returns: tag list (sorted alphbetically)
+sub GetWritableTags(;$)
+{
+    local $_;
+    my $group = shift;
+    my (%writableTags, $exifTool);
+
+    $group and $exifTool = new Image::ExifTool;
+    LoadAllTables();
+    my @tableNames = keys %allTables;
+
+    while (@tableNames) {
+        my $tableName = pop @tableNames;
+        my $table = GetTagTable($tableName);
+        # attempt to load Write tables if autoloaded
+        my @path = split(/::/,$tableName);
+        if (@path > 3) {
+            my $i = $#path - 1;
+            $path[$i] = "Write$path[$i]";   # add 'Write' before class name
+            my $module = join('::',@path[0..($#path-1)]);
+            eval "require $module"; # (fails silently if nothing loaded)
+        }
+        my $tagID;
+        foreach $tagID (TagTableKeys($table)) {
+            my @infoArray = GetTagInfoList($table,$tagID);
+            my $tagInfo;
+            foreach $tagInfo (@infoArray) {
+                my $tag = $$tagInfo{Name} || die "no name for tag!\n";
+                my $writable = $$tagInfo{Writable};
+                next unless $writable or ($table->{WRITABLE} and
+                    not defined $writable and not $$tagInfo{SubDirectory});
+                if ($group) {
+                    my @groups = $exifTool->GetGroup($tagInfo);
+                    next unless grep /^$group$/i, @groups;
+                }
+                $writableTags{$tag} = 1;
+            }
+        }
+    }
+    return sort keys %writableTags;
+}
+
+#------------------------------------------------------------------------------
+# Get list of all group names
+# Inputs: 1) Group family number
+# Returns: List of group names (sorted alphabetically)
+sub GetAllGroups($)
+{
+    local $_;
+    my $family = shift || 0;
+
+    LoadAllTables();    # first load all our tables
+
+    my @tableNames = ( keys %allTables );
+
+    # loop through all tag tables and get all group names
+    my %allGroups;
+    while (@tableNames) {
+        my $table = GetTagTable(pop @tableNames);
+        my $defaultGroup;
+        $defaultGroup = $table->{GROUPS}->{$family} if $table->{GROUPS};
+        $allGroups{$defaultGroup} = 1 if defined $defaultGroup;
+        foreach (TagTableKeys($table)) {
+            my @infoArray = GetTagInfoList($table,$_);
+            my ($tagInfo, $groups, $group);
+            if ($groups = $$table{GROUPS} and $group = $$groups{$family}) {
+                $allGroups{$group} = 1;
+            }
+            foreach $tagInfo (@infoArray) {
+                if ($groups = $$tagInfo{Groups} and $group = $$groups{$family}) {
+                    $allGroups{$group} = 1;
+                }
+            }
+        }
+    }
+    return sort keys %allGroups;
+}
+
+#------------------------------------------------------------------------------
+# get priority group list for new values
+# Inputs: 0) ExifTool object reference
+# Returns: List of group names
+sub GetNewGroups($)
+{
+    my $self = shift;
+    return @{$self->{WRITE_GROUPS}};
+}
+
+#------------------------------------------------------------------------------
+# Get list of all deletable group names
+# Returns: List of group names (sorted alphabetically)
+sub GetDeleteGroups()
+{
+    return sort @delGroups;
+}
+
+#==============================================================================
+# Functions below this are not part of the public API
+
+#------------------------------------------------------------------------------
+# convert tag names to values in a string (ie. "${EXIF:ISO}x $$" --> "100x $")
+# Inputs: 0) ExifTool object ref, 1) reference to list of found tags
+#         2) string with embedded tag names, 3) Options:
+#               undef    - set missing tags to '',
+#              'Error'   - issue minor error on missing tag (and return undef),
+#              'Warn'    - issue minor warning on missing tag (and return undef),
+#               Hash ref - hash for return of tag/value pairs
+# Returns: string with embedded tag values (or '$info{TAGNAME}' entries with Hash ref option)
+# Notes: tag names are not case sensitive, uses MissingTagValue option if set
+sub InsertTagValues($$$;$)
+{
+    my ($self, $foundTags, $line, $opt) = @_;
+    my $rtnStr = '';
+    while ($line =~ /(.*?)\$(\{?[\w-]+|\$)(.*)/s) {
+        my (@tags, $pre, $var, $group, $family, $val, $tg, @vals);
+        ($pre, $var, $line) = ($1, $2, $3);
+        # "$$" represents a "$" symbol
+        if ($var eq '$') {
+            $rtnStr .= "$pre$var";
+            next;
+        }
+        # check to see if this is a group name
+        if ($line =~ /^:([\w-]+)(.*)/s) {
+            $group = $var;
+            ($var, $line) = ($1, $2);
+            $line =~ s/^\}// if $group =~ s/^\{//;
+            $var = "$group:$var";
+        } else {
+            $line =~ s/^\}// if $var =~ s/^\{//;
+        }
+        push @tags, $var;
+        ExpandShortcuts(\@tags);
+        @tags or $rtnStr .= $pre, next;
+
+        for (;;) {
+            my $tag = shift @tags;
+            my ($group, $family);
+            if ($tag =~ /(.+?)(\d+)?:(.+)/) {
+                ($group, $family, $tag) = ($1, $2, $3);
+                $group = '*' if lc($group) eq 'all';
+                # find the specified tag
+                my @matches = grep /^$tag(\s|$)/i, @$foundTags;
+                foreach $tg (@matches) {
+                    if ($group ne '*') {
+                        my @groups = $self->GetGroup($tg, $family);
+                        my ($grp) = grep /^$group$/i, @groups;
+                        next unless defined $grp;
+                    }
+                    if (defined $val and $tg =~ / \((\d+)\)$/) {
+                        # take the most recently extracted tag
+                        my $tagNum = $1;
+                        next if $tag !~ / \((\d+)\)$/ or $1 > $tagNum;
+                    }
+                    $val = $self->GetValue($tg);
+                    $tag = $tg;
+                    last unless $tag =~ / /;    # all done if we got our best match
+                }
+            } else {
+                # remove trailing bracket if we had a leading one
+                $val = $self->GetValue($tag);
+                unless (defined $val) {
+                    # check for tag name with different case
+                    ($tg) = grep /^$tag$/i, @$foundTags;
+                    if (defined $tg) {
+                        $val = $self->GetValue($tg);
+                        $tag = $tg;
+                    }
+                }
+            }
+            if (ref $val eq 'ARRAY') {
+                $val = join(', ', @$val);
+            } elsif (ref $val eq 'SCALAR') {
+                $val = $$val;
+            } elsif (not defined $val) {
+                last unless @tags;
+                next;
+            }
+            last unless @tags;
+            push @vals, $val;
+            undef $val;
+        }
+        if (@vals) {
+            push @vals, $val if defined $val;
+            $val = join '', @vals;
+        }
+        unless (defined $val or ref $opt) {
+            $val = $self->{OPTIONS}->{MissingTagValue};
+            unless (defined $val) {
+                no strict 'refs';
+                return undef if $opt and &$opt($self, "Tag '$var' not defined", 1);
+                $val = '';
+            }
+        }
+        if (ref $opt eq 'HASH') {
+            $rtnStr .= "$pre\$info{'$var'}";
+            $$opt{$var} = $val;
+        } else {
+            $rtnStr .= "$pre$val";
+        }
+    }
+    return $rtnStr . $line;
+}
+
+#------------------------------------------------------------------------------
+# Is specified tag writable
+# Inputs: 0) tag name, case insensitive (optional group name currently ignored)
+# Returns: 0=exists but not writable, 1=writable, undef=doesn't exist
+sub IsWritable($)
+{
+    my $tag = shift;
+    $tag =~ s/^(.+)://; # ignore group name
+    my @tagInfo = FindTagInfo($tag);
+    return undef unless @tagInfo;
+    my $tagInfo;
+    foreach $tagInfo (@tagInfo) {
+        return 1 if $$tagInfo{Writable} or $tagInfo->{Table}->{WRITABLE};
+    }
+    return 0;
+}
+
+#------------------------------------------------------------------------------
+# Create directory for specified file
+# Inputs: 0) complete file name including path
+# Returns: 1 = directory created, 0 = nothing done, -1 = error
+sub CreateDirectory($)
+{
+    local $_;
+    my $file = shift;
+    my $rtnVal = 0;
+    my $dir;
+    ($dir = $file) =~ s/[^\/]*$//;  # remove filename from path specification
+    if ($dir and not -d $dir) {
+        my @parts = split /\//, $dir;
+        $dir = '';
+        foreach (@parts) {
+            $dir .= $_;
+            if (length $dir and not -d $dir) {
+                # create directory since it doesn't exist
+                mkdir($dir, 0777) or return -1;
+                $rtnVal = 1;
+            }
+            $dir .= '/';
+        }
+    }
+    return $rtnVal;
+}
+
+#------------------------------------------------------------------------------
+# Get new file name
+# Inputs: 0) existing name, 1) new name
+# Returns: new file path name
+sub GetNewFileName($$)
+{
+    my ($oldName, $newName) = @_;
+    my ($dir, $name) = ($oldName =~ m{(.*/)(.*)});
+    ($dir, $name) = ('', $oldName) unless defined $dir;
+    if ($newName =~ m{/$}) {
+        $newName = "$newName$name"; # change dir only
+    } elsif ($newName !~ m{/}) {
+        $newName = "$dir$newName";  # change name only if newname doesn't specify dir
+    }                               # else change dir and name
+    return $newName;
+}
+
+#------------------------------------------------------------------------------
+# Reverse hash lookup
+# Inputs: 0) value, 1) hash reference
+# Returns: Hash key or undef if not found (plus flag for multiple matches in list context)
+sub ReverseLookup($$)
+{
+    my ($val, $conv) = @_;
+    my $multi;
+    if ($val =~ /^Unknown\s*\((.+)\)$/i) {
+        $val = $1;    # was unknown
+        if ($val =~ /^0x([\da-fA-F]+)$/) {
+            $val = hex($val);   # convert hex value
+        }
+    } else {
+        my @patterns = (
+            "^\Q$val\E\$",      # exact match
+            "^(?i)\Q$val\E\$",  # case-insensitive
+            "^(?i)\Q$val\E",    # beginning of string
+            "(?i)\Q$val\E",     # substring
+        );
+        my ($pattern, $found, $matches);
+        foreach $pattern (@patterns) {
+            $matches = scalar grep /$pattern/, values(%$conv);
+            next unless $matches;
+            # multiple matches are bad unless they were exact
+            last if $matches > 1 and $pattern !~ /\$$/;
+            foreach (sort keys %$conv) {
+                if ($$conv{$_} =~ /$pattern/) {
+                    $val = $_;
+                    $found = 1;
+                    last;
+                }
+            }
+            last;
+        }
+        unless ($found) {
+            undef $val;
+            $multi = 1 if $matches > 1;
+        }
+    }
+    if (wantarray) {
+        return ($val, $multi);
+    } else {
+        return $val;
+    }
+}
+
+#------------------------------------------------------------------------------
+# Return true if we are deleting or overwriting the specified tag
+# Inputs: 0) new value hash reference
+#         2) optional tag value if deleting specific values
+# Returns: >0 - tag should be deleted
+#          =0 - the tag should be preserved
+#          <0 - not sure, we need the value to know
+sub IsOverwriting($;$)
+{
+    my ($newValueHash, $value) = @_;
+    return 0 unless $newValueHash;
+    # overwrite regardless if no DelValues specified
+    return 1 unless $newValueHash->{DelValue};
+    # apply time shift if necessary
+    if (defined $newValueHash->{Shift}) {
+        return -1 unless defined $value;
+        my $type = $newValueHash->{TagInfo}->{Shift};
+        my $shift = $newValueHash->{Shift};
+        require 'Image/ExifTool/Shift.pl';
+        my $err = ApplyShift($type, $shift, $value, $newValueHash);
+        if ($err) {
+            my $tag = $newValueHash->{TagInfo}->{Name};
+            $newValueHash->{Self}->Warn("$err when shifting $tag");
+            return 0;
+        }
+        # don't bother overwriting if value is the same
+        return 0 if $value eq $newValueHash->{Value}->[0];
+        return 1;
+    }
+    # never overwrite if DelValue list exists but is empty
+    return 0 unless @{$newValueHash->{DelValue}};
+    # return "don't know" if we don't have a value to test
+    return -1 unless defined $value;
+    # return 1 if value matches a DelValue
+    my $val;
+    foreach $val (@{$newValueHash->{DelValue}}) {
+        return 1 if $value eq $val;
+    }
+    return 0;
+}
+
+#------------------------------------------------------------------------------
+# Return true if we are creating the specified tag even if it didn't exist before
+# Inputs: 0) new value hash reference
+# Returns: true if we should add the tag
+sub IsCreating($)
+{
+    return $_[0]->{IsCreating};
+}
+
+#------------------------------------------------------------------------------
+# Get write group for specified tag
+# Inputs: 0) new value hash reference
+# Returns: Write group name
+sub GetWriteGroup($)
+{
+    return $_[0]->{WriteGroup};
+}
+
+#------------------------------------------------------------------------------
+# Get new value hash for specified tagInfo/writeGroup
+# Inputs: 0) ExifTool object reference, 1) reference to tag info hash
+#         2) Write group name, 3) Options: 'delete' or 'create'
+# Returns: new value hash reference for specified write group
+#          (or first new value hash in linked list if write group not specified)
+sub GetNewValueHash($$;$$)
+{
+    my ($self, $tagInfo, $writeGroup, $opts) = @_;
+    my $newValueHash = $self->{NEW_VALUE}->{$tagInfo};
+
+    my %opts;   # quick lookup for options
+    $opts and $opts{$opts} = 1;
+    $writeGroup = '' unless defined $writeGroup;
+
+    if ($writeGroup) {
+        # find the new value in the list with the specified write group
+        while ($newValueHash and $newValueHash->{WriteGroup} ne $writeGroup) {
+            $newValueHash = $newValueHash->{Next};
+        }
+    }
+    # remove this entry if deleting, or if creating a new entry and
+    # this entry is marked with "Save" flag
+    if (defined $newValueHash and ($opts{'delete'} or
+        ($opts{'create'} and $newValueHash->{Save})))
+    {
+        $self->RemoveNewValueHash($newValueHash, $tagInfo);
+        undef $newValueHash;
+    }
+    if (not defined $newValueHash and $opts{'create'}) {
+        # create a new entry
+        $newValueHash = {
+            TagInfo => $tagInfo,
+            WriteGroup => $writeGroup,
+            Self => $self,
+        };
+        # add entry to our NEW_VALUE hash
+        if ($self->{NEW_VALUE}->{$tagInfo}) {
+            # add to end of linked list
+            my $lastHash = LastInList($self->{NEW_VALUE}->{$tagInfo});
+            $lastHash->{Next} = $newValueHash;
+        } else {
+            $self->{NEW_VALUE}->{$tagInfo} = $newValueHash;
+        }
+    }
+    return $newValueHash;
+}
+
+#------------------------------------------------------------------------------
+# Load all tag tables
+sub LoadAllTables()
+{
+    unless ($loadedAllTables) {
+        # load all of our non-referenced tables (first our modules)
+        my $table;
+        foreach $table (@loadAllTables) {
+            my $tableName = "Image::ExifTool::$table";
+            $tableName .= '::Main' unless $table =~ /:/;
+            GetTagTable($tableName);
+        }
+        # (then our special tables)
+        GetTagTable('Image::ExifTool::Extra');
+        GetTagTable('Image::ExifTool::Composite');
+        # recursively load all tables referenced by the current tables
+        my @tableNames = ( keys %allTables );
+        my %pushedTables;
+        while (@tableNames) {
+            $table = GetTagTable(shift @tableNames);
+            # call write proc if it exists in case it adds tags to the table
+            my $writeProc = $table->{WRITE_PROC};
+            $writeProc and &$writeProc();
+            # recursively scan through tables in subdirectories
+            foreach (TagTableKeys($table)) {
+                my @infoArray = GetTagInfoList($table,$_);
+                my $tagInfo;
+                foreach $tagInfo (@infoArray) {
+                    my $subdir = $$tagInfo{SubDirectory} or next;
+                    my $tableName = $$subdir{TagTable} or next;
+                    # next if table already loaded or queued for loading
+                    next if $allTables{$tableName} or $pushedTables{$tableName};
+                    push @tableNames, $tableName;   # must scan this one too
+                    $pushedTables{$tableName} = 1;
+                }
+            }
+        }
+        $loadedAllTables = 1;
+    }
+}
+
+#------------------------------------------------------------------------------
+# Remove new value hash from linked list (and save if necessary)
+# Inputs: 0) ExifTool object reference, 1) new value hash ref, 2) tagInfo ref
+sub RemoveNewValueHash($$$)
+{
+    my ($self, $newValueHash, $tagInfo) = @_;
+    my $firstHash = $self->{NEW_VALUE}->{$tagInfo};
+    if ($newValueHash eq $firstHash) {
+        # remove first entry from linked list
+        if ($newValueHash->{Next}) {
+            $self->{NEW_VALUE}->{$tagInfo} = $newValueHash->{Next};
+        } else {
+            delete $self->{NEW_VALUE}->{$tagInfo};
+        }
+    } else {
+        # find the list element pointing to this hash
+        $firstHash = $firstHash->{Next} while $firstHash->{Next} ne $newValueHash;
+        # remove from linked list
+        $firstHash->{Next} = $newValueHash->{Next};
+    }
+    # save the existing entry if necessary
+    if ($newValueHash->{Save}) {
+        my $saveHash = $self->{SAVE_NEW_VALUE};
+        # add to linked list of saved new value hashes
+        $newValueHash->{Next} = $saveHash->{$tagInfo};
+        $saveHash->{$tagInfo} = $newValueHash;
+    }
+}
+
+#------------------------------------------------------------------------------
+# Remove all new value entries for specified group
+# Inputs: 0) ExifTool object reference, 1) group name
+sub RemoveNewValuesForGroup($$)
+{
+    my ($self, $group) = @_;
+
+    return unless $self->{NEW_VALUE};
+
+    # make list of all groups we must remove
+    my @groups = ( $group );
+    push @groups, @{$removeGroups{$group}} if $removeGroups{$group};
+
+    my ($out, @keys, $hashKey);
+    $out = $self->{OPTIONS}->{TextOut} if $self->{OPTIONS}->{Verbose} > 1;
+
+    # loop though all new values, and remove any in this group
+    @keys = keys %{$self->{NEW_VALUE}};
+    foreach $hashKey (@keys) {
+        my $newValueHash = $self->{NEW_VALUE}->{$hashKey};
+        # loop through each entry in linked list
+        for (;;) {
+            my $nextHash = $newValueHash->{Next};
+            my $tagInfo = $newValueHash->{TagInfo};
+            my $grp0 = $self->GetGroup($tagInfo, 0);
+            my $wgrp = $newValueHash->{WriteGroup};
+            if (grep /^($grp0|$wgrp)$/, @groups) {
+                $out and print $out "Removed new value for $wgrp:$$tagInfo{Name}\n";
+                # remove from linked list
+                $self->RemoveNewValueHash($newValueHash, $tagInfo);
+            }
+            $newValueHash = $nextHash or last;
+        }
+    }
+}
+
+#------------------------------------------------------------------------------
+# Get list of tagInfo hashes for all new data
+# Inputs: 0) ExifTool object reference, 1) optional tag table pointer
+# Returns: list of tagInfo hashes
+sub GetNewTagInfoList($;$)
+{
+    my ($self, $tagTablePtr) = @_;
+    my @tagInfoList;
+    if ($self->{NEW_VALUE}) {
+        my $hashKey;
+        foreach $hashKey (keys %{$self->{NEW_VALUE}}) {
+            my $tagInfo = $self->{NEW_VALUE}->{$hashKey}->{TagInfo};
+            next if $tagTablePtr and $tagTablePtr ne $tagInfo->{Table};
+            push @tagInfoList, $tagInfo;
+        }
+    }
+    return @tagInfoList;
+}
+
+#------------------------------------------------------------------------------
+# Get hash of tagInfo references keyed on tagID for a specific table
+# Inputs: 0) ExifTool object reference, 1-N) tag table pointers
+# Returns: hash reference
+sub GetNewTagInfoHash($@)
+{
+    my $self = shift;
+    my (%tagInfoHash, $hashKey);
+    while ($self->{NEW_VALUE}) {
+        my $tagTablePtr = shift || last;
+        GenerateTagIDs($tagTablePtr);  # make sure IDs are generated
+        foreach $hashKey (keys %{$self->{NEW_VALUE}}) {
+            my $tagInfo = $self->{NEW_VALUE}->{$hashKey}->{TagInfo};
+            next if $tagTablePtr and $tagTablePtr ne $tagInfo->{Table};
+            $tagInfoHash{$$tagInfo{TagID}} = $tagInfo;
+        }
+    }
+    return \%tagInfoHash;
+}
+
+#------------------------------------------------------------------------------
+# Get a tagInfo/tagID hash for subdirectories we need to add
+# Inputs: 0) ExifTool object reference, 1) parent tag table reference
+#         2) parent directory name (taken from GROUP0 of tag table if not defined)
+# Returns: Reference to Hash of subdirectory tagInfo references keyed by tagID
+#          (plus Reference to edit directory hash in list context)
+sub GetAddDirHash($$;$)
+{
+    my ($self, $tagTablePtr, $parent) = @_;
+    $parent or $parent = $tagTablePtr->{GROUPS}->{0};
+    my $tagID;
+    my %addDirHash;
+    my %editDirHash;
+    my $addDirs = $self->{ADD_DIRS};
+    my $editDirs = $self->{EDIT_DIRS};
+    foreach $tagID (TagTableKeys($tagTablePtr)) {
+        my @infoArray = GetTagInfoList($tagTablePtr,$tagID);
+        my $tagInfo;
+        foreach $tagInfo (@infoArray) {
+            next unless $$tagInfo{SubDirectory};
+            # get name for this sub directory
+            # (take directory name from SubDirectory DirName if it exists,
+            #  otherwise Group0 name of SubDirectory TagTable or tag Group1 name)
+            my $dirName = $tagInfo->{SubDirectory}->{DirName};
+            unless ($dirName) {
+                # use tag name for directory name and save for next time
+                $dirName = $$tagInfo{Name};
+                $tagInfo->{SubDirectory}->{DirName} = $dirName;
+            }
+            # save this directory information if we are writing it
+            if ($$editDirs{$dirName} and $$editDirs{$dirName} eq $parent) {
+                $editDirHash{$tagID} = $tagInfo;
+                $addDirHash{$tagID} = $tagInfo if $$addDirs{$dirName};
+            }
+        }
+    }
+    if (wantarray) {
+        return (\%addDirHash, \%editDirHash);
+    } else {
+        return \%addDirHash;
+    }
+}
+
+#------------------------------------------------------------------------------
+# Get localized version of tagInfo hash (used by MIE and XMP)
+# Inputs: 0) tagInfo hash ref, 1) locale code (ie. "en_CA" for MIE)
+# Returns: new tagInfo hash ref, or undef if invalid
+# - sets LangCode member in new tagInfo
+sub GetLangInfo($$)
+{
+    my ($tagInfo, $langCode) = @_;
+    # make a new tagInfo hash for this locale
+    my $table = $$tagInfo{Table};
+    Image::ExifTool::GenerateTagIDs($table);
+    my $tagID = $$tagInfo{TagID} . '-' . $langCode;
+    my $langInfo = $$table{$tagID};
+    unless ($langInfo) {
+        # make a new tagInfo entry for this locale
+        $langInfo = {
+            %$tagInfo,
+            Name => $$tagInfo{Name} . '-' . $langCode,
+            Description => Image::ExifTool::MakeDescription($$tagInfo{Name}) .
+                           " ($langCode)",
+            LangCode => $langCode,
+        };
+        AddTagToTable($table, $tagID, $langInfo);
+    }
+    return $langInfo;
+}
+
+#------------------------------------------------------------------------------
+# initialize ADD_DIRS and EDIT_DIRS hashes for all directories that need
+# need to be created or will have tags changed in them
+# Inputs: 0) ExifTool object reference, 1) file type string (or map hash ref)
+#         2) preferred family 0 group name for creating tags
+sub InitWriteDirs($$;$)
+{
+    my ($self, $fileType, $preferredGroup) = @_;
+    my $editDirs = $self->{EDIT_DIRS} = { };
+    my $addDirs = $self->{ADD_DIRS} = { };
+    my $fileDirs = $dirMap{$fileType};
+    unless ($fileDirs) {
+        return unless ref $fileType eq 'HASH';
+        $fileDirs = $fileType;
+    }
+    my @tagInfoList = $self->GetNewTagInfoList();
+    my $tagInfo;
+    foreach $tagInfo (@tagInfoList) {
+        my $newValueHash = $self->GetNewValueHash($tagInfo);
+        for (;;) {
+            # are we creating this tag? (otherwise just deleting or editing it)
+            my $isCreating = $newValueHash->{IsCreating};
+            # tag belongs to directory specified by WriteGroup, or by
+            # the Group0 name if WriteGroup not defined
+            my $dirName = $newValueHash->{WriteGroup};
+            # remove MIE copy number(s) if they exist
+            if ($dirName =~ /^MIE\d*(-[a-z]+)?\d*$/i) {
+                $dirName = 'MIE' . ($1 || '');
+            }
+            my @dirNames;
+            while ($dirName) {
+                my $parent = $$fileDirs{$dirName};
+                if (ref $parent) {
+                    push @dirNames, reverse @$parent;
+                    $parent = pop @dirNames;
+                }
+                $$editDirs{$dirName} = $parent;
+                # (if another group is taking priority, only create
+                # directory if specifically adding tags to this group
+                # or if this tag isn't being added to the priority group
+                if ($isCreating and (not $preferredGroup or
+                    $preferredGroup eq $self->GetGroup($tagInfo, 0) or not
+                    $newValueHash->{CreateGroups}->{$preferredGroup}))
+                {
+                    $$addDirs{$dirName} = $parent;
+                }
+                $dirName = $parent || shift @dirNames
+            }
+            last unless $newValueHash->{Next};
+            # cycle through all hashes in linked list
+            $newValueHash = $newValueHash->{Next};
+        }
+    }
+    if (%{$self->{DEL_GROUP}}) {
+        # add delete groups to list of edited groups
+        foreach (keys %{$self->{DEL_GROUP}}) {
+            my $dirName = $_;
+            # translate necessary group 0 names
+            $dirName = $translateWriteGroup{$dirName} if $translateWriteGroup{$dirName};
+            my @dirNames;
+            while ($dirName) {
+                my $parent = $$fileDirs{$dirName};
+                if (ref $parent) {
+                    push @dirNames, reverse @$parent;
+                    $parent = pop @dirNames;
+                }
+                $$editDirs{$dirName} = $parent;
+                $dirName = $parent || shift @dirNames
+            }
+        }
+    }
+    # special case to edit JFIF to get resolutions if editing EXIF information
+    if ($$editDirs{IFD0} and $$fileDirs{JFIF}) {
+        $$editDirs{JFIF} = 'IFD1';
+        $$editDirs{APP0} = undef;
+    }
+  
+    if ($self->{OPTIONS}->{Verbose}) {
+        my $out = $self->{OPTIONS}->{TextOut};
+        print $out "  Editing tags in: ";
+        foreach (sort keys %$editDirs) { print $out "$_ "; }
+        print $out "\n";
+        return unless $self->{OPTIONS}->{Verbose} > 1;
+        print $out "  Creating tags in: ";
+        foreach (sort keys %$addDirs) { print $out "$_ "; }
+        print $out "\n";
+    }
+}
+
+#------------------------------------------------------------------------------
+# Write an image directory
+# Inputs: 0) ExifTool object reference, 1) source directory information reference
+#         2) tag table reference, 3) optional reference to writing procedure
+# Returns: New directory data or undefined on error
+sub WriteDirectory($$$;$)
+{
+    my ($self, $dirInfo, $tagTablePtr, $writeProc) = @_;
+
+    $tagTablePtr or return undef;
+    my $out;
+    $out = $self->{OPTIONS}->{TextOut} if $self->{OPTIONS}->{Verbose};
+    # set directory name from default group0 name if not done already
+    my $dirName = $$dirInfo{DirName};
+    my $grp0 = $tagTablePtr->{GROUPS}->{0};
+    $dirName or $dirName = $$dirInfo{DirName} = $grp0;
+    if (%{$self->{DEL_GROUP}}) {
+        my $delGroup = $self->{DEL_GROUP};
+        # delete entire directory if specified
+        my $grp1 = $dirName;
+        my $delFlag = ($$delGroup{$grp0} or $$delGroup{$grp1});
+        if ($delFlag) {
+            unless ($self->{FILE_TYPE} =~ /^(JPEG|PNG|JP2)$/) {
+                # restrict delete logic to prevent entire tiff image from being killed
+                # (don't allow IFD0 to be deleted, and delete only ExifIFD if EXIF specified)
+                if ($grp1 eq 'IFD0') {
+                    my $type = $self->{TIFF_TYPE} || $self->{FILE_TYPE};
+                    $$delGroup{IFD0} and $self->Warn("Can't delete IFD0 from $type image",1);
+                    undef $grp1;
+                } elsif ($grp0 eq 'EXIF' and $$delGroup{$grp0}) {
+                    undef $grp1 unless $$delGroup{$grp1} or $grp1 eq 'ExifIFD';
+                }
+            }
+            if ($grp1) {
+                if ($$dirInfo{DataPt} or $$dirInfo{RAF}) {
+                    ++$self->{CHANGED};
+                    $out and print $out "  Deleting $grp1\n";
+                    # can no longer validate TIFF_END if deleting an entire IFD
+                    delete $self->{TIFF_END} if $dirName =~ /IFD/;
+                }
+                if ($delFlag == 2 and $self->{ADD_DIRS}->{$grp1}) {
+                    # create new empty directory
+                    my $data = '';
+                    my %dirInfo = (
+                        DirName => $$dirInfo{DirName},
+                        DirStart => 0,
+                        DirLen => 0,
+                        NewDataPos => $$dirInfo{NewDataPos},
+                        Fixup => $$dirInfo{Fixup},
+                    );
+                    $dirInfo = \%dirInfo;
+                } else {
+                    return '';
+                }
+            }
+        }
+    }
+    # copy or delete new directory as a block if specified
+    my $tagInfo = $Image::ExifTool::Extra{$dirName};
+    if ($tagInfo and $self->{NEW_VALUE}->{$tagInfo}) {
+        my $newVal = GetNewValues($self->{NEW_VALUE}->{$tagInfo});
+        if (defined $newVal and length $newVal) {
+            $out and print $out "  Writing $dirName as a block\n";
+            ++$self->{CHANGED};
+            return $newVal;
+        } else {
+            $out and print $out "  Deleting $dirName as a block\n";
+            ++$self->{CHANGED};
+            return '';
+        }
+    }
+    # use default proc from tag table if no proc specified
+    $writeProc or $writeProc = $$tagTablePtr{WRITE_PROC} or return undef;
+    # guard against writing the same directory twice
+    if (defined $$dirInfo{DataPt} and defined $$dirInfo{DirStart} and defined $$dirInfo{DataPos}) {
+        my $addr = $$dirInfo{DirStart} + $$dirInfo{DataPos} + ($$dirInfo{Base}||0);
+        if ($self->{PROCESSED}->{$addr}) {
+            if ($self->Error("$dirName pointer references previous $self->{PROCESSED}->{$addr} directory", 1)) {
+                return undef;
+            } else {
+                $self->Warn("Deleting duplicate $dirName directory");
+                $out and print $out "  Deleting $dirName\n";
+                return '';  # delete the duplicate directory
+            }
+        }
+        $self->{PROCESSED}->{$addr} = $dirName;
+    }
+    # be sure the tag ID's are generated, because the write proc will need them
+    GenerateTagIDs($tagTablePtr);
+    my $oldDir = $self->{DIR_NAME};
+    if ($out and (not defined $oldDir or $oldDir ne $dirName)) {
+        my $verb = ($$dirInfo{DataPt} or $$dirInfo{DirLen}) ? 'Rewriting' : 'Creating';
+        print $out "  $verb $dirName\n";
+    }
+    my $saveOrder = GetByteOrder();
+    my $oldChanged = $self->{CHANGED};
+    $self->{DIR_NAME} = $dirName;
+    my $newData = &$writeProc($self, $dirInfo, $tagTablePtr);
+    $self->{DIR_NAME} = $oldDir;
+    $self->{CHANGED} = $oldChanged unless defined $newData; # nothing changed if error occurred
+    SetByteOrder($saveOrder);
+    print $out "  Deleting $dirName\n" if $out and defined $newData and not length $newData;
+    return $newData;
+}
+
+#------------------------------------------------------------------------------
+# Uncommon utility routines to for reading binary data values
+# Inputs: 0) data reference, 1) offset into data
+sub Get64s($$)
+{
+    my ($dataPt, $pos) = @_;
+    my $pt = GetByteOrder() eq 'MM' ? 0 : 4;    # get position of high word
+    my $hi = Get32s($dataPt, $pos + $pt);       # preserve sign bit of high word
+    my $lo = Get32u($dataPt, $pos + 4 - $pt);
+    return $hi * 4294967296 + $lo;
+}
+sub Get64u($$)
+{
+    my ($dataPt, $pos) = @_;
+    my $pt = GetByteOrder() eq 'MM' ? 0 : 4;    # get position of high word
+    my $hi = Get32u($dataPt, $pos + $pt);       # (unsigned this time)
+    my $lo = Get32u($dataPt, $pos + 4 - $pt);
+    return $hi * 4294967296 + $lo;
+}
+# Decode extended 80-bit float used by Apple SANE and Intel 8087
+# (note: different than the IEEE standard 80-bit float)
+sub GetExtended($$)
+{
+    my ($dataPt, $pos) = @_;
+    my $pt = GetByteOrder() eq 'MM' ? 0 : 2;    # get position of exponent
+    my $exp = Get16u($dataPt, $pos + $pt);
+    my $sig = Get64u($dataPt, $pos + 2 - $pt);  # get significand as int64u
+    my $sign = $exp & 0x8000 ? -1 : 1;
+    $exp = ($exp & 0x7fff) - 16383 - 63; # (-63 to fractionalize significand)
+    return $sign * $sig * 2 ** $exp;
+}
+
+#------------------------------------------------------------------------------
+# Dump data in hex and ASCII to console
+# Inputs: 0) data reference, 1) length or undef, 2-N) Options:
+# Options: Start => offset to start of data (default=0)
+#          Addr => address to print for data start (default=DataPos+Start)
+#          DataPos => address of start of data
+#          Width => width of printout (bytes, default=16)
+#          Prefix => prefix to print at start of line (default='')
+#          MaxLen => maximum length to dump
+#          Out => output file reference
+sub HexDump($;$%)
+{
+    my $dataPt = shift;
+    my $len    = shift;
+    my %opts   = @_;
+    my $start  = $opts{Start}  || 0;
+    my $addr   = $opts{Addr};
+    my $wid    = $opts{Width}  || 16;
+    my $prefix = $opts{Prefix} || '';
+    my $out    = $opts{Out}    || \*STDOUT;
+    my $maxLen = $opts{MaxLen};
+    my $datLen = length($$dataPt) - $start;
+    my $more;
+
+    $addr = $start + ($opts{DataPos} || 0) unless defined $addr;
+    if (not defined $len) {
+        $len = $datLen;
+    } elsif ($len > $datLen) {
+        print $out "$prefix    Warning: Attempted dump outside data\n";
+        print $out "$prefix    ($len bytes specified, but only $datLen available)\n";
+        $len = $datLen;
+    }
+    if ($maxLen and $len > $maxLen) {
+        # print one line less to allow for $more line below
+        $maxLen = int(($maxLen - 1) / $wid) * $wid;
+        $more = $len - $maxLen;
+        $len = $maxLen;
+    }
+    my $format = sprintf("%%-%ds", $wid * 3);
+    my $i;
+    for ($i=0; $i<$len; $i+=$wid) {
+        $wid > $len-$i and $wid = $len-$i;
+        printf $out "$prefix%8.4x: ", $addr+$i;
+        my $dat = substr($$dataPt, $i+$start, $wid);
+        my $s = join(' ',(unpack('H*',$dat) =~ /../g));
+        printf $out $format, $s;
+        $dat =~ tr /\x00-\x1f\x7f-\xff/./;
+        print $out "[$dat]\n";
+    }
+    $more and printf $out "$prefix    [snip $more bytes]\n";
+}
+
+#------------------------------------------------------------------------------
+# Print verbose tag information
+# Inputs: 0) ExifTool object reference, 1) tag ID
+#         2) tag info reference (or undef)
+#         3-N) extra parms:
+# Parms: Index => Index of tag in menu (starting at 0)
+#        Value => Tag value
+#        DataPt => reference to value data block
+#        DataPos => location of data block in file
+#        Size => length of value data within block
+#        Format => value format string
+#        Count => number of values
+#        Extra => Extra Verbose=2 information to put after tag number
+#        Table => Reference to tag table
+#        --> plus any of these HexDump() options: Start, Addr, Width
+sub VerboseInfo($$$%)
+{
+    my ($self, $tagID, $tagInfo, %parms) = @_;
+    my $verbose = $self->{OPTIONS}->{Verbose};
+    my $out = $self->{OPTIONS}->{TextOut};
+    my ($tag, $tagDesc, $line, $hexID);
+
+    # generate hex number if tagID is numerical
+    if (defined $tagID) {
+        $tagID =~ /^\d+$/ and $hexID = sprintf("0x%.4x", $tagID);
+    } else {
+        $tagID = 'Unknown';
+    }
+    # get tag name
+    if ($tagInfo and $$tagInfo{Name}) {
+        $tag = $$tagInfo{Name};
+    } else {
+        my $prefix;
+        $prefix = $parms{Table}->{TAG_PREFIX} if $parms{Table};
+        if ($prefix or $hexID) {
+            $prefix = 'Unknown' unless $prefix;
+            $tag = $prefix . '_' . ($hexID ? $hexID : $tagID);
+        } else {
+            $tag = $tagID;
+        }
+    }
+    my $dataPt = $parms{DataPt};
+    my $size = $parms{Size};
+    $size = length $$dataPt unless defined $size or not $dataPt;
+    my $indent = $self->{INDENT};
+
+    # Level 1: print tag/value information
+    $line = $indent;
+    my $index = $parms{Index};
+    if (defined $index) {
+        $line .= $index . ') ';
+        $line .= ' ' if $index < 10;
+        $indent .= '    '; # indent everything else to align with tag name
+    }
+    $line .= $tag;
+    if ($tagInfo and $$tagInfo{SubDirectory}) {
+        $line .= ' (SubDirectory) -->';
+    } elsif (defined $parms{Value}) {
+        $line .= ' = ' . $self->Printable($parms{Value});
+    } elsif ($dataPt) {
+        my $start = $parms{Start} || 0;
+        $line .= ' = ' . $self->Printable(substr($$dataPt,$start,$size));
+    }
+    print $out "$line\n";
+
+    # Level 2: print detailed information about the tag
+    if ($verbose > 1 and ($parms{Extra} or $parms{Format} or
+        $parms{DataPt} or defined $size or $tagID =~ /\//))
+    {
+        $line = $indent;
+        $line .= '- Tag ' . ($hexID ? $hexID : "'$tagID'");
+        $line .= $parms{Extra} if defined $parms{Extra};
+        my $format = $parms{Format};
+        if ($format or defined $size) {
+            $line .= ' (';
+            if (defined $size) {
+                $line .= "$size bytes";
+                $line .= ', ' if $format;
+            }
+            if ($format) {
+                $line .= $format;
+                $line .= '['.$parms{Count}.']' if $parms{Count};
+            }
+            $line .= ')';
+        }
+        $line .= ':' if $verbose > 2 and $parms{DataPt};
+        print $out "$line\n";
+    }
+
+    # Level 3: do hex dump of value
+    if ($verbose > 2 and $parms{DataPt}) {
+        $parms{Out} = $out;
+        $parms{Prefix} = $indent;
+        # limit dump length unless verbose > 3
+        $parms{MaxLen} = 96 unless $verbose > 3;
+        HexDump($dataPt, $size, %parms);
+    }
+}
+
+#------------------------------------------------------------------------------
+# Dump trailer information
+# Inputs: 0) ExifTool object ref, 1) dirInfo hash (RAF, DirName, DataPos, DirLen)
+# Notes: Restores current file position before returning
+sub DumpTrailer($$)
+{
+    my ($self, $dirInfo) = @_;
+    my $raf = $$dirInfo{RAF};
+    my $curPos = $raf->Tell();
+    my $trailer = $$dirInfo{DirName} || 'Unknown';
+    my $pos = $$dirInfo{DataPos};
+    my $verbose = $self->{OPTIONS}->{Verbose};
+    my $htmlDump = $self->{HTML_DUMP};
+    my ($buff, $buf2);
+    my $size = $$dirInfo{DirLen};
+    $pos = $curPos unless defined $pos;
+
+    # get full trailer size if not specified
+    for (;;) {
+        unless ($size) {
+            $raf->Seek(0, 2) or last;
+            $size = $raf->Tell() - $pos;
+            last unless $size;
+        }
+        $raf->Seek($pos, 0) or last;
+        if ($htmlDump) {
+            my $num = $raf->Read($buff, $size) or return;
+            my $desc = "$trailer trailer";
+            $desc = "[$desc]" if $trailer eq 'Unknown';
+            $self->HtmlDump($pos, $num, $desc, undef, 0x08);
+            last;
+        }
+        my $out = $self->{OPTIONS}->{TextOut};
+        printf $out "$trailer trailer (%d bytes at offset 0x%.4x):\n", $size, $pos;
+        last unless $verbose > 2;
+        my $num = $size;    # number of bytes to read
+        # limit size if not very verbose
+        if ($verbose < 5) {
+            my $limit = $verbose < 4 ? 96 : 512;
+            $num = $limit if $num > $limit;
+        }
+        $raf->Read($buff, $num) == $num or return;
+        # read the end of the trailer too if not done already
+        if ($size > 2 * $num) {
+            $raf->Seek($pos + $size - $num, 0);
+            $raf->Read($buf2, $num);
+        } elsif ($size > $num) {
+            $raf->Seek($pos + $num, 0);
+            $raf->Read($buf2, $size - $num);
+            $buff .= $buf2;
+            undef $buf2;
+        }
+        HexDump(\$buff, undef, Addr => $pos, Out => $out);
+        if (defined $buf2) {
+            print "    [snip ", $size - $num * 2, " bytes]\n";
+            HexDump(\$buf2, undef, Addr => $pos + $size - $num, Out => $out);
+        }
+        last;
+    }
+    $raf->Seek($curPos, 0);
+}
+
+#------------------------------------------------------------------------------
+# Dump unknown trailer information
+# Inputs: 0) ExifTool ref, 1) dirInfo ref (with RAF, DataPos and DirLen defined)
+# Notes: changes dirInfo elements
+sub DumpUnknownTrailer($$)
+{
+    my ($self, $dirInfo) = @_;
+    my $pos = $$dirInfo{DataPos};
+    my $endPos = $pos + $$dirInfo{DirLen};
+    # account for preview image trailer
+    my $prePos = $self->{VALUE}->{PreviewImageStart};
+    my $preLen = $self->{VALUE}->{PreviewImageLength};
+    if ($prePos and $preLen and $prePos + $preLen > $pos) {
+        # dump data before preview image
+        if ($prePos > $pos) {
+            $$dirInfo{DirName} = 'Unknown';
+            $$dirInfo{DirLen}  = $prePos - $pos;
+            $self->DumpTrailer($dirInfo);
+        }
+        # dump preview image if verbose (it is htmlDump'd by ExtractImage)
+        if ($self->{OPTIONS}->{Verbose}) {
+            $$dirInfo{DirName} = 'PreviewImage';
+            $$dirInfo{DataPos} = $prePos;
+            $$dirInfo{DirLen}  = $preLen;
+            $self->DumpTrailer($dirInfo);
+        }
+        return if $prePos + $preLen >= $endPos;
+        # set dirInfo for remaining unknown data
+        $$dirInfo{DataPos} = $prePos + $preLen;
+        $$dirInfo{DirLen} = $endPos - $$dirInfo{DataPos};
+    }
+    # dump unknown trailer
+    $$dirInfo{DirName} = 'Unknown';
+    $self->DumpTrailer($dirInfo);
+}
+
+#------------------------------------------------------------------------------
+# Find last element in linked list
+# Inputs: 0) element in list
+# Returns: Last element in list
+sub LastInList($)
+{
+    my $element = shift;
+    while ($element->{Next}) {
+        $element = $element->{Next};
+    }
+    return $element;
+}
+
+#------------------------------------------------------------------------------
+# Print verbose directory information
+# Inputs: 0) ExifTool object reference, 1) directory name or dirInfo ref
+#         2) number of entries in directory (or 0 if unknown)
+#         3) optional size of directory in bytes
+sub VerboseDir($$;$$)
+{
+    my ($self, $name, $entries, $size) = @_;
+    return unless $self->{OPTIONS}->{Verbose};
+    if (ref $name eq 'HASH') {
+        $size = $$name{DirLen} unless $size;
+        $name = $$name{Name} || $$name{DirName};
+    }
+    my $indent = substr($self->{INDENT}, 0, -2);
+    my $out = $self->{OPTIONS}->{TextOut};
+    my $str;
+    if ($entries) {
+        $str = " with $entries entries";
+    } elsif ($size) {
+        $str = ", $size bytes";
+    } else {
+        $str = '';
+    }
+    print $out "$indent+ [$name directory$str]\n";
+}
+
+#------------------------------------------------------------------------------
+# Pack Unicode numbers into UTF8 string
+# Inputs: 0-N) list of Unicode numbers
+# Returns: Packed UTF-8 string
+sub PackUTF8(@)
+{
+    my @out;
+    while (@_) {
+        my $ch = pop;
+        unshift(@out, $ch), next if $ch < 0x80;
+        unshift(@out, 0x80 | ($ch & 0x3f));
+        $ch >>= 6;
+        unshift(@out, 0xc0 | $ch), next if $ch < 0x20;
+        unshift(@out, 0x80 | ($ch & 0x3f));
+        $ch >>= 6;
+        unshift(@out, 0xe0 | $ch), next if $ch < 0x10;
+        unshift(@out, 0x80 | ($ch & 0x3f));
+        $ch >>= 6;
+        unshift(@out, 0xf0 | ($ch & 0x07));
+    }
+    return pack('C*', @out);
+}
+
+#------------------------------------------------------------------------------
+# Unpack numbers from UTF8 string
+# Inputs: 0) UTF-8 string
+# Returns: List of Unicode numbers (sets $evalWarning on error)
+sub UnpackUTF8($)
+{
+    my (@out, $pos);
+    pos($_[0]) = $pos = 0;  # start at beginning of string
+    for (;;) {
+        my ($ch, $newPos, $val, $byte);
+        if ($_[0] =~ /([\x80-\xff])/g) {
+            $ch = ord($1);
+            $newPos = pos($_[0]) - 1;
+        } else {
+            $newPos = length $_[0];
+        }
+        # unpack 7-bit characters
+        my $len = $newPos - $pos;
+        push @out, unpack("x${pos}C$len",$_[0]) if $len;
+        last unless defined $ch;
+        $pos = $newPos + 1;
+        # minimum lead byte for 2-byte sequence is 0xc2 (overlong sequences
+        # not allowed), 0xf8-0xfd are restricted by RFC 3629 (no 5 or 6 byte
+        # sequences), and 0xfe and 0xff are not valid in UTF-8 strings
+        if ($ch < 0xc2 or $ch >= 0xf8) {
+            push @out, ord('?');    # invalid UTF-8
+            $evalWarning = 'Bad UTF-8';
+            next;
+        }
+        # decode 2, 3 and 4-byte sequences
+        my $n = 1;
+        if ($ch < 0xe0) {
+            $val = $ch & 0x1f;      # 2-byte sequence
+        } elsif ($ch < 0xf0) {
+            $val = $ch & 0x0f;      # 3-byte sequence
+            ++$n;
+        } else {
+            $val = $ch & 0x07;      # 4-byte sequence
+            $n += 2;
+        }
+        unless ($_[0] =~ /\G([\x80-\xbf]{$n})/g) {
+            pos($_[0]) = $pos;      # restore position
+            push @out, ord('?');    # invalid UTF-8
+            $evalWarning = 'Bad UTF-8';
+            next;
+        }
+        foreach $byte (unpack 'C*', $1) {
+            $val = ($val << 6) | ($byte & 0x3f);
+        }
+        push @out, $val;    # save Unicode character value
+        $pos += $n;         # position at end of UTF-8 character
+    }
+    return @out;
+}
+
+#------------------------------------------------------------------------------
+# Convert Unicode characters to Windows Latin1 (cp1252)
+# Inputs: 0) 16-bit Unicode character string, 1) unpack format
+#         3) optional ExifTool ref to set warning on encoding error
+# Returns: 8-bit Windows Latin1 encoded string (truncates at null terminator)
+my %unicode2latin = (
+    0x20ac => 0x80,  0x0160 => 0x8a,  0x2013 => 0x96,
+    0x201a => 0x82,  0x2039 => 0x8b,  0x2014 => 0x97,
+    0x0192 => 0x83,  0x0152 => 0x8c,  0x02dc => 0x98,
+    0x201e => 0x84,  0x017d => 0x8e,  0x2122 => 0x99,
+    0x2026 => 0x85,  0x2018 => 0x91,  0x0161 => 0x9a,
+    0x2020 => 0x86,  0x2019 => 0x92,  0x203a => 0x9b,
+    0x2021 => 0x87,  0x201c => 0x93,  0x0153 => 0x9c,
+    0x02c6 => 0x88,  0x201d => 0x94,  0x017e => 0x9e,
+    0x2030 => 0x89,  0x2022 => 0x95,  0x0178 => 0x9f,
+);
+sub Unicode2Latin($$;$)
+{
+    my ($val, $fmt) = @_;
+    my @uni = unpack("$fmt*",$val);
+    foreach (@uni) {
+        next if $_ <= 0xff;
+        $_ = $unicode2latin{$_};
+        next if $_;
+        $_ = ord('?');  # set to '?'
+        my $self = $_[2];
+        if ($self and not $$self{LatinWarning}) {
+            $self->Warn('Some character(s) could not be encoded in Latin');
+            $$self{LatinWarning} = 1;
+        }
+    }
+    # repack as a Latin string
+    my $outVal = pack('C*', @uni);
+    $outVal =~ s/\0.*//s;    # truncate at null terminator
+    return $outVal;
+}
+
+#------------------------------------------------------------------------------
+# Convert Windows Latin1 characters to Unicode
+# Inputs: 0) 8-bit Windows Latin1 character string (cp1252), 1) unpack format
+# Returns: 16-bit Unicode character string
+my %latin2unicode;
+sub Latin2Unicode($$)
+{
+    # create reverse lookup table if necessary
+    unless (%latin2unicode) {
+        foreach (keys %unicode2latin) {
+            $latin2unicode{$unicode2latin{$_}} = $_;
+        }
+    }
+    my ($val, $fmt) = @_;
+    my @latin = unpack('C*', $val);
+    foreach (@latin) {
+        $_ = $latin2unicode{$_} if $latin2unicode{$_};
+    }
+    # repack as a 16-bit unicode string
+    my $outVal = pack("$fmt*", @latin);
+    return $outVal;
+}
+
+#------------------------------------------------------------------------------
+# Convert Unicode characters to UTF-8
+# Inputs: 0) Unicode character string, 1) Unicode character format (n,v,N,V)
+# Returns: UTF-8 encoded string (truncates at null terminator)
+sub Unicode2UTF8($$)
+{
+    my ($val, $fmt) = @_;
+    my $outVal;
+    if ($] >= 5.006001) {
+        # let Perl do it
+        $outVal = pack('C0U*',unpack("$fmt*",$val));
+    } else {
+        # do it ourself
+        $outVal = PackUTF8(unpack("$fmt*",$val));
+    }
+    $outVal =~ s/\0.*//s;    # truncate at null terminator
+    return $outVal;
+}
+
+#------------------------------------------------------------------------------
+# Convert UTF-8 encoded string to Unicode string
+# Input: 0) UTF-8 string, 1) Unicode format, 2) optional ExifTool ref for warnings
+# Returns: Unicode character string in specified format
+sub UTF82Unicode($$;$)
+{
+    my ($str, $fmt, $exifTool) = @_;
+    undef $evalWarning;
+    if ($] >= 5.006001) {
+        # handle warnings from malformed UTF-8
+        local $SIG{'__WARN__'} = \&SetWarning;
+        # repack UTF-8 string in specified format
+        $str = pack("$fmt*", unpack('U0U*',$str));
+    } else {
+        # do it ourself
+        $str = pack("$fmt*", UnpackUTF8($str));
+    }
+    if ($evalWarning and $exifTool and not $$exifTool{WarnBadUTF8}) {
+        $exifTool->Warn('Malformed UTF-8 character(s)');
+        $$exifTool{WarnBadUTF8} = 1;
+    }
+    return $str;
+}
+
+#------------------------------------------------------------------------------
+# Convert 16-bit unicode character string to 8-bit (Latin or UTF-8)
+# Inputs: 0) ExifTool ref, 1) 16-bit unicode string (in specified byte order)
+#         2) Optional byte order (current byte order used if not specified)
+# Returns: 8-bit character string (truncates at null terminator)
+my %unpackShort = ( 'II' => 'v', 'MM' => 'n' );
+sub Unicode2Charset($$;$) {
+    my ($self, $val, $byteOrder) = @_;
+    # check for (and remove) byte order mark and set byte order accordingly if it exists
+    $val =~ s/^(\xff\xfe|\xfe\xff)// and $byteOrder = ($1 eq "\xff\xfe") ? 'MM' : 'II';
+    my $fmt = $unpackShort{$byteOrder || GetByteOrder()};
+    # convert to Latin if specified or if no UTF-8 support in this Perl version
+    if ($self->Options('Charset') eq 'Latin') {
+        return Unicode2Latin($val, $fmt, $self);
+    } else {
+        return Unicode2UTF8($val, $fmt);
+    }
+}
+
+#------------------------------------------------------------------------------
+# Convert 8-bit character string to 16-bit unicode
+# Inputs: 0) ExifTool ref, 1) Latin or UTF-8 string, 2) optional byte order
+# Returns: 16-bit unicode character string (in specified byte order)
+sub Charset2Unicode($$;$)
+{
+    my ($self, $val, $byteOrder) = @_;
+    my $fmt = $unpackShort{$byteOrder || GetByteOrder()};
+    if ($self->Options('Charset') eq 'Latin') {
+        return Latin2Unicode($val, $fmt);
+    } else {
+        return UTF82Unicode($val, $fmt, $self);
+    }
+}
+
+#------------------------------------------------------------------------------
+# Assemble a continuing fraction into a rational value
+# Inputs: 0) numerator, 1) denominator
+#         2-N) list of fraction denominators, deepest first
+# Returns: numerator, denominator (in list context)
+sub AssembleRational($$@)
+{
+    @_ < 3 and return @_;
+    my ($num, $denom, $frac) = splice(@_, 0, 3);
+    return AssembleRational($frac*$num+$denom, $num, @_);
+}
+
+#------------------------------------------------------------------------------
+# Convert a floating point number into a rational
+# Inputs: 0) floating point number, 1) optional maximum value (defaults to 0x7fffffff)
+# Returns: numerator, denominator (in list context)
+# Notes: these routines were a bit tricky, but fun to write!
+sub Rationalize($;$)
+{
+    my $val = shift;
+    # Note: Just testing "if $val" doesn't work because '0.0' is true!  (ugghh!)
+    return (0, 1) if $val == 0;
+    my $sign = $val < 0 ? ($val = -$val, -1) : 1;
+    my ($num, $denom, @fracs);
+    my $frac = $val;
+    my $maxInt = shift || 0x7fffffff;
+    for (;;) {
+        my ($n, $d) = AssembleRational(int($frac + 0.5), 1, @fracs);
+        if ($n > $maxInt or $d > $maxInt) {
+            last if defined $num;
+            return ($sign, $maxInt) if $val < 1;
+            return ($sign * $maxInt, 1);
+        }
+        ($num, $denom) = ($n, $d);      # save last good values
+        my $err = ($n/$d-$val) / $val;  # get error of this rational
+        last if abs($err) < 1e-8;       # all done if error is small
+        my $int = int($frac);
+        unshift @fracs, $int;
+        last unless $frac -= $int;
+        $frac = 1 / $frac;
+    }
+    return ($num * $sign, $denom);
+}
+
+#------------------------------------------------------------------------------
+# Utility routines to for writing binary data values
+# Inputs: 0) value, 1) data ref, 2) offset
+# Notes: prototype is (@) so values can be passed from list if desired
+sub Set16s(@)
+{
+    my $val = shift;
+    $val < 0 and $val += 0x10000;
+    return Set16u($val, @_);
+}
+sub Set32s(@)
+{
+    my $val = shift;
+    $val < 0 and $val += 0xffffffff, ++$val;
+    return Set32u($val, @_);
+}
+sub SetRational64u(@) {
+    my ($numer,$denom) = Rationalize($_[0],0xffffffff);
+    my $val = Set32u($numer) . Set32u($denom);
+    $_[1] and substr(${$_[1]}, $_[2], length($val)) = $val;
+    return $val;
+}
+sub SetRational64s(@) {
+    my ($numer,$denom) = Rationalize($_[0]);
+    my $val = Set32s($numer) . Set32u($denom);
+    $_[1] and substr(${$_[1]}, $_[2], length($val)) = $val;
+    return $val;
+}
+sub SetRational32u(@) {
+    my ($numer,$denom) = Rationalize($_[0],0xffff);
+    my $val = Set16u($numer) . Set16u($denom);
+    $_[1] and substr(${$_[1]}, $_[2], length($val)) = $val;
+    return $val;
+}
+sub SetRational32s(@) {
+    my ($numer,$denom) = Rationalize($_[0],0xffff);
+    my $val = Set16s($numer) . Set16u($denom);
+    $_[1] and substr(${$_[1]}, $_[2], length($val)) = $val;
+    return $val;
+}
+sub SetFixed16u(@) {
+    my $val = int(shift() * 0x100 + 0.5);
+    return Set16u($val, @_);
+}
+sub SetFixed16s(@) {
+    my $val = shift;
+    return Set16s(int($val * 0x100 + ($val < 0 ? -0.5 : 0.5)), @_);
+}
+sub SetFixed32u(@) {
+    my $val = int(shift() * 0x10000 + 0.5);
+    return Set32u($val, @_);
+}
+sub SetFixed32s(@) {
+    my $val = shift;
+    return Set32s(int($val * 0x10000 + ($val < 0 ? -0.5 : 0.5)), @_);
+}
+sub SetFloat(@) {
+    my $val = SwapBytes(pack('f',$_[0]), 4);
+    $_[1] and substr(${$_[1]}, $_[2], length($val)) = $val;
+    return $val;
+}
+sub SetDouble(@) {
+    # swap 32-bit words (ARM quirk) and bytes if necessary
+    my $val = SwapBytes(SwapWords(pack('d',$_[0])), 8);
+    $_[1] and substr(${$_[1]}, $_[2], length($val)) = $val;
+    return $val;
+}
+#------------------------------------------------------------------------------
+# hash lookups for writing binary data values
+my %writeValueProc = (
+    int8s => \&Set8s,
+    int8u => \&Set8u,
+    int16s => \&Set16s,
+    int16u => \&Set16u,
+    int32s => \&Set32s,
+    int32u => \&Set32u,
+    rational32s => \&SetRational32s,
+    rational32u => \&SetRational32u,
+    rational64s => \&SetRational64s,
+    rational64u => \&SetRational64u,
+    fixed16u => \&SetFixed16u,
+    fixed16s => \&SetFixed16s,
+    fixed32u => \&SetFixed32u,
+    fixed32s => \&SetFixed32s,
+    float => \&SetFloat,
+    double => \&SetDouble,
+    ifd => \&Set32u,
+);
+# verify that we can write floats on this platform
+{
+    my %writeTest = (
+        float =>  [ -3.14159, 'c0490fd0' ],
+        double => [ -3.14159, 'c00921f9f01b866e' ],
+    );
+    my $format;
+    my $oldOrder = GetByteOrder();
+    SetByteOrder('MM');
+    foreach $format (keys %writeTest) {
+        my ($val, $hex) = @{$writeTest{$format}};
+        # add floating point entries if we can write them
+        next if unpack('H*', &{$writeValueProc{$format}}($val)) eq $hex;
+        delete $writeValueProc{$format};    # we can't write them
+    }
+    SetByteOrder($oldOrder);
+}
+
+#------------------------------------------------------------------------------
+# write binary data value (with current byte ordering)
+# Inputs: 0) value, 1) format string
+#         2) optional number of values (1 or string length if not specified)
+#         3) optional data reference, 4) value offset
+# Returns: packed value (and sets value in data) or undef on error
+sub WriteValue($$;$$$$)
+{
+    my ($val, $format, $count, $dataPt, $offset) = @_;
+    my $proc = $writeValueProc{$format};
+    my $packed;
+
+    if ($proc) {
+        my @vals = split(' ',$val);
+        if ($count) {
+            $count = @vals if $count < 0;
+        } else {
+            $count = 1;   # assume 1 if count not specified
+        }
+        $packed = '';
+        while ($count--) {
+            $val = shift @vals;
+            return undef unless defined $val;
+            # validate numerical formats
+            if ($format =~ /^int/) {
+                return undef unless IsInt($val) or IsHex($val);
+            } else {
+                return undef unless IsFloat($val);
+            }
+            $packed .= &$proc($val);
+        }
+    } elsif ($format eq 'string' or $format eq 'undef') {
+        $format eq 'string' and $val .= "\0";   # null-terminate strings
+        if ($count and $count > 0) {
+            my $diff = $count - length($val);
+            if ($diff) {
+                #warn "wrong string length!\n";
+                # adjust length of string to match specified count
+                if ($diff < 0) {
+                    if ($format eq 'string') {
+                        return undef unless $count;
+                        $val = substr($val, 0, $count - 1) . "\0";
+                    } else {
+                        $val = substr($val, 0, $count);
+                    }
+                } else {
+                    $val .= "\0" x $diff;
+                }
+            }
+        } else {
+            $count = length($val);
+        }
+        $dataPt and substr($$dataPt, $offset, $count) = $val;
+        return $val;
+    } else {
+        warn "Sorry, Can't write $format values on this platform\n";
+        return undef;
+    }
+    $dataPt and substr($$dataPt, $offset, length($packed)) = $packed;
+    return $packed;
+}
+
+#------------------------------------------------------------------------------
+# Encode bit mask (the inverse of DecodeBits())
+# Inputs: 0) value to encode, 1) Reference to hash for encoding (or undef)
+#         2) optional number of bits per word (defaults to 32), 3) total bits
+# Returns: bit mask or undef on error (plus error string in list context)
+sub EncodeBits($$;$$)
+{
+    my ($val, $lookup, $bits, $num) = @_;
+    $bits or $bits = 32;
+    $num or $num = $bits;
+    my $words = int(($num + $bits - 1) / $bits);
+    my @outVal = (0) x $words;
+    if ($val ne '(none)') {
+        my @vals = split /\s*,\s*/, $val;
+        foreach $val (@vals) {
+            my $bit;
+            if ($lookup) {
+                $bit = ReverseLookup($val, $lookup);
+                unless (defined $bit) {
+                    if ($val =~ /\[(\d+)\]/) { # numerical bit specification
+                        $bit = $1;
+                    } else {
+                        # don't return error string unless more than one value
+                        return undef unless @vals > 1 and wantarray;
+                        return (undef, "no match for '$val'");
+                    }
+                }
+            } else {
+                $bit = $val;
+            }
+            return (undef, 'bit number too high') if $bit >= $num;
+            my $word = int($bit / $bits);
+            $outVal[$word] |= (1 << ($bit - $word * $bits));
+        }
+    }
+    return "@outVal";
+}
+
+#------------------------------------------------------------------------------
+# get current position in output file
+# Inputs: 0) file or scalar reference
+# Returns: Current position or -1 on error
+sub Tell($)
+{
+    my $outfile = shift;
+    if (UNIVERSAL::isa($outfile,'GLOB')) {
+        return tell($outfile);
+    } else {
+        return length($$outfile);
+    }
+}
+
+#------------------------------------------------------------------------------
+# write to file or memory
+# Inputs: 0) file or scalar reference, 1-N) list of stuff to write
+# Returns: true on success
+sub Write($@)
+{
+    my $outfile = shift;
+    if (UNIVERSAL::isa($outfile,'GLOB')) {
+        return print $outfile @_;
+    } elsif (ref $outfile eq 'SCALAR') {
+        $$outfile .= join('', @_);
+        return 1;
+    }
+    return 0;
+}
+
+#------------------------------------------------------------------------------
+# Read/rewrite trailer information (including multiple trailers)
+# Inputs: 0) ExifTool object ref, 1) DirInfo ref:
+# - requires RAF and DirName
+# - OutFile is a scalar reference for writing
+# - scans from current file position if ScanForAFCP is set
+# Returns: 1 if trailer was processed or couldn't be processed (or written OK)
+#          0 if trailer was recognized but offsets need fixing (or write error)
+# - DirName, DirLen, DataPos, Offset, Fixup and OutFile are updated
+# - preserves current file position and byte order
+sub ProcessTrailers($$)
+{
+    my ($self, $dirInfo) = @_;
+    my $dirName = $$dirInfo{DirName};
+    my $outfile = $$dirInfo{OutFile};
+    my $offset = $$dirInfo{Offset} || 0;
+    my $fixup = $$dirInfo{Fixup};
+    my $raf = $$dirInfo{RAF};
+    my $pos = $raf->Tell();
+    my $byteOrder = GetByteOrder();
+    my $success = 1;
+
+    for (;;) { # loop through all trailers
+        require "Image/ExifTool/$dirName.pm";
+        my $proc = "Image::ExifTool::${dirName}::Process$dirName";
+        my $outBuff;
+        if ($outfile) {
+            # write to local buffer so we can add trailer in proper order later
+            $$outfile and $$dirInfo{OutFile} = \$outBuff, $outBuff = '';
+            # must generate new fixup if necessary so we can shift
+            # the old fixup separately after we prepend this trailer
+            delete $$dirInfo{Fixup};
+        }
+        delete $$dirInfo{DirLen};       # reset trailer length
+        $$dirInfo{Offset} = $offset;    # set offset from end of file
+        $$dirInfo{Trailer} = 1;         # set Trailer flag in case proc cares
+
+        # read or write this trailer
+        # (proc takes Offset as offset from end of trailer to end of file,
+        #  and returns DataPos and DirLen, and Fixup if applicable)
+        no strict 'refs';
+        my $result = &$proc($self, $dirInfo);
+        use strict 'refs';
+
+        if ($outfile) {
+            if ($result > 0) {
+                if ($outBuff) {
+                    # write trailers to OutFile in original order
+                    $$outfile = $outBuff . $$outfile;
+                    # must adjust old fixup start if it exists
+                    $$fixup{Start} += length($outBuff) if $fixup;
+                    $outBuff = '';      # free memory
+                }
+                if ($fixup) {
+                    # add new fixup information if any
+                    $fixup->AddFixup($$dirInfo{Fixup}) if $$dirInfo{Fixup};
+                } else {
+                    $fixup = $$dirInfo{Fixup};  # save fixup
+                }
+            } else {
+                $success = 0 if $self->Error("Error rewriting $dirName trailer", 1);
+                last;
+            }
+        } elsif ($result < 0) {
+            # can't continue if we must scan for this trailer
+            $success = 0;
+            last;
+        }
+        last unless $result > 0 and $$dirInfo{DirLen};
+        # look for next trailer
+        $offset += $$dirInfo{DirLen};
+        my $nextTrail = IdentifyTrailer($raf, $offset) or last;
+        $dirName = $$dirInfo{DirName} = $$nextTrail{DirName};
+        $raf->Seek($pos, 0);
+    }
+    SetByteOrder($byteOrder);       # restore original byte order
+    $raf->Seek($pos, 0);            # restore original file position
+    $$dirInfo{OutFile} = $outfile;  # restore original outfile
+    $$dirInfo{Offset} = $offset;    # return offset to start of first trailer
+    $$dirInfo{Fixup} = $fixup;      # return fixup information
+    return $success;
+}
+
+#------------------------------------------------------------------------------
+# Write trailer buffer to file (applying fixups if necessary)
+# Inputs: 0) ExifTool object ref, 1) trailer dirInfo ref, 2) output file ref
+# Returns: 1 on success
+sub WriteTrailerBuffer($$$)
+{
+    my ($self, $trailInfo, $outfile) = @_;
+    if ($self->{DEL_GROUP}->{Trailer}) {
+        $self->VPrint(0, "  Deleting trailer ($$trailInfo{Offset} bytes)\n");
+        ++$self->{CHANGED};
+        return 1;
+    }
+    my $pos = Tell($outfile);
+    my $trailPt = $$trailInfo{OutFile};
+    # apply fixup if necessary (AFCP requires this)
+    if ($$trailInfo{Fixup}) {
+        if ($pos > 0) {
+            # shift offsets to final AFCP location and write it out
+            $trailInfo->{Fixup}->{Shift} += $pos;
+            $trailInfo->{Fixup}->ApplyFixup($trailPt);
+        } else {
+            $self->Error("Can't get file position for trailer offset fixup",1);
+        }
+    }
+    return Write($outfile, $$trailPt);
+}
+
+#------------------------------------------------------------------------------
+# Add trailers as a block
+# Inputs: 0) ExifTool object ref, 1) [optional] trailer data raf,
+#         1 or 2-N) trailer types to add (or none to add all)
+# Returns: new trailer ref, or undef
+# - increments CHANGED if trailer was added
+sub AddNewTrailers($;@)
+{
+    my ($self, @types) = @_;
+    my $trailPt;
+    ref $types[0] and $trailPt = shift @types;
+    $types[0] or shift @types; # (in case undef data ref is passed)
+    # add all possible trailers if none specified (currently only CanonVRD)
+    @types or @types = qw(CanonVRD);
+    # add trailers as a block
+    my $type;
+    foreach $type (@types) {
+        next unless $self->{NEW_VALUE}->{$Image::ExifTool::Extra{$type}};
+        my $val = $self->GetNewValues($type) or next;
+        my $verb = $trailPt ? 'Writing' : 'Adding';
+        $self->VPrint(0, "  $verb $type as a block\n");
+        if ($trailPt) {
+            $$trailPt .= $val;
+        } else {
+            $trailPt = \$val;
+        }
+        ++$$self{CHANGED};
+    }
+    return $trailPt;
+}
+
+#------------------------------------------------------------------------------
+# Write segment, splitting up into multiple segments if necessary
+# Inputs: 0) file or scalar reference, 1) segment marker
+#         2) segment header, 3) segment data ref, 4) segment type
+# Returns: number of segments written, or 0 on error
+sub WriteMultiSegment($$$$;$)
+{
+    my ($outfile, $marker, $header, $dataPt, $type) = @_;
+    $type or $type = '';
+    my $len = length($$dataPt);
+    my $hdr = "\xff" . chr($marker);
+    my $count = 0;
+    my $maxLen = $maxSegmentLen - length($header);
+    $maxLen -= 2 if $type eq 'ICC'; # leave room for segment counters
+    my $num = int(($len + $maxLen - 1) / $maxLen);  # number of segments to write
+    my $n;
+    # write data, splitting into multiple segments if necessary
+    # (each segment gets its own header)
+    for ($n=0; $n<$len; $n+=$maxLen) {
+        ++$count;
+        my $size = $len - $n;
+        $size > $maxLen and $size = $maxLen;
+        my $buff = substr($$dataPt,$n,$size);
+        $size += length($header);
+        if ($type eq 'ICC') {
+            $buff = pack('CC', $count, $num) . $buff;
+            $size += 2;
+        }
+        # write the new segment with appropriate header
+        my $segHdr = $hdr . pack('n', $size + 2);
+        Write($outfile, $segHdr, $header, $buff) or return 0;
+    }
+    return $count;
+}
+
+#------------------------------------------------------------------------------
+# WriteJPEG : Write JPEG image
+# Inputs: 0) ExifTool object reference, 1) dirInfo reference
+# Returns: 1 on success, 0 if this wasn't a valid JPEG file, or -1 if
+#          an output file was specified and a write error occurred
+sub WriteJPEG($$)
+{
+    my ($self, $dirInfo) = @_;
+    my $outfile = $$dirInfo{OutFile};
+    my $raf = $$dirInfo{RAF};
+    my ($ch,$s,$length);
+    my $verbose = $self->{OPTIONS}->{Verbose};
+    my $out = $self->{OPTIONS}->{TextOut};
+    my $rtnVal = 0;
+    my ($err, %doneDir);
+    my %dumpParms = ( Out => $out );
+    my ($writeBuffer, $oldOutfile); # used to buffer writing until PreviewImage position is known
+
+    # check to be sure this is a valid JPG file
+    return 0 unless $raf->Read($s,2) == 2 and $s eq "\xff\xd8";
+    $dumpParms{MaxLen} = 128 unless $verbose > 3;
+
+    delete $self->{PREVIEW_INFO};   # reset preview information
+    delete $self->{DEL_PREVIEW};    # reset flag to delete preview
+
+    Write($outfile, $s) or $err = 1;
+    # figure out what segments we need to write for the tags we have set
+    my $addDirs = $self->{ADD_DIRS};
+    my $editDirs = $self->{EDIT_DIRS};
+    my $delGroup = $self->{DEL_GROUP};
+
+    # set input record separator to 0xff (the JPEG marker) to make reading quicker
+    my $oldsep = $/;
+    $/ = "\xff";
+#
+# pre-scan image to determine if any create-able segment already exists
+#
+    my $pos = $raf->Tell();
+    my ($marker, @dirOrder, %dirCount);
+    Prescan: for (;;) {
+        # read up to next marker (JPEG markers begin with 0xff)
+        $raf->ReadLine($s) or last;
+        # JPEG markers can be padded with unlimited 0xff's
+        for (;;) {
+            $raf->Read($ch, 1) or last Prescan;
+            $marker = ord($ch);
+            last unless $marker == 0xff;
+        }
+        # SOS signifies end of meta information
+        if ($marker == 0xda) {
+            push(@dirOrder, 'SOS');
+            $dirCount{SOS} = 1;
+            last;
+        }
+        my $dirName;
+        # handle SOF markers: SOF0-SOF15, except DHT(0xc4), JPGA(0xc8) and DAC(0xcc)
+        if (($marker & 0xf0) == 0xc0 and ($marker == 0xc0 or $marker & 0x03)) {
+            last unless $raf->Seek(7, 1);
+        # read data for all markers except stand-alone
+        # markers 0x00, 0x01 and 0xd0-0xd7 (NULL, TEM, RST0-RST7)
+        } elsif ($marker!=0x00 and $marker!=0x01 and ($marker<0xd0 or $marker>0xd7)) {
+            # read record length word
+            last unless $raf->Read($s, 2) == 2;
+            my $len = unpack('n',$s);   # get data length
+            last unless defined($len) and $len >= 2;
+            $len -= 2;  # subtract size of length word
+            if (($marker & 0xf0) == 0xe0) {  # is this an APP segment?
+                my $n = $len < 64 ? $len : 64;
+                $raf->Read($s, $n) == $n or last;
+                $len -= $n;
+                if ($marker == 0xe0) {
+                    $s =~ /^JFIF\0/        and $dirName = 'JFIF';
+                    $s =~ /^JFXX\0\x10/    and $dirName = 'JFXX';
+                } elsif ($marker == 0xe1) {
+                    $s =~ /^$exifAPP1hdr/  and $dirName = 'IFD0';
+                    $s =~ /^$xmpAPP1hdr/   and $dirName = 'XMP';
+                } elsif ($marker == 0xe2) {
+                    $s =~ /^ICC_PROFILE\0/ and $dirName = 'ICC_Profile';
+                } elsif ($marker == 0xec) {
+                    $s =~ /^Ducky/         and $dirName = 'Ducky';
+                } elsif ($marker == 0xed) {
+                    $s =~ /^$psAPP13hdr/   and $dirName = 'Photoshop';
+                }
+                # initialize doneDir as a flag that the directory exists
+                # (unless we are deleting it anyway)
+                $doneDir{$dirName} = 0 if defined $dirName and not $$delGroup{$dirName};
+            }
+            $raf->Seek($len, 1) or last;
+        }
+        $dirName or $dirName = JpegMarkerName($marker);
+        $dirCount{$dirName} = ($dirCount{$dirName} || 0) + 1;
+        push @dirOrder, $dirName;
+    }
+    unless ($marker and $marker == 0xda) {
+        $self->Error('Corrupted JPEG image');
+        return 1;
+    }
+    $raf->Seek($pos, 0) or $self->Error('Seek error'), return 1;
+#
+# re-write the image
+#
+    my ($combinedSegData, $segPos);
+    # read through each segment in the JPEG file
+    Marker: for (;;) {
+
+        # read up to next marker (JPEG markers begin with 0xff)
+        my $segJunk;
+        $raf->ReadLine($segJunk) or $segJunk = '';
+        # remove the 0xff but write the rest of the junk up to this point
+        chomp($segJunk);
+        Write($outfile, $segJunk) if length $segJunk;
+        # JPEG markers can be padded with unlimited 0xff's
+        for (;;) {
+            $raf->Read($ch, 1) or $self->Error('Format error'), return 1;
+            $marker = ord($ch);
+            last unless $marker == 0xff;
+        }
+        # read the segment data
+        my $segData;
+        # handle SOF markers: SOF0-SOF15, except DHT(0xc4), JPGA(0xc8) and DAC(0xcc)
+        if (($marker & 0xf0) == 0xc0 and ($marker == 0xc0 or $marker & 0x03)) {
+            last unless $raf->Read($segData, 7) == 7;
+        # read data for all markers except stand-alone
+        # markers 0x00, 0x01 and 0xd0-0xd7 (NULL, TEM, RST0-RST7)
+        } elsif ($marker!=0x00 and $marker!=0x01 and ($marker<0xd0 or $marker>0xd7)) {
+            # read record length word
+            last unless $raf->Read($s, 2) == 2;
+            my $len = unpack('n',$s);   # get data length
+            last unless defined($len) and $len >= 2;
+            $segPos = $raf->Tell();
+            $len -= 2;  # subtract size of length word
+            last unless $raf->Read($segData, $len) == $len;
+        }
+        # initialize variables for this segment
+        my $hdr = "\xff" . chr($marker);    # segment header
+        my $markerName = JpegMarkerName($marker);
+        my $dirName = shift @dirOrder;      # get directory name
+#
+# create all segments that must come before this one
+# (nothing comes before SOI or after SOS)
+#
+        while ($markerName ne 'SOI') {
+            if (exists $$addDirs{JFIF} and not defined $doneDir{JFIF}) {
+                $doneDir{JFIF} = 1;
+                if ($verbose) {
+                    print $out "Creating APP0:\n";
+                    print $out "  Creating JFIF with default values\n";
+                }
+                my $jfif = "\x01\x02\x01\0\x48\0\x48\0\0";
+                SetByteOrder('MM');
+                my $tagTablePtr = GetTagTable('Image::ExifTool::JFIF::Main');
+                my %dirInfo = (
+                    DataPt   => \$jfif,
+                    DirStart => 0,
+                    DirLen   => length $jfif,
+                );
+                my $newData = $self->WriteDirectory(\%dirInfo, $tagTablePtr);
+                if (defined $newData and length $newData) {
+                    my $app0hdr = "\xff\xe0" . pack('n', length($newData) + 7);
+                    Write($outfile,$app0hdr,"JFIF\0",$newData) or $err = 1;
+                }
+            }
+            # don't create anything before APP0 or APP1 EXIF (containing IFD0)
+            last if $markerName eq 'APP0' or $dirCount{IFD0};
+            # EXIF information must come immediately after APP0
+            if (exists $$addDirs{IFD0} and not defined $doneDir{IFD0}) {
+                $doneDir{IFD0} = 1;
+                $verbose and print $out "Creating APP1:\n";
+                # write new EXIF data
+                $self->{TIFF_TYPE} = 'APP1';
+                my $tagTablePtr = GetTagTable('Image::ExifTool::Exif::Main');
+                # use specified byte ordering or ordering from maker notes if set
+                my $byteOrder = $self->Options('ByteOrder') ||
+                    $self->GetNewValues('ExifByteOrder') || $self->{MAKER_NOTE_BYTE_ORDER} || 'MM';
+                unless (SetByteOrder($byteOrder)) {
+                    warn "Invalid byte order '$byteOrder'\n";
+                    $byteOrder = $self->{MAKER_NOTE_BYTE_ORDER} || 'MM';
+                    SetByteOrder($byteOrder);
+                }
+                my %dirInfo = (
+                    NewDataPos => 8,    # new data will come after TIFF header
+                    DirName => 'IFD0',
+                    Parent  => $markerName,
+                );
+                my $buff = $self->WriteDirectory(\%dirInfo, $tagTablePtr);
+                if (defined $buff and length $buff) {
+                    my $tiffHdr = $byteOrder . Set16u(42) . Set32u(8); # standard TIFF header
+                    my $size = length($buff) + length($tiffHdr) + length($exifAPP1hdr);
+                    if ($size <= $maxSegmentLen) {
+                        # switch to buffered output if required
+                        if ($self->{PREVIEW_INFO} and not $oldOutfile) {
+                            $writeBuffer = '';
+                            $oldOutfile = $outfile;
+                            $outfile = \$writeBuffer;
+                            # account for segment,EXIF and TIFF headers
+                            $self->{PREVIEW_INFO}->{Fixup}->{Start} += 18;
+                        }
+                        # write the new segment with appropriate header
+                        my $app1hdr = "\xff\xe1" . pack('n', $size + 2);
+                        Write($outfile,$app1hdr,$exifAPP1hdr,$tiffHdr,$buff) or $err = 1;
+                    } else {
+                        delete $self->{PREVIEW_INFO};
+                        $self->Warn("EXIF APP1 segment too large! ($size bytes)");
+                    }
+                }
+            }
+            # APP13 Photoshop segment next
+            last if $dirCount{Photoshop};
+            if (exists $$addDirs{Photoshop} and not defined $doneDir{Photoshop}) {
+                $doneDir{Photoshop} = 1;
+                $verbose and print $out "Creating APP13:\n";
+                # write new APP13 Photoshop record to memory
+                my $tagTablePtr = GetTagTable('Image::ExifTool::Photoshop::Main');
+                my %dirInfo = (
+                    Parent => $markerName,
+                );
+                my $buff = $self->WriteDirectory(\%dirInfo, $tagTablePtr);
+                if (defined $buff and length $buff) {
+                    WriteMultiSegment($outfile, 0xed, $psAPP13hdr, \$buff) or $err = 1;
+                    ++$self->{CHANGED};
+                }
+            }
+            # then APP1 XMP segment
+            last if $dirCount{XMP};
+            if (exists $$addDirs{XMP} and not defined $doneDir{XMP}) {
+                $doneDir{XMP} = 1;
+                $verbose and print $out "Creating APP1:\n";
+                # write new XMP data
+                my $tagTablePtr = GetTagTable('Image::ExifTool::XMP::Main');
+                my %dirInfo = (
+                    Parent   => $markerName,
+                );
+                my $buff = $self->WriteDirectory(\%dirInfo, $tagTablePtr);
+                if (defined $buff and length $buff) {
+                    my $size = length($buff) + length($xmpAPP1hdr);
+                    if ($size <= $maxSegmentLen) {
+                        # write the new segment with appropriate header
+                        my $app1hdr = "\xff\xe1" . pack('n', $size + 2);
+                        Write($outfile, $app1hdr, $xmpAPP1hdr, $buff) or $err = 1;
+                    } else {
+                        $self->Warn("XMP APP1 segment too large! ($size bytes)");
+                    }
+                }
+            }
+            # then APP2 ICC_Profile segment
+            last if $dirCount{ICC_Profile};
+            if (exists $$addDirs{ICC_Profile} and not defined $doneDir{ICC_Profile}) {
+                $doneDir{ICC_Profile} = 1;
+                next if $$delGroup{ICC_Profile} and $$delGroup{ICC_Profile} != 2;
+                $verbose and print $out "Creating APP2:\n";
+                # write new ICC_Profile data
+                my $tagTablePtr = GetTagTable('Image::ExifTool::ICC_Profile::Main');
+                my %dirInfo = (
+                    Parent   => $markerName,
+                );
+                my $buff = $self->WriteDirectory(\%dirInfo, $tagTablePtr);
+                if (defined $buff and length $buff) {
+                    WriteMultiSegment($outfile, 0xe2, "ICC_PROFILE\0", \$buff, 'ICC') or $err = 1;
+                    ++$self->{CHANGED};
+                }
+            }
+            # then APP12 Ducky segment
+            last if $dirCount{Ducky};
+            if (exists $$addDirs{Ducky} and not defined $doneDir{Ducky}) {
+                $doneDir{Ducky} = 1;
+                $verbose and print $out "Creating APP12 Ducky:\n";
+                # write new Ducky segment data
+                my $tagTablePtr = GetTagTable('Image::ExifTool::APP12::Ducky');
+                my %dirInfo = (
+                    Parent   => $markerName,
+                );
+                my $buff = $self->WriteDirectory(\%dirInfo, $tagTablePtr);
+                if (defined $buff and length $buff) {
+                    my $size = length($buff) + 5;
+                    if ($size <= $maxSegmentLen) {
+                        # write the new segment with appropriate header
+                        my $app12hdr = "\xff\xec" . pack('n', $size + 2);
+                        Write($outfile, $app12hdr, 'Ducky', $buff) or $err = 1;
+                    } else {
+                        $self->Warn("APP12 Ducky segment too large! ($size bytes)");
+                    }
+                }
+            }
+            # finally, COM segment
+            last if $dirCount{COM};
+            if (exists $$addDirs{COM} and not defined $doneDir{COM}) {
+                $doneDir{COM} = 1;
+                next if $$delGroup{File} and $$delGroup{File} != 2;
+                my $newComment = $self->GetNewValues('Comment');
+                if (defined $newComment and length($newComment)) {
+                    $verbose and print $out "Creating COM:\n";
+                    $verbose > 1 and print $out "    + Comment = '$newComment'\n";
+                    WriteMultiSegment($outfile, 0xfe, '', \$newComment) or $err = 1;
+                    ++$self->{CHANGED};
+                }
+            }
+            last;   # didn't want to loop anyway
+        }
+        # decrement counter for this directory since we are about to process it
+        --$dirCount{$dirName};
+#
+# rewrite existing segments
+#
+        # handle SOF markers: SOF0-SOF15, except DHT(0xc4), JPGA(0xc8) and DAC(0xcc)
+        if (($marker & 0xf0) == 0xc0 and ($marker == 0xc0 or $marker & 0x03)) {
+            $verbose and print $out "JPEG $markerName:\n";
+            Write($outfile, $hdr, $segData) or $err = 1;
+            next;
+        } elsif ($marker == 0xda) {             # SOS
+            $verbose and print $out "JPEG SOS\n";
+            # write SOS segment
+            $s = pack('n', length($segData) + 2);
+            Write($outfile, $hdr, $s, $segData) or $err = 1;
+            my ($buff, $endPos, $trailInfo);
+            my $delPreview = $self->{DEL_PREVIEW};
+            $trailInfo = IdentifyTrailer($raf) unless $$delGroup{Trailer};
+            unless ($oldOutfile or $delPreview or $trailInfo or $$delGroup{Trailer}) {
+                # blindly copy the rest of the file
+                while ($raf->Read($buff, 65536)) {
+                    Write($outfile, $buff) or $err = 1, last;
+                }
+                $rtnVal = 1;  # success unless we have a file write error
+                last;         # all done
+            }
+            # write the rest of the image (as quickly as possible) up to the EOI
+            my $endedWithFF;
+            for (;;) {
+                my $n = $raf->Read($buff, 65536) or last Marker;
+                if (($endedWithFF and $buff =~ m/^\xd9/sg) or
+                    $buff =~ m/\xff\xd9/sg)
+                {
+                    $rtnVal = 1; # the JPEG is OK
+                    # write up to the EOI
+                    my $pos = pos($buff);
+                    Write($outfile, substr($buff, 0, $pos)) or $err = 1;
+                    $buff = substr($buff, $pos);
+                    last;
+                }
+                unless ($n == 65536) {
+                    $self->Error('JPEG EOI marker not found');
+                    last Marker;
+                }
+                Write($outfile, $buff) or $err = 1;
+                $endedWithFF = substr($buff, 65535, 1) eq "\xff" ? 1 : 0;
+            }
+            # remember position of last data copied
+            $endPos = $raf->Tell() - length($buff);
+            # rewrite trailers if they exist
+            if ($trailInfo) {
+                my $tbuf = '';
+                $raf->Seek(-length($buff), 1);  # seek back to just after EOI
+                $$trailInfo{OutFile} = \$tbuf;  # rewrite the trailer
+                $$trailInfo{ScanForAFCP} = 1;   # scan if necessary
+                $self->ProcessTrailers($trailInfo) or undef $trailInfo;
+            }
+            if ($oldOutfile) {
+                # locate preview image and fix up preview offsets
+                if (length($buff) < 1024) { # make sure we have at least 1kB of trailer
+                    my $buf2;
+                    $buff .= $buf2 if $raf->Read($buf2, 1024);
+                }
+                # get new preview image position (subtract 10 for segment and EXIF headers)
+                my $newPos = length($$outfile) - 10;
+                my $junkLen;
+                # adjust position if image isn't at the start (ie. Olympus E-1/E-300)
+                if ($buff =~ m/(\xff\xd8\xff.|.\xd8\xff\xdb)/sg) {
+                    $junkLen = pos($buff) - 4;
+                    $newPos += $junkLen;
+                }
+                # fix up the preview offsets to point to the start of the new image
+                my $previewInfo = $self->{PREVIEW_INFO};
+                delete $self->{PREVIEW_INFO};
+                my $fixup = $previewInfo->{Fixup};
+                $newPos += ($previewInfo->{BaseShift} || 0);
+                if ($previewInfo->{Relative}) {
+                    # adjust for our base by looking at how far the pointer got shifted
+                    $newPos -= $fixup->GetMarkerPointers($outfile, 'PreviewImage');
+                }
+                $fixup->SetMarkerPointers($outfile, 'PreviewImage', $newPos);
+                # clean up and write the buffered data
+                $outfile = $oldOutfile;
+                undef $oldOutfile;
+                Write($outfile, $writeBuffer) or $err = 1;
+                undef $writeBuffer;
+                # write preview image
+                if ($previewInfo->{Data} ne 'LOAD') {
+                    # write any junk that existed before the preview image
+                    Write($outfile, substr($buff,0,$junkLen)) or $err = 1 if $junkLen;
+                    # write the saved preview image
+                    Write($outfile, $previewInfo->{Data}) or $err = 1;
+                    delete $previewInfo->{Data};
+                    # (don't increment CHANGED because we could be rewriting existing preview)
+                    $delPreview = 1;    # remove old preview
+                }
+            }
+            # copy over preview image if necessary
+            unless ($delPreview) {
+                my $extra;
+                if ($trailInfo) {
+                    # copy everything up to start of first processed trailer
+                    $extra = $$trailInfo{DataPos} - $endPos;
+                } else {
+                    # copy everything up to end of file
+                    $raf->Seek(0, 2) or $err = 1;
+                    $extra = $raf->Tell() - $endPos;
+                }
+                if ($extra > 0) {
+                    if ($$delGroup{Trailer}) {
+                        $verbose and print $out "  Deleting unknown trailer ($extra bytes)\n";
+                        ++$self->{CHANGED};
+                    } else {
+                        # copy over unknown trailer
+                        $verbose and print $out "  Preserving unknown trailer ($extra bytes)\n";
+                        $raf->Seek($endPos, 0) or $err = 1;
+                        while ($extra) {
+                            my $n = $extra < 65536 ? $extra : 65536;
+                            $raf->Read($buff, $n) == $n or $err = 1, last;
+                            Write($outfile, $buff) or $err = 1, last;
+                            $extra -= $n;
+                        }
+                    }
+                }
+            }
+            # write trailer if necessary
+            if ($trailInfo) {
+                $self->WriteTrailerBuffer($trailInfo, $outfile) or $err = 1;
+                undef $trailInfo;
+            }
+            last;   # all done parsing file
+
+        } elsif ($marker==0x00 or $marker==0x01 or ($marker>=0xd0 and $marker<=0xd7)) {
+            $verbose and $marker and print $out "JPEG $markerName:\n";
+            # handle stand-alone markers 0x00, 0x01 and 0xd0-0xd7 (NULL, TEM, RST0-RST7)
+            Write($outfile, $hdr) or $err = 1;
+            next;
+        }
+        #
+        # NOTE: A 'next' statement after this point will cause $$segDataPt
+        #       not to be written if there is an output file, so in this case
+        #       the $self->{CHANGED} flags must be updated
+        #
+        my $segDataPt = \$segData;
+        $length = length($segData);
+        if ($verbose) {
+            print $out "JPEG $markerName ($length bytes):\n";
+            if ($verbose > 2 and $markerName =~ /^APP/) {
+                HexDump($segDataPt, undef, %dumpParms);
+            }
+        }
+        my ($segType, $del);
+        # rewrite this segment only if we are changing a tag which
+        # is contained in its directory (or deleting '*', in which
+        # case we need to identify the segment type)
+        while (exists $$editDirs{$markerName} or $$delGroup{'*'}) {
+            my $oldChanged = $self->{CHANGED};
+            if ($marker == 0xe0) {              # APP0 (JFIF, CIFF)
+                if ($$segDataPt =~ /^JFIF\0/) {
+                    $segType = 'JFIF';
+                    $$delGroup{JFIF} and $del = 1, last;
+                    SetByteOrder('MM');
+                    my $tagTablePtr = GetTagTable('Image::ExifTool::JFIF::Main');
+                    my %dirInfo = (
+                        DataPt   => $segDataPt,
+                        DataPos  => $segPos,
+                        DataLen  => $length,
+                        DirStart => 5,     # directory starts after identifier
+                        DirLen   => $length-5,
+                        Parent   => $markerName,
+                    );
+                    my $newData = $self->WriteDirectory(\%dirInfo, $tagTablePtr);
+                    if (defined $newData and length $newData) {
+                        $$segDataPt = "JFIF\0" . $newData;
+                    }
+                } elsif ($$segDataPt =~ /^JFXX\0\x10/) {
+                    $segType = 'JFXX';
+                    $$delGroup{JFIF} and $del = 1;
+                } elsif ($$segDataPt =~ /^(II|MM).{4}HEAPJPGM/s) {
+                    $segType = 'CIFF';
+                    $$delGroup{CIFF} and $del = 1, last;
+                    my $newData = '';
+                    my %dirInfo = (
+                        RAF => new File::RandomAccess($segDataPt),
+                        OutFile => \$newData,
+                    );
+                    require Image::ExifTool::CanonRaw;
+                    if (Image::ExifTool::CanonRaw::WriteCRW($self, \%dirInfo) > 0) {
+                        if (length $newData) {
+                            $$segDataPt = $newData;
+                        } else {
+                            undef $segDataPt;
+                            $del = 1;   # delete this segment
+                        }
+                    }
+                }
+            } elsif ($marker == 0xe1) {         # APP1 (EXIF, XMP)
+                # check for EXIF data
+                if ($$segDataPt =~ /^$exifAPP1hdr/) {
+                    $segType = 'EXIF';
+                    $doneDir{IFD0} and $self->Warn('Multiple APP1 EXIF segments');
+                    $doneDir{IFD0} = 1;
+                    last unless $$editDirs{IFD0};
+                    # write new EXIF data to memory
+                    my $buff = $exifAPP1hdr; # start with APP1 EXIF header
+                    # rewrite EXIF as if this were a TIFF file in memory
+                    my %dirInfo = (
+                        DataPt => $segDataPt,
+                        DataPos => $segPos,
+                        DirStart => 6,
+                        Base => $segPos + 6,
+                        OutFile => \$buff,
+                        Parent => $markerName,
+                    );
+                    my $result = $self->ProcessTIFF(\%dirInfo);
+                    $segDataPt = \$buff;
+                    unless ($result > 0) { # check for problems writing the EXIF
+                        last Marker unless $self->Options('IgnoreMinorErrors');
+                        $$segDataPt = $exifAPP1hdr . $self->{EXIF_DATA}; # restore original EXIF
+                        $self->{CHANGED} = $oldChanged;
+                    }
+                    # switch to buffered output if required
+                    if ($self->{PREVIEW_INFO} and not $oldOutfile) {
+                        $writeBuffer = '';
+                        $oldOutfile = $outfile;
+                        $outfile = \$writeBuffer;
+                        # must account for segment, EXIF and TIFF headers
+                        $self->{PREVIEW_INFO}->{Fixup}->{Start} += 18;
+                    }
+                    # delete segment if IFD contains no entries
+                    $del = 1 unless length($$segDataPt) > length($exifAPP1hdr);
+                # check for XMP data
+                } elsif ($$segDataPt =~ /^$xmpAPP1hdr/) {
+                    $segType = 'XMP';
+                    $doneDir{XMP} and $self->Warn('Multiple APP1 XMP segments');
+                    $doneDir{XMP} = 1;
+                    last unless $$editDirs{XMP};
+                    my $start = length $xmpAPP1hdr;
+                    my $tagTablePtr = GetTagTable('Image::ExifTool::XMP::Main');
+                    my %dirInfo = (
+                        Base     => 0,
+                        DataPt   => $segDataPt,
+                        DataPos  => $segPos,
+                        DataLen  => $length,
+                        DirStart => $start,
+                        DirLen   => $length - $start,
+                        Parent   => $markerName,
+                    );
+                    my $newData = $self->WriteDirectory(\%dirInfo, $tagTablePtr);
+                    if (defined $newData) {
+                        undef $$segDataPt;  # free the old buffer
+                        # add header to new segment unless empty
+                        $newData = $xmpAPP1hdr . $newData if length $newData;
+                        $segDataPt = \$newData;
+                    } else {
+                        $self->{CHANGED} = $oldChanged;
+                    }
+                    $del = 1 unless length $$segDataPt;
+                }
+            } elsif ($marker == 0xe2) {         # APP2 (ICC Profile, FPXR)
+                if ($$segDataPt =~ /^ICC_PROFILE\0/) {
+                    $segType = 'ICC_Profile';
+                    $$delGroup{ICC_Profile} and $del = 1, last;
+                    # must concatenate blocks of profile
+                    my $block_num = ord(substr($$segDataPt, 12, 1));
+                    my $blocks_tot = ord(substr($$segDataPt, 13, 1));
+                    $combinedSegData = '' if $block_num == 1;
+                    unless (defined $combinedSegData) {
+                        $self->Warn('APP2 ICC_Profile segments out of sequence');
+                        next Marker;
+                    }
+                    $combinedSegData .= substr($$segDataPt, 14);
+                    # continue accumulating segments unless this is the last
+                    next Marker unless $block_num == $blocks_tot;
+                    $doneDir{ICC_Profile} and $self->Warn('Multiple ICC_Profile records');
+                    $doneDir{ICC_Profile} = 1;
+                    $segDataPt = \$combinedSegData;
+                    $length = length $combinedSegData;
+                    my $tagTablePtr = GetTagTable('Image::ExifTool::ICC_Profile::Main');
+                    my %dirInfo = (
+                        DataPt   => $segDataPt,
+                        DataPos  => $segPos + 14,
+                        DataLen  => $length,
+                        DirStart => 0,
+                        DirLen   => $length,
+                        Parent   => $markerName,
+                    );
+                    my $newData = $self->WriteDirectory(\%dirInfo, $tagTablePtr);
+                    if (defined $newData) {
+                        undef $$segDataPt;  # free the old buffer
+                        $segDataPt = \$newData;
+                    }
+                    length $$segDataPt or $del = 1, last;
+                    # write as ICC multi-segment
+                    WriteMultiSegment($outfile, $marker, "ICC_PROFILE\0", $segDataPt, 'ICC') or $err = 1;
+                    undef $combinedSegData;
+                    undef $$segDataPt;
+                    next Marker;
+                } elsif ($$segDataPt =~ /^FPXR\0/) {
+                    $segType = 'FPXR';
+                    $$delGroup{FlashPix} and $del = 1;
+                }
+            } elsif ($marker == 0xe3) {         # APP3 (Kodak Meta)
+                if ($$segDataPt =~ /^(Meta|META|Exif)\0\0/) {
+                    $segType = 'Kodak Meta';
+                    $$delGroup{Meta} and $del = 1;
+                }
+            } elsif ($marker == 0xe5) {         # APP5 (Ricoh RMETA)
+                if ($$segDataPt =~ /^RMETA\0/) {
+                    $segType = 'Ricoh RMETA';
+                    $$delGroup{RMETA} and $del = 1;
+                }
+            } elsif ($marker == 0xec) {         # APP12 (Ducky)
+                if ($$segDataPt =~ /^Ducky/) {
+                    $segType = 'Ducky';
+                    $$delGroup{Ducky} and $del = 1, last;
+                    $doneDir{Ducky} and $self->Warn('Multiple APP12 Ducky segments');
+                    $doneDir{Ducky} = 1;
+                    last unless $$editDirs{Ducky};
+                    my $tagTablePtr = GetTagTable('Image::ExifTool::APP12::Ducky');
+                    my %dirInfo = (
+                        DataPt   => $segDataPt,
+                        DataPos  => $segPos,
+                        DataLen  => $length,
+                        DirStart => 5,     # directory starts after identifier
+                        DirLen   => $length-5,
+                        Parent   => $markerName,
+                    );
+                    my $newData = $self->WriteDirectory(\%dirInfo, $tagTablePtr);
+                    if (defined $newData) {
+                        undef $$segDataPt;  # free the old buffer
+                        # add header to new segment unless empty
+                        $newData = 'Ducky' . $newData if length $newData;
+                        $segDataPt = \$newData;
+                    } else {
+                        $self->{CHANGED} = $oldChanged;
+                    }
+                    $del = 1 unless length $$segDataPt;
+                }
+            } elsif ($marker == 0xed) {         # APP13 (Photoshop)
+                if ($$segDataPt =~ /^$psAPP13hdr/) {
+                    $segType = 'Photoshop';
+                    # add this data to the combined data if it exists
+                    if (defined $combinedSegData) {
+                        $combinedSegData .= substr($$segDataPt,length($psAPP13hdr));
+                        $segDataPt = \$combinedSegData;
+                        $length = length $combinedSegData;  # update length
+                    }
+                    # peek ahead to see if the next segment is photoshop data too
+                    if ($dirOrder[0] eq 'Photoshop') {
+                        # initialize combined data if necessary
+                        $combinedSegData = $$segDataPt unless defined $combinedSegData;
+                        next Marker;    # get the next segment to combine
+                    }
+                    $doneDir{Photoshop} and $self->Warn('Multiple Photoshop records');
+                    $doneDir{Photoshop} = 1;
+                    # process APP13 Photoshop record
+                    my $tagTablePtr = GetTagTable('Image::ExifTool::Photoshop::Main');
+                    my %dirInfo = (
+                        DataPt   => $segDataPt,
+                        DataPos  => $segPos,
+                        DataLen  => $length,
+                        DirStart => 14,     # directory starts after identifier
+                        DirLen   => $length-14,
+                        Parent   => $markerName,
+                    );
+                    my $newData = $self->WriteDirectory(\%dirInfo, $tagTablePtr);
+                    if (defined $newData) {
+                        undef $$segDataPt;  # free the old buffer
+                        $segDataPt = \$newData;
+                    } else {
+                        $self->{CHANGED} = $oldChanged;
+                    }
+                    length $$segDataPt or $del = 1, last;
+                    # write as multi-segment
+                    WriteMultiSegment($outfile, $marker, $psAPP13hdr, $segDataPt) or $err = 1;
+                    undef $combinedSegData;
+                    undef $$segDataPt;
+                    next Marker;
+                }
+            } elsif ($marker == 0xfe) {         # COM (JPEG comment)
+                my $newComment;
+                unless ($doneDir{COM}) {
+                    $doneDir{COM} = 1;
+                    unless ($$delGroup{File} and $$delGroup{File} != 2) {
+                        my $tagInfo = $Image::ExifTool::Extra{Comment};
+                        my $newValueHash = $self->GetNewValueHash($tagInfo);
+                        if (IsOverwriting($newValueHash, $segData) or $$delGroup{File}) {
+                            $newComment = GetNewValues($newValueHash);
+                        } else {
+                            delete $$editDirs{COM}; # we aren't editing COM after all
+                            last;
+                        }
+                    }
+                }
+                $verbose > 1 and print $out "    - Comment = '$$segDataPt'\n";
+                if (defined $newComment and length $newComment) {
+                    # write out the comments
+                    $verbose > 1 and print $out "    + Comment = '$newComment'\n";
+                    WriteMultiSegment($outfile, 0xfe, '', \$newComment) or $err = 1;
+                } else {
+                    $verbose and print $out "  Deleting COM segment\n";
+                }
+                ++$self->{CHANGED};     # increment the changed flag
+                undef $segDataPt;       # don't write existing comment
+            }
+            last;   # didn't want to loop anyway
+        }
+        # delete necessary segments (including unknown segments if deleting all)
+        if ($del or ($$delGroup{'*'} and not $segType and $marker>=0xe0 and $marker<=0xef)) {
+            $segType = 'unknown' unless $segType;
+            $verbose and print $out "  Deleting $markerName $segType segment\n";
+            ++$self->{CHANGED};
+            next Marker;
+        }
+        # write out this segment if $segDataPt is still defined
+        if (defined $segDataPt) {
+            # write the data for this record (the data could have been
+            # modified, so recalculate the length word)
+            my $size = length($$segDataPt);
+            if ($size > $maxSegmentLen) {
+                $self->Error("$markerName segment too large! ($size bytes)");
+                $err = 1;
+            } else {
+                $s = pack('n', length($$segDataPt) + 2);
+                Write($outfile, $hdr, $s, $$segDataPt) or $err = 1;
+            }
+            undef $$segDataPt;  # free the buffer
+        }
+    }
+    # if oldOutfile is still set, there was an error copying the JPEG
+    $oldOutfile and return 0;
+    if ($rtnVal) {
+        # add any new trailers we are creating
+        my $trailPt = $self->AddNewTrailers();
+        Write($outfile, $$trailPt) or $err = 1 if $trailPt;
+    }
+    $/ = $oldsep;     # restore separator to original value
+    # set return value to -1 if we only had a write error
+    $rtnVal = -1 if $rtnVal and $err;
+    return $rtnVal;
+}
+
+#------------------------------------------------------------------------------
+# Validate an image for writing
+# Inputs: 0) ExifTool object reference, 1) raw value reference
+# Returns: error string or undef on success
+sub CheckImage($$)
+{
+    my ($self, $valPtr) = @_;
+    if (length($$valPtr) and $$valPtr!~/^\xff\xd8/ and not
+        $self->Options('IgnoreMinorErrors'))
+    {
+        return '[minor] Not a valid image';
+    }
+    return undef;
+}
+
+#------------------------------------------------------------------------------
+# check a value for validity
+# Inputs: 0) value reference, 1) format string, 2) optional count
+# Returns: error string, or undef on success
+# Notes: May modify value (if a count is specified for a string, it is null-padded
+# to the specified length, and floating point values are rounded to integer if required)
+sub CheckValue($$;$)
+{
+    my ($valPtr, $format, $count) = @_;
+    my (@vals, $n);
+
+    if ($format eq 'string' or $format eq 'undef') {
+        return undef unless $count and $count > 0;
+        my $len = length($$valPtr);
+        if ($format eq 'string') {
+            $len >= $count and return 'String too long';
+        } else {
+            $len > $count and return 'Data too long';
+        }
+        if ($len < $count) {
+            $$valPtr .= "\0" x ($count - $len);
+        }
+        return undef;
+    }
+    if ($count and $count != 1) {
+        @vals = split(' ',$$valPtr);
+        $count < 0 and ($count = @vals or return undef);
+    } else {
+        $count = 1;
+        @vals = ( $$valPtr );
+    }
+    return "Too many values specified ($count required)" if @vals > $count;
+    return "Not enough values specified ($count required)" if @vals < $count;
+    my $val;
+    for ($n=0; $n<$count; ++$n) {
+        $val = shift @vals;
+        if ($format =~ /^int/) {
+            # make sure the value is integer
+            unless (IsInt($val)) {
+                if (IsHex($val)) {
+                    $val = $$valPtr = hex($val);
+                } else {
+                    # round single floating point values to the nearest integer
+                    return 'Not an integer' unless IsFloat($val) and $count == 1;
+                    $val = $$valPtr = int($val + ($val < 0 ? -0.5 : 0.5));
+                }
+            }
+            my $rng = $intRange{$format} or return "Bad int format: $format";
+            return "Value below $format minimum" if $val < $$rng[0];
+            # (allow 0xfeedfeed code as value for 16-bit pointers)
+            return "Value above $format maximum" if $val > $$rng[1] and $val != 0xfeedfeed;
+        } elsif ($format =~ /^rational/ or $format eq 'float' or $format eq 'double') {
+            # make sure the value is a valid floating point number
+            return 'Not a floating point number' unless IsFloat($val);
+            if ($format =~ /^rational\d+u$/ and $val < 0) {
+                return 'Must be a positive number';
+            }
+        }
+    }
+    return undef;   # success!
+}
+
+#------------------------------------------------------------------------------
+# check new value for binary data block
+# Inputs: 0) ExifTool object ref, 1) tagInfo hash ref, 2) raw value ref
+# Returns: error string or undef (and may modify value) on success
+sub CheckBinaryData($$$)
+{
+    my ($self, $tagInfo, $valPtr) = @_;
+    my $format = $$tagInfo{Format};
+    unless ($format) {
+        my $table = $$tagInfo{Table};
+        if ($table and $$table{FORMAT}) {
+            $format = $$table{FORMAT};
+        } else {
+            # use default 'int8u' unless specified
+            $format = 'int8u';
+        }
+    }
+    my $count;
+    if ($format =~ /(.*)\[(.*)\]/) {
+        $format = $1;
+        $count = $2;
+        # can't evaluate $count now because we don't know $size yet
+        undef $count if $count =~ /\$size/;
+    }
+    return CheckValue($valPtr, $format, $count);
+}
+
+#------------------------------------------------------------------------------
+# copy image data from one file to another
+# Inputs: 0) ExifTool object reference
+#         1) reference to list of image data [ position, size, pad bytes ]
+#         2) output file ref
+# Returns: true on success
+sub CopyImageData($$$)
+{
+    my ($self, $imageDataBlocks, $outfile) = @_;
+    my $raf = $self->{RAF};
+    my ($dataBlock, $buff, $err);
+    foreach $dataBlock (@$imageDataBlocks) {
+        my ($pos, $size, $pad) = @$dataBlock;
+        $raf->Seek($pos, 0) or $err = 'read', last;
+        while ($size) {
+            # copy in blocks of 64kB or smaller
+            my $n = $size > 65536 ? 65536 : $size;
+            $raf->Read($buff, $n) == $n or $err = 'read', last;
+            Write($outfile, $buff) or $err = 'writ', last;
+            $size -= $n;
+        }
+        # pad if necessary
+        Write($outfile, "\0" x $pad) or $err = 'writ' if $pad;
+        last if $err;
+    }
+    if ($err) {
+        $self->Error("Error ${err}ing image data");
+        return 0;
+    }
+    return 1;
+}
+
+#------------------------------------------------------------------------------
+# write to binary data block
+# Inputs: 0) ExifTool object ref, 1) source dirInfo ref, 2) tag table ref
+# Returns: Binary data block or undefined on error
+sub WriteBinaryData($$$)
+{
+    my ($self, $dirInfo, $tagTablePtr) = @_;
+    $self or return 1;    # allow dummy access to autoload this package
+
+    # get default format ('int8u' unless specified)
+    my $defaultFormat = $$tagTablePtr{FORMAT} || 'int8u';
+    my $increment = FormatSize($defaultFormat);
+    unless ($increment) {
+        warn "Unknown format $defaultFormat\n";
+        return undef;
+    }
+    # extract data members first if necessary
+    if ($$tagTablePtr{DATAMEMBER}) {
+        $$dirInfo{DataMember} = $$tagTablePtr{DATAMEMBER};
+        $self->ProcessBinaryData($dirInfo, $tagTablePtr);
+        delete $$dirInfo{DataMember};
+    }
+    my $dataPt = $$dirInfo{DataPt};
+    my $dirStart = $$dirInfo{DirStart} || 0;
+    my $dirLen = $$dirInfo{DirLen} || length($$dataPt) - $dirStart;
+    my $newData = substr($$dataPt, $dirStart, $dirLen) or return undef;
+    my $dirName = $$dirInfo{DirName};
+    my $verbose = $self->Options('Verbose');
+    my $tagInfo;
+    $dataPt = \$newData;
+    foreach $tagInfo ($self->GetNewTagInfoList($tagTablePtr)) {
+        my $tagID = $tagInfo->{TagID};
+        # must check to be sure this tagInfo applies (ie. evaluate the condition!)
+        my $writeInfo = $self->GetTagInfo($tagTablePtr, $tagID);
+        next unless $writeInfo and $writeInfo eq $tagInfo;
+        my $count = 1;
+        my $format = $$tagInfo{Format};
+        if ($format) {
+            if ($format =~ /(.*)\[(.*)\]/) {
+                $format = $1;
+                $count = $2;
+                my $size = $dirLen; # used in eval
+                # evaluate count to allow count to be based on previous values
+                #### eval Format size (%val, $size)
+                $count = eval $count;
+                $@ and warn($@), next;
+            }
+        } else {
+            $format = $defaultFormat;
+        }
+        my $entry = $tagID * $increment;        # relative offset of this entry
+        my $val = ReadValue($dataPt, $entry, $format, $count, $dirLen-$entry);
+        next unless defined $val;
+        my $newValueHash = $self->GetNewValueHash($tagInfo);
+        next unless IsOverwriting($newValueHash, $val);
+        my $newVal = GetNewValues($newValueHash);
+        next unless defined $newVal;    # can't delete from a binary table
+        # set the size
+        if ($$tagInfo{DataTag} and not $$tagInfo{IsOffset}) {
+            warn 'Internal error' unless $newVal == 0xfeedfeed;
+            my $data = $self->GetNewValues($$tagInfo{DataTag});
+            $newVal = length($data) if defined $data;
+        }
+        my $rtnVal = WriteValue($newVal, $format, $count, $dataPt, $entry);
+        if (defined $rtnVal) {
+            if ($verbose > 1) {
+                my $out = $self->{OPTIONS}->{TextOut};
+                print $out "    - $dirName:$$tagInfo{Name} = '$val'\n";
+                print $out "    + $dirName:$$tagInfo{Name} = '$newVal'\n";
+            }
+            ++$self->{CHANGED};
+        }
+    }
+    # add necessary fixups for any offsets
+    if ($tagTablePtr->{IS_OFFSET} and $$dirInfo{Fixup}) {
+        my $fixup = $$dirInfo{Fixup};
+        my $tagID;
+        foreach $tagID (@{$tagTablePtr->{IS_OFFSET}}) {
+            $tagInfo = $self->GetTagInfo($tagTablePtr, $tagID) or next;
+            my $entry = $tagID * $increment;    # (no offset to dirStart for new dir data)
+            next unless $entry <= $dirLen - 4;
+            # (Ricoh has 16-bit preview image offsets, so can't just assume int32u)
+            my $format = $$tagInfo{Format} || $$tagTablePtr{FORMAT} || 'int32u';
+            $fixup->AddFixup($entry, $$tagInfo{DataTag}, $format);
+            # handle the preview image now if this is a JPEG file
+            next unless $self->{FILE_TYPE} eq 'JPEG' and $$tagInfo{DataTag} and
+                $$tagInfo{DataTag} eq 'PreviewImage' and defined $$tagInfo{OffsetPair};
+            my $offset = ReadValue($dataPt, $entry, $format, 1, $dirLen-$entry);
+            $entry = $$tagInfo{OffsetPair} * $increment;
+            my $size = ReadValue($dataPt, $entry, $format, 1, $dirLen-$entry);
+            my $previewInfo = $self->{PREVIEW_INFO};
+            $previewInfo or $previewInfo = $self->{PREVIEW_INFO} = { };
+            # set flag indicating we are using short pointers
+            $previewInfo->{IsShort} = 1 unless $format eq 'int32u';
+            $previewInfo->{Data} = $self->GetNewValues('PreviewImage');
+            unless (defined $previewInfo->{Data}) {
+                if ($offset >= 0 and $offset + $size <= $$dirInfo{DataLen}) {
+                    $previewInfo->{Data} = substr(${$$dirInfo{DataPt}},$offset,$size);
+                } else {
+                    $previewInfo->{Data} = 'LOAD'; # flag to load preview later
+                }
+            }
+        }
+    }
+    return $newData;
+}
+
+#------------------------------------------------------------------------------
+# Write TIFF as a directory
+# Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
+# Returns: New directory data or undefined on error
+sub WriteTIFF($$$)
+{
+    my ($self, $dirInfo, $tagTablePtr) = @_;
+    my $buff;
+    $$dirInfo{OutFile} = \$buff;
+    return $buff if $self->ProcessTIFF($dirInfo, $tagTablePtr) > 0;
+    return undef;
+}
+
+1; # end
+
+__END__
+
+=head1 NAME
+
+Image::ExifTool::Writer.pl - ExifTool routines for writing meta information
+
+=head1 SYNOPSIS
+
+These routines are autoloaded by Image::ExifTool when required.
+
+=head1 DESCRIPTION
+
+This module contains ExifTool write routines and other infrequently
+used routines.
+
+=head1 AUTHOR
+
+Copyright 2003-2007, Phil Harvey (phil at owl.phy.queensu.ca)
+
+This library is free software; you can redistribute it and/or modify it
+under the same terms as Perl itself.
+
+=head1 SEE ALSO
+
+L<Image::ExifTool(3pm)|Image::ExifTool>
+
+=cut
Index: /gsdl/trunk/perllib/cpan/Image/ExifTool/XMP.pm
===================================================================
--- /gsdl/trunk/perllib/cpan/Image/ExifTool/XMP.pm	(revision 16842)
+++ /gsdl/trunk/perllib/cpan/Image/ExifTool/XMP.pm	(revision 16842)
@@ -0,0 +1,2291 @@
+#------------------------------------------------------------------------------
+# File:         XMP.pm
+#
+# Description:  Read XMP meta information
+#
+# Revisions:    11/25/2003 - P. Harvey Created
+#               10/28/2004 - P. Harvey Major overhaul to conform with XMP spec
+#               02/27/2005 - P. Harvey Also read UTF-16 and UTF-32 XMP
+#               08/30/2005 - P. Harvey Split tag tables into separate namespaces
+#               10/24/2005 - P. Harvey Added ability to parse .XMP files
+#               08/25/2006 - P. Harvey Added ability to handle blank nodes
+#               08/22/2007 - P. Harvey Added ability to handle alternate language tags
+#
+# References:   1) http://www.adobe.com/products/xmp/pdfs/xmpspec.pdf
+#               2) http://www.w3.org/TR/rdf-syntax-grammar/  (20040210)
+#               3) http://www.portfoliofaq.com/pfaq/v7mappings.htm
+#               4) http://www.iptc.org/IPTC4XMP/
+#               5) http://creativecommons.org/technology/xmp
+#               6) http://www.optimasc.com/products/fileid/xmp-extensions.pdf
+#               7) Lou Salkind private communication
+#               8) http://partners.adobe.com/public/developer/en/xmp/sdk/XMPspecification.pdf
+#
+# Notes:      - I am handling property qualifiers as if they were separate
+#               properties (with no associated namespace).
+#
+#             - Currently, there is no special treatment of the following
+#               properties which could potentially affect the extracted
+#               information: xml:base, rdf:parseType (note that parseType
+#               Literal isn't allowed by the XMP spec).
+#
+#             - The family 2 group names will be set to 'Unknown' for any XMP
+#               tags not found in the XMP or Exif tag tables.
+#------------------------------------------------------------------------------
+
+package Image::ExifTool::XMP;
+
+use strict;
+use vars qw($VERSION $AUTOLOAD @ISA @EXPORT_OK);
+use Image::ExifTool qw(:Utils);
+use Image::ExifTool::Exif;
+require Exporter;
+
+$VERSION = '1.78';
+@ISA = qw(Exporter);
+@EXPORT_OK = qw(EscapeXML UnescapeXML);
+
+sub ProcessXMP($$;$);
+sub WriteXMP($$;$);
+sub ParseXMPElement($$$;$$$);
+sub DecodeBase64($);
+sub SaveBlankInfo($$$;$);
+sub ProcessBlankInfo($$$;$);
+sub ValidateXMP($;$);
+sub UnescapeChar($$);
+
+# conversions for GPS coordinates
+sub ToDegrees
+{
+    require Image::ExifTool::GPS;
+    Image::ExifTool::GPS::ToDegrees($_[0], 1);
+}
+my %latConv = (
+    ValueConv    => \&ToDegrees,
+    RawConv => 'require Image::ExifTool::GPS; $val', # to load Composite tags and routines
+    ValueConvInv => q{
+        require Image::ExifTool::GPS;
+        Image::ExifTool::GPS::ToDMS($self, $val, 2, "N");
+    },
+    PrintConv    => 'Image::ExifTool::GPS::ToDMS($self, $val, 1, "N")',
+    PrintConvInv => \&ToDegrees,
+);
+my %longConv = (
+    ValueConv    => \&ToDegrees,
+    RawConv => 'require Image::ExifTool::GPS; $val',
+    ValueConvInv => q{
+        require Image::ExifTool::GPS;
+        Image::ExifTool::GPS::ToDMS($self, $val, 2, "E");
+    },
+    PrintConv    => 'Image::ExifTool::GPS::ToDMS($self, $val, 1, "E")',
+    PrintConvInv => \&ToDegrees,
+);
+my %dateTimeInfo = (
+    Groups => { 2 => 'Time'   },
+    Writable => 'date',
+    Shift => 'Time',
+    PrintConv => '$self->ConvertDateTime($val)',
+);
+
+# XMP namespaces which we don't want to contribute to generated EXIF tag names
+my %ignoreNamespace = ( 'x'=>1, 'rdf'=>1, 'xmlns'=>1, 'xml'=>1);
+
+# translate XMP namespaces when reading
+my %xlatNamespace = (
+    # shorten ugly IPTC Core namespace prefix
+    'Iptc4xmpCore' => 'iptcCore',
+    'photomechanic'=> 'photomech',
+    'MicrosoftPhoto' => 'microsoft',
+    # also translate older 'xap...' prefixes to 'xmp...'
+    'xap'          => 'xmp',
+    'xapBJ'        => 'xmpBJ',
+    'xapMM'        => 'xmpMM',
+    'xapRights'    => 'xmpRights',
+);
+
+# these are the attributes that we handle for properties that contain
+# sub-properties.  Attributes for simple properties are easy, and we
+# just copy them over.  These are harder since we don't store attributes
+# for properties without simple values.  (maybe this will change...)
+my %recognizedAttrs = (
+    'x:xaptk' => 1,
+    'x:xmptk' => 1,
+    'rdf:about' => 1,
+    'rdf:parseType' => 1,
+    'rdf:nodeID' => 1,
+);
+
+# main XMP tag table
+%Image::ExifTool::XMP::Main = (
+    GROUPS => { 2 => 'Unknown' },
+    PROCESS_PROC => \&ProcessXMP,
+    WRITE_PROC => \&WriteXMP,
+    dc => {
+        Name => 'dc', # (otherwise generated name would be 'Dc')
+        SubDirectory => { TagTable => 'Image::ExifTool::XMP::dc' },
+    },
+    xmp => {
+        Name => 'xmp',
+        SubDirectory => { TagTable => 'Image::ExifTool::XMP::xmp' },
+    },
+    xmpDM => {
+        Name => 'xmpDM',
+        SubDirectory => { TagTable => 'Image::ExifTool::XMP::xmpDM' },
+    },
+    xmpRights => {
+        Name => 'xmpRights',
+        SubDirectory => { TagTable => 'Image::ExifTool::XMP::xmpRights' },
+    },
+    xmpMM => {
+        Name => 'xmpMM',
+        SubDirectory => { TagTable => 'Image::ExifTool::XMP::xmpMM' },
+    },
+    xmpBJ => {
+        Name => 'xmpBJ',
+        SubDirectory => { TagTable => 'Image::ExifTool::XMP::xmpBJ' },
+    },
+    xmpTPg => {
+        Name => 'xmpTPg',
+        SubDirectory => { TagTable => 'Image::ExifTool::XMP::xmpTPg' },
+    },
+    pdf => {
+        Name => 'pdf',
+        SubDirectory => { TagTable => 'Image::ExifTool::XMP::pdf' },
+    },
+    photoshop => {
+        Name => 'photoshop',
+        SubDirectory => { TagTable => 'Image::ExifTool::XMP::photoshop' },
+    },
+    crs => {
+        Name => 'crs',
+        SubDirectory => { TagTable => 'Image::ExifTool::XMP::crs' },
+    },
+    # crss - it would be difficult to add the ability to write this
+    aux => {
+        Name => 'aux',
+        SubDirectory => { TagTable => 'Image::ExifTool::XMP::aux' },
+    },
+    tiff => {
+        Name => 'tiff',
+        SubDirectory => { TagTable => 'Image::ExifTool::XMP::tiff' },
+    },
+    exif => {
+        Name => 'exif',
+        SubDirectory => { TagTable => 'Image::ExifTool::XMP::exif' },
+    },
+    iptcCore => {
+        Name => 'iptcCore',
+        SubDirectory => { TagTable => 'Image::ExifTool::XMP::iptcCore' },
+    },
+    PixelLive => {
+        SubDirectory => { TagTable => 'Image::ExifTool::XMP::PixelLive' },
+    },
+    xmpPLUS => {
+        Name => 'xmpPLUS',
+        SubDirectory => { TagTable => 'Image::ExifTool::XMP::xmpPLUS' },
+    },
+    cc => {
+        Name => 'cc',
+        SubDirectory => { TagTable => 'Image::ExifTool::XMP::cc' },
+    },
+    dex => {
+        Name => 'dex',
+        SubDirectory => { TagTable => 'Image::ExifTool::XMP::dex' },
+    },
+    photomech => {
+        Name => 'photomech',
+        SubDirectory => { TagTable => 'Image::ExifTool::PhotoMechanic::XMP' },
+    },
+    mediapro => {
+        Name => 'mediapro',
+        SubDirectory => { TagTable => 'Image::ExifTool::XMP::MediaPro' },
+    },
+    microsoft => {
+        Name => 'microsoft',
+        SubDirectory => { TagTable => 'Image::ExifTool::XMP::Microsoft' },
+    },
+    lr => {
+        Name => 'lr',
+        SubDirectory => { TagTable => 'Image::ExifTool::XMP::Lightroom' },
+    },
+    DICOM => {
+        Name => 'DICOM',
+        SubDirectory => { TagTable => 'Image::ExifTool::XMP::DICOM' },
+    },
+);
+
+#
+# Tag tables for all XMP schemas:
+#
+# Writable - only need to define this for writable tags if not plain text
+#            (boolean, integer, rational, real, date or lang-alt)
+# List - XMP list type (Bag, Seq or Alt, or set to 1 for elements in Struct lists)
+#
+# (Note that family 1 group names are generated from the property namespace, not
+#  the group1 names below which exist so the groups will appear in the list.)
+#
+my %xmpTableDefaults = (
+    WRITE_PROC => \&WriteXMP,
+    WRITABLE => 'string',
+    LANG_INFO => \&GetLangInfo,
+);
+
+# Dublin Core schema properties (dc)
+%Image::ExifTool::XMP::dc = (
+    %xmpTableDefaults,
+    GROUPS => { 1 => 'XMP-dc', 2 => 'Other' },
+    NAMESPACE => 'dc',
+    NOTES => 'Dublin Core schema tags.',
+    contributor => { Groups => { 2 => 'Author' }, List => 'Bag' },
+    coverage    => { },
+    creator     => { Groups => { 2 => 'Author' }, List => 'Seq' },
+    date        => { %dateTimeInfo, List => 'Seq' },
+    description => { Groups => { 2 => 'Image'  }, Writable => 'lang-alt' },
+   'format'     => { Groups => { 2 => 'Image'  } },
+    identifier  => { Groups => { 2 => 'Image'  } },
+    language    => { List => 'Bag' },
+    publisher   => { Groups => { 2 => 'Author' }, List => 'Bag' },
+    relation    => { List => 'Bag' },
+    rights      => { Groups => { 2 => 'Author' }, Writable => 'lang-alt' },
+    source      => { Groups => { 2 => 'Author' } },
+    subject     => { Groups => { 2 => 'Image'  }, List => 'Bag' },
+    title       => { Groups => { 2 => 'Image'  }, Writable => 'lang-alt' },
+    type        => { Groups => { 2 => 'Image'  }, List => 'Bag' },
+);
+
+# XMP Basic schema properties (xmp, xap)
+%Image::ExifTool::XMP::xmp = (
+    %xmpTableDefaults,
+    GROUPS => { 1 => 'XMP-xmp', 2 => 'Image' },
+    NAMESPACE => 'xmp',
+    NOTES => q{
+        XMP Basic schema tags.  If the older "xap", "xapBJ", "xapMM" or "xapRights"
+        namespace prefixes are found, they are translated to the newer "xmp",
+        "xmpBJ", "xmpMM" and "xmpRights" prefixes for use in family 1 group names.
+    },
+    Advisory    => { List => 'Bag' },
+    BaseURL     => { },
+    CreateDate  => { %dateTimeInfo },
+    CreatorTool => { },
+    Identifier  => { Avoid => 1, List => 'Bag' },
+    Label       => { },
+    MetadataDate=> { %dateTimeInfo },
+    ModifyDate  => { %dateTimeInfo },
+    Nickname    => { },
+    Rating      => { Writable => 'integer' },
+    Thumbnails  => {
+        SubDirectory => { },
+        Struct => 'Thumbnail',
+        List => 'Alt',
+    },
+    ThumbnailsHeight    => { List => 1 },
+    ThumbnailsWidth     => { List => 1 },
+    ThumbnailsFormat    => { List => 1 },
+    ThumbnailsImage     => {
+        # Eventually may want to handle this like a normal thumbnail image
+        # -- but not yet!  (need to write EncodeBase64() routine....)
+        # Name => 'ThumbnailImage',
+        List => 1,
+        # translate Base64-encoded thumbnail
+        ValueConv => 'Image::ExifTool::XMP::DecodeBase64($val)',
+    },
+);
+
+# XMP Rights Management schema properties (xmpRights, xapRights)
+%Image::ExifTool::XMP::xmpRights = (
+    %xmpTableDefaults,
+    GROUPS => { 1 => 'XMP-xmpRights', 2 => 'Author' },
+    NAMESPACE => 'xmpRights',
+    NOTES => 'XMP Rights Management schema tags.',
+    Certificate     => { },
+    Marked          => { Writable => 'boolean' },
+    Owner           => { List => 'Bag' },
+    UsageTerms      => { Writable => 'lang-alt' },
+    WebStatement    => { },
+);
+
+# XMP Media Management schema properties (xmpMM, xapMM)
+%Image::ExifTool::XMP::xmpMM = (
+    %xmpTableDefaults,
+    GROUPS => { 1 => 'XMP-xmpMM', 2 => 'Other' },
+    NAMESPACE => 'xmpMM',
+    NOTES => 'XMP Media Management schema tags.',
+    DerivedFrom     => {
+        SubDirectory => { },
+        Struct => 'ResourceRef',
+    },
+    DerivedFromInstanceID       => { },
+    DerivedFromDocumentID       => { },
+    DerivedFromVersionID        => { },
+    DerivedFromRenditionClass   => { },
+    DerivedFromRenditionParams  => { },
+    DerivedFromManager          => { },
+    DerivedFromManagerVariant   => { },
+    DerivedFromManageTo         => { },
+    DerivedFromManageUI         => { },
+    DocumentID      => { },
+    History         => {
+        SubDirectory => { },
+        Struct => 'ResourceEvent',
+        List => 'Seq',
+    },
+    # we treat these like list items since History is a list
+    HistoryAction           => { List => 1 },
+    HistoryInstanceID       => { List => 1 },
+    HistoryParameters       => { List => 1 },
+    HistorySoftwareAgent    => { List => 1 },
+    HistoryWhen             => { List => 1, %dateTimeInfo },
+    InstanceID      => { }, #PH (CS3)
+    ManagedFrom     => { SubDirectory => { }, Struct => 'ResourceRef' },
+    ManagedFromInstanceID       => { },
+    ManagedFromDocumentID       => { },
+    ManagedFromVersionID        => { },
+    ManagedFromRenditionClass   => { },
+    ManagedFromRenditionParams  => { },
+    ManagedFromManager          => { },
+    ManagedFromManagerVariant   => { },
+    ManagedFromManageTo         => { },
+    ManagedFromManageUI         => { },
+    Manager         => { Groups => { 2 => 'Author' } },
+    ManageTo        => { Groups => { 2 => 'Author' } },
+    ManageUI        => { },
+    ManagerVariant  => { },
+    PreservedFileName => { },   # undocumented
+    RenditionClass  => { },
+    RenditionParams => { },
+    VersionID       => { },
+    Versions        => {
+        SubDirectory => { },
+        Struct => 'Version',
+        List => 'Seq',
+    },
+    VersionsComments    => { List => 1 },   # we treat these like list items
+    VersionsEvent       => { SubDirectory => { }, Struct => 'ResourceEvent' },
+    VersionsEventAction         => { List => 1 },
+    VersionsEventInstanceID     => { List => 1 },
+    VersionsEventParameters     => { List => 1 },
+    VersionsEventSoftwareAgent  => { List => 1 },
+    VersionsEventWhen           => { List => 1, %dateTimeInfo },
+    VersionsModifyDate          => { List => 1, %dateTimeInfo },
+    VersionsModifier            => { List => 1 },
+    VersionsVersion             => { List => 1 },
+    LastURL                     => { },
+    RenditionOf         => { SubDirectory => { }, Struct => 'ResourceRef' },
+    RenditionOfInstanceID       => { },
+    RenditionOfDocumentID       => { },
+    RenditionOfVersionID        => { },
+    RenditionOfRenditionClass   => { },
+    RenditionOfRenditionParams  => { },
+    RenditionOfManager          => { },
+    RenditionOfManagerVariant   => { },
+    RenditionOfManageTo         => { },
+    RenditionOfManageUI         => { },
+    SaveID          => { Writable => 'integer' },
+);
+
+# XMP Basic Job Ticket schema properties (xmpBJ, xapBJ)
+%Image::ExifTool::XMP::xmpBJ = (
+    %xmpTableDefaults,
+    GROUPS => { 1 => 'XMP-xmpBJ', 2 => 'Other' },
+    NAMESPACE => 'xmpBJ',
+    NOTES => 'XMP Basic Job Ticket schema tags.',
+    # Note: JobRef is a List of structures.  To accomplish this, we set the XMP
+    # List=>'Bag', but since SubDirectory is defined, this tag isn't writable
+    # directly.  Then we need to set List=>1 for the members so the Writer logic
+    # will allow us to add list elements.
+    JobRef => {
+        SubDirectory => { },
+        Struct => 'JobRef',
+        List => 'Bag',
+    },
+    JobRefName  => { List => 1 },   # we treat these like list items
+    JobRefId    => { List => 1 },
+    JobRefUrl   => { List => 1 },
+);
+
+# XMP Paged-Text schema properties (xmpTPg)
+%Image::ExifTool::XMP::xmpTPg = (
+    %xmpTableDefaults,
+    GROUPS => { 1 => 'XMP-xmpTPg', 2 => 'Image' },
+    NAMESPACE => 'xmpTPg',
+    NOTES => 'XMP Paged-Text schema tags.',
+    MaxPageSize => { SubDirectory => { }, Struct => 'Dimensions' },
+    MaxPageSizeW    => { Writable => 'real' },
+    MaxPageSizeH    => { Writable => 'real' },
+    MaxPageSizeUnit => { },
+    NPages      => { Writable => 'integer' },
+    Fonts       => {
+        SubDirectory => { },
+        Struct => 'Font',
+        List => 'Bag',
+    },
+    FontsFontName       => { List => 1 },
+    FontsFontFamily     => { List => 1 },
+    FontsFontFace       => { List => 1 },
+    FontsFontType       => { List => 1 },
+    FontsVersionString  => { List => 1 },
+    FontsComposite      => { List => 1, Writable => 'boolean' },
+    FontsFontFileName   => { List => 1 },
+    FontsChildFontFiles => { List => 1 },
+    Colorants   => {
+        SubDirectory => { },
+        Struct => 'Colorant',
+        List => 'Seq',
+    },
+    ColorantsSwatchName => { List => 1 },
+    ColorantsMode       => { List => 1 },
+    ColorantsType       => { List => 1 },
+    ColorantsCyan       => { List => 1, Writable => 'real' },
+    ColorantsMagenta    => { List => 1, Writable => 'real' },
+    ColorantsYellow     => { List => 1, Writable => 'real' },
+    ColorantsBlack      => { List => 1, Writable => 'real' },
+    ColorantsRed        => { List => 1, Writable => 'integer' },
+    ColorantsGreen      => { List => 1, Writable => 'integer' },
+    ColorantsBlue       => { List => 1, Writable => 'integer' },
+    ColorantsL          => { List => 1, Writable => 'real' },
+    ColorantsA          => { List => 1, Writable => 'integer' },
+    ColorantsB          => { List => 1, Writable => 'integer' },
+    PlateNames  => { List => 'Seq' },
+);
+
+# XMP Dynamic Media schema properties (xmpDM)
+%Image::ExifTool::XMP::xmpDM = (
+    %xmpTableDefaults,
+    GROUPS => { 1 => 'XMP-xmpDM', 2 => 'Image' },
+    NAMESPACE => 'xmpDM',
+    NOTES => 'XMP Dynamic Media schema tags.',
+    projectRef => {
+        SubDirectory => { },
+        Struct => 'ProjectLink',
+    },
+    projectRefType      => { },
+    projectRefPath      => { },
+    videoFrameRate      => { },
+    videoFrameSize => {
+        SubDirectory => { },
+        Struct => 'Dimensions',
+    },
+    videoFrameSizeW     => { Writable => 'real' },
+    videoFrameSizeH     => { Writable => 'real' },
+    videoFrameSizeUnit  => { },
+    videoPixelAspectRatio => { Writable => 'rational' },
+    videoPixelDepth     => { },
+    videoColorSpace     => { },
+    videoAlphaMode      => { },
+    videoAlphaPremultipleColor => {
+        SubDirectory => { },
+        Struct => 'Colorant',
+    },
+    videoAlphaPremultipleColorSwatchName => { },
+    videoAlphaPremultipleColorMode       => { },
+    videoAlphaPremultipleColorType       => { },
+    videoAlphaPremultipleColorCyan       => { Writable => 'real' },
+    videoAlphaPremultipleColorMagenta    => { Writable => 'real' },
+    videoAlphaPremultipleColorYellow     => { Writable => 'real' },
+    videoAlphaPremultipleColorBlack      => { Writable => 'real' },
+    videoAlphaPremultipleColorRed        => { Writable => 'integer' },
+    videoAlphaPremultipleColorGreen      => { Writable => 'integer' },
+    videoAlphaPremultipleColorBlue       => { Writable => 'integer' },
+    videoAlphaPremultipleColorL          => { Writable => 'real' },
+    videoAlphaPremultipleColorA          => { Writable => 'integer' },
+    videoAlphaPremultipleColorB          => { Writable => 'integer' },
+    videoAlphaUnityIsTransparent => { Writable => 'boolean' },
+    videoCompressor     => { },
+    videoFieldOrder     => { },
+    pullDown            => { },
+    audioSampleRate     => { Writable => 'integer' },
+    audioSampleType     => { },
+    audioChannelType    => { },
+    audioCompressor     => { },
+    speakerPlacement    => { },
+    fileDataRate        => { Writable => 'rational' },
+    tapeName            => { },
+    altTapeName         => { },
+    startTimecode => {
+        SubDirectory => { },
+        Struct => 'Timecode',
+    },
+    startTimecodeTimeValue  => { },
+    startTimecodeTimeFormat => { },
+    altTimecode => {
+        SubDirectory => { },
+        Struct => 'Timecode',
+    },
+    altTimecodeTimeValue    => { },
+    altTimecodeTimeFormat   => { },
+    duration            => { },
+    scene               => { Avoid => 1 },
+    shotName            => { },
+    shotDate            => { %dateTimeInfo },
+    shotLocation        => { },
+    logComment          => { },
+    markers => {
+        SubDirectory => { },
+        Struct => 'Marker',
+        List => 'Seq',
+    },
+    markersStartTime    => { List => 1 },
+    markersDuration     => { List => 1 },
+    markersComment      => { List => 1 },
+    markersName         => { List => 1 },
+    markersLocation     => { List => 1 },
+    markersTarget       => { List => 1 },
+    markersType         => { List => 1 },
+    contributedMedia => {
+        SubDirectory => { },
+        Struct => 'Media',
+        List => 'Bag',
+    },
+    contributedMediaPath         => { List => 1 },
+    contributedMediaTrack        => { List => 1 },
+    contributedMediaStartTime    => { List => 1 },
+    contributedMediaDuration     => { List => 1 },
+    contributedMediaManaged      => { List => 1, Writable => 'boolean' },
+    contributedMediaWebStatement => { List => 1 },
+    absPeakAudioFilePath => { },
+    relativePeakAudioFilePath => { },
+    videoModDate    => { %dateTimeInfo },
+    audioModDate    => { %dateTimeInfo },
+    metadataModDate => { %dateTimeInfo },
+    artist          => { Avoid => 1, Groups => { 2 => 'Author' } },
+    album           => { },
+    trackNumber     => { Writable => 'integer' },
+    genre           => { },
+    copyright       => { Avoid => 1, Groups => { 2 => 'Author' } },
+    releaseDate     => { %dateTimeInfo },
+    composer        => { Groups => { 2 => 'Author' } },
+    engineer        => { },
+    tempo           => { Writable => 'real' },
+    instrument      => { },
+    introTime       => { },
+    outCue          => { },
+    relativeTimestamp => { },
+    loop            => { Writable => 'boolean' },
+    numberOfBeats   => { Writable => 'real' },
+    key             => { },
+    stretchMode     => { },
+    timeScaleParams => {
+        SubDirectory => { },
+        Struct => 'TimeScaleStretch',
+    },
+    timeScaleParamsQuality  => { },
+    timeScaleParamsFrameSize=> { Writable => 'real' },
+    timeScaleParamsFrameOverlappingPercentage => { Writable => 'real' },
+    resampleParams => {
+        SubDirectory => { },
+        Struct => 'ResampleStretch',
+    },
+    resampleParamsQuality   => { },
+    beatSpliceParams => {
+        SubDirectory => { },
+        Struct => 'BeatSpliceStretch',
+    },
+    beatSpliceParamsUseFileBeatsMarker => { Writable => 'boolean' },
+    beatSpliceParamsRiseInDecibel      => { Writable => 'real' },
+    beatSpliceParamsRiseInTimeDuration => { },
+    timeSignature   => { },
+    scaleType       => { },
+);
+
+# PDF schema properties (pdf)
+%Image::ExifTool::XMP::pdf = (
+    %xmpTableDefaults,
+    GROUPS => { 1 => 'XMP-pdf', 2 => 'Image' },
+    NAMESPACE => 'pdf',
+    NOTES => q{
+        Adobe PDF schema tags.  The official XMP specification defines only
+        Keywords, PDFVersion and Producer.  The other tags are included because they
+        have been observed in PDF files, but Creator, Subject and Title are avoided
+        when writing due to name conflicts with XMP-dc tags.
+    },
+    Author      => { Groups => { 2 => 'Author' } }, #PH
+    ModDate     => { %dateTimeInfo }, #PH
+    CreationDate=> { %dateTimeInfo }, #PH
+    Creator     => { Groups => { 2 => 'Author' }, Avoid => 1 },
+    Subject     => { Avoid => 1 },
+    Title       => { Avoid => 1 },
+    Keywords    => { },
+    PDFVersion  => { },
+    Producer    => { Groups => { 2 => 'Author' } },
+);
+
+# Photoshop schema properties (photoshop)
+%Image::ExifTool::XMP::photoshop = (
+    %xmpTableDefaults,
+    GROUPS => { 1 => 'XMP-photoshop', 2 => 'Image' },
+    NAMESPACE => 'photoshop',
+    NOTES => 'Adobe Photoshop schema tags.',
+    AuthorsPosition => { Groups => { 2 => 'Author' } },
+    CaptionWriter   => { Groups => { 2 => 'Author' } },
+    Category        => { },
+    City            => { Groups => { 2 => 'Location' } },
+    Country         => { Groups => { 2 => 'Location' } },
+    ColorMode       => { }, #PH
+    Credit          => { Groups => { 2 => 'Author' } },
+    DateCreated     => { %dateTimeInfo },
+    History         => { }, #PH (CS3)
+    Headline        => { },
+    Instructions    => { },
+    ICCProfile      => { Name => 'ICCProfileName' }, #PH
+    LegacyIPTCDigest=> { }, #PH
+    SidecarForExtension => { }, #PH (CS3)
+    Source          => { Groups => { 2 => 'Author' }, Avoid => 1 },
+    State           => { Groups => { 2 => 'Location' } },
+    # the documentation doesn't show this as a 'Bag', but that's the
+    # way Photoshop7.0 writes it - PH
+    SupplementalCategories  => { List => 'Bag' },
+    TransmissionReference   => { },
+    Urgency         => { Writable => 'integer' },
+);
+
+# Photoshop Camera Raw Schema properties (crs) - (ref 8,PH)
+%Image::ExifTool::XMP::crs = (
+    %xmpTableDefaults,
+    GROUPS => { 1 => 'XMP-crs', 2 => 'Image' },
+    NAMESPACE => 'crs',
+    NOTES => 'Photoshop Camera Raw Schema tags.',
+    AlreadyApplied  => { Writable => 'boolean' }, #PH (written by LightRoom beta 4.1)
+    AutoBrightness  => { Writable => 'boolean' },
+    AutoContrast    => { Writable => 'boolean' },
+    AutoExposure    => { Writable => 'boolean' },
+    AutoShadows     => { Writable => 'boolean' },
+    BlueHue         => { Writable => 'integer' },
+    BlueSaturation  => { Writable => 'integer' },
+    Brightness      => { Writable => 'integer' },
+    CameraProfile   => { },
+    ChromaticAberrationB=> { Writable => 'integer' },
+    ChromaticAberrationR=> { Writable => 'integer' },
+    ColorNoiseReduction => { Writable => 'integer' },
+    Contrast        => { Writable => 'integer', Avoid => 1 },
+    CropTop         => { Writable => 'real' },
+    CropLeft        => { Writable => 'real' },
+    CropBottom      => { Writable => 'real' },
+    CropRight       => { Writable => 'real' },
+    CropAngle       => { Writable => 'real' },
+    CropWidth       => { Writable => 'real' },
+    CropHeight      => { Writable => 'real' },
+    CropUnits => {
+        Writable => 'integer',
+        PrintConv => {
+            0 => 'pixels',
+            1 => 'inches',
+            2 => 'cm',
+        },
+    },
+    Exposure        => { Writable => 'real' },
+    GreenHue        => { Writable => 'integer' },
+    GreenSaturation => { Writable => 'integer' },
+    HasCrop         => { Writable => 'boolean' },
+    HasSettings     => { Writable => 'boolean' },
+    LuminanceSmoothing  => { Writable => 'integer' },
+    RawFileName     => { },
+    RedHue          => { Writable => 'integer' },
+    RedSaturation   => { Writable => 'integer' },
+    Saturation      => { Writable => 'integer', Avoid => 1 },
+    Shadows         => { Writable => 'integer' },
+    ShadowTint      => { Writable => 'integer' },
+    Sharpness       => { Writable => 'integer', Avoid => 1 },
+    Temperature     => { Writable => 'integer' },
+    Tint            => { Writable => 'integer' },
+    ToneCurve       => { List => 'Seq' },
+    ToneCurveName => {
+        PrintConv => {
+            Linear           => 'Linear',
+           'Medium Contrast' => 'Medium Contrast',
+           'Strong Contrast' => 'Strong Contrast',
+            Custom           => 'Custom',
+        },
+    },
+    Version         => { },
+    VignetteAmount  => { Writable => 'integer' },
+    VignetteMidpoint=> { Writable => 'integer' },
+    WhiteBalance    => {
+        Avoid => 1,
+        PrintConv => {
+           'As Shot'    => 'As Shot',
+            Auto        => 'Auto',
+            Daylight    => 'Daylight',
+            Cloudy      => 'Cloudy',
+            Shade       => 'Shade',
+            Tungsten    => 'Tungsten',
+            Fluorescent => 'Fluorescent',
+            Flash       => 'Flash',
+            Custom      => 'Custom',
+        },
+    },
+    # new tags observed in Adobe Lightroom output - PH
+    CameraProfileDigest         => { },
+    Clarity                     => { Writable => 'integer' },
+    ConvertToGrayscale          => { Writable => 'boolean' },
+    Defringe                    => { Writable => 'integer' },
+    FillLight                   => { Writable => 'integer' },
+    HighlightRecovery           => { Writable => 'integer' },
+    HueAdjustmentAqua           => { Writable => 'integer' },
+    HueAdjustmentBlue           => { Writable => 'integer' },
+    HueAdjustmentGreen          => { Writable => 'integer' },
+    HueAdjustmentMagenta        => { Writable => 'integer' },
+    HueAdjustmentOrange         => { Writable => 'integer' },
+    HueAdjustmentPurple         => { Writable => 'integer' },
+    HueAdjustmentRed            => { Writable => 'integer' },
+    HueAdjustmentYellow         => { Writable => 'integer' },
+    IncrementalTemperature      => { Writable => 'integer' },
+    IncrementalTint             => { Writable => 'integer' },
+    LuminanceAdjustmentAqua     => { Writable => 'integer' },
+    LuminanceAdjustmentBlue     => { Writable => 'integer' },
+    LuminanceAdjustmentGreen    => { Writable => 'integer' },
+    LuminanceAdjustmentMagenta  => { Writable => 'integer' },
+    LuminanceAdjustmentOrange   => { Writable => 'integer' },
+    LuminanceAdjustmentPurple   => { Writable => 'integer' },
+    LuminanceAdjustmentRed      => { Writable => 'integer' },
+    LuminanceAdjustmentYellow   => { Writable => 'integer' },
+    ParametricDarks             => { Writable => 'integer' },
+    ParametricHighlights        => { Writable => 'integer' },
+    ParametricHighlightSplit    => { Writable => 'integer' },
+    ParametricLights            => { Writable => 'integer' },
+    ParametricMidtoneSplit      => { Writable => 'integer' },
+    ParametricShadows           => { Writable => 'integer' },
+    ParametricShadowSplit       => { Writable => 'integer' },
+    SaturationAdjustmentAqua    => { Writable => 'integer' },
+    SaturationAdjustmentBlue    => { Writable => 'integer' },
+    SaturationAdjustmentGreen   => { Writable => 'integer' },
+    SaturationAdjustmentMagenta => { Writable => 'integer' },
+    SaturationAdjustmentOrange  => { Writable => 'integer' },
+    SaturationAdjustmentPurple  => { Writable => 'integer' },
+    SaturationAdjustmentRed     => { Writable => 'integer' },
+    SaturationAdjustmentYellow  => { Writable => 'integer' },
+    SharpenDetail               => { Writable => 'integer' },
+    SharpenEdgeMasking          => { Writable => 'integer' },
+    SharpenRadius               => { Writable => 'real' },
+    SplitToningBalance          => { Writable => 'integer' },
+    SplitToningHighlightHue     => { Writable => 'integer' },
+    SplitToningHighlightSaturation => { Writable => 'integer' },
+    SplitToningShadowHue        => { Writable => 'integer' },
+    SplitToningShadowSaturation => { Writable => 'integer' },
+    Vibrance                    => { Writable => 'integer' },
+);
+
+# Tiff schema properties (tiff)
+%Image::ExifTool::XMP::tiff = (
+    %xmpTableDefaults,
+    GROUPS => { 1 => 'XMP-tiff', 2 => 'Image' },
+    NAMESPACE => 'tiff',
+    NOTES => 'EXIF schema for TIFF tags.',
+    ImageWidth  => { Writable => 'integer' },
+    ImageLength => {
+        Name => 'ImageHeight',
+        Writable => 'integer',
+    },
+    BitsPerSample => { Writable => 'integer', List => 'Seq' },
+    Compression => {
+        Writable => 'integer',
+        PrintConv => \%Image::ExifTool::Exif::compression,
+    },
+    PhotometricInterpretation => {
+        Writable => 'integer',
+        PrintConv => \%Image::ExifTool::Exif::photometricInterpretation,
+    },
+    Orientation => {
+        Writable => 'integer',
+        PrintConv => \%Image::ExifTool::Exif::orientation,
+    },
+    SamplesPerPixel => { Writable => 'integer' },
+    PlanarConfiguration => {
+        Writable => 'integer',
+        PrintConv => {
+            1 => 'Chunky',
+            2 => 'Planar',
+        },
+    },
+    YCbCrSubSampling => {
+        PrintConv => {
+            '1 1' => 'YCbCr4:4:4',
+            '2 1' => 'YCbCr4:2:2',
+            '2 2' => 'YCbCr4:2:0',
+            '4 1' => 'YCbCr4:1:1',
+            '4 2' => 'YCbCr4:1:0',
+            '1 2' => 'YCbCr4:4:0',
+        },
+    },
+    YCbCrPositioning => {
+        Writable => 'integer',
+        PrintConv => {
+            1 => 'Centered',
+            2 => 'Co-sited',
+        },
+    },
+    XResolution => { Writable => 'rational' },
+    YResolution => { Writable => 'rational' },
+    ResolutionUnit => {
+        Writable => 'integer',
+        PrintConv => {
+            1 => 'None',
+            2 => 'inches',
+            3 => 'cm',
+        },
+    },
+    TransferFunction      => { Writable => 'integer',  List => 'Seq' },
+    WhitePoint            => { Writable => 'rational', List => 'Seq' },
+    PrimaryChromaticities => { Writable => 'rational', List => 'Seq' },
+    YCbCrCoefficients     => { Writable => 'rational', List => 'Seq' },
+    ReferenceBlackWhite   => { Writable => 'rational', List => 'Seq' },
+    DateTime => {
+        Description => 'Date/Time Modified',
+        %dateTimeInfo,
+    },
+    ImageDescription => { Writable => 'lang-alt' },
+    Make  => { Groups => { 2 => 'Camera' } },
+    Model => {
+        Description => 'Camera Model Name',
+        Groups => { 2 => 'Camera' },
+    },
+    Software  => { },
+    Artist    => { Groups => { 2 => 'Author' } },
+    Copyright => {
+        Groups => { 2 => 'Author' },
+        Writable => 'lang-alt',
+    },
+    NativeDigest => { }, #PH
+);
+
+# Exif schema properties (exif)
+%Image::ExifTool::XMP::exif = (
+    %xmpTableDefaults,
+    GROUPS => { 1 => 'XMP-exif', 2 => 'Image' },
+    NAMESPACE => 'exif',
+    NOTES => 'EXIF schema for EXIF tags.',
+    ExifVersion     => { },
+    FlashpixVersion => { },
+    ColorSpace => {
+        Writable => 'integer',
+        PrintConv => {
+            1 => 'sRGB',
+            2 => 'Adobe RGB',
+            0xffff => 'Uncalibrated',
+            0xffffffff => 'Uncalibrated',
+        },
+    },
+    ComponentsConfiguration => {
+        List => 'Seq',
+        Writable => 'integer',
+        PrintConv => {
+            0 => '.',
+            1 => 'Y',
+            2 => 'Cb',
+            3 => 'Cr',
+            4 => 'R',
+            5 => 'G',
+            6 => 'B',
+        },
+    },
+    CompressedBitsPerPixel => {
+        Writable => 'rational',
+    },
+    PixelXDimension => {
+        Name => 'ExifImageWidth',
+        Writable => 'integer',
+    },
+    PixelYDimension => {
+        Name => 'ExifImageLength',
+        Writable => 'integer',
+    },
+    MakerNote => { },
+    UserComment => {
+        Writable => 'lang-alt',
+    },
+    RelatedSoundFile => { },
+    DateTimeOriginal => {
+        Description => 'Date/Time Original',
+        %dateTimeInfo,
+    },
+    DateTimeDigitized => {
+        Description => 'Date/Time Digitized',
+        %dateTimeInfo,
+    },
+    ExposureTime => {
+        Writable => 'rational',
+        PrintConv => 'Image::ExifTool::Exif::PrintExposureTime($val)',
+        PrintConvInv => 'eval $val',
+    },
+    FNumber => {
+        Writable => 'rational',
+        PrintConv => 'sprintf("%.1f",$val)',
+        PrintConvInv => '$val',
+    },
+    ExposureProgram => {
+        Groups => { 2 => 'Camera' },
+        Writable => 'integer',
+        PrintConv => {
+            1 => 'Manual',
+            2 => 'Program AE',
+            3 => 'Aperture-priority AE',
+            4 => 'Shutter speed priority AE',
+            5 => 'Creative (Slow speed)',
+            6 => 'Action (High speed)',
+            7 => 'Portrait',
+            8 => 'Landscape',
+        },
+    },
+    SpectralSensitivity => {
+        Groups => { 2 => 'Camera' },
+    },
+    ISOSpeedRatings => {
+        Name => 'ISO',
+        Writable => 'integer',
+        List => 'Seq',
+    },
+    OECF => {
+        Name => 'Opto-ElectricConvFactor',
+        Groups => { 2 => 'Camera' },
+        SubDirectory => { },
+        Struct => 'OECF',
+    },
+    OECFColumns => {
+        Groups => { 2 => 'Camera' },
+        Writable => 'integer',
+    },
+    OECFRows => {
+        Groups => { 2 => 'Camera' },
+        Writable => 'integer',
+    },
+    OECFNames => {
+        Groups => { 2 => 'Camera' },
+        List => 'Seq',
+    },
+    OECFValues => {
+        Groups => { 2 => 'Camera' },
+        Writable => 'rational',
+        List => 'Seq',
+    },
+    ShutterSpeedValue => {
+        Writable => 'rational',
+        ValueConv => 'abs($val)<100 ? 1/(2**$val) : 0',
+        PrintConv => 'Image::ExifTool::Exif::PrintExposureTime($val)',
+        ValueConvInv => '$val>0 ? -log($val)/log(2) : 0',
+        # do eval to convert things like '1/100'
+        PrintConvInv => 'eval $val',
+    },
+    ApertureValue => {
+        Writable => 'rational',
+        ValueConv => 'sqrt(2) ** $val',
+        PrintConv => 'sprintf("%.1f",$val)',
+        ValueConvInv => '$val>0 ? 2*log($val)/log(2) : 0',
+        PrintConvInv => '$val',
+    },
+    BrightnessValue => {
+        Writable => 'rational',
+    },
+    ExposureBiasValue => {
+        Name => 'ExposureCompensation',
+        Writable => 'rational',
+        PrintConv => 'Image::ExifTool::Exif::ConvertFraction($val)',
+        PrintConvInv => '$val',
+    },
+    MaxApertureValue => {
+        Groups => { 2 => 'Camera' },
+        Writable => 'rational',
+        ValueConv => 'sqrt(2) ** $val',
+        PrintConv => 'sprintf("%.1f",$val)',
+        ValueConvInv => '$val>0 ? 2*log($val)/log(2) : 0',
+        PrintConvInv => '$val',
+    },
+    SubjectDistance => {
+        Groups => { 2 => 'Camera' },
+        Writable => 'rational',
+        PrintConv => '$val eq "inf" ? $val : "$val m"',
+        PrintConvInv => '$val=~s/\s*m$//;$val',
+    },
+    MeteringMode => {
+        Groups => { 2 => 'Camera' },
+        Writable => 'integer',
+        PrintConv => {
+            1 => 'Average',
+            2 => 'Center-weighted average',
+            3 => 'Spot',
+            4 => 'Multi-spot',
+            5 => 'Multi-segment',
+            6 => 'Partial',
+            255 => 'Other',
+        },
+    },
+    LightSource => {
+        Groups => { 2 => 'Camera' },
+        PrintConv =>  \%Image::ExifTool::Exif::lightSource,
+    },
+    Flash => {
+        Groups => { 2 => 'Camera' },
+        SubDirectory => { },
+        Struct => 'Flash',
+    },
+    FlashFired => {
+        Groups => { 2 => 'Camera' },
+        Writable => 'boolean',
+    },
+    FlashReturn => {
+        Groups => { 2 => 'Camera' },
+        Writable => 'integer',
+        PrintConv => {
+            0 => 'No return detection',
+            2 => 'Return not detected',
+            3 => 'Return detected',
+        },
+    },
+    FlashMode => {
+        Groups => { 2 => 'Camera' },
+        Writable => 'integer',
+        PrintConv => {
+            0 => 'Unknown',
+            1 => 'On',
+            2 => 'Off',
+            3 => 'Auto',
+        },
+    },
+    FlashFunction => {
+        Groups => { 2 => 'Camera' },
+        Writable => 'boolean',
+    },
+    FlashRedEyeMode => {
+        Groups => { 2 => 'Camera' },
+        Writable => 'boolean',
+    },
+    FocalLength=> {
+        Groups => { 2 => 'Camera' },
+        Writable => 'rational',
+        PrintConv => 'sprintf("%.1fmm",$val)',
+        PrintConvInv => '$val=~s/mm$//;$val',
+    },
+    SubjectArea => {
+        Writable => 'integer',
+        List => 'Seq',
+    },
+    FlashEnergy => {
+        Groups => { 2 => 'Camera' },
+        Writable => 'rational',
+    },
+    SpatialFrequencyResponse => {
+        Groups => { 2 => 'Camera' },
+        SubDirectory => { },
+        Struct => 'OECF',
+    },
+    SpatialFrequencyResponseColumns => {
+        Groups => { 2 => 'Camera' },
+        Writable => 'integer',
+    },
+    SpatialFrequencyResponseRows => {
+        Groups => { 2 => 'Camera' },
+        Writable => 'integer',
+    },
+    SpatialFrequencyResponseNames => {
+        Groups => { 2 => 'Camera' },
+        List => 'Seq',
+    },
+    SpatialFrequencyResponseValues => {
+        Groups => { 2 => 'Camera' },
+        Writable => 'rational',
+        List => 'Seq',
+    },
+    FocalPlaneXResolution => {
+        Groups => { 2 => 'Camera' },
+        Writable => 'rational',
+    },
+    FocalPlaneYResolution => {
+        Groups => { 2 => 'Camera' },
+        Writable => 'rational',
+    },
+    FocalPlaneResolutionUnit => {
+        Groups => { 2 => 'Camera' },
+        Writable => 'integer',
+        PrintConv => {
+            1 => 'None', # (not standard EXIF)
+            2 => 'inches',
+            3 => 'cm',
+            4 => 'mm',   # (not standard EXIF)
+            5 => 'um',   # (not standard EXIF)
+        },
+    },
+    SubjectLocation => {
+        Writable => 'integer',
+        List => 'Seq',
+    },
+    ExposureIndex => {
+        Writable => 'rational',
+    },
+    SensingMethod => {
+        Groups => { 2 => 'Camera' },
+        Writable => 'integer',
+        PrintConv => {
+            1 => 'Not defined',
+            2 => 'One-chip color area',
+            3 => 'Two-chip color area',
+            4 => 'Three-chip color area',
+            5 => 'Color sequential area',
+            7 => 'Trilinear',
+            8 => 'Color sequential linear',
+        },
+    },
+    FileSource => {
+        Writable => 'integer',
+        PrintConv => {
+            1 => 'Film Scanner',
+            2 => 'Reflection Print Scanner',
+            3 => 'Digital Camera',
+        }
+    },
+    SceneType  => { Writable => 'integer', PrintConv => { 1 => 'Directly photographed' } },
+    CFAPattern => {
+        SubDirectory => { },
+        Struct => 'CFAPattern',
+    },
+    CFAPatternColumns   => { Writable => 'integer' },
+    CFAPatternRows      => { Writable => 'integer' },
+    CFAPatternValues    => { List => 'Seq', Writable => 'integer' },
+    CustomRendered => {
+        Writable => 'integer',
+        PrintConv => {
+            0 => 'Normal',
+            1 => 'Custom',
+        },
+    },
+    ExposureMode => {
+        Groups => { 2 => 'Camera' },
+        Writable => 'integer',
+        PrintConv => {
+            0 => 'Auto',
+            1 => 'Manual',
+            2 => 'Auto bracket',
+        },
+    },
+    WhiteBalance => {
+        Groups => { 2 => 'Camera' },
+        Writable => 'integer',
+        PrintConv => {
+            0 => 'Auto',
+            1 => 'Manual',
+        },
+    },
+    DigitalZoomRatio => { Writable => 'rational' },
+    FocalLengthIn35mmFilm => {
+        Name => 'FocalLengthIn35mmFormat',
+        Writable => 'integer',
+        Groups => { 2 => 'Camera' },
+    },
+    SceneCaptureType => {
+        Groups => { 2 => 'Camera' },
+        Writable => 'integer',
+        PrintConv => {
+            0 => 'Standard',
+            1 => 'Landscape',
+            2 => 'Portrait',
+            3 => 'Night',
+        },
+    },
+    GainControl => {
+        Groups => { 2 => 'Camera' },
+        Writable => 'integer',
+        PrintConv => {
+            0 => 'None',
+            1 => 'Low gain up',
+            2 => 'High gain up',
+            3 => 'Low gain down',
+            4 => 'High gain down',
+        },
+    },
+    Contrast => {
+        Groups => { 2 => 'Camera' },
+        Writable => 'integer',
+        PrintConv => {
+            0 => 'Normal',
+            1 => 'Low',
+            2 => 'High',
+        },
+        PrintConvInv => 'Image::ExifTool::Exif::ConvertParameter($val)',
+    },
+    Saturation => {
+        Groups => { 2 => 'Camera' },
+        Writable => 'integer',
+        PrintConv => {
+            0 => 'Normal',
+            1 => 'Low',
+            2 => 'High',
+        },
+        PrintConvInv => 'Image::ExifTool::Exif::ConvertParameter($val)',
+    },
+    Sharpness => {
+        Groups => { 2 => 'Camera' },
+        Writable => 'integer',
+        PrintConv => {
+            0 => 'Normal',
+            1 => 'Soft',
+            2 => 'Hard',
+        },
+        PrintConvInv => 'Image::ExifTool::Exif::ConvertParameter($val)',
+    },
+    DeviceSettingDescription => {
+        Groups => { 2 => 'Camera' },
+        SubDirectory => { },
+        Struct => 'DeviceSettings',
+    },
+    DeviceSettingDescriptionColumns => {
+        Groups => { 2 => 'Camera' },
+        Writable => 'integer',
+    },
+    DeviceSettingDescriptionRows => {
+        Groups => { 2 => 'Camera' },
+        Writable => 'integer',
+    },
+    DeviceSettingDescriptionSettings => {
+        Groups => { 2 => 'Camera' },
+        List => 'Seq',
+    },
+    SubjectDistanceRange => {
+        Groups => { 2 => 'Camera' },
+        Writable => 'integer',
+        PrintConv => {
+            0 => 'Unknown',
+            1 => 'Macro',
+            2 => 'Close',
+            3 => 'Distant',
+        },
+    },
+    ImageUniqueID   => { },
+    GPSVersionID    => { Groups => { 2 => 'Location' } },
+    GPSLatitude     => { Groups => { 2 => 'Location' }, %latConv },
+    GPSLongitude    => { Groups => { 2 => 'Location' }, %longConv },
+    GPSAltitudeRef  => {
+        Groups => { 2 => 'Location' },
+        Writable => 'integer',
+        PrintConv => {
+            0 => 'Above Sea Level',
+            1 => 'Below Sea Level',
+        },
+    },
+    GPSAltitude => {
+        Groups => { 2 => 'Location' },
+        Writable => 'rational',
+        PrintConv => '$val eq "inf" ? $val : "$val m"',
+        PrintConvInv => '$val=~s/\s*m$//;$val',
+    },
+    GPSTimeStamp    => { %dateTimeInfo },
+    GPSSatellites   => { Groups => { 2 => 'Location' } },
+    GPSStatus => {
+        Groups => { 2 => 'Location' },
+        PrintConv => {
+            A => 'Measurement In Progress',
+            V => 'Measurement Interoperability',
+        },
+    },
+    GPSMeasureMode => {
+        Groups => { 2 => 'Location' },
+        Writable => 'integer',
+        PrintConv => {
+            2 => '2-Dimensional',
+            3 => '3-Dimensional',
+        },
+    },
+    GPSDOP => {
+        Groups => { 2 => 'Location' },
+        Writable => 'rational',
+    },
+    GPSSpeedRef => {
+        Groups => { 2 => 'Location' },
+        PrintConv => {
+            K => 'km/h',
+            M => 'mph',
+            N => 'knots',
+        },
+    },
+    GPSSpeed => {
+        Groups => { 2 => 'Location' },
+        Writable => 'rational',
+    },
+    GPSTrackRef => {
+        Groups => { 2 => 'Location' },
+        PrintConv => {
+            M => 'Magnetic North',
+            T => 'True North',
+        },
+    },
+    GPSTrack => {
+        Groups => { 2 => 'Location' },
+        Writable => 'rational',
+    },
+    GPSImgDirectionRef => {
+        PrintConv => {
+            M => 'Magnetic North',
+            T => 'True North',
+        },
+    },
+    GPSImgDirection => {
+        Groups => { 2 => 'Location' },
+        Writable => 'rational',
+    },
+    GPSMapDatum     => { Groups => { 2 => 'Location' } },
+    GPSDestLatitude => { Groups => { 2 => 'Location' }, %latConv },
+    GPSDestLongitude=> { Groups => { 2 => 'Location' }, %longConv },
+    GPSDestBearingRef => {
+        Groups => { 2 => 'Location' },
+        PrintConv => {
+            M => 'Magnetic North',
+            T => 'True North',
+        },
+    },
+    GPSDestBearing => {
+        Groups => { 2 => 'Location' },
+        Writable => 'rational',
+    },
+    GPSDestDistanceRef => {
+        Groups => { 2 => 'Location' },
+        PrintConv => {
+            K => 'Kilometers',
+            M => 'Miles',
+            N => 'Nautical Miles',
+        },
+    },
+    GPSDestDistance => {
+        Groups => { 2 => 'Location' },
+        Writable => 'rational',
+    },
+    GPSProcessingMethod => { Groups => { 2 => 'Location' } },
+    GPSAreaInformation  => { Groups => { 2 => 'Location' } },
+    GPSDifferential => {
+        Groups => { 2 => 'Location' },
+        Writable => 'integer',
+        PrintConv => {
+            0 => 'No Correction',
+            1 => 'Differential Corrected',
+        },
+    },
+    NativeDigest => { }, #PH
+);
+
+# Auxiliary schema properties (aux) - not fully documented
+%Image::ExifTool::XMP::aux = (
+    %xmpTableDefaults,
+    GROUPS => { 1 => 'XMP-aux', 2 => 'Camera' },
+    NAMESPACE => 'aux',
+    NOTES => 'Photoshop Auxiliary schema tags.',
+    Firmware        => { }, #7
+    FlashCompensation => { Writable => 'rational' }, #7
+    ImageNumber     => { }, #7
+    LensInfo        => { }, #7
+    Lens            => { },
+    OwnerName       => { }, #7
+    SerialNumber    => { },
+);
+
+# IPTC Core schema properties (Iptc4xmpCore)
+%Image::ExifTool::XMP::iptcCore = (
+    %xmpTableDefaults,
+    GROUPS => { 1 => 'XMP-iptcCore', 2 => 'Author' },
+    NAMESPACE => 'Iptc4xmpCore',
+    NOTES => q{
+        IPTC Core schema tags.  The actual IPTC Core namespace schema prefix is
+        "Iptc4xmpCore", which is the prefix recorded in the file, but ExifTool
+        shortens this for the "XMP-iptcCore" family 1 group name.
+    },
+    CountryCode         => { Groups => { 2 => 'Location' } },
+    CreatorContactInfo => {
+        SubDirectory => { },
+        Struct => 'ContactInfo',
+    },
+    CreatorContactInfoCiAdrCity   => { Description => 'Creator City' },
+    CreatorContactInfoCiAdrCtry   => { Description => 'Creator Country' },
+    CreatorContactInfoCiAdrExtadr => { Description => 'Creator Address' },
+    CreatorContactInfoCiAdrPcode  => { Description => 'Creator Postal Code' },
+    CreatorContactInfoCiAdrRegion => { Description => 'Creator Region' },
+    CreatorContactInfoCiEmailWork => { Description => 'Creator Work Email' },
+    CreatorContactInfoCiTelWork   => { Description => 'Creator Work Telephone' },
+    CreatorContactInfoCiUrlWork   => { Description => 'Creator Work URL' },
+    IntellectualGenre   => { Groups => { 2 => 'Other' } },
+    Location            => { Groups => { 2 => 'Location' } },
+    Scene               => { Groups => { 2 => 'Other' }, List => 'Bag' },
+    SubjectCode         => { Groups => { 2 => 'Other' }, List => 'Bag' },
+);
+
+# PixelLive schema properties (PixelLive) (ref 3)
+%Image::ExifTool::XMP::PixelLive = (
+    GROUPS => { 1 => 'XMP-PixelLive', 2 => 'Image' },
+    NAMESPACE => 'PixelLive',
+    WRITE_PROC => \&WriteXMP,
+    NOTES => q{
+        PixelLive schema tags.  These tags are not writable becase they are very
+        uncommon and I haven't been able to locate a reference which gives the
+        namespace URI.
+    },
+    AUTHOR    => { Name => 'Author',   Avoid => 1, Groups => { 2 => 'Author'} },
+    COMMENTS  => { Name => 'Comments', Avoid => 1 },
+    COPYRIGHT => { Name => 'Copyright',Avoid => 1, Groups => { 2 => 'Author'} },
+    DATE      => { Name => 'Date',     Avoid => 1, Groups => { 2 => 'Time'} },
+    GENRE     => { Name => 'Genre',    Avoid => 1 },
+    TITLE     => { Name => 'Title',    Avoid => 1 },
+);
+
+# Picture Licensing Universal System schema properties (xmpPLUS)
+%Image::ExifTool::XMP::xmpPLUS = (
+    %xmpTableDefaults,
+    GROUPS => { 1 => 'XMP-xmpPLUS', 2 => 'Author' },
+    NAMESPACE => 'xmpPLUS',
+    NOTES => 'XMP Picture Licensing Universal System (PLUS) schema tags.',
+    CreditLineReq   => { Writable => 'boolean' },
+    ReuseAllowed    => { Writable => 'boolean' },
+);
+
+# Creative Commons schema properties (cc) (ref 5)
+%Image::ExifTool::XMP::cc = (
+    %xmpTableDefaults,
+    GROUPS => { 1 => 'XMP-cc', 2 => 'Author' },
+    NAMESPACE => 'cc',
+    NOTES => 'Creative Commons schema tags.',
+    license => { },
+);
+
+# Description Explorer schema properties (dex) (ref 6)
+%Image::ExifTool::XMP::dex = (
+    %xmpTableDefaults,
+    GROUPS => { 1 => 'XMP-dex', 2 => 'Image' },
+    NAMESPACE => 'dex',
+    NOTES => q{
+        Description Explorer schema tags.  These tags are not very common.  The
+        Source and Rating tags are avoided when writing due to name conflicts with
+        other XMP tags.
+    },
+    crc32       => { Name => 'CRC32', Writable => 'integer' },
+    source      => { Avoid => 1 },
+    shortdescription => {
+        Name => 'ShortDescription',
+        Writable => 'lang-alt',
+    },
+    licensetype => {
+        Name => 'LicenseType',
+        PrintConv => {
+            unknown        => 'Unknown',
+            shareware      => 'Shareware',
+            freeware       => 'Freeware',
+            adware         => 'Adware',
+            demo           => 'Demo',
+            commercial     => 'Commercial',
+           'public domain' => 'Public Domain',
+           'open source'   => 'Open Source',
+        },
+    },
+    revision    => { },
+    rating      => { Avoid => 1 },
+    os          => { Name => 'OS', Writable => 'integer' },
+    ffid        => { Name => 'FFID' },
+);
+
+# IView MediaPro schema properties (mediapro) (ref PH)
+%Image::ExifTool::XMP::MediaPro = (
+    %xmpTableDefaults,
+    GROUPS => { 1 => 'XMP-mediapro', 2 => 'Image' },
+    NAMESPACE => 'mediapro',
+    NOTES => 'IView MediaPro schema tags.',
+    Event       => { },
+    Location    => {
+        Avoid => 1,
+        Groups => { 2 => 'Location' },
+        Notes => 'avoided due to conflict with XMP-iptcCore:Location',
+    },
+    Status      => { },
+    People      => { List => 'Bag' },
+    UserFields  => { List => 'Bag' },
+    CatalogSets => { List => 'Bag' },
+);
+
+# Microsoft Photo schema properties (MicrosoftPhoto) (ref PH)
+%Image::ExifTool::XMP::Microsoft = (
+    %xmpTableDefaults,
+    GROUPS => { 1 => 'XMP-microsoft', 2 => 'Image' },
+    NAMESPACE => 'MicrosoftPhoto',
+    NOTES => q{
+        Microsoft Photo schema tags.  This is likely not a complete list, but
+        represents tags which have been observed in sample images.  The actual
+        namespace prefix is "MicrosoftPhoto", but ExifTool shortens this to
+        "XMP-microsoft" in the family 1 group name.
+    },
+    CameraSerialNumber => { },
+    DateAcquired       => { %dateTimeInfo },
+    FlashManufacturer  => { },
+    FlashModel         => { },
+    LastKeywordIPTC    => { List => 'Bag' },
+    LastKeywordXMP     => { List => 'Bag' },
+    LensManufacturer   => { },
+    LensModel          => { },
+    Rating => {
+        Name => 'RatingPercent',
+        Notes => q{
+            normal Rating values of 1,2,3,4 and 5 stars correspond to RatingPercent
+            values of 1,25,50,75 and 99 respectively
+        },
+    },
+);
+
+# Adobe Lightroom schema properties (lr) (ref PH)
+%Image::ExifTool::XMP::Lightroom = (
+    %xmpTableDefaults,
+    GROUPS => { 1 => 'XMP-lr', 2 => 'Image' },
+    NAMESPACE => 'lr',
+    NOTES => 'Adobe Lightroom "lr" schema tags.',
+    privateRTKInfo => { },
+    hierarchicalSubject => { List => 'Bag' },
+);
+
+# DICOM schema properties (DICOM) (ref PH, written by CS3)
+%Image::ExifTool::XMP::DICOM = (
+    %xmpTableDefaults,
+    GROUPS => { 1 => 'XMP-DICOM', 2 => 'Image' },
+    NAMESPACE => 'DICOM',
+    NOTES => 'DICOM schema tags.',
+    # change some tag names to correspond with DICOM tags
+    PatientName             => { Name => 'PatientsName' },
+    PatientID               => { },
+    PatientSex              => { Name => 'PatientsSex' },
+    PatientDOB              => {
+        Name => 'PatientsBirthDate',
+        %dateTimeInfo,
+    },
+    StudyID                 => { },
+    StudyPhysician          => { },
+    StudyDateTime           => { %dateTimeInfo },
+    StudyDescription        => { },
+    SeriesNumber            => { },
+    SeriesModality          => { },
+    SeriesDateTime          => { %dateTimeInfo },
+    SeriesDescription       => { },
+    EquipmentInstitution    => { },
+    EquipmentManufacturer   => { },
+);
+
+# table to add tags in other namespaces
+%Image::ExifTool::XMP::other = (
+    GROUPS => { 2 => 'Unknown' },
+    LANG_INFO => \&GetLangInfo,
+);
+
+# Composite XMP tags
+%Image::ExifTool::XMP::Composite = (
+    # get latitude/logitude reference from XMP lat/long tags
+    # (used to set EXIF GPS position from XMP tags)
+    GPSLatitudeRef => {
+        Require => {
+            0 => 'XMP:GPSLatitude',
+        },
+        ValueConv => '$val[0] < 0 ? "S" : "N"',
+        PrintConv => {
+            N => 'North',
+            S => 'South',
+        },
+    },
+    GPSLongitudeRef => {
+        Require => {
+            0 => 'XMP:GPSLongitude',
+        },
+        ValueConv => '$val[0] < 0 ? "W" : "E"',
+        PrintConv => {
+            E => 'East',
+            W => 'West',
+        },
+    },
+);
+
+# add our composite tags
+Image::ExifTool::AddCompositeTags('Image::ExifTool::XMP');
+
+#------------------------------------------------------------------------------
+# AutoLoad our writer routines when necessary
+#
+sub AUTOLOAD
+{
+    return Image::ExifTool::DoAutoLoad($AUTOLOAD, @_);
+}
+
+#------------------------------------------------------------------------------
+# Escape necessary XML characters in UTF-8 string
+# Inputs: 0) string to be escaped
+# Returns: escaped string
+my %charName = ('"'=>'quot', '&'=>'amp', "'"=>'#39', '<'=>'lt', '>'=>'gt');
+sub EscapeXML($)
+{
+    my $str = shift;
+    $str =~ s/([&><'"])/&$charName{$1};/sg; # escape necessary XML characters
+    return $str;
+}
+
+#------------------------------------------------------------------------------
+# Unescape XML character references (entities and numerical)
+# Inputs: 0) string to be unescaped
+#         1) optional hash reference to convert entity names to numbers
+# Returns: unescaped string
+my %charNum = ('quot'=>34, 'amp'=>38, 'apos'=>39, 'lt'=>60, 'gt'=>62);
+sub UnescapeXML($;$)
+{
+    my ($str, $conv) = @_;
+    $conv = \%charNum unless $conv;
+    $str =~ s/&(#?\w+);/UnescapeChar($1,$conv)/sge;
+    return $str;
+}
+
+#------------------------------------------------------------------------------
+# Convert XML character reference to UTF-8
+# Inputs: 0) XML character reference stripped of the '&' and ';' (ie. 'quot', '#34', '#x22')
+#         1) hash reference for looking up character numbers by name
+# Returns: UTF-8 equivalent (or original character on conversion error)
+sub UnescapeChar($$)
+{
+    my ($ch, $conv) = @_;
+    my $val = $$conv{$ch};
+    unless (defined $val) {
+        if ($ch =~ /^#x([0-9a-fA-F]+)$/) {
+            $val = hex($1);
+        } elsif ($ch =~ /^#(\d+)$/) {
+            $val = $1;
+        } else {
+            return "&$ch;"; # should issue a warning here?
+        }
+    }
+    if ($val < 0x80) {
+        return chr($val);   # simple ASCII
+    } elsif ($] >= 5.006001) {
+        return pack('C0U', $val);
+    } else {
+        return Image::ExifTool::PackUTF8($val);
+    }
+}
+
+#------------------------------------------------------------------------------
+# Utility routine to decode a base64 string
+# Inputs: 0) base64 string
+# Returns:   reference to decoded data
+sub DecodeBase64($)
+{
+    local($^W) = 0; # unpack('u',...) gives bogus warning in 5.00[123]
+    my $str = shift;
+
+    # truncate at first unrecognized character (base 64 data
+    # may only contain A-Z, a-z, 0-9, +, /, =, or white space)
+    $str =~ s/[^A-Za-z0-9+\/= \t\n\r\f].*//;
+    # translate to uucoded and remove padding and white space
+    $str =~ tr/A-Za-z0-9+\/= \t\n\r\f/ -_/d;
+
+    # convert the data to binary in chunks
+    my $chunkSize = 60;
+    my $uuLen = pack('c', 32 + $chunkSize * 3 / 4); # calculate length byte
+    my $dat = '';
+    my ($i, $substr);
+    # loop through the whole chunks
+    my $len = length($str) - $chunkSize;
+    for ($i=0; $i<=$len; $i+=$chunkSize) {
+        $substr = substr($str, $i, $chunkSize);     # get a chunk of the data
+        $dat .= unpack('u', $uuLen . $substr);      # decode it
+    }
+    $len += $chunkSize;
+    # handle last partial chunk if necessary
+    if ($i < $len) {
+        $uuLen = pack('c', 32 + ($len-$i) * 3 / 4); # recalculate length
+        $substr = substr($str, $i, $len-$i);        # get the last partial chunk
+        $dat .= unpack('u', $uuLen . $substr);      # decode it
+    }
+    return \$dat;
+}
+
+#------------------------------------------------------------------------------
+# Generate a name for this XMP tag
+# Inputs: 0) reference to tag property name list
+# Returns: tagID and outtermost interesting namespace
+sub GetXMPTagID($)
+{
+    my $props = shift;
+    my ($tag, $prop, $namespace);
+    foreach $prop (@$props) {
+        # split name into namespace and property name
+        # (Note: namespace can be '' for property qualifiers)
+        my ($ns, $nm) = ($prop =~ /(.*?):(.*)/) ? ($1, $2) : ('', $prop);
+        $nm =~ s/ .*//;     # remove nodeID if it exists
+        if ($ignoreNamespace{$ns}) {
+            # special case: don't ignore rdf numbered items
+            next unless $prop =~ /^rdf:(_\d+)$/;
+            $tag .= $1;
+        } else {
+            # all uppercase is ugly, so convert it
+            if ($nm !~ /[a-z]/) {
+                my $xlatNS = $xlatNamespace{$ns} || $ns;
+                my $info = $Image::ExifTool::XMP::Main{$xlatNS};
+                my $table;
+                if (ref $info eq 'HASH' and $info->{SubDirectory}) {
+                    $table = GetTagTable($info->{SubDirectory}->{TagTable});
+                }
+                unless ($table and $table->{$nm}) {
+                    $nm = lc($nm);
+                    $nm =~ s/_([a-z])/\u$1/g;
+                }
+            }
+            if (defined $tag) {
+                $tag .= ucfirst($nm);       # add to tag name
+            } else {
+                $tag = $nm;
+            }
+        }
+        # save namespace of first property to contribute to tag name
+        $namespace = $ns unless defined $namespace;
+    }
+    if (wantarray) {
+        return ($tag, $namespace);
+    } else {
+        return $tag;
+    }
+}
+
+#------------------------------------------------------------------------------
+# Get localized version of tagInfo hash
+# Inputs: 0) tagInfo hash ref, 1) language code (ie. "x-default")
+# Returns: new tagInfo hash ref, or undef if invalid
+sub GetLangInfo($$)
+{
+    my ($tagInfo, $langCode) = @_;
+    # only allow alternate language tags in lang-alt lists
+    return undef unless $$tagInfo{Writable} and $$tagInfo{Writable} eq 'lang-alt';
+    $langCode =~ tr/_/-/;   # RFC 3066 specifies '-' as a separator
+    return Image::ExifTool::GetLangInfo($tagInfo, $langCode);
+}
+
+#------------------------------------------------------------------------------
+# Get standard case for language code
+# Inputs: 0) Language code
+# Returns: Language code in standard case
+sub StandardLangCase($)
+{
+    my $lang = shift;
+    # make 2nd subtag uppercase only if it is 2 letters
+    return lc($1) . uc($2) . lc($3) if $lang =~ /^([a-z]{2,3}|[xi])(-[a-z]{2})\b(.*)/i;
+    return lc($lang);
+}
+
+#------------------------------------------------------------------------------
+# Scan for XMP in a file
+# Inputs: 0) ExifTool object ref, 1) RAF reference
+# Returns: 1 if xmp was found, 0 otherwise
+# Notes: Currently only recognizes UTF8-encoded XMP
+sub ScanForXMP($$)
+{
+    my ($exifTool, $raf) = @_;
+    my ($buff, $xmp);
+    my $lastBuff = '';
+
+    $exifTool->VPrint(0,"Scanning for XMP\n");
+    for (;;) {
+        defined $buff or $raf->Read($buff, 65536) or return 0;
+        unless (defined $xmp) {
+            $lastBuff .= $buff;
+            unless ($lastBuff =~ /(<\?xpacket begin=)/g) {
+                # must keep last 15 bytes to match 16-byte "xpacket begin" string
+                $lastBuff = length($buff) <= 15 ? $buff : substr($buff, -15);
+                undef $buff;
+                next;
+            }
+            $xmp = $1;
+            $buff = substr($lastBuff, pos($lastBuff));
+        }
+        $xmp .= $buff;
+        my $pos = length($xmp) - length($buff) - 18; # 18 is length("<xpacket end...")-1
+        pos($xmp) = $pos if $pos > 0;
+        if ($xmp =~ /<\?xpacket end=['"][wr]['"]\?>/g) {
+            $xmp = substr($xmp, 0, pos($xmp));
+            # XMP is not valid if it contains null bytes
+            pos($xmp) = 0;
+            last unless $xmp =~ /\0/g;
+            my $null = pos $xmp;
+            while ($xmp =~ /\0/g) {
+                $null = pos($xmp);
+            }
+            # re-parse beginning after last null byte
+            $buff = substr($xmp, $null);
+            $lastBuff = '';
+            undef $xmp;
+        } else {
+            undef $buff;
+        }
+    }
+    unless ($exifTool->{VALUE}->{FileType}) {
+        $exifTool->{FILE_TYPE} = $exifTool->{FILE_EXT};
+        $exifTool->SetFileType('<unknown file containing XMP>');
+    }
+    my %dirInfo = (
+        DataPt => \$xmp,
+        DirLen => length $xmp,
+        DataLen => length $xmp,
+    );
+    ProcessXMP($exifTool, \%dirInfo);
+    return 1;
+}
+
+#------------------------------------------------------------------------------
+# We found an XMP property name/value
+# Inputs: 0) ExifTool object ref, 1) Pointer to tag table
+#         2) reference to array of XMP property names (last is current property)
+#         3) property value, 4) xml:lang attribute (or undef)
+# Returns: 1 if valid tag was found
+sub FoundXMP($$$$;$)
+{
+    local $_;
+    my ($exifTool, $tagTablePtr, $props, $val, $lang) = @_;
+
+    my ($tag, $namespace) = GetXMPTagID($props);
+    return 0 unless $tag;   # ignore things that aren't valid tags
+
+    # translate namespace if necessary
+    $namespace = $xlatNamespace{$namespace} if $xlatNamespace{$namespace};
+    my $info = $tagTablePtr->{$namespace};
+    my $table;
+    if ($info) {
+        $table = $info->{SubDirectory}->{TagTable} or warn "Missing TagTable for $tag!\n";
+    } else {
+        $table = 'Image::ExifTool::XMP::other';
+    }
+    # change pointer to the table for this namespace
+    $tagTablePtr = GetTagTable($table);
+
+    # look up this tag in the appropriate table
+    my $tagInfo = $exifTool->GetTagInfo($tagTablePtr, $tag);
+
+    unless ($tagInfo) {
+        if ($exifTool->{OPTIONS}->{Verbose}) {
+            my $group1 = $namespace ? "XMP-$namespace" : $tagTablePtr->{GROUPS}->{1};
+            $exifTool->VPrint(0, $exifTool->{INDENT}, "[adding $group1:$tag]\n");
+        }
+        # construct tag information for this unknown tag
+        $tagInfo = { Name => ucfirst($tag), WasAdded => 1 };
+        # make this a List type if necessary and not lang-alt
+        $$tagInfo{List} = $1 if @$props > 2 and not $lang and
+            $$props[-1] =~ /^rdf:li \d+$/ and $$props[-2] =~ /^rdf:(Bag|Seq|Alt)/;
+        Image::ExifTool::AddTagToTable($tagTablePtr, $tag, $tagInfo);
+    }
+    if (defined $lang and lc($lang) ne 'x-default') {
+        $lang = StandardLangCase($lang);
+        my $langInfo = GetLangInfo($tagInfo, $lang);
+        $tagInfo = $langInfo if $langInfo;
+    }
+    if ($exifTool->{OPTIONS}->{Charset} ne 'UTF8' and $val =~ /[\x80-\xff]/) {
+        # convert from UTF-8 to specified characterset
+        $val = $exifTool->UTF82Charset($val);
+    }
+    # convert rational and date values to a more sensible format
+    my $fmt = $$tagInfo{Writable};
+    my $new = $$tagInfo{WasAdded};
+    if ($fmt or $new) {
+        if (($new or $fmt eq 'rational') and $val =~ m{^(-?\d+)/(-?\d+)$}) {
+            $val = $1 / $2 if $2;       # calculate quotient
+        } elsif ($new or $fmt eq 'date') {
+            if ($val =~ /^(\d{4})-(\d{2})-(\d{2})[T ](\d{2}:\d{2})(:\d{2})?(\S*)$/) {
+                my $s = $5 || ':00';        # seconds may be missing
+                $val = "$1:$2:$3 $4$s$6";   # convert back to EXIF time format
+            } elsif ($fmt and $fmt eq 'date' and $val =~ /^(\d{4})(-\d{2}){0,2}/) {
+                $val =~ tr/-/:/;
+            }
+        }
+    }
+    $tag = $exifTool->FoundTag($tagInfo, UnescapeXML($val));
+    $exifTool->SetGroup1($tag, "XMP-$namespace") if $namespace;
+
+    if ($exifTool->Options('Verbose')) {
+        my $tagID = join('/',@$props);
+        $exifTool->VerboseInfo($tagID, $tagInfo, Value=>$val);
+    }
+    return 1;
+}
+
+#------------------------------------------------------------------------------
+# Recursively parse nested XMP data element
+# Inputs: 0) ExifTool object reference
+#         1) Pointer to tag table
+#         2) reference to XMP data
+#         3) start of xmp element
+#         4) reference to array of enclosing XMP property names (undef if none)
+#         5) reference to blank node information hash
+# Returns: Number of contained XMP elements
+sub ParseXMPElement($$$;$$$)
+{
+    my ($exifTool, $tagTablePtr, $dataPt, $start, $propListPt, $blankInfo) = @_;
+    my $count = 0;
+    my $isWriting = $exifTool->{XMP_CAPTURE};
+    $start or $start = 0;
+    $propListPt or $propListPt = [ ];
+
+    my $processBlankInfo;
+    # create empty blank node information hash if necessary
+    $blankInfo or $blankInfo = $processBlankInfo = { Prop => { } };
+    # keep track of current nodeID at this nesting level
+    my $oldNodeID = $$blankInfo{NodeID};
+
+    pos($$dataPt) = $start;
+    Element: for (;;) {
+        # reset nodeID before processing each element
+        my $nodeID = $$blankInfo{NodeID} = $oldNodeID;
+        # get next element
+        last unless $$dataPt =~ m/<([\w:-]+)(.*?)>/sg;
+        my ($prop, $attrs) = ($1, $2);
+        my $val = '';
+        # only look for closing token if this is not an empty element
+        # (empty elements end with '/', ie. <a:b/>)
+        if ($attrs !~ s/\/$//) {
+            my $nesting = 1;
+            for (;;) {
+# this match fails with perl 5.6.2 (perl bug!), but it works without
+# the '(.*?)', so do it the hard way instead...
+#                $$dataPt =~ m/(.*?)<\/$prop>/sg or last Element;
+#                my $val2 = $1;
+                my $pos = pos($$dataPt);
+                $$dataPt =~ m/<\/$prop>/sg or last Element;
+                my $len = pos($$dataPt) - $pos - length($prop) - 3;
+                my $val2 = substr($$dataPt, $pos, $len);
+                # increment nesting level for each contained similar opening token
+                ++$nesting while $val2 =~ m/<$prop\b.*?(\/?)>/sg and $1 ne '/';
+                $val .= $val2;
+                --$nesting or last;
+                $val .= "</$prop>";
+            }
+        }
+        if ($prop eq 'rdf:li') {
+            # add index to list items so we can keep them in order
+            # (this also enables us to keep structure elements grouped properly
+            # for lists of structures, like JobRef)
+            $prop .= sprintf(' %.3d', $count);
+        } elsif ($prop eq 'rdf:Description') {
+            # trim comments and whitespace from rdf:Description properties only
+            $val =~ s/<!--.*?-->//g;
+            $val =~ s/^\s*(.*)\s*$/$1/;
+        } elsif ($prop eq 'xmp:xmpmeta') {
+            # patch MicrosoftPhoto unconformity
+            $prop = 'x:xmpmeta';
+        }
+
+        # extract property attributes
+        my (%attrs, @attrs);
+        while ($attrs =~ m/(\S+?)=(['"])(.*?)\2/sg) {
+            push @attrs, $1;    # preserve order
+            $attrs{$1} = $3;
+        }
+
+        # add nodeID to property path (with leading ' #') if it exists
+        if (defined $attrs{'rdf:nodeID'}) {
+            $nodeID = $$blankInfo{NodeID} = $attrs{'rdf:nodeID'};
+            delete $attrs{'rdf:nodeID'};
+            $prop .= ' #' . $nodeID;
+        }
+
+        # push this property name onto our hierarchy list
+        push @$propListPt, $prop;
+
+        # handle properties inside element attributes (RDF shorthand format):
+        # (attributes take the form a:b='c' or a:b="c")
+        my ($shortName, $shorthand, $ignored);
+        foreach $shortName (@attrs) {
+            my $propName = $shortName;
+            my ($ns, $name);
+            if ($propName =~ /(.*?):(.*)/) {
+                $ns = $1;   # specified namespace
+                $name = $2;
+            } elsif ($prop =~ /(\S*?):/) {
+                $ns = $1;   # assume same namespace as parent
+                $name = $propName;
+                $propName = "$ns:$name";    # generate full property name
+            } else {
+                # a property qualifier is the only property name that may not
+                # have a namespace, and a qualifier shouldn't have attributes,
+                # but what the heck, let's allow this anyway
+                $ns = '';
+                $name = $propName;
+            }
+            if ($isWriting) {
+                # keep track of our namespaces when writing
+                if ($ns eq 'xmlns') {
+                    unless ($name eq 'x' or $name eq 'iX') {
+                        my $nsUsed = $exifTool->{XMP_NS};
+                        $$nsUsed{$name} = $attrs{$shortName} unless defined $$nsUsed{$name};
+                    }
+                    next;
+                } elsif ($recognizedAttrs{$propName}) {
+                    # save UUID to use same ID when writing
+                    if ($propName eq 'rdf:about') {
+                        if (not $exifTool->{XMP_UUID}) {
+                            $exifTool->{XMP_UUID} = $attrs{$shortName};
+                        } elsif ($exifTool->{XMP_UUID} ne $attrs{$shortName}) {
+                            $exifTool->Error("Multiple XMP UUID's not handled", 1);
+                        }
+                    }
+                    next;
+                }
+            }
+            if ($ignoreNamespace{$ns}) {
+                $ignored = $propName;
+                next;
+            }
+            my $shortVal = $attrs{$shortName};
+            delete $attrs{$shortName};  # don't re-use this attribute
+            push @$propListPt, $propName;
+            # save this shorthand XMP property
+            if (defined $nodeID) {
+                SaveBlankInfo($blankInfo, $propListPt, $shortVal);
+            } elsif ($isWriting) {
+                CaptureXMP($exifTool, $propListPt, $shortVal);
+            } else {
+                FoundXMP($exifTool, $tagTablePtr, $propListPt, $shortVal);
+            }
+            pop @$propListPt;
+            $shorthand = 1;
+        }
+        if ($isWriting) {
+            if (ParseXMPElement($exifTool, $tagTablePtr, \$val, 0, $propListPt, $blankInfo)) {
+                # undefine value since we found more properties within this one
+                undef $val;
+                # set an error on any ignored attributes here, because they will be lost
+                $exifTool->{XMP_ERROR} = "Can't handle XMP attribute '$ignored'" if $ignored;
+            }
+            if (defined $val and (length $val or not $shorthand)) {
+                if (defined $nodeID) {
+                    SaveBlankInfo($blankInfo, $propListPt, $val, \%attrs);
+                } else {
+                    CaptureXMP($exifTool, $propListPt, $val, \%attrs);
+                }
+            }
+        } else {
+            # if element value is empty, take value from 'resource' attribute
+            # (preferentially) or 'about' attribute (if no 'resource')
+            $val = $2 if $val eq '' and ($attrs =~ /\bresource=(['"])(.*?)\1/ or
+                                         $attrs =~ /\babout=(['"])(.*?)\1/);
+
+            # look for additional elements contained within this one
+            if (!ParseXMPElement($exifTool, $tagTablePtr, \$val, 0, $propListPt, $blankInfo)) {
+                # there are no contained elements, so this must be a simple property value
+                # (unless we already extracted shorthand values from this element)
+                if (length $val or not $shorthand) {
+                    if (defined $nodeID) {
+                        SaveBlankInfo($blankInfo, $propListPt, $val);
+                    } else {
+                        FoundXMP($exifTool, $tagTablePtr, $propListPt, $val, $attrs{'xml:lang'});
+                    }
+                }
+            }
+        }
+        pop @$propListPt;
+        ++$count;
+    }
+#
+# process resources referenced by blank nodeID's
+#
+    if ($processBlankInfo and %{$$blankInfo{Prop}}) {
+        ProcessBlankInfo($exifTool, $tagTablePtr, $blankInfo, $isWriting);
+        %$blankInfo = ();   # free some memory
+    }
+    return $count;  # return the number of elements found at this level
+}
+
+#------------------------------------------------------------------------------
+# Process XMP data
+# Inputs: 0) ExifTool object reference, 1) DirInfo reference, 2) Pointer to tag table
+# Returns: 1 on success
+sub ProcessXMP($$;$)
+{
+    my ($exifTool, $dirInfo, $tagTablePtr) = @_;
+    my $dataPt = $$dirInfo{DataPt};
+    my $dirStart = $$dirInfo{DirStart} || 0;
+    my $dirLen = $$dirInfo{DirLen};
+    my $dataLen = $$dirInfo{DataLen};
+    my $rtnVal = 0;
+    my $bom = 0;
+    my ($buff, $fmt, $isXML);
+
+    # read information from XMP file if necessary
+    unless ($dataPt) {
+        my $raf = $$dirInfo{RAF} or return 0;
+        $raf->Read($buff, 128) or return 0;
+        my ($buf2, $double);
+        ($buf2 = $buff) =~ tr/\0//d;    # cheap conversion to UTF-8
+        # check to see if this is XMP format
+        # - CS2 writes .XMP files without the "xpacket begin"
+        unless ($buf2 =~ /^(<\?xpacket begin=|<x(mp)?:xmpmeta)/) {
+            # also recognize XML files (and .XMP files with BOM)
+            if ($buf2 =~ /^(\xfe\xff)(<\?xml|<x(mp)?:xmpmeta)/g) {
+                $fmt = 'n';     # UTF-16 or 32 MM with BOM
+            } elsif ($buf2 =~ /^(\xff\xfe)(<\?xml|<x(mp)?:xmpmeta)/g) {
+                $fmt = 'v';     # UTF-16 or 32 II with BOM
+            } elsif ($buf2 =~ /^(\xef\xbb\xbf)?(<\?xml|<x(mp)?:xmpmeta)/g) {
+                $fmt = 0;       # UTF-8 with BOM or unknown encoding without BOM
+            } elsif ($buf2 =~ /^(\xfe\xff|\xff\xfe|\xef\xbb\xbf)(<\?xpacket begin=)/g) {
+                $double = $1;   # double-encoded UTF
+            } else {
+                return 0;       # not XMP or XML
+            }
+            $bom = 1 if $1;
+            if ($2 eq '<?xml') {
+                return 0 unless $buf2 =~ /<x(mp)?:xmpmeta/;
+                $isXML = 1;
+            }
+            if ($buff =~ /^\0\0/) {
+                $fmt = 'N';     # UTF-32 MM with or without BOM
+            } elsif ($buff =~ /^..\0\0/) {
+                $fmt = 'V';     # UTF-32 II with or without BOM
+            } elsif (not $fmt) {
+                if ($buff =~ /^\0/) {
+                    $fmt = 'n'; # UTF-16 MM without BOM
+                } elsif ($buff =~ /^.\0/) {
+                    $fmt = 'v'; # UTF-16 II without BOM
+                }
+            }
+        }
+        $raf->Seek(0, 2) or return 0;
+        my $size = $raf->Tell() or return 0;
+        $raf->Seek(0, 0) or return 0;
+        $raf->Read($buff, $size) == $size or return 0;
+        # decode the first layer of double-encoded UTF text
+        if ($double) {
+            $buff = substr($buff, length $double);  # remove leading BOM
+            Image::ExifTool::SetWarning(undef);     # clear old warning
+            local $SIG{'__WARN__'} = \&Image::ExifTool::SetWarning;
+            my $tmp;
+            # assume that character data has been re-encoded in UTF, so re-pack
+            # as characters and look for warnings indicating a false assumption
+            if ($double eq "\xef\xbb\xbf") {
+                $tmp = Image::ExifTool::UTF82Unicode($buff, 'C');
+            } else {
+                my $fmt = ($double eq "\xfe\xff") ? 'n' : 'v';
+                $tmp = pack('C*', unpack("$fmt*",$buff));
+            }
+            if (Image::ExifTool::GetWarning()) {
+                $exifTool->Warn('Superfluous BOM at start of XMP');
+            } else {
+                $exifTool->Warn('XMP is double UTF-encoded');
+                $buff = $tmp;   # use the decoded XMP
+            }
+            $size = length $buff;
+        }
+        $dataPt = \$buff;
+        $dirStart = 0;
+        $dirLen = $dataLen = $size;
+        $exifTool->SetFileType();
+    }
+   
+    # take substring if necessary
+    if ($dataLen != $dirStart + $dirLen) {
+        $buff = substr($$dataPt, $dirStart, $dirLen);
+        $dataPt = \$buff;
+        $dirStart = 0;
+    }
+    if ($exifTool->{REQ_TAG_LOOKUP}->{xmp}) {
+        $exifTool->FoundTag('XMP', substr($$dataPt, $dirStart, $dirLen));
+    }
+    if ($exifTool->Options('Verbose') and not $exifTool->{XMP_CAPTURE}) {
+        $exifTool->VerboseDir('XMP', 0, $dirLen);
+    }
+#
+# convert UTF-16 or UTF-32 encoded XMP to UTF-8 if necessary
+#
+    my $begin = '<?xpacket begin=';
+    pos($$dataPt) = $dirStart;
+    delete $$exifTool{XMP_IS_XML};
+    if ($isXML) {
+        $$exifTool{XMP_IS_XML} = 1;
+        $$exifTool{XMP_NO_XPACKET} = 1 + $bom;
+    } elsif ($$dataPt =~ /\G\Q$begin\E/gc) {
+        delete $$exifTool{XMP_NO_XPACKET};
+    } elsif ($$dataPt =~ /<x(mp)?:xmpmeta/gc) {
+        $$exifTool{XMP_NO_XPACKET} = 1 + $bom;
+    } else {
+        delete $$exifTool{XMP_NO_XPACKET};
+        # check for UTF-16 encoding (insert one \0 between characters)
+        $begin = join "\0", split //, $begin;
+        if ($$dataPt =~ /\G(\0)?\Q$begin\E\0./g) {
+            # validate byte ordering by checking for U+FEFF character
+            if ($1) {
+                # should be big-endian since we had a leading \0
+                $fmt = 'n' if $$dataPt =~ /\G\xfe\xff/g;
+            } else {
+                $fmt = 'v' if $$dataPt =~ /\G\0\xff\xfe/g;
+            }
+        } else {
+            # check for UTF-32 encoding (with three \0's between characters)
+            $begin =~ s/\0/\0\0\0/g;
+            # must reset pos because it was killed by previous unsuccessful //g match
+            pos($$dataPt) = $dirStart;
+            if ($$dataPt !~ /\G(\0\0\0)?\Q$begin\E\0\0\0./g) {
+                $fmt = 0;   # set format to zero as indication we didn't find encoded XMP
+            } elsif ($1) {
+                # should be big-endian
+                $fmt = 'N' if $$dataPt =~ /\G\0\0\xfe\xff/g;
+            } else {
+                $fmt = 'V' if $$dataPt =~ /\G\0\0\0\xff\xfe\0\0/g;
+            }
+        }
+        defined $fmt or $exifTool->Warn('XMP character encoding error');
+    }
+    if ($fmt) {
+        # translate into UTF-8
+        if ($] >= 5.006001) {
+            $buff = pack('C0U*', unpack("x$dirStart$fmt*",$$dataPt));
+        } else {
+            $buff = Image::ExifTool::PackUTF8(unpack("x$dirStart$fmt*",$$dataPt));
+        }
+        $dataPt = \$buff;
+        $dirStart = 0;
+    }
+    # avoid scanning for XMP later in case ScanForXMP is set
+    $$exifTool{FoundXMP} = 1;
+
+    # parse the XMP
+    $tagTablePtr or $tagTablePtr = GetTagTable('Image::ExifTool::XMP::Main');
+    $rtnVal = 1 if ParseXMPElement($exifTool, $tagTablePtr, $dataPt, $dirStart);
+
+    # return DataPt if successful in case we want it for writing
+    $$dirInfo{DataPt} = $dataPt if $rtnVal and $$dirInfo{RAF};
+
+    return $rtnVal;
+}
+
+
+1;  #end
+
+__END__
+
+=head1 NAME
+
+Image::ExifTool::XMP - Read XMP meta information
+
+=head1 SYNOPSIS
+
+This module is loaded automatically by Image::ExifTool when required.
+
+=head1 DESCRIPTION
+
+XMP stands for Extensible Metadata Platform.  It is a format based on XML
+that Adobe developed for embedding metadata information in image files.
+This module contains the definitions required by Image::ExifTool to read XMP
+information.
+
+=head1 AUTHOR
+
+Copyright 2003-2007, Phil Harvey (phil at owl.phy.queensu.ca)
+
+This library is free software; you can redistribute it and/or modify it
+under the same terms as Perl itself.
+
+=head1 REFERENCES
+
+=over 4
+
+=item L<http://www.adobe.com/products/xmp/pdfs/xmpspec.pdf>
+
+=item L<http://www.w3.org/TR/rdf-syntax-grammar/>
+
+=item L<http://www.portfoliofaq.com/pfaq/v7mappings.htm>
+
+=item L<http://www.iptc.org/IPTC4XMP/>
+
+=item L<http://creativecommons.org/technology/xmp>
+
+=item L<http://www.optimasc.com/products/fileid/xmp-extensions.pdf>
+
+=back
+
+=head1 SEE ALSO
+
+L<Image::ExifTool::TagNames/XMP Tags>,
+L<Image::ExifTool(3pm)|Image::ExifTool>
+
+=cut
