]> git.imager.perl.org - imager.git/commitdiff
- added support for registering file readers
authorTony Cook <tony@develop=help.com>
Thu, 23 Mar 2006 14:09:57 +0000 (14:09 +0000)
committerTony Cook <tony@develop=help.com>
Thu, 23 Mar 2006 14:09:57 +0000 (14:09 +0000)
- 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.

Imager.pm
Imager.xs
lib/Imager/Files.pod
t/t70newgif.t

index ad669f5a6f243f14434559e6f71425d70c0a7061..ecb6d6f91b15a7d046ef2917377c419be0e62e04 100644 (file)
--- 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 { 
index b137cc19b57eee3e93db3c3e4fea41fe79348b12..4777a7ca15a1b99bfe132ace34ac20aac53b0116 100644 (file)
--- 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
index 2f1fc97035f4149875512499ff6fc2962f9c840f..927680c74e571748ab4ed193bb8d56eff9742062 100644 (file)
@@ -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 C<Imager::File::>I<your-format-name>
+where I<your-format-name> 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
index fef823d33d8b8b570be52db48c8cabca724ce9cb..6b989da17e509879e2c2d54825f61df0658a2f5a 100644 (file)
@@ -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');