Bug 26145: Add the ability to upload a cover image per item
[koha.git] / C4 / Images.pm
blob811c144991c366be25a4b6b42ad34ea5ef0e3a7e
1 package C4::Images;
3 # Copyright (C) 2011 C & P Bibliography Services
4 # Jared Camins-Esakov <jcamins@cpbibliograpy.com>
6 # This file is part of Koha.
8 # Koha is free software; you can redistribute it and/or modify it
9 # under the terms of the GNU General Public License as published by
10 # the Free Software Foundation; either version 3 of the License, or
11 # (at your option) any later version.
13 # Koha is distributed in the hope that it will be useful, but
14 # WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 # GNU General Public License for more details.
18 # You should have received a copy of the GNU General Public License
19 # along with Koha; if not, see <http://www.gnu.org/licenses>.
21 use Modern::Perl;
23 use C4::Context;
24 use GD;
25 use Koha::Exceptions;
27 use vars qw($debug $noimage @ISA @EXPORT);
29 BEGIN {
31 require Exporter;
32 @ISA = qw(Exporter);
33 @EXPORT = qw(
34 &PutImage
35 &RetrieveImage
36 &ListImagesForBiblio
37 &DelImage
39 $debug = $ENV{KOHA_DEBUG} || $ENV{DEBUG} || 0;
41 $noimage = pack( "H*",
42 '47494638396101000100800000FFFFFF'
43 . '00000021F90401000000002C00000000'
44 . '010001000002024401003B' );
47 =head2 PutImage
49 PutImage({ biblionumber => $biblionumber, itemnumber => $itemnumber, src_image => $srcimage, replace => $replace });
51 Stores binary image data and thumbnail in database, optionally replacing existing images for the given biblio or item.
53 =cut
55 sub PutImage {
56 my ( $params ) = @_;
58 my $biblionumber = $params->{biblionumber};
59 my $itemnumber = $params->{itemnumber};
60 my $srcimage = $params->{src_image};
61 my $replace = $params->{replace};
63 Koha::Exceptions::WrongParameter->throw(
64 'PutImage cannot be called with both biblionumber and itemnumber')
65 if $biblionumber and $itemnumber;
67 Koha::Exceptions::WrongParameter->throw(
68 'PutImage must be called with "replace" if itemnumber is passed. Only 1 cover per item is allowed.')
69 if $itemnumber and not $replace;
72 return -1 unless defined($srcimage);
74 if ($biblionumber && $replace) {
75 foreach ( ListImagesForBiblio($biblionumber) ) {
76 DelImage($_);
80 my $dbh = C4::Context->dbh;
81 my $query =
82 "INSERT INTO biblioimages (biblionumber, itemnumber, mimetype, imagefile, thumbnail) VALUES (?,?,?,?,?);";
83 my $sth = $dbh->prepare($query);
85 my $mimetype = 'image/png'
86 ; # GD autodetects three basic image formats: PNG, JPEG, XPM; we will convert all to PNG which is lossless...
88 # Check the pixel size of the image we are about to import...
89 my $thumbnail = _scale_image( $srcimage, 140, 200 )
90 ; # MAX pixel dims are 140 X 200 for thumbnail...
91 my $fullsize = _scale_image( $srcimage, 600, 800 )
92 ; # MAX pixel dims are 600 X 800 for full-size image...
93 $debug and warn "thumbnail is " . length($thumbnail) . " bytes.";
95 $sth->execute( $biblionumber, $itemnumber, $mimetype, $fullsize->png(),
96 $thumbnail->png() );
97 my $dberror = $sth->errstr;
98 warn sprintf("Error returned inserting %s.%s.", ($biblionumber || $itemnumber, $mimetype)) if $sth->errstr;
99 undef $thumbnail;
100 undef $fullsize;
101 return $dberror;
104 =head2 RetrieveImage
105 my ($imagedata, $error) = RetrieveImage($imagenumber);
107 Retrieves the specified image.
109 =cut
111 sub RetrieveImage {
112 my ($imagenumber) = @_;
114 my $dbh = C4::Context->dbh;
115 my $query =
116 'SELECT imagenumber, mimetype, imagefile, thumbnail FROM biblioimages WHERE imagenumber = ?';
117 my $sth = $dbh->prepare($query);
118 $sth->execute($imagenumber);
119 my $imagedata = $sth->fetchrow_hashref;
120 if ( !$imagedata ) {
121 $imagedata->{'thumbnail'} = $noimage;
122 $imagedata->{'imagefile'} = $noimage;
124 if ( $sth->err ) {
125 warn "Database error!" if $debug;
127 return $imagedata;
130 =head2 ListImagesForBiblio
131 my (@images) = ListImagesForBiblio($biblionumber);
133 Gets a list of all images associated with a particular biblio.
135 =cut
137 sub ListImagesForBiblio {
138 my ($biblionumber) = @_;
140 my @imagenumbers;
141 my $dbh = C4::Context->dbh;
142 my $query = 'SELECT imagenumber FROM biblioimages WHERE biblionumber = ?';
143 my $sth = $dbh->prepare($query);
144 $sth->execute($biblionumber);
145 while ( my $row = $sth->fetchrow_hashref ) {
146 push @imagenumbers, $row->{'imagenumber'};
148 return @imagenumbers;
151 =head2 GetImageForItem
152 my $image = GetImageForItem($itemnumber);
154 Gets the image associated with a particular item.
156 =cut
158 sub GetImageForItem {
159 my ($itemnumber) = @_;
161 my $dbh = C4::Context->dbh;
162 return $dbh->selectrow_array(
163 'SELECT imagenumber FROM biblioimages WHERE itemnumber = ?',
164 undef, $itemnumber );
167 =head2 DelImage
169 my ($dberror) = DelImage($imagenumber);
171 Removes the image with the supplied imagenumber.
173 =cut
175 sub DelImage {
176 my ($imagenumber) = @_;
177 warn "Imagenumber passed to DelImage is $imagenumber" if $debug;
178 my $dbh = C4::Context->dbh;
179 my $query = "DELETE FROM biblioimages WHERE imagenumber = ?;";
180 my $sth = $dbh->prepare($query);
181 $sth->execute($imagenumber);
182 my $dberror = $sth->errstr;
183 warn "Database error!" if $sth->errstr;
184 return $dberror;
187 sub _scale_image {
188 my ( $image, $maxwidth, $maxheight ) = @_;
189 my ( $width, $height ) = $image->getBounds();
190 $debug and warn "image is $width pix X $height pix.";
191 if ( $width > $maxwidth || $height > $maxheight ) {
193 # $debug and warn "$filename exceeds the maximum pixel dimensions of $maxwidth X $maxheight. Resizing...";
194 my $percent_reduce; # Percent we will reduce the image dimensions by...
195 if ( $width > $maxwidth ) {
196 $percent_reduce =
197 sprintf( "%.5f", ( $maxwidth / $width ) )
198 ; # If the width is oversize, scale based on width overage...
200 else {
201 $percent_reduce =
202 sprintf( "%.5f", ( $maxheight / $height ) )
203 ; # otherwise scale based on height overage.
205 my $width_reduce = sprintf( "%.0f", ( $width * $percent_reduce ) );
206 my $height_reduce = sprintf( "%.0f", ( $height * $percent_reduce ) );
207 $debug
208 and warn "Reducing image by "
209 . ( $percent_reduce * 100 )
210 . "\% or to $width_reduce pix X $height_reduce pix";
211 my $newimage = GD::Image->new( $width_reduce, $height_reduce, 1 )
212 ; #'1' creates true color image...
213 $newimage->copyResampled( $image, 0, 0, 0, 0, $width_reduce,
214 $height_reduce, $width, $height );
215 return $newimage;
217 else {
218 return $image;
222 =head2 NoImage
224 C4::Images->NoImage;
226 Returns the gif to be used when there is no image matching the request, and
227 its mimetype (image/gif).
229 =cut
231 sub NoImage {
232 return $noimage, 'image/gif';