Rev 229 Rev 236
1 <?php 1 <?php
2   2  
3 /** 3 /**
4 * Thumbnail class. 4 * Thumbnail class.
5 * 5 *
6 * @author Tamlyn Rhodes <tam at zenology dot co dot uk> 6 * @author Tamlyn Rhodes <tam at zenology dot co dot uk>
7 * @license http://opensource.org/licenses/gpl-license.php GNU General Public License 7 * @license http://opensource.org/licenses/gpl-license.php GNU General Public License
8 * @copyright (c)2003-2005 Tamlyn Rhodes 8 * @copyright (c)2003-2005 Tamlyn Rhodes
9 * @version $Id: thumbnail.class.php,v 1.13 2006/06/24 20:33:00 tamlyn Exp $ 9 * @version $Id: thumbnail.class.php,v 1.13 2006/06/24 20:33:00 tamlyn Exp $
10 */ 10 */
11   11  
12   12  
13 /** 13 /**
14 * Creates and manages image thumbnails 14 * Creates and manages image thumbnails
15 * @package singapore 15 * @package singapore
16 * @author Tamlyn Rhodes <tam at zenology dot co dot uk> 16 * @author Tamlyn Rhodes <tam at zenology dot co dot uk>
17 * @copyright (c)2003-2005 Tamlyn Rhodes 17 * @copyright (c)2003-2005 Tamlyn Rhodes
18 */ 18 */
19 class sgThumbnail 19 class sgThumbnail
20 { 20 {
21 var $config; 21 var $config;
22 var $maxWidth = 0; 22 var $maxWidth = 0;
23 var $maxHeight = 0; 23 var $maxHeight = 0;
24 var $thumbWidth = 0; 24 var $thumbWidth = 0;
25 var $thumbHeight = 0; 25 var $thumbHeight = 0;
26 var $cropWidth = 0; 26 var $cropWidth = 0;
27 var $cropHeight = 0; 27 var $cropHeight = 0;
28 var $forceSize = false; 28 var $forceSize = false;
29 var $imagePath = ""; 29 var $imagePath = "";
30 var $thumbPath = ""; 30 var $thumbPath = "";
31 31
32 function sgThumbnail(&$img, $type) 32 function sgThumbnail(&$img, $type)
33 { 33 {
34 $this->config =& sgConfig::getInstance(); 34 $this->config =& sgConfig::getInstance();
35 $this->image =& $img; 35 $this->image =& $img;
36 36
37 $widthVar = "thumb_width_".$type; 37 $widthVar = "thumb_width_".$type;
38 $heightVar = "thumb_height_".$type; 38 $heightVar = "thumb_height_".$type;
39 $cropVar = "thumb_crop_".$type; 39 $cropVar = "thumb_crop_".$type;
40 $this->maxWidth = $this->config->$widthVar; 40 $this->maxWidth = $this->config->$widthVar;
41 $this->maxHeight = $this->config->$heightVar; 41 $this->maxHeight = $this->config->$heightVar;
42 if(isset($this->config->$cropVar)) 42 if(isset($this->config->$cropVar))
43 $this->forceSize = $this->config->$cropVar; 43 $this->forceSize = $this->config->$cropVar;
44 44
45 if($this->image == null) return; 45 if($this->image == null) return;
46 46
47 $this->imagePath = $this->image->realPath(); 47 $this->imagePath = $this->image->realPath();
48 $this->thumbPath = $this->config->base_path.Singapore::thumbnailPath($this->image->parent->id, $this->image->id, $this->maxWidth, $this->maxHeight, $this->forceSize); 48 $this->thumbPath = $this->config->base_path.Singapore::thumbnailPath($this->image->parent->id, $this->image->id, $this->maxWidth, $this->maxHeight, $this->forceSize);
49 $this->thumbURL = $this->config->base_url .Singapore::thumbnailPath($this->image->parent->id, $this->image->id, $this->maxWidth, $this->maxHeight, $this->forceSize); 49 $this->thumbURL = $this->config->base_url .Singapore::thumbnailPath($this->image->parent->id, $this->image->id, $this->maxWidth, $this->maxHeight, $this->forceSize);
50   50  
51 //security check: make sure requested file is in galleries directory 51 //security check: make sure requested file is in galleries directory
52 if(!Singapore::isSubPath($this->config->base_path.$this->config->pathto_galleries, $this->imagePath) && !$this->image->isRemote()) 52 if(!Singapore::isSubPath($this->config->base_path.$this->config->pathto_galleries, $this->imagePath) && !$this->image->isRemote())
53 return; 53 return;
54 54
55 //security check: make sure $image has a valid extension 55 //security check: make sure $image has a valid extension
56 if(!$this->image->isRemote() && !preg_match("/.+\.(".$this->config->recognised_extensions.")$/i",$this->image->id)) 56 if(!$this->image->isRemote() && !preg_match("/.+\.(".$this->config->recognised_extensions.")$/i",$this->image->id))
57 return; 57 return;
58 58
59 $this->calculateDimensions(); 59 $this->calculateDimensions();
60 60
61 //link straight to image if it smaller than required size 61 //link straight to image if it smaller than required size
62 if($this->image->width <= $this->thumbWidth && $this->image->height <= $this->thumbHeight) { 62 if($this->image->width <= $this->thumbWidth && $this->image->height <= $this->thumbHeight) {
63 $this->thumbURL = $this->image->realURL(); 63 $this->thumbURL = $this->image->realURL();
64 return; 64 return;
65 } 65 }
66 66
67 $imageModified = @filemtime($this->imagePath); 67 $imageModified = @filemtime($this->imagePath);
68 $thumbModified = @filemtime($this->thumbPath); 68 $thumbModified = @filemtime($this->thumbPath);
69 69
70 if($imageModified > $thumbModified || !$thumbModified) 70 if($imageModified > $thumbModified || !$thumbModified)
71 $this->buildThumbnail(); 71 $this->buildThumbnail();
72 } 72 }
73 73
74 /** Accessor methods */ 74 /** Accessor methods */
75 function width() { return $this->thumbWidth; } 75 function width() { return $this->thumbWidth; }
76 function height() { return $this->thumbHeight; } 76 function height() { return $this->thumbHeight; }
77 function URL() { return $this->thumbURL; } 77 function URL() { return $this->thumbURL; }
78 78
79 /** Private methods */ 79 /** Private methods */
80 80
81 /** 81 /**
82 * Calculates thumbnail dimensions. 82 * Calculates thumbnail dimensions.
83 */ 83 */
84 function calculateDimensions() 84 function calculateDimensions()
85 { 85 {
86 //if aspect ratio is to be constrained set crop size 86 //if aspect ratio is to be constrained set crop size
87 if($this->forceSize) { 87 if($this->forceSize) {
88 $newAspect = $this->maxWidth/$this->maxHeight; 88 $newAspect = $this->maxWidth/$this->maxHeight;
89 $oldAspect = $this->image->realWidth()/$this->image->realHeight(); 89 $oldAspect = $this->image->realWidth()/$this->image->realHeight();
90 if($newAspect > $oldAspect) { 90 if($newAspect > $oldAspect) {
91 $this->cropWidth = $this->image->realWidth(); 91 $this->cropWidth = $this->image->realWidth();
92 $this->cropHeight = round($this->image->realHeight()*($oldAspect/$newAspect)); 92 $this->cropHeight = round($this->image->realHeight()*($oldAspect/$newAspect));
93 } else { 93 } else {
94 $this->cropWidth = round($this->image->realWidth()*($newAspect/$oldAspect)); 94 $this->cropWidth = round($this->image->realWidth()*($newAspect/$oldAspect));
95 $this->cropHeight = $this->image->realHeight(); 95 $this->cropHeight = $this->image->realHeight();
96 } 96 }
97 //else crop size is image size 97 //else crop size is image size
98 } else { 98 } else {
99 $this->cropWidth = $this->image->realWidth(); 99 $this->cropWidth = $this->image->realWidth();
100 $this->cropHeight = $this->image->realHeight(); 100 $this->cropHeight = $this->image->realHeight();
101 } 101 }
102 102
103 if($this->cropHeight > $this->maxHeight && ($this->cropWidth <= $this->maxWidth 103 if($this->cropHeight > $this->maxHeight && ($this->cropWidth <= $this->maxWidth
104 || ($this->cropWidth > $this->maxWidth && round($this->cropHeight/$this->cropWidth * $this->maxWidth) > $this->maxHeight))) { 104 || ($this->cropWidth > $this->maxWidth && round($this->cropHeight/$this->cropWidth * $this->maxWidth) > $this->maxHeight))) {
105 $this->thumbWidth = round($this->cropWidth/$this->cropHeight * $this->maxHeight); 105 $this->thumbWidth = round($this->cropWidth/$this->cropHeight * $this->maxHeight);
106 $this->thumbHeight = $this->maxHeight; 106 $this->thumbHeight = $this->maxHeight;
107 } elseif($this->cropWidth > $this->maxWidth) { 107 } elseif($this->cropWidth > $this->maxWidth) {
108 $this->thumbWidth = $this->maxWidth; 108 $this->thumbWidth = $this->maxWidth;
109 $this->thumbHeight = round($this->cropHeight/$this->cropWidth * $this->maxWidth); 109 $this->thumbHeight = round($this->cropHeight/$this->cropWidth * $this->maxWidth);
110 } else { 110 } else {
111 $this->thumbWidth = $this->image->realWidth(); 111 $this->thumbWidth = $this->image->realWidth();
112 $this->thumbHeight = $this->image->realHeight(); 112 $this->thumbHeight = $this->image->realHeight();
113 } 113 }
114 } 114 }
115 115
116 function buildThumbnail() { 116 function buildThumbnail() {
117 //set cropping offset 117 //set cropping offset
118 $cropX = floor(($this->image->width-$this->cropWidth)/2); 118 $cropX = floor(($this->image->width-$this->cropWidth)/2);
119 $cropY = floor(($this->image->height-$this->cropHeight)/2); 119 $cropY = floor(($this->image->height-$this->cropHeight)/2);
120 120
121 //check thumbs directory exists and create it if not 121 //check thumbs directory exists and create it if not
122 if(!file_exists(dirname($this->thumbPath))) 122 if(!file_exists(dirname($this->thumbPath)))
123 Singapore::mkdir(dirname($this->thumbPath)); 123 Singapore::mkdir(dirname($this->thumbPath));
124 124
125 //if file is remote then copy locally first 125 //if file is remote then copy locally first
126 if($this->image->isRemote()) { 126 if($this->image->isRemote()) {
127 $ip = @fopen($this->imagePath, "rb"); 127 $ip = @fopen($this->imagePath, "rb");
128 $tp = @fopen($this->thumbPath, "wb"); 128 $tp = @fopen($this->thumbPath, "wb");
129 if($ip && $tp) { 129 if($ip && $tp) {
130 while(fwrite($tp,fread($ip, 4095))); 130 while(fwrite($tp,fread($ip, 4095)));
131 fclose($tp); 131 fclose($tp);
132 fclose($ip); 132 fclose($ip);
133 $this->imagePath = $this->thumbPath; 133 $this->imagePath = $this->thumbPath;
134 } 134 }
135 } 135 }
136 136
137 switch($this->config->thumbnail_software) { 137 switch($this->config->thumbnail_software) {
138 case "im" : //use ImageMagick v5.x 138 case "im" : //use ImageMagick v5.x
139 $cmd = '"'.$this->config->pathto_convert.'"'; 139 $cmd = '"'.$this->config->pathto_convert.'"';
140 if($this->forceSize) $cmd .= " -crop {$this->cropWidth}x{$this->cropHeight}+{$this->cropX}+{$this->cropY}"; 140 if($this->forceSize) $cmd .= " -crop {$this->cropWidth}x{$this->cropHeight}+{$this->cropX}+{$this->cropY}";
141 $cmd .= " -geometry {$this->thumbWidth}x{$this->thumbHeight}"; 141 $cmd .= " -geometry {$this->thumbWidth}x{$this->thumbHeight}";
142 if($this->image->type == 2) $cmd .= " -quality ".$this->config->thumbnail_quality; 142 if($this->image->type == 2) $cmd .= " -quality ".$this->config->thumbnail_quality;
143 if($this->config->progressive_thumbs) $cmd .= " -interlace Plane"; 143 if($this->config->progressive_thumbs) $cmd .= " -interlace Plane";
144 if($this->config->remove_jpeg_profile) $cmd .= ' +profile "*"'; 144 if($this->config->remove_jpeg_profile) $cmd .= ' +profile "*"';
145 $cmd .= ' '.escapeshellarg($this->imagePath).' '.escapeshellarg($this->thumbPath); 145 $cmd .= ' '.escapeshellarg($this->imagePath).' '.escapeshellarg($this->thumbPath);
146 146
147 exec($cmd); 147 exec($cmd);
148 148
149 break; 149 break;
150 case "im6" : //use ImageMagick v6.x 150 case "im6" : //use ImageMagick v6.x
151 $cmd = '"'.$this->config->pathto_convert.'"'; 151 $cmd = '"'.$this->config->pathto_convert.'"';
152 $cmd .= ' '.escapeshellarg($this->imagePath); 152 $cmd .= ' '.escapeshellarg($this->imagePath);
153 if($this->config->progressive_thumbs) $cmd .= " -interlace Plane"; 153 if($this->config->progressive_thumbs) $cmd .= " -interlace Plane";
154 if($this->image->type == 2) $cmd .= " -quality ".$this->config->thumbnail_quality; 154 if($this->image->type == 2) $cmd .= " -quality ".$this->config->thumbnail_quality;
155 if($this->forceSize) $cmd .= " -crop {$this->cropWidth}x{$this->cropHeight}+{$this->cropX}+{$this->cropY}"; 155 if($this->forceSize) $cmd .= " -crop {$this->cropWidth}x{$this->cropHeight}+{$this->cropX}+{$this->cropY}";
156 $cmd .= " -resize {$this->thumbWidth}x{$this->thumbHeight}"; 156 $cmd .= " -resize {$this->thumbWidth}x{$this->thumbHeight}";
157 if($this->config->remove_jpeg_profile) $cmd .= ' +profile "*"'; 157 if($this->config->remove_jpeg_profile) $cmd .= ' +profile "*"';
158 $cmd .= ' '.escapeshellarg($this->thumbPath); 158 $cmd .= ' '.escapeshellarg($this->thumbPath);
159 159
160 exec($cmd); 160 exec($cmd);
161 161
162 break; 162 break;
163 case "gd2" : 163 case "gd2" :
164 case "gd1" : 164 case "gd1" :
165 default : //use GD by default 165 default : //use GD by default
166 //read in image as appropriate type 166 //read in image as appropriate type
167 switch($this->image->type) { 167 switch($this->image->type) {
168 case 1 : $image = ImageCreateFromGIF($this->imagePath); break; 168 case 1 : $image = ImageCreateFromGIF($this->imagePath); break;
169 case 3 : $image = ImageCreateFromPNG($this->imagePath); break; 169 case 3 : $image = ImageCreateFromPNG($this->imagePath); break;
170 case 2 : 170 case 2 :
171 default: $image = ImageCreateFromJPEG($this->imagePath); break; 171 default: $image = ImageCreateFromJPEG($this->imagePath); break;
172 } 172 }
173 173
174 if($image) { 174 if($image) {
175 switch($this->config->thumbnail_software) { 175 switch($this->config->thumbnail_software) {
176 case "gd2" : 176 case "gd2" :
177 //create blank truecolor image 177 //create blank truecolor image
178 $thumb = ImageCreateTrueColor($this->thumbWidth,$this->thumbHeight); 178 $thumb = ImageCreateTrueColor($this->thumbWidth,$this->thumbHeight);
179 //resize image with resampling 179 //resize image with resampling
180 ImageCopyResampled( 180 ImageCopyResampled(
181 $thumb, $image, 181 $thumb, $image,
182 0, 0, $cropX, $cropY, 182 0, 0, $cropX, $cropY,
183 $this->thumbWidth, $this->thumbHeight, $this->cropWidth, $this->cropHeight); 183 $this->thumbWidth, $this->thumbHeight, $this->cropWidth, $this->cropHeight);
184 break; 184 break;
185 case "gd1" : 185 case "gd1" :
186 default : 186 default :
187 //create blank 256 color image 187 //create blank 256 color image
188 $thumb = ImageCreate($this->thumbWidth,$this->thumbHeight); 188 $thumb = ImageCreate($this->thumbWidth,$this->thumbHeight);
189 //resize image 189 //resize image
190 ImageCopyResized( 190 ImageCopyResized(
191 $thumb, $image, 191 $thumb, $image,
192 0, 0, $cropX, $cropY, 192 0, 0, $cropX, $cropY,
193 $this->thumbWidth, $this->thumbHeight, $this->cropWidth, $this->cropHeight); 193 $this->thumbWidth, $this->thumbHeight, $this->cropWidth, $this->cropHeight);
194 break; 194 break;
195 } 195 }
196 } /*else { 196 } /*else {
197 $thumb = ImageCreate($this->thumbWidth, $this->thumbHeight); 197 $thumb = ImageCreate($this->thumbWidth, $this->thumbHeight);
198 $bg = ImageColorAllocate($thumb, 255, 255, 255); 198 $bg = ImageColorAllocate($thumb, 255, 255, 255);
199 $text = ImageColorAllocate($thumb, 255, 0, 0); 199 $text = ImageColorAllocate($thumb, 255, 0, 0);
200 ImageString($thumb, 1, 0, 0, "Cannot load source image", $text); 200 ImageString($thumb, 1, 0, 0, "Cannot load source image", $text);
201 }*/ 201 }*/
202 202
203 //set image interlacing 203 //set image interlacing
204 @ImageInterlace($thumb, $this->config->progressive_thumbs); 204 @ImageInterlace($thumb, $this->config->progressive_thumbs);
205 205
206 //output image of appropriate type 206 //output image of appropriate type
207 switch($this->image->type) { 207 switch($this->image->type) {
208 case 1 : 208 case 1 :
209 //GIF images are saved as PNG 209 //GIF images are saved as PNG
210 case 3 : 210 case 3 :
211 ImagePNG($thumb, $this->thumbPath); 211 ImagePNG($thumb, $this->thumbPath);
212 break; 212 break;
213 case 2 : 213 case 2 :
214 default: 214 default:
215 ImageJPEG($thumb, $this->thumbPath, $this->config->thumbnail_quality); 215 ImageJPEG($thumb, $this->thumbPath, $this->config->thumbnail_quality);
216 break; 216 break;
217 } 217 }
218 @ImageDestroy($image); 218 @ImageDestroy($image);
219 @ImageDestroy($thumb); 219 @ImageDestroy($thumb);
220 } 220 }
221 221
222 //set file permissions on newly created thumbnail 222 //set file permissions on newly created thumbnail
223 @chmod($this->thumbPath, octdec($this->config->file_mode)); 223 @chmod($this->thumbPath, octdec($this->config->file_mode));
224 } 224 }
225   225  
226 } 226 }
227   227  
228 ?> 228 ?>