1 <?php
2 /**
3 * @author Gasper Kozak
4 * @copyright 2007-2011
5
6 This file is part of WideImage.
7
8 WideImage is free software; you can redistribute it and/or modify
9 it under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 WideImage is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with WideImage; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
21
22 * @package WideImage
23 **/
24
25 require_once WideImage::path() . 'Exception.php';
26
27 require_once WideImage::path() . 'Image.php';
28 require_once WideImage::path() . 'TrueColorImage.php';
29 require_once WideImage::path() . 'PaletteImage.php';
30
31 require_once WideImage::path() . 'Coordinate.php';
32 require_once WideImage::path() . 'Canvas.php';
33 require_once WideImage::path() . 'MapperFactory.php';
34 require_once WideImage::path() . 'OperationFactory.php';
35
36 require_once WideImage::path() . 'Font/TTF.php';
37 require_once WideImage::path() . 'Font/GDF.php';
38 require_once WideImage::path() . 'Font/PS.php';
39
40 /**
41 * @package Exceptions
42 */
43 class WideImage_InvalidImageHandleException extends WideImage_Exception {}
44
45 /**
46 * @package Exceptions
47 */
48 class WideImage_InvalidImageSourceException extends WideImage_Exception {}
49
50 /**
51 * @package Exceptions
52 *
53 * Class for invalid GD function calls result (for example those that return bool)
54 */
55 class WideImage_GDFunctionResultException extends WideImage_Exception {}
56
57 /**
58 * The gateway class for loading images and core library functions
59 *
60 * @package WideImage
61 */
62 class WideImage
63 {
64 const SIDE_TOP_LEFT = 1;
65 const SIDE_TOP = 2;
66 const SIDE_TOP_RIGHT = 4;
67 const SIDE_RIGHT = 8;
68 const SIDE_BOTTOM_RIGHT = 16;
69 const SIDE_BOTTOM = 32;
70 const SIDE_BOTTOM_LEFT = 64;
71 const SIDE_LEFT = 128;
72 const SIDE_ALL = 255;
73
74 /**
75 * @var string Path to the library base directory
76 */
77 protected static $path = null;
78
79 /**
80 * Returns the library version
81 * @return string The library version
82 */
83 static function version()
84 {
85 return '11.02.19';
86 }
87
88 /**
89 * Returns the path to the library
90 * @return string
91 */
92 static function path()
93 {
94 if (self::$path === null)
95 self::$path = dirname(__FILE__) . DIRECTORY_SEPARATOR;
96 return self::$path;
97 }
98
99 /**
100 * Checks whether the gd library is loaded, and throws an exception otherwise
101 */
102 static function checkGD()
103 {
104 if (!extension_loaded('gd'))
105 throw new WideImage_Exception("WideImage requires the GD extension, but it's apparently not loaded.");
106 }
107
108 /**
109 * Registers a custom mapper for image loading and saving
110 *
111 * Example:
112 * <code>
113 * WideImage::registerCustomMapper('WideImage_Mapper_TGA', 'image/tga', 'tga');
114 * </code>
115 *
116 * @param string $mapper_class_name
117 * @param string $mime_type
118 * @param string $extension
119 */
120 static function registerCustomMapper($mapper_class_name, $mime_type, $extension)
121 {
122 WideImage_MapperFactory::registerMapper($mapper_class_name, $mime_type, strtoupper($extension));
123 }
124
125 /**
126 * Loads an image from a file, URL, HTML input file field, binary string, or a valid image handle.
127 * The image format is auto-detected.
128 *
129 * Currently supported formats: PNG, GIF, JPG, BMP, TGA, GD, GD2.
130 *
131 * This function analyzes the input and decides whether to use WideImage::loadFromHandle(),
132 * WideImage::loadFromFile(), WideImage::loadFromUpload() or WideImage::loadFromString(),
133 * all of which you can also call directly to spare WideImage some guessing.
134 *
135 * Arrays are supported for upload fields; it returns an array of loaded images.
136 * To load only a single image from an array field, use WideImage::loadFromUpload('img', $i),
137 * where $i is the index of the image you want to load.
138 *
139 * <code>
140 * $img = WideImage::load('http://url/image.png'); // image URL
141 * $img = WideImage::load('/path/to/image.png'); // local file path
142 * $img = WideImage::load('img'); // upload field name
143 * $img = WideImage::load(imagecreatetruecolor(10, 10)); // a GD resource
144 * $img = WideImage::load($image_data); // binary string containing image data
145 * </code>
146 *
147 * @param mixed $source File name, url, HTML file input field name, binary string, or a GD image resource
148 * @return WideImage_Image WideImage_PaletteImage or WideImage_TrueColorImage instance
149 */
150 static function load($source)
151 {
152 $predictedSourceType = '';
153
154 if ($source == '')
155 $predictedSourceType = 'String';
156
157 // Creating image via a valid resource
158 if (!$predictedSourceType && self::isValidImageHandle($source))
159 $predictedSourceType = 'Handle';
160
161 // Check for binary string
162 if (!$predictedSourceType)
163 {
164 // search first $binLength bytes (at a maximum) for ord<32 characters (binary image data)
165 $binLength = 64;
166 $sourceLength = strlen($source);
167 $maxlen = ($sourceLength > $binLength) ? $binLength : $sourceLength;
168 for ($i = 0; $i < $maxlen; $i++)
169 if (ord($source[$i]) < 32)
170 {
171 $predictedSourceType = 'String';
172 break;
173 }
174 }
175
176 // Uploaded image (array uploads not supported)
177 if (isset($_FILES[$source]) && isset($_FILES[$source]['tmp_name']))
178 $predictedSourceType = 'Upload';
179
180 // Otherwise, must be a file or an URL
181 if (!$predictedSourceType)
182 $predictedSourceType = 'File';
183
184 return call_user_func(array('WideImage', 'loadFrom' . $predictedSourceType), $source);
185 }
186
187 /**
188 * Create and load an image from a file or URL. The image format is auto-detected.
189 *
190 * @param string $uri File or url
191 * @return WideImage_Image WideImage_PaletteImage or WideImage_TrueColorImage instance
192 */
193 static function loadFromFile($uri)
194 {
195 $data = file_get_contents($uri);
196 $handle = @imagecreatefromstring($data);
197 if (!self::isValidImageHandle($handle))
198 {
199 try
200 {
201 // try to find a mapper first
202 $mapper = WideImage_MapperFactory::selectMapper($uri);
203 if ($mapper)
204 $handle = $mapper->load($uri);
205 }
206 catch (WideImage_UnsupportedFormatException $e)
207 {
208 // mapper not found
209 }
210
211 // try all custom mappers
212 if (!self::isValidImageHandle($handle))
213 {
214 $custom_mappers = WideImage_MapperFactory::getCustomMappers();
215 foreach ($custom_mappers as $mime_type => $mapper_class)
216 {
217 $mapper = WideImage_MapperFactory::selectMapper(null, $mime_type);
218 $handle = $mapper->loadFromString($data);
219 if (self::isValidImageHandle($handle))
220 break;
221 }
222 }
223 }
224
225 if (!self::isValidImageHandle($handle))
226 throw new WideImage_InvalidImageSourceException("File '{$uri}' appears to be an invalid image source.");
227
228 return self::loadFromHandle($handle);
229 }
230
231 /**
232 * Create and load an image from a string. Format is auto-detected.
233 *
234 * @param string $string Binary data, i.e. from BLOB field in the database
235 * @return WideImage_Image WideImage_PaletteImage or WideImage_TrueColorImage instance
236 */
237 static function loadFromString($string)
238 {
239 if (strlen($string) < 128)
240 throw new WideImage_InvalidImageSourceException("String doesn't contain image data.");
241
242 $handle = @imagecreatefromstring($string);
243 if (!self::isValidImageHandle($handle))
244 {
245 $custom_mappers = WideImage_MapperFactory::getCustomMappers();
246 foreach ($custom_mappers as $mime_type => $mapper_class)
247 {
248 $mapper = WideImage_MapperFactory::selectMapper(null, $mime_type);
249 $handle = $mapper->loadFromString($string);
250 if (self::isValidImageHandle($handle))
251 break;
252 }
253 }
254
255 if (!self::isValidImageHandle($handle))
256 throw new WideImage_InvalidImageSourceException("String doesn't contain valid image data.");
257
258 return self::loadFromHandle($handle);
259 }
260
261 /**
262 * Create and load an image from an image handle.
263 *
264 * <b>Note:</b> the resulting image object takes ownership of the passed
265 * handle. When the newly-created image object is destroyed, the handle is
266 * destroyed too, so it's not a valid image handle anymore. In order to
267 * preserve the handle for use after object destruction, you have to call
268 * WideImage_Image::releaseHandle() on the created image instance prior to its
269 * destruction.
270 *
271 * <code>
272 * $handle = imagecreatefrompng('file.png');
273 * $image = WideImage::loadFromHandle($handle);
274 * </code>
275 *
276 * @param resource $handle A valid GD image resource
277 * @return WideImage_Image WideImage_PaletteImage or WideImage_TrueColorImage instance
278 */
279 static function loadFromHandle($handle)
280 {
281 if (!self::isValidImageHandle($handle))
282 throw new WideImage_InvalidImageSourceException("Handle is not a valid GD image resource.");
283
284 if (imageistruecolor($handle))
285 return new WideImage_TrueColorImage($handle);
286 else
287 return new WideImage_PaletteImage($handle);
288 }
289
290 /**
291 * This method loads a file from the $_FILES array. The image format is auto-detected.
292 *
293 * You only have to pass the field name as the parameter. For array fields, this function will
294 * return an array of image objects, unless you specify the $index parameter, which will
295 * load the desired image.
296 *
297 * @param $field_name Name of the key in $_FILES array
298 * @param int $index The index of the file to load (if the input field is an array)
299 * @return WideImage_Image The loaded image
300 */
301 static function loadFromUpload($field_name, $index = null)
302 {
303 if (!array_key_exists($field_name, $_FILES))
304 throw new WideImage_InvalidImageSourceException("Upload field '{$field_name}' doesn't exist.");
305
306 if (is_array($_FILES[$field_name]['tmp_name']))
307 {
308 if (isset($_FILES[$field_name]['tmp_name'][$index]))
309 $filename = $_FILES[$field_name]['tmp_name'][$index];
310 else
311 {
312 $result = array();
313 foreach ($_FILES[$field_name]['tmp_name'] as $idx => $tmp_name)
314 $result[$idx] = self::loadFromFile($tmp_name);
315 return $result;
316 }
317 }
318 else
319 $filename = $_FILES[$field_name]['tmp_name'];
320
321 if (!file_exists($filename))
322 throw new WideImage_InvalidImageSourceException("Uploaded file doesn't exist.");
323 return self::loadFromFile($filename);
324 }
325
326 /**
327 * Factory method for creating a palette image
328 *
329 * @param int $width
330 * @param int $height
331 * @return WideImage_PaletteImage
332 */
333 static function createPaletteImage($width, $height)
334 {
335 return WideImage_PaletteImage::create($width, $height);
336 }
337
338 /**
339 * Factory method for creating a true-color image
340 *
341 * @param int $width
342 * @param int $height
343 * @return WideImage_TrueColorImage
344 */
345 static function createTrueColorImage($width, $height)
346 {
347 return WideImage_TrueColorImage::create($width, $height);
348 }
349
350 /**
351 * Check whether the given handle is a valid GD resource
352 *
353 * @param mixed $handle The variable to check
354 * @return bool
355 */
356 static function isValidImageHandle($handle)
357 {
358 return (is_resource($handle) && get_resource_type($handle) == 'gd');
359 }
360
361 /**
362 * Throws exception if the handle isn't a valid GD resource
363 *
364 * @param mixed $handle The variable to check
365 */
366 static function assertValidImageHandle($handle)
367 {
368 if (!self::isValidImageHandle($handle))
369 throw new WideImage_InvalidImageHandleException("{$handle} is not a valid image handle.");
370 }
371 }
372
373 WideImage::checkGD();
374
375 WideImage::registerCustomMapper('WideImage_Mapper_BMP', 'image/bmp', 'bmp');
376 WideImage::registerCustomMapper('WideImage_Mapper_TGA', 'image/tga', 'tga');
377