#------------------------------------------------------------------------------
# File:         MIFF.pm
#
# Description:  Read MIFF (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 MIFF 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-2006, 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

