From: Tony Cook Date: Thu, 23 Mar 2006 14:09:57 +0000 (+0000) Subject: - added support for registering file readers X-Git-Tag: Imager-0.51_01~27 X-Git-Url: http://git.imager.perl.org/imager.git/commitdiff_plain/53a6bbd48b0530c5e3f013ddd242db8960b79b80 - added support for registering file readers - if you supply and unknown type value (or Imager probes and finds one) then Imager will attempt to load "Imager::File::\Utypecode" (this can register a file reader.) - note: these changes may mean slightly different errors if you call read_multi() with no file or type parameters, since read_multi() now attempts to do the same file format probe that read() does. --- diff --git a/Imager.pm b/Imager.pm index ad669f5a..ecb6d6f9 100644 --- a/Imager.pm +++ b/Imager.pm @@ -143,6 +143,12 @@ use Imager::Font; unload_plugin )]); +# registered file readers +my %readers; + +# modules we attempted to autoload +my %attempted_to_load; + BEGIN { require Exporter; @ISA = qw(Exporter); @@ -1203,6 +1209,12 @@ sub read { return undef; } + _reader_autoload($input{type}); + + if ($readers{$input{type}} && $readers{$input{type}}{single}) { + return $readers{$input{type}}{single}->($self, $IO, %input); + } + unless ($formats{$input{'type'}}) { $self->_set_error("format '$input{'type'}' not supported"); return; @@ -1336,6 +1348,46 @@ sub read { return $self; } +sub register_reader { + my ($class, %opts) = @_; + + defined $opts{type} + or die "register_reader called with no type parameter\n"; + + my $type = $opts{type}; + + defined $opts{single} || defined $opts{multiple} + or die "register_reader called with no single or multiple parameter\n"; + + $readers{$type} = { }; + if ($opts{single}) { + $readers{$type}{single} = $opts{single}; + } + if ($opts{multiple}) { + $readers{$type}{multiple} = $opts{multiple}; + } + + return 1; +} + +# probes for an Imager::File::whatever module +sub _reader_autoload { + my $type = shift; + + return if $formats{$type} || $readers{$type}; + + return unless $type =~ /^\w+$/; + + my $file = "Imager/File/\U$type\E.pm"; + + unless ($attempted_to_load{$file}) { + eval { + ++$attempted_to_load{$file}; + require $file; + }; + } +} + sub _fix_gif_positions { my ($opts, $opt, $msg, @imgs) = @_; @@ -1613,19 +1665,31 @@ sub write_multi { sub read_multi { my ($class, %opts) = @_; - if ($opts{file} && !exists $opts{'type'}) { + my ($IO, $file) = $class->_get_reader_io(\%opts, $opts{'type'}) + or return; + + my $type = $opts{'type'}; + unless ($type) { + $type = i_test_format_probe($IO, -1); + } + + if ($opts{file} && !$type) { # guess the type - my $type = $FORMATGUESS->($opts{file}); - $opts{'type'} = $type; + $type = $FORMATGUESS->($opts{file}); } - unless ($opts{'type'}) { + + unless ($type) { $ERRSTR = "No type parameter supplied and it couldn't be guessed"; return; } - my ($IO, $file) = $class->_get_reader_io(\%opts, $opts{'type'}) - or return; - if ($opts{'type'} eq 'gif') { + _reader_autoload($type); + + if ($readers{$type} && $readers{$type}{multiple}) { + return $readers{$type}{multiple}->($IO, %opts); + } + + if ($type eq 'gif') { my @imgs; @imgs = i_readgif_multi_wiol($IO); if (@imgs) { @@ -1638,7 +1702,7 @@ sub read_multi { return; } } - elsif ($opts{'type'} eq 'tiff') { + elsif ($type eq 'tiff') { my @imgs = i_readtiff_multi_wiol($IO, -1); if (@imgs) { return map { diff --git a/Imager.xs b/Imager.xs index b137cc19..4777a7ca 100644 --- a/Imager.xs +++ b/Imager.xs @@ -2162,7 +2162,7 @@ i_exif_enabled() #endif -char * +const char * i_test_format_probe(ig, length) Imager::IO ig int length diff --git a/lib/Imager/Files.pod b/lib/Imager/Files.pod index 2f1fc970..927680c7 100644 --- a/lib/Imager/Files.pod +++ b/lib/Imager/Files.pod @@ -778,6 +778,115 @@ values for the first line and so on, you can use the interleave option: There are no PNG specific tags. +=head1 ADDING NEW FORMATS + +To support a new format for reading, call the register_reader() class +method: + +=over + +=item register_reader + +Registers single or multiple image read functions. + +Parameters: + +=over + +=item * + +type - the identifier of the file format, if Imager's +i_test_format_probe() can identify the format then this value should +match i_test_format_probe()'s result. + +This parameter is required. + +=item * + +single - a code ref to read a single image from a file. This is +supplied: + +=over + +=item * + +the object that read() was called on, + +=item * + +an Imager::IO object that should be used to read the file, and + +=item * + +all the parameters supplied to the read() method. + +=back + +The single parameter is required. + +=item * + +multiple - a code ref which is called to read multiple images from a +file. This is supplied: + +=over + +=item * + +an Imager::IO object that should be used to read the file, and + +=item * + +all the parameters supplied to the read_multi() method. + +=back + +=back + +Example: + + # from Imager::File::ICO + Imager->register_reader + ( + type=>'ico', + single => + sub { + my ($im, $io, %hsh) = @_; + $im->{IMG} = i_readico_single($io, $hsh{page} || 0); + + unless ($im->{IMG}) { + $im->_set_error(Imager->_error_as_msg); + return; + } + return $im; + }, + multiple => + sub { + my ($io, %hsh) = @_; + + my @imgs = i_readico_multi($io); + unless (@imgs) { + Imager->_set_error(Imager->_error_as_msg); + return; + } + return map { + bless { IMG => $_, DEBUG => $Imager::DEBUG, ERRSTR => undef }, 'Imager' + } @imgs; + }, + ); + +=back + +If you name the reader module CI +where I is a fully upper case version of the type +value you would pass to read() or read_multi() then Imager will +attempt to load that module if it has no other way to read that +format. + +For example, if you create a module Imager::File::GIF and the user has +built Imager without it's normal GIF support then an attempt to read a +GIF image will attempt to load Imager::File::GIF. + =head1 EXAMPLES =head2 Producing an image from a CGI script diff --git a/t/t70newgif.t b/t/t70newgif.t index fef823d3..6b989da1 100644 --- a/t/t70newgif.t +++ b/t/t70newgif.t @@ -79,7 +79,7 @@ if (i_has_format("gif")) { my @imgs = Imager->read_multi(); @imgs and print "not "; print "ok 9\n"; - Imager->errstr =~ /type parameter/ or print "not "; + Imager->errstr =~ /callback parameter missing/ or print "not "; print "ok 10 # ",Imager->errstr,"\n"; @imgs = Imager->read_multi(type=>'gif');