Ignore:
Timestamp:
2011-06-01T12:33:42+12:00 (13 years ago)
Author:
sjm84
Message:

Updating the ExifTool perl modules

File:
1 edited

Legend:

Unmodified
Added
Removed
  • main/trunk/greenstone2/perllib/cpan/Image/ExifTool/GIF.pm

    r16842 r24107  
    66# Revisions:    10/18/2005 - P. Harvey Separated from ExifTool.pm
    77#
    8 # References:   http://www.w3.org/Graphics/GIF/spec-gif89a.txt
    9 #
    10 # Notes:        GIF really doesn't have much meta information, except for
    11 #               comments which are allowed in GIF89a images
     8# References:   1) http://www.w3.org/Graphics/GIF/spec-gif89a.txt
     9#               2) http://www.adobe.com/devnet/xmp/
     10#               3) http://graphcomp.com/info/specs/ani_gif.html
    1211#------------------------------------------------------------------------------
    1312
     
    1615use strict;
    1716use vars qw($VERSION);
    18 use Image::ExifTool qw(:DataAccess);
    19 
    20 $VERSION = '1.04';
     17use Image::ExifTool qw(:DataAccess :Utils);
     18
     19$VERSION = '1.06';
     20
     21# road map of directory locations in GIF images
     22my %gifMap = (
     23    XMP => 'GIF',
     24);
     25
     26%Image::ExifTool::GIF::Main = (
     27    GROUPS => { 2 => 'Image' },
     28    VARS => { NO_ID => 1 },
     29    NOTES => q{
     30        This table lists information extracted from GIF images. See
     31        L<http://www.w3.org/Graphics/GIF/spec-gif89a.txt> for the official GIF89a
     32        specification.
     33    },
     34    GIFVersion => { },
     35    FrameCount => { Notes => 'number of animated images' },
     36    Text       => { Notes => 'text displayed in image' },
     37    Comment    => {
     38        # for documentation only -- flag as writable for the docs, but
     39        # it won't appear in the TagLookup because there is no WRITE_PROC
     40        Writable => 1,
     41    },
     42    Duration   => {
     43        Notes => 'duration of a single animation iteration',
     44        PrintConv => 'sprintf("%.2f s",$val)',
     45    },
     46    ScreenDescriptor => {
     47        SubDirectory => { TagTable => 'Image::ExifTool::GIF::Screen' },
     48    },
     49    AnimationExtension => {
     50        SubDirectory => { TagTable => 'Image::ExifTool::GIF::Animate' },
     51    },
     52    XMPExtension => { # (for documentation only)
     53        SubDirectory => { TagTable => 'Image::ExifTool::XMP::Main' },
     54    },
     55);
     56
     57# GIF locical screen descriptor
     58%Image::ExifTool::GIF::Screen = (
     59    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
     60    GROUPS => { 2 => 'Image' },
     61    NOTES => 'Information extracted from the GIF logical screen descriptor.',
     62    0 => {
     63        Name => 'ImageWidth',
     64        Format => 'int16u',
     65    },
     66    2 => {
     67        Name => 'ImageHeight',
     68        Format => 'int16u',
     69    },
     70    4.1 => {
     71        Name => 'HasColorMap',
     72        Mask => 0x80,
     73        PrintConv => { 0x00 => 'No', 0x80 => 'Yes' },
     74    },
     75    4.2 => {
     76        Name => 'ColorResolutionDepth',
     77        Mask => 0x70,
     78        ValueConv => '($val >> 4) + 1',
     79    },
     80    4.3 => {
     81        Name => 'BitsPerPixel',
     82        Mask => 0x07,
     83        ValueConv => '$val + 1',
     84    },
     85    5 => 'BackgroundColor',
     86);
     87
     88# GIF Netscape 2.0 animation extension
     89%Image::ExifTool::GIF::Animate = (
     90    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
     91    GROUPS => { 2 => 'Image' },
     92    NOTES => 'Information extracted from the "NETSCAPE2.0" animation extension.',
     93    2 => {
     94        Name => 'AnimationIterations',
     95        Format => 'int16u',
     96        PrintConv => '$val ? $val : "Infinite"',
     97    },
     98);
    2199
    22100#------------------------------------------------------------------------------
     
    28106{
    29107    my ($exifTool, $dirInfo) = @_;
    30     my ($type, $a, $s, $ch, $length, $buff);
    31     my ($err, $newComment, $setComment);
     108    my $outfile = $$dirInfo{OutFile};
     109    my $raf = $$dirInfo{RAF};
    32110    my $verbose = $exifTool->Options('Verbose');
    33111    my $out = $exifTool->Options('TextOut');
    34     my $outfile = $$dirInfo{OutFile};
    35     my $raf = $$dirInfo{RAF};
     112    my ($a, $s, $ch, $length, $buff, $comment);
     113    my ($err, $newComment, $setComment);
     114    my ($addDirs, %doneDir);
     115    my ($frameCount, $delayTime) = (0, 0);
     116
     117    # verify this is a valid GIF file
     118    return 0 unless $raf->Read($buff, 6) == 6
     119        and $buff =~ /^GIF(8[79]a)$/
     120        and $raf->Read($s, 7) == 7;
     121
     122    my $ver = $1;
    36123    my $rtnVal = 0;
    37 
    38     # verify this is a valid GIF file
    39     # (must do a RAF read until we know the file is ours)
    40     return 0 unless $raf->Read($type, 6) == 6
    41         and $type =~ /^GIF8[79]a$/
    42         and $raf->Read($s, 4) == 4;
    43 
    44     $verbose and print $out "GIF file version $type\n";
     124    my $tagTablePtr = GetTagTable('Image::ExifTool::GIF::Main');
     125    SetByteOrder('II');
     126
    45127    if ($outfile) {
    46         Write($outfile, $type, $s) or $err = 1;
    47         if ($exifTool->{DEL_GROUP}->{File}) {
     128        $exifTool->InitWriteDirs(\%gifMap, 'XMP'); # make XMP the preferred group for GIF
     129        $addDirs = $exifTool->{ADD_DIRS};
     130        # determine if we are editing the File:Comment tag
     131        my $delGroup = $exifTool->{DEL_GROUP};
     132        if ($$delGroup{File}) {
    48133            $setComment = 1;
    49             if ($exifTool->{DEL_GROUP}->{File} == 2) {
     134            if ($$delGroup{File} == 2) {
    50135                $newComment = $exifTool->GetNewValues('Comment');
    51136            }
    52137        } else {
    53             my $newValueHash;
    54             $newComment = $exifTool->GetNewValues('Comment', \$newValueHash);
    55             $setComment = 1 if $newValueHash;
    56         }
     138            my $nvHash;
     139            $newComment = $exifTool->GetNewValues('Comment', \$nvHash);
     140            $setComment = 1 if $nvHash;
     141        }
     142        # change to GIF 89a if adding comment or XMP
     143        $buff = 'GIF89a' if $$addDirs{XMP} or defined $newComment;
     144        Write($outfile, $buff, $s) or $err = 1;
     145    } else {
     146        $exifTool->SetFileType();   # set file type
     147        $exifTool->HandleTag($tagTablePtr, 'GIFVersion', $ver);
     148        $exifTool->HandleTag($tagTablePtr, 'ScreenDescriptor', $s);
    57149    }
    58     $exifTool->SetFileType();   # set file type
    59     my ($w, $h) = unpack("v"x2, $s);
    60     $exifTool->FoundTag('ImageWidth', $w);
    61     $exifTool->FoundTag('ImageHeight', $h);
    62     if ($raf->Read($s, 3) == 3) {
    63         Write($outfile, $s) or $err = 1 if $outfile;
    64         if (ord($s) & 0x80) { # does this image contain a color table?
    65             # calculate color table size
    66             $length = 3 * (2 << (ord($s) & 0x07));
    67             $raf->Read($buff, $length) == $length or return 0; # skip color table
    68             Write($outfile, $buff) or $err = 1 if $outfile;
    69         }
    70         # write the comment first if necessary
    71         if ($outfile and defined $newComment) {
    72             if ($type ne 'GIF87a') {
    73                 # write comment marker
    74                 Write($outfile, "\x21\xfe") or $err = 1;
    75                 my $len = length($newComment);
    76                 # write out the comment in 255-byte chunks, each
    77                 # chunk beginning with a length byte
    78                 my $n;
    79                 for ($n=0; $n<$len; $n+=255) {
    80                     my $size = $len - $n;
    81                     $size > 255 and $size = 255;
    82                     my $str = substr($newComment,$n,$size);
    83                     Write($outfile, pack('C',$size), $str) or $err = 1;
    84                 }
    85                 Write($outfile, "\0") or $err = 1;  # empty chunk as terminator
    86                 undef $newComment;
    87                 ++$exifTool->{CHANGED};     # increment file changed flag
    88             } else {
    89                 $exifTool->Warn("The GIF87a format doesn't support comments");
    90             }
    91         }
    92         my $comment;
    93         for (;;) {
    94             last unless $raf->Read($ch, 1);
    95             if (ord($ch) == 0x2c) {
     150    my $flags = Get8u(\$s, 4);
     151    if ($flags & 0x80) { # does this image contain a color table?
     152        # calculate color table size
     153        $length = 3 * (2 << ($flags & 0x07));
     154        $raf->Read($buff, $length) == $length or return 0; # skip color table
     155        Write($outfile, $buff) or $err = 1 if $outfile;
     156    }
     157    # write the comment first if necessary
     158    if ($outfile and defined $newComment) {
     159        # write comment marker
     160        Write($outfile, "\x21\xfe") or $err = 1;
     161        $verbose and print $out "  + Comment = $newComment\n";
     162        my $len = length($newComment);
     163        # write out the comment in 255-byte chunks, each
     164        # chunk beginning with a length byte
     165        my $n;
     166        for ($n=0; $n<$len; $n+=255) {
     167            my $size = $len - $n;
     168            $size > 255 and $size = 255;
     169            my $str = substr($newComment,$n,$size);
     170            Write($outfile, pack('C',$size), $str) or $err = 1;
     171        }
     172        Write($outfile, "\0") or $err = 1;  # empty chunk as terminator
     173        undef $newComment;
     174        ++$exifTool->{CHANGED};     # increment file changed flag
     175    }
     176#
     177# loop through GIF blocks
     178#
     179Block:
     180    for (;;) {
     181        last unless $raf->Read($ch, 1);
     182        if ($outfile and ord($ch) != 0x21) {
     183            # add application extension containing XMP block if necessary
     184            # (this will place XMP before the first non-extension block)
     185            if (exists $$addDirs{XMP} and not defined $doneDir{XMP}) {
     186                $doneDir{XMP} = 1;
     187                # write new XMP data
     188                my $xmpTable = GetTagTable('Image::ExifTool::XMP::Main');
     189                my %dirInfo = ( Parent => 'GIF' );
     190                $verbose and print $out "Creating XMP application extension block:\n";
     191                $buff = $exifTool->WriteDirectory(\%dirInfo, $xmpTable);
     192                if (defined $buff and length $buff) {
     193                    my $lz = pack('C*',1,reverse(0..255),0);
     194                    Write($outfile, "\x21\xff\x0bXMP DataXMP", $buff, $lz) or $err = 1;
     195                    ++$doneDir{XMP};    # set to 2 to indicate we added XMP
     196                } else {
     197                    $verbose and print $out "  -> no XMP to add\n";
     198                }
     199            }
     200        }
     201        if (ord($ch) == 0x2c) {
     202            ++$frameCount;
     203            Write($outfile, $ch) or $err = 1 if $outfile;
     204            # image descriptor
     205            last unless $raf->Read($buff, 8) == 8 and $raf->Read($ch, 1);
     206            Write($outfile, $buff, $ch) or $err = 1 if $outfile;
     207            if ($verbose) {
     208                my ($left, $top, $w, $h) = unpack('v*', $buff);
     209                print $out "Image: left=$left top=$top width=$w height=$h\n";
     210            }
     211            if (ord($ch) & 0x80) { # does color table exist?
     212                $length = 3 * (2 << (ord($ch) & 0x07));
     213                # skip the color table
     214                last unless $raf->Read($buff, $length) == $length;
     215                Write($outfile, $buff) or $err = 1 if $outfile;
     216            }
     217            # skip "LZW Minimum Code Size" byte
     218            last unless $raf->Read($buff, 1);
     219            Write($outfile,$buff) or $err = 1 if $outfile;
     220            # skip image blocks
     221            for (;;) {
     222                last unless $raf->Read($ch, 1);
    96223                Write($outfile, $ch) or $err = 1 if $outfile;
    97                 # image descriptor
    98                 last unless $raf->Read($buff, 8) == 8;
    99                 last unless $raf->Read($ch, 1);
    100                 Write($outfile, $buff, $ch) or $err = 1 if $outfile;
    101                 if (ord($ch) & 0x80) { # does color table exist?
    102                     $length = 3 * (2 << (ord($ch) & 0x07));
    103                     # skip the color table
    104                     last unless $raf->Read($buff, $length) == $length;
    105                     Write($outfile, $buff) or $err = 1 if $outfile;
    106                 }
    107                 # skip "LZW Minimum Code Size" byte
    108                 last unless $raf->Read($buff, 1);
     224                last unless ord($ch);
     225                last unless $raf->Read($buff, ord($ch));
    109226                Write($outfile,$buff) or $err = 1 if $outfile;
    110                 # skip image blocks
    111                 for (;;) {
    112                     last unless $raf->Read($ch, 1);
    113                     Write($outfile, $ch) or $err = 1 if $outfile;
    114                     last unless ord($ch);
    115                     last unless $raf->Read($buff, ord($ch));
    116                     Write($outfile,$buff) or $err = 1 if $outfile;
    117                 }
    118                 next;  # continue with next field
    119             }
     227            }
     228            next;  # continue with next field
     229        }
    120230#               last if ord($ch) == 0x3b;  # normal end of GIF marker
    121             unless (ord($ch) == 0x21) {
    122                 if ($outfile) {
    123                     Write($outfile, $ch) or $err = 1;
    124                     # copy the rest of the file
    125                     while ($raf->Read($buff, 65536)) {
    126                         Write($outfile, $buff) or $err = 1;
    127                     }
    128                 }
    129                 $rtnVal = 1;
    130                 last;
    131             }
    132             # get extension block type/size
    133             last unless $raf->Read($s, 2) == 2;
    134             # get marker and block size
    135             ($a,$length) = unpack("C"x2, $s);
    136             if ($a == 0xfe) {  # is this a comment?
    137                 if ($setComment) {
    138                     ++$exifTool->{CHANGED}; # increment the changed flag
    139                 } else {
    140                     Write($outfile, $ch, $s) or $err = 1 if $outfile;
    141                 }
    142                 while ($length) {
    143                     last unless $raf->Read($buff, $length) == $length;
    144                     $verbose > 2 and Image::ExifTool::HexDump(\$buff, undef, Out => $out);
    145                     if (defined $comment) {
    146                         $comment .= $buff;  # add to comment string
    147                     } else {
    148                         $comment = $buff;
    149                     }
    150                     last unless $raf->Read($ch, 1);  # read next block header
    151                     unless ($setComment) {
    152                         Write($outfile, $buff, $ch) or $err = 1 if $outfile;
    153                     }
    154                     $length = ord($ch);  # get next block size
    155                 }
    156                 last if $length;    # was a read error if length isn't zero
    157                 unless ($outfile) {
    158                     $rtnVal = 1;
    159                     $exifTool->FoundTag('Comment', $comment) if $comment;
    160                     undef $comment;
    161                     # assume no more than one comment in FastScan mode
    162                     last if $exifTool->Options('FastScan');
    163                 }
     231        unless (ord($ch) == 0x21) {
     232            if ($outfile) {
     233                Write($outfile, $ch) or $err = 1;
     234                # copy the rest of the file
     235                while ($raf->Read($buff, 65536)) {
     236                    Write($outfile, $buff) or $err = 1;
     237                }
     238            }
     239            $rtnVal = 1;
     240            last;
     241        }
     242        # get extension block type/size
     243        last unless $raf->Read($s, 2) == 2;
     244        # get marker and block size
     245        ($a,$length) = unpack("C"x2, $s);
     246
     247        if ($a == 0xfe) {                           # comment extension
     248
     249            if ($setComment) {
     250                ++$exifTool->{CHANGED}; # increment the changed flag
    164251            } else {
    165252                Write($outfile, $ch, $s) or $err = 1 if $outfile;
    166                 # skip the block
    167                 while ($length) {
    168                     last unless $raf->Read($buff, $length) == $length;
    169                     Write($outfile, $buff) or $err = 1 if $outfile;
    170                     last unless $raf->Read($ch, 1);  # read next block header
    171                     Write($outfile, $ch) or $err = 1 if $outfile;
    172                     $length = ord($ch);  # get next block size
    173                 }
    174             }
    175         }
     253            }
     254            while ($length) {
     255                last unless $raf->Read($buff, $length) == $length;
     256                if ($verbose > 2 and not $outfile) {
     257                    Image::ExifTool::HexDump(\$buff, undef, Out => $out);
     258                }
     259                # add buffer to comment string
     260                $comment = defined $comment ? $comment . $buff : $buff;
     261                last unless $raf->Read($ch, 1);  # read next block header
     262                $length = ord($ch);  # get next block size
     263
     264                # write or delete comment
     265                next unless $outfile;
     266                if ($setComment) {
     267                    $verbose and print $out "  - Comment = $buff\n";
     268                } else {
     269                    Write($outfile, $buff, $ch) or $err = 1;
     270                }
     271            }
     272            last if $length;    # was a read error if length isn't zero
     273            unless ($outfile) {
     274                $rtnVal = 1;
     275                $exifTool->FoundTag('Comment', $comment) if $comment;
     276                undef $comment;
     277                # assume no more than one comment in FastScan mode
     278                last if $exifTool->Options('FastScan');
     279            }
     280            next;
     281
     282        } elsif ($a == 0xff and $length == 0x0b) {  # application extension
     283
     284            last unless $raf->Read($buff, $length) == $length;
     285            if ($verbose) {
     286                my @a = unpack('a8a3', $buff);
     287                s/\0.*//s foreach @a;
     288                print $out "Application Extension: @a\n";
     289            }
     290            if ($buff eq 'XMP DataXMP') {   # XMP data (ref 2)
     291                my $hdr = "$ch$s$buff";
     292                # read XMP data
     293                my $xmp = '';
     294                for (;;) {
     295                    $raf->Read($ch, 1) or last Block;   # read next block header
     296                    $length = ord($ch) or last;         # get next block size
     297                    $raf->Read($buff, $length) == $length or last Block;
     298                    $xmp .= $ch . $buff;
     299                }
     300                # get length of XMP without landing zone data
     301                # (note that LZ data may not be exactly the same as what we use)
     302                my $xmpLen;
     303                if ($xmp =~ /<\?xpacket end=['"][wr]['"]\?>/g) {
     304                    $xmpLen = pos($xmp);
     305                } else {
     306                    $xmpLen = length($xmp);
     307                }
     308                my %dirInfo = (
     309                    DataPt  => \$xmp,
     310                    DataLen => length $xmp,
     311                    DirLen  => $xmpLen,
     312                    Parent  => 'GIF',
     313                );
     314                my $xmpTable = GetTagTable('Image::ExifTool::XMP::Main');
     315                if ($outfile) {
     316                    if ($doneDir{XMP} and $doneDir{XMP} > 1) {
     317                        $exifTool->Warn('Duplicate XMP block created');
     318                    }
     319                    my $newXMP = $exifTool->WriteDirectory(\%dirInfo, $xmpTable);
     320                    if (not defined $newXMP) {
     321                        Write($outfile, $hdr, $xmp) or $err = 1;  # write original XMP
     322                        $doneDir{XMP} = 1;
     323                    } elsif (length $newXMP) {
     324                        if ($newXMP =~ /\0/) { # (check just to be safe)
     325                            $exifTool->Error('XMP contained NULL character');
     326                        } else {
     327                            # write new XMP and landing zone
     328                            my $lz = pack('C*',1,reverse(0..255),0);
     329                            Write($outfile, $hdr, $newXMP, $lz) or $err = 1;
     330                        }
     331                        $doneDir{XMP} = 1;
     332                    } # else we are deleting the XMP
     333                } else {
     334                    $exifTool->ProcessDirectory(\%dirInfo, $xmpTable);
     335                }
     336                next;
     337            } elsif ($buff eq 'NETSCAPE2.0') {      # animated GIF extension (ref 3)
     338                $raf->Read($buff, 5) == 5 or last;
     339                # make sure this contains the expected data
     340                if ($buff =~ /^\x03\x01(..)\0$/) {
     341                    $exifTool->HandleTag($tagTablePtr, 'AnimationExtension', $buff);
     342                }
     343                $raf->Seek(-$length-5, 1) or last;  # seek back to start of block
     344            } else {
     345                $raf->Seek(-$length, 1) or last;
     346            }
     347
     348        } elsif ($a == 0xf9 and $length == 4) {     # graphic control extension
     349
     350            last unless $raf->Read($buff, $length) == $length;
     351            # sum the indivual delay times
     352            my $delay = Get16u(\$buff, 1);
     353            $delayTime += $delay;
     354            $verbose and printf $out "Graphic Control: delay=%.2f\n", $delay / 100;
     355            $raf->Seek(-$length, 1) or last;
     356
     357        } elsif ($a == 0x01 and $length == 12) {    # plain text extension
     358
     359            last unless $raf->Read($buff, $length) == $length;
     360            Write($outfile, $ch, $s, $buff) or $err = 1 if $outfile;
     361            if ($verbose) {
     362                my ($left, $top, $w, $h) = unpack('v4', $buff);
     363                print $out "Text: left=$left top=$top width=$w height=$h\n";
     364            }
     365            my $text = '';
     366            for (;;) {
     367                last unless $raf->Read($ch, 1);
     368                $length = ord($ch) or last;
     369                last unless $raf->Read($buff, $length) == $length;
     370                Write($outfile, $ch, $buff) or $err = 1 if $outfile; # write block
     371                $text .= $buff;
     372            }
     373            Write($outfile, "\0") or $err = 1 if $outfile;  # write terminator block
     374            $exifTool->HandleTag($tagTablePtr, 'Text', $text);
     375            next;
     376        }
     377        Write($outfile, $ch, $s) or $err = 1 if $outfile;
     378        # skip the block
     379        while ($length) {
     380            last unless $raf->Read($buff, $length) == $length;
     381            Write($outfile, $buff) or $err = 1 if $outfile;
     382            last unless $raf->Read($ch, 1);  # read next block header
     383            Write($outfile, $ch) or $err = 1 if $outfile;
     384            $length = ord($ch);  # get next block size
     385        }
     386    }
     387    unless ($outfile) {
     388        $exifTool->HandleTag($tagTablePtr, 'FrameCount', $frameCount) if $frameCount > 1;
     389        $exifTool->HandleTag($tagTablePtr, 'Duration', $delayTime/100) if $delayTime;
     390        # for historical reasons, the GIF Comment tag is in the Extra table
    176391        $exifTool->FoundTag('Comment', $comment) if $comment;
    177392    }
     393
    178394    # set return value to -1 if we only had a write error
    179395    $rtnVal = -1 if $rtnVal and $err;
     
    197413
    198414This module contains definitions required by Image::ExifTool to read and
    199 write GIF meta information.  GIF87a images contain no meta information, and
    200 only the Comment tag is currently supported in GIF89a images.
     415write GIF meta information.
    201416
    202417=head1 AUTHOR
    203418
    204 Copyright 2003-2007, Phil Harvey (phil at owl.phy.queensu.ca)
     419Copyright 2003-2011, Phil Harvey (phil at owl.phy.queensu.ca)
    205420
    206421This library is free software; you can redistribute it and/or modify it
     
    213428=item L<http://www.w3.org/Graphics/GIF/spec-gif89a.txt>
    214429
     430=item L<http://www.adobe.com/devnet/xmp/>
     431
     432=item L<http://graphcomp.com/info/specs/ani_gif.html>
     433
    215434=back
    216435
Note: See TracChangeset for help on using the changeset viewer.