File Coverage

blib/lib/Data/FormValidator/Filters/Image.pm
Criterion Covered Total %
statement 7 9 77.7
branch n/a
condition n/a
subroutine 3 3 100.0
pod n/a
total 10 12 83.3


line stmt bran cond sub pod time code
1             package Data::FormValidator::Filters::Image;
2              
3 1     1   174271 use strict;
  1         2  
  1         57  
4              
5 1     1   7 use File::Basename;
  1         2  
  1         131  
6 1     1   5791 use Image::Magick;
  0            
  0            
7             use IO::File;
8             use MIME::Types;
9              
10             =pod
11              
12             =head1 NAME
13              
14             Data::FormValidator::Filters::Image - Filter that allows you to shrink incoming image uploads using Data::FormValidator
15              
16             =head1 SYNOPSIS
17              
18             use Data::FormValidator::Filters::Image qw( image_filter );
19              
20             # Build a Data::FormValidator Profile:
21             my $my_profile = {
22             required => qw( uploaded_image ),
23             field_filters => {
24             uploaded_image => image_filter(max_width => 800, max_height => 600),
25             },
26             };
27              
28             # Be sure to use a CGI.pm object as the form input
29             # when using this filter
30             my $q = new CGI;
31             my $dfv = Data::FormValidator->check($q,$my_profile);
32              
33             =head1 DESCRIPTION
34              
35             Many users when uploading image files never bother to shrink them down to a reasonable size.
36             Instead of declining the upload because it is too large, this module will shrink the image
37             down to a reasonable size during the form validation stage.
38              
39             The filter will try to fail gracefully by leaving the upload as is if the image resize
40             operation fails.
41              
42             =cut
43              
44             use vars qw( $VERSION @ISA @EXPORT @EXPORT_OK );
45              
46             BEGIN {
47             require Exporter;
48              
49             $VERSION = '0.40';
50              
51             @ISA = qw( Exporter );
52              
53             @EXPORT = ();
54             @EXPORT_OK = qw( image_filter );
55             }
56              
57             =pod
58              
59             =head1 FILTERS
60              
61              
62             =head2 image_filter( max_width => $width, max_height => $height )
63              
64             This will create a filter that will reduce the size of an image that is
65             being uploaded so that it is bounded by the width and height provided.
66             The image will be scaled in a way that will not distort or stretch
67             the image.
68              
69             example:
70              
71             - upload an image that is 800 x 600
72             - specify a max width of 100 and max height of 100
73              
74             The resulting image will be 100 x 75, since that is the
75             largest scaled image we can create that is still within
76             the bounds we specified.
77              
78             =cut
79              
80             sub image_filter {
81             my %options = @_;
82             my $max_width = delete $options{max_width};
83             my $max_height = delete $options{max_height};
84              
85             return
86             sub { return __shrink_image( shift, $max_width, $max_height, %options ) };
87             }
88              
89             sub __shrink_image {
90             my $fh = shift;
91             my $max_width = shift;
92             my $max_height = shift;
93             my @the_rest = @_;
94              
95             # if we weren't given *any* options, there's no point filtering; we're not
96             # going to resize the image.
97             if ((!defined $max_width) && (!defined $max_height) && !@the_rest) {
98             return $fh;
99             }
100              
101             return $fh unless $fh && ref $fh eq 'Fh';
102             my $filename = $fh->asString;
103             $filename =~ s/^.*[\/\\]//; # strip off any path information that IE puts in the filename
104             binmode $fh;
105              
106             my ($result, $image);
107             eval {
108             # turn the Fh from CGI.pm back into a regular Perl filehandle, then
109             # let ImageMagick read the image from _that_ fh.
110             my $fh_copy = IO::File->new_from_fd(fileno($fh), 'r');
111             $image = Image::Magick->new;
112             $result = $image->Read( file => $fh_copy );
113             };
114             if ($@) {
115             #warn "Uploaded file was not an image: $@";
116             seek( $fh, 0, 0 );
117             return $fh;
118             }
119             if ("$result") { # quotes are there as per the Image::Magick examples
120             #warn "$result";
121             seek( $fh, 0, 0 );
122             return $fh;
123             }
124              
125             my ( $nw, $nh ) = my ( $ow, $oh ) = $image->Get( 'width', 'height' );
126              
127             unless ( $ow && $oh ) {
128             #warn "Image has no width or height";
129             seek( $fh, 0, 0 );
130             return $fh;
131             }
132              
133             if ( $max_width && $nw > $max_width ) {
134             $nw = $max_width;
135             $nh = $oh * ( $max_width / $ow );
136             }
137             if ( $max_height && $nh > $max_height ) {
138             $nh = $max_height;
139             $nw = $ow * ( $max_height / $oh );
140             }
141              
142             if (($oh <= $max_height) && ($ow <= $max_width)) {
143             #warn "Image does not need to be resized";
144             seek( $fh, 0, 0 );
145             return $fh;
146             }
147              
148             $result = $image->Resize( width => $nw, height => $nh, @the_rest );
149             if ("$result") { # quotes are there as per the Image::Magick examples
150             #warn "$result";
151             seek( $fh, 0, 0 );
152             return $fh;
153             }
154              
155             #########################
156             # Create a file handle object to simulate a CGI.pm upload
157             # Pulled directly from CGI.pm by Lincoln Stein
158             my $tmp_filename;
159             my $seqno = unpack( "%16C*", join( '', localtime, values %ENV ) );
160             $seqno += int rand(100);
161             my $newfh;
162             for ( my $cnt = 10 ; $cnt > 0 ; $cnt-- ) {
163             next unless my $tmpfile = new CGITempFile($seqno);
164             $tmp_filename = $tmpfile->as_string;
165             last
166             if defined( $newfh = Fh->new( $filename, $tmp_filename, 0 ) );
167             $seqno += int rand(100);
168             }
169             die "CGI open of tmpfile: $!\n" unless defined $newfh;
170             $CGI::DefaultClass->binmode($newfh)
171             if $CGI::needs_binmode
172             && defined fileno($newfh);
173             #########################
174              
175             $image->Write( file => $newfh, filename => $filename );
176             if ("$result") { # quotes are there as per the Image::Magick examples
177             #warn "$result";
178             seek( $fh, 0, 0 );
179             return $fh;
180             }
181              
182             # rewind both filehandles before we return
183             seek( $newfh, 0, 0 );
184             seek( $fh, 0, 0 );
185             return $newfh;
186             }
187              
188             1;
189              
190             __END__