File Coverage

blib/lib/Catalyst/View/Thumbnail.pm
Criterion Covered Total %
statement 18 60 30.0
branch 0 24 0.0
condition 0 12 0.0
subroutine 6 8 75.0
pod 1 2 50.0
total 25 106 23.5


line stmt bran cond sub pod time code
1             package Catalyst::View::Thumbnail;
2              
3 1     1   19851 use warnings;
  1         3  
  1         31  
4 1     1   4 use strict;
  1         2  
  1         22  
5 1     1   662 use parent 'Catalyst::View';
  1         339  
  1         5  
6 1     1   635688 use Image::Info qw/image_info/;
  1         1614  
  1         68  
7 1     1   1405 use Imager;
  1         36185  
  1         8  
8 1     1   60 use List::Util qw/min max/;
  1         2  
  1         714  
9              
10             =head1 NAME
11              
12             Catalyst::View::Thumbnail - Catalyst view to resize images for thumbnails
13              
14             =cut
15              
16             our $VERSION = '0.03';
17              
18             sub process {
19 0     0 1   my ($self, $c) = @_;
20              
21 0           my $image = $self->render_image($c);
22            
23             # render_image() will return an Imager object on success,
24             # or an error message on failure.
25            
26 0 0         if (UNIVERSAL::isa($image, 'Imager')) {
27 0   0       my $mime_type = $c->stash->{image_type} || image_info(\$c->stash->{image})->{file_media_type};
28 0           (my $file_type = $mime_type) =~ s!^image/!!;
29              
30 0           my $thumbnail;
31 0           $image->write(
32             data => \$thumbnail,
33             type => $file_type,
34             );
35            
36 0           $c->response->content_type($mime_type);
37 0           $c->response->body($thumbnail);
38             } else {
39 0           my $error = qq/Couldn't render image: $image/;
40 0           $c->log->error($error);
41 0           $c->error($error);
42 0           return 0;
43             }
44             }
45              
46              
47             sub render_image {
48 0     0 0   my ($self, $c) = @_;
49            
50 0 0         return "Image data missing from stash" unless $c->stash->{image};
51            
52 0           my $image = Imager->new();
53 0 0         $image->read(data => $c->stash->{image}) or return $image->errstr;
54            
55 0 0         if ($c->stash->{zoom}) {
56             $image = $image->crop(
57             width => $image->getwidth * ($c->stash->{zoom} / 100),
58 0 0         height => $image->getheight * ($c->stash->{zoom} / 100),
59             ) or return $image->errstr;
60             }
61            
62 0 0 0       if ($c->stash->{x} or $c->stash->{y}) {
63 0           $c->log->debug('Creating thumbnail image');
64 0           my $source_aspect = $image->getwidth / $image->getheight;
65 0           $c->log->debug('Source width: ' . $image->getwidth . ' height: ' . $image->getheight . ' aspect ratio: ' . $source_aspect);
66 0   0       $c->stash->{x} ||= $c->stash->{y} * $source_aspect;
67 0   0       $c->stash->{y} ||= $c->stash->{x} / $source_aspect;
68              
69 0           $c->log->debug('Target width: ' . $c->stash->{x} . ' height: ' . $c->stash->{y});
70            
71 0 0         unless ($c->stash->{scaling} eq 'fit') {
72 0           my $thumbnail_aspect = $c->stash->{x} / $c->stash->{y};
73 0           $c->log->debug('Thumbnail aspect ratio: ' . $thumbnail_aspect);
74            
75 0 0         if ($source_aspect > $thumbnail_aspect) {
76 0           $c->log->debug('Cropping image to fit aspect ratio of thumbnail');
77 0           $c->log->debug('Source aspect > thumbnail aspect');
78 0           $c->log->debug('Cropping to width: '.$image->getheight * $thumbnail_aspect.' x height: '.$image->getheight);
79 0 0         $image = $image->crop(
80             width => $image->getheight * $thumbnail_aspect,
81             height => $image->getheight,
82             ) or return $image->errstr;
83             }
84            
85 0 0         if ($source_aspect < $thumbnail_aspect) {
86 0           $c->log->debug('Cropping image to fit aspect ratio of thumbnail');
87 0           $c->log->debug('Source aspect < thumbnail aspect');
88 0           $c->log->debug('Cropping to width: '.$image->getwidth.' x height: '.$image->getwidth / $thumbnail_aspect);
89 0 0         $image = $image->crop(
90             width => $image->getwidth,
91             height => $image->getwidth / $thumbnail_aspect,
92             ) or return $image->errstr;
93             }
94             }
95            
96 0           $c->log->debug('Scaling image to thumbnail');
97             $image = $image->scale(
98             xpixels => $c->stash->{x},
99             ypixels => $c->stash->{y},
100 0 0         type => 'min',
101             qtype => 'mixing',
102             ) or return $image->errstr;
103             }
104              
105 0           return $image;
106             }
107              
108             =head1 SYNOPSIS
109              
110             Create a thumbnail view:
111              
112             script/myapp_create view Thumbnail Thumbnail
113              
114             Then in your controller:
115              
116             sub thumbnail :Local :Args(1) {
117             my ($self, $c, $image_id) = @_;
118            
119             my $image_obj = $c->model('MyApp::Images')->find({id=>$image_id})
120             or $c->detach('/default');
121            
122             $c->stash->{x} = 100; # Create a 100px square thumbnail
123             $c->stash->{y} = 100;
124             $c->stash->{image} = $image_obj->data;
125            
126             $c->forward('View::Thumbnail');
127             }
128              
129             =head1 DESCRIPTION
130              
131             Catalyst::View::Thumbnail resizes images to produce thumbnails, with options to set the desired x or y
132             dimensions (or both), and specify a zoom level and scaling type.
133              
134             =head2 Options
135              
136             The view is controlled by setting the following values in the stash:
137              
138             =over
139              
140             =item image
141              
142             Contains the raw data for the full-size source image.
143              
144             This is a mandatory option.
145              
146             =item x
147              
148             The width (in pixels) of the thumbnail.
149              
150             This is optional, but at least one of the C<x> or C<y> parameters must be set.
151              
152             =item y
153              
154             The height (in pixels) of the thumbnail.
155              
156             This is optional, but at least one of the C<x> or C<y> parameters must be set.
157              
158             =item zoom
159              
160             Zoom level, expressed as a number between 1 and 100.
161              
162             If the C<zoom> option is given, the thumbnail will be 'zoomed-in' by the
163             appropriate amount, e.g. a zoom level of 80 will create a thumbnail using the
164             middle 80% of the source image.
165              
166             This parameter is optional, if omitted then a zoom level of 100 will be used,
167             i.e. create thumbnails using 100% of the source image.
168              
169             =item scaling
170              
171             Scaling type, can be either 'fit' or 'fill'.
172              
173             If both the C<x> and C<y> parameters are set, the aspect ratio (x/y) of the
174             thumbnail image may not match the aspect ratio of the source image.
175              
176             To prevent the thumbnail from looking 'stretched', there is a choice of two
177             scaling options:
178              
179             =over
180              
181             =item fit
182              
183             Fits the thumbnail within the specified C<x> and C<y> dimensions, preserving
184             all of the source image.
185              
186             Note that by using this scaling method, the generated thumbnails may be smaller
187             than the the specified C<x> and C<y> dimensions.
188              
189             =item fill
190              
191             Fills the thumbnail to the exact C<x> and C<y> dimensions as specified, cropping
192             the source image as necessary.
193              
194             =back
195              
196             This parameter is optional, and will default to 'fill' if omitted.
197              
198             =item image_type
199              
200             Mime type for the output image. This is normally the same as the input image.
201             If you set this the Imager library will produce an image of that format. This
202             is useful when you want to convert something like a tiff to a jpeg. Note
203             that the conversions can be strange so this may not be a good idea for all images.
204             See the C<Imager> documentation for more details.
205              
206             =back
207              
208             =head2 Image formats
209              
210             The generated thumbnails will always be produced in the same format (PNG, JPG, etc)
211             as the source image.
212              
213             Catalyst::View::Thumbnail uses the L<Imager> module to crop and resize images,
214             so will accept any image format supported by I<Imager>.
215              
216             Please see the L<Imager> documentation for more details and installation notes.
217              
218             =head1 SEE ALSO
219              
220             Catalyst::View::Thumbnail tutorial (with examples): L<http://perl.jonallen.info/writing/articles/creating-thumbnails-with-catalyst>
221              
222             =head1 AUTHOR
223              
224             Jon Allen (JJ), C<< <jj@jonallen.info> >>
225              
226             =head1 BUGS
227              
228             Please report any bugs or feature requests to C<bug-catalyst-view-thumbnail at rt.cpan.org>, or through
229             the web interface at L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Catalyst-View-Thumbnail>. I will be notified, and then you'll
230             automatically be notified of progress on your bug as I make changes.
231              
232             =head1 SUPPORT
233              
234             Commercial support, customisation, and training for this module is available
235             from Penny's Arcade Limited - contact L<info@pennysarcade.co.uk> for details.
236              
237             You can also look for information at:
238              
239             =over 4
240              
241             =item * RT: CPAN's request tracker
242              
243             L<http://rt.cpan.org/NoAuth/Bugs.html?Dist=Catalyst-View-Thumbnail>
244              
245             =item * AnnoCPAN: Annotated CPAN documentation
246              
247             L<http://annocpan.org/dist/Catalyst-View-Thumbnail>
248              
249             =item * CPAN Ratings
250              
251             L<http://cpanratings.perl.org/d/Catalyst-View-Thumbnail>
252              
253             =item * Search CPAN
254              
255             L<http://search.cpan.org/dist/Catalyst-View-Thumbnail/>
256              
257             =back
258              
259             =head1 COPYRIGHT & LICENSE
260              
261             Copyright (C) 2009 Jon Allen (JJ).
262              
263             This module is free software; you
264             can redistribute it and/or modify it under the same terms
265             as Perl 5.10.0. For more details, see the full text of the
266             licenses in the directory LICENSES.
267              
268             This module is distributed in the hope that it will be
269             useful, but it is provided "as is" and without any express
270             or implied warranties.
271              
272             =cut
273              
274             1; # End of Catalyst::View::Thumbnail