root/branches/athena/lib/MT/Image.pm @ 1092

Revision 1092, 8.0 kB (checked in by hachi, 2 years ago)

Merging release-15 to athena branch. svn merge -r59987:60375 http://svn.sixapart.com/repos/eng/movabletype/branches/release-15 .

  • Property svn:keywords set to Author Date Id Revision
Line 
1# Copyright 2001-2007 Six Apart. This code cannot be redistributed without
2# permission from www.sixapart.com.  For more information, consult your
3# Movable Type license.
4#
5# $Id$
6
7package MT::Image;
8
9use strict;
10use MT;
11use base qw( MT::ErrorHandler );
12
13sub new {
14    my $class = shift;
15    $class .= "::" . MT->config->ImageDriver;
16    my $image = bless {}, $class;
17    $image->load_driver
18        or return $class->error( $image->errstr );
19    if (@_) {
20        $image->init(@_)
21            or return $class->error( $image->errstr );
22    }
23    $image;
24}
25
26sub get_dimensions {
27    my $image = shift;
28    my %param = @_;
29    my($w, $h) = ($image->{width}, $image->{height});
30    if (my $pct = $param{Scale}) {
31        ($w, $h) = (int($w * $pct / 100), int($h * $pct / 100));
32    } else {
33        if ($param{Width} && $param{Height}) {
34            ($w, $h) = ($param{Width}, $param{Height});
35        } else {
36            my $x = $param{Width} || $w;
37            my $y = $param{Height} || $h;
38            my $w_pct = $x / $w;
39            my $h_pct = $y / $h;
40            my $pct = $param{Width} ? $w_pct : $h_pct;
41            ($w, $h) = (int($w * $pct), int($h * $pct));
42        }
43    }
44    ($w, $h);
45}
46
47package MT::Image::ImageMagick;
48@MT::Image::ImageMagick::ISA = qw( MT::Image );
49
50sub load_driver {
51    my $image = shift;
52    eval { require Image::Magick };
53    if (my $err = $@) {
54        return $image->error(MT->translate("Can't load Image::Magick: [_1]", $err));
55    }
56    1;
57}
58
59sub init {
60    my $image = shift;
61    my %param = @_;
62    my %arg = ();
63    if (my $type = $param{Type}) {
64        %arg = (magick => lc($type));
65    } elsif (my $file = $param{Filename}) {
66        (my $ext = $file) =~ s/.*\.//;
67        %arg = (magick => lc($ext));
68    }
69    my $magick = $image->{magick} = Image::Magick->new(%arg);
70    if (my $file = $param{Filename}) {
71        my $x = $magick->Read($file);
72        return $image->error(MT->translate(
73            "Reading file '[_1]' failed: [_2]", $file, $x)) if $x;
74        ($image->{width}, $image->{height}) = $magick->Get('width', 'height');
75    } elsif ($param{Data}) {
76        my $x = $magick->BlobToImage($param{Data});
77        return $image->error(MT->translate(
78            "Reading image failed: [_1]", $x)) if $x;
79        ($image->{width}, $image->{height}) = $magick->Get('width', 'height');
80    }
81    $image;
82}
83
84sub scale {
85    my $image = shift;
86    my($w, $h) = $image->get_dimensions(@_);
87    my $magick = $image->{magick};
88    my $err = $magick->can('Resize') ?
89              $magick->Resize(width => $w, height => $h) :
90              $magick->Scale(width => $w, height => $h);
91    return $image->error(MT->translate(
92        "Scaling to [_1]x[_2] failed: [_3]", $w, $h, $err)) if $err;
93    $magick->Profile("*") if $magick->can('Profile');
94    wantarray ? ($magick->ImageToBlob, $w, $h) : $magick->ImageToBlob;
95}
96
97package MT::Image::NetPBM;
98@MT::Image::NetPBM::ISA = qw( MT::Image );
99
100sub load_driver {
101    my $image = shift;
102    eval { require IPC::Run };
103    if (my $err = $@) {
104        return $image->error(MT->translate("Can't load IPC::Run: [_1]", $err));
105    }
106    my $pbm = $image->_find_pbm or return;
107    1;
108}
109
110sub init {
111    my $image = shift;
112    my %param = @_;
113    if (my $file = $param{Filename}) {
114        $image->{file} = $file;
115        if (!defined $param{Type}) {
116            (my $ext = $file) =~ s/.*\.//;
117            $param{Type} = uc $ext;
118        }
119    } elsif (my $blob = $param{Data}) {
120        $image->{data} = $blob;
121    }
122    my %Types = (jpg => 'jpeg', gif => 'gif', 'png' => 'png');
123    my $type = $image->{type} = $Types{ lc $param{Type} };
124    my($out, $err);
125    my $pbm = $image->_find_pbm or return;
126    my @in = ("$pbm${type}topnm", ($image->{file} ? $image->{file} : ()));
127    my @out = ("${pbm}pnmfile", '-allimages');
128    IPC::Run::run(\@in, '<', ($image->{file} ? \undef : \$image->{data}), '|',
129        \@out, \$out, \$err)
130        or return $image->error(MT->translate(
131            "Reading image failed: [_1]", $err));
132    ($image->{width}, $image->{height}) = $out =~ /(\d+)\s+by\s+(\d+)/;
133    $image;
134}
135
136sub scale {
137    my $image = shift;
138    my($w, $h) = $image->get_dimensions(@_);
139    my $type = $image->{type};
140    my($out, $err);
141    my $pbm = $image->_find_pbm or return;
142    my @in = ("$pbm${type}topnm", ($image->{file} ? $image->{file} : ()));
143    my @scale = ("${pbm}pnmscale", '-width', $w, '-height', $h);
144    my @out;
145    for my $try (qw( ppm pnm )) {
146        my $prog = "${pbm}${try}to$type";
147        @out = ($prog), last if -x $prog;
148    }
149    my(@quant);
150    if ($type eq 'gif') {
151        push @quant, ([ "${pbm}ppmquant", 256 ], '|');
152    }
153    IPC::Run::run(\@in, '<', ($image->{file} ? \undef : \$image->{data}), '|',
154        \@scale, '|',
155        @quant,
156        \@out, \$out, \$err)
157        or return $image->error(MT->translate(
158            "Scaling to [_1]x[_2] failed: [_3]", $w, $h, $err));
159    wantarray ? ($out, $w, $h) : $out;
160}
161
162sub _find_pbm {
163    my $image = shift;
164    return $image->{__pbm_path} if $image->{__pbm_path};
165    my @NetPBM = qw( /usr/local/netpbm/bin /usr/local/bin /usr/bin );
166    my $pbm;
167    for my $path (MT->config->NetPBMPath, @NetPBM) {
168        next unless $path;
169        $path .= '/' unless $path =~ m!/$!;
170        $pbm = $path, last if -x "${path}pnmscale";
171    }
172    return $image->error(MT->translate(
173        "You do not have a valid path to the NetPBM tools on your machine."))
174        unless $pbm;
175    $image->{__pbm_path} = $pbm;
176}
177
1781;
179__END__
180
181=head1 NAME
182
183MT::Image - Movable Type image manipulation routines
184
185=head1 SYNOPSIS
186
187    use MT::Image;
188    my $img = MT::Image->new( Filename => '/path/to/image.jpg' );
189    my($blob, $w, $h) = $img->scale( Width => 100 );
190
191    open FH, ">thumb.jpg" or die $!;
192    binmode FH;
193    print FH $blob;
194    close FH;
195
196=head1 DESCRIPTION
197
198I<MT::Image> contains image manipulation routines using either the
199I<NetPBM> tools or the I<ImageMagick> and I<Image::Magick> Perl module.
200The backend framework used (NetPBM or ImageMagick) depends on the value of
201the I<ImageDriver> setting in the F<mt.cfg> file (or, correspondingly, set
202on an instance of the I<MT::ConfigMgr> class).
203
204Currently all this is used for is to create thumbnails from uploaded images.
205
206=head1 USAGE
207
208=head2 MT::Image->new(%arg)
209
210Constructs a new I<MT::Image> object. Returns the new object on success; on
211error, returns C<undef>, and the error message is in C<MT::Image-E<gt>errstr>.
212
213I<%arg> can contain:
214
215=over 4
216
217=item * Filename
218
219The path to an image to load.
220
221=item * Data
222
223The actual contents of an image, already loaded from a file, a database,
224etc.
225
226=item * Type
227
228The image format of the data in I<Data>. This should be either I<JPG> or
229I<GIF>.
230
231=back
232
233=head2 $img->scale(%arg)
234
235Creates a thumbnail from the image represented by I<$img>; on success, returns
236a list containing the binary contents of the thumbnail image, the width of the
237scaled image, and the height of the scaled image. On error, returns C<undef>,
238and the error message is in C<$img-E<gt>errstr>.
239
240I<%arg> can contain:
241
242=over 4
243
244=item * Width
245
246=item * Height
247
248The width and height of the final image, respectively. If you provide only one
249of these arguments, the other dimension will be scaled appropriately. If you
250provide neither, the image will be scaled to C<100%> of the original (that is,
251the same size). If you provide both, the image will likely look rather
252distorted.
253
254=item * Scale
255
256To be used instead of I<Width> and I<Height>; the value should be a percentage
257(ie C<100> to return the original image without resizing) by which both the
258width and height will be scaled equally.
259
260=back
261
262=head2 $img->get_dimensions(%arg)
263
264This utility method returns a width and height value pair after applying
265the given arguments. Valid arguments are the same as the L<scale> method.
266If 'Width' is given, a proportionate height will be calculated. If a
267'Height' is given, the width will be calculated. If 'Scale' is given
268the height and width will be calculated based on that scale (a value
269between 1 to 100).
270
271=head1 AUTHOR & COPYRIGHT
272
273Please see the I<MT> manpage for author, copyright, and license information.
274
275=cut
Note: See TracBrowser for help on using the browser.