CWIS Developer Documentation

- Home
- Main Page
- Namespaces
- Classes
- Files

- File List
- File Members

Axis--Image.php
Go to the documentation of this file.
1 <?PHP
2
3 #
4 # Axis--Image.php
5 # A PHP Object to Support Image File Manipulation
6 #
7 # NOTE: To use this object, either PHP must have internal support for the image formats
8 # to be manipulated (can be checked with the imagetypes() function) or the web server
9 # must be running with safe_mode off and certain external executables must be available
10 # to be invoked by the server. (A list of the needed executables can be obtained via
11 # the Image::RequiredExternalExecutables() method.)
12 #
13 # Copyright 2002-2004 Axis Data
14 # This code is free software that can be used or redistributed under the
15 # terms of Version 2 of the GNU General Public License, as published by the
16 # Free Software Foundation (http://www.fsf.org).
17 #
18 # Author: Edward Almasy (ealmasy@axisdata.com)
19 #
20 # Part of the AxisPHP library v1.2.5
21 # For more information see http://www.axisdata.com/AxisPHP/
22 #
23
24 class Image {
25
26 # ---- PUBLIC INTERFACE --------------------------------------------------
27
28 function Image($SourceFileName, $DebugLevel = 0)
29 {
30 # set debug level
31 $this->DebugLevel = $DebugLevel;
32
33 # save source file name
34 $this->SourceFileName = $SourceFileName;
35
36 # set default values
37 $this->JpegSaveQuality = 80;
38 $this->ErrorStatus = AI_OKAY;
39 $this->FailedCommand = "";
40
41 # get GD library version
42 if (extension_loaded("gd"))
43 {
44 if (in_array("imagecreatetruecolor", get_extension_funcs("gd")))
45 {
46 $this->GDVersion = 2;
47 }
48 else
49 {
50 $this->GDVersion = 1;
51 }
52 }
53 else
54 {
55 $this->GDVersion = 0;
56 }
57
58 # if source file is readable
59 if (is_readable(realpath($SourceFileName)))
60 {
61 # if internal support is available for this image type
62 if ($this->ImageFormatSupportedByPhp())
63 {
64 if ($this->DebugLevel > 0) { print("AI: using internal funcs for decoding image<br>\n"); }
65
66 # create PHP image object
67 switch ($this->Type())
68 {
69 case IMGTYPE_JPEG:
70 if ($this->DebugLevel > 1) { print("AI: file format is JPEG<br>\n"); }
71 $this->ImageObj = imagecreatefromjpeg($this->SourceFileName);
72 break;
73
74 case IMGTYPE_GIF:
75 if ($this->DebugLevel > 1) { print("AI: file format is GIF<br>\n"); }
76 $this->ImageObj = imagecreatefromgif($this->SourceFileName);
77 break;
78
79 case IMGTYPE_BMP:
80 if ($this->DebugLevel > 1) { print("AI: file format is BMP<br>\n"); }
81 $this->ImageObj = imagecreatefrombmp($this->SourceFileName);
82 break;
83
84 case IMGTYPE_PNG:
85 if ($this->DebugLevel > 1) { print("AI: file format is PNG<br>\n"); }
86 $this->ImageObj = imagecreatefrompng($this->SourceFileName);
87 break;
88
89 default:
90 $this->ErrorStatus = AI_INTERNALERROR;
91 break;
92 }
93
94 # if PHP image object creation failed
95 if (FALSE === $this->ImageObj)
96 {
97 # set error status
98 $this->ErrorStatus = AI_IMGOBJCREATEFAILED;
99 }
100 }
101
102 # if external command execution possible
103 if (ini_get("safe_mode") != "1")
104 {
105 # determine external save command to convert image to portable format
106 switch ($this->Type())
107 {
108 case IMGTYPE_BMP:
109 $this->DecodeCommand = "bmptoppm ";
110 break;
111
112 case IMGTYPE_GIF:
113 $this->DecodeCommand = "giftopnm ";
114 break;
115
116 case IMGTYPE_PNG:
117 $this->DecodeCommand = "pngtopnm ";
118 break;
119
120 case IMGTYPE_JPEG:
121 $this->DecodeCommand = "djpeg ";
122 break;
123
124 default:
125 $this->ErrorStatus = AI_UNKNOWNTYPE;
126 break;
127 }
128 $this->DecodeCommand .= realpath($this->SourceFileName)." ";
129 }
130 else
131 {
132 # if format wasn't supported internally
133 if (!$this->ImageFormatSupportedByPhp())
134 {
135 # set error status to indicate unsupported image format
136 $this->ErrorStatus = AI_UNSUPPORTEDFORMAT;
137 }
138 }
139 }
140 else
141 {
142 # set error status
143 $this->ErrorStatus = AI_FILEUNREADABLE;
144 }
145 }
146
147 # save image with a new name and (optionally) a new type
148 function SaveAs($FileName, $NewImageType = NULL)
149 {
150 # assume we will succeed
151 $this->ErrorStatus = AI_OKAY;
152
153 # if destination file exists and is not writable
154 if (file_exists($FileName) && (is_writable($FileName) != TRUE))
155 {
156 # set error code
157 $this->ErrorStatus = AI_DESTINATIONUNWRITABLE;
158 }
159 # else if destination directory is not writable
160 elseif (is_writable(dirname($FileName)) != TRUE)
161 {
162 # set error code
163 $this->ErrorStatus = AI_DESTINATIONUNWRITABLE;
164 }
165 else
166 {
167 # if no image type specified try to determine based on file name or use source file type
168 if ($NewImageType == NULL)
169 {
170 if ($this->Type($FileName) != IMGTYPE_UNKNOWN)
171 { $NewImageType = $this->Type($FileName); }
172 else
173 { $NewImageType = $this->Type(); }
174 }
175
176 # if input and output types both supported by internal functions
177 if ($this->ImageFormatSupportedByPhp() && $this->ImageFormatSupportedByPhp($NewImageType))
178 {
179 # if image cropping or scaling was requested
180 if (isset($this->CroppedXSize)
181 || isset($this->ScaledXSize)
182 || isset($this->ScaledYSize))
183 {
184 # determine destination image size
185 if (isset($this->ScaledXSize) && isset($this->ScaledYSize)
186 && ($this->MaintainAspectRatio != TRUE))
187 {
188 $DstXSize = $this->ScaledXSize;
189 $DstYSize = $this->ScaledYSize;
190 }
191 elseif (isset($this->ScaledXSize)
192 || ($this->ScaledXSize > $this->ScaledYSize))
193 {
194 $DstXSize = $this->ScaledXSize;
195 $DstYSize = ($this->ScaledXSize * $this->YSize())
196 / $this->XSize();
197 }
198 elseif (isset($this->ScaledYSize))
199 {
200 $DstXSize = ($this->ScaledYSize * $this->XSize())
201 / $this->YSize();
202 $DstYSize = $this->ScaledYSize;
203 }
204 elseif (isset($this->CroppedXSize))
205 {
206 $DstXSize = $this->CroppedXSize;
207 $DstYSize = $this->CroppedYSize;
208 }
209 else
210 {
211 $DstXSize = $this->XSize();
212 $DstYSize = $this->YSize();
213 }
214
215 # create destination image object
216 if (($NewImageType == IMGTYPE_GIF) || ($this->GDVersion < 2))
217 {
218 $DstImage = imagecreate($DstXSize, $DstYSize);
219 }
220 else
221 {
222 $DstImage = imagecreatetruecolor($DstXSize, $DstYSize);
223 }
224
225 # determine area of source image to use
226 if (isset($this->CroppedXSize))
227 {
228 $SrcXSize = $this->CroppedXSize;
229 $SrcYSize = $this->CroppedYSize;
230 }
231 else
232 {
233 $SrcXSize = $this->XSize();
234 $SrcYSize = $this->YSize();
235 }
236
237 # copy/scale portion of original image to destination image
238 if ($this->GDVersion >= 2)
239 {
240 imagecopyresampled($DstImage, $this->ImageObj,
241 0, 0,
242 $this->CroppedXOrigin, $this->CroppedYOrigin,
243 $DstXSize, $DstYSize,
244 $SrcXSize, $SrcYSize);
245 }
246 else
247 {
248 imagecopyresized($DstImage, $this->ImageObj,
249 0, 0,
250 $this->CroppedXOrigin, $this->CroppedYOrigin,
251 $DstXSize, $DstYSize,
252 $SrcXSize, $SrcYSize);
253 }
254 }
255 else
256 {
257 $DstImage =& $this->ImageObj;
258 }
259
260 # save image to new file
261 switch ($NewImageType)
262 {
263 case IMGTYPE_GIF:
264 imagegif($DstImage, $FileName);
265 break;
266
267 case IMGTYPE_JPEG:
268 imagejpeg($DstImage, $FileName, $this->JpegSaveQuality);
269 break;
270
271 case IMGTYPE_PNG:
272 imagepng($DstImage, $FileName);
273 break;
274
275 case IMGTYPE_BMP:
276 imagewbmp($DstImage, $FileName);
277
278 default:
279 $this->ErrorStatus = AI_INTERNALERROR;
280 break;
281 }
282 }
283 else
284 {
285 # build command (convert image to intermediate form)
286 $Command = $this->DecodeCommand;
287
288 # build command (crop if requested)
289 if (isset($this->CroppedXSize))
290 {
291 $Command .= "| pnmcut ".$this->CroppedXOrigin." ".$this->CroppedYOrigin." "
292 .$this->CroppedXSize." ".$this->CroppedYSize." ";
293 }
294
295 # build command (scale if requested)
296 if (isset($this->ScaledXSize) || isset($this->ScaledYSize))
297 {
298 $Command .= "| pnmscale ";
299 if ($this->MaintainAspectRatio
300 && isset($this->ScaledXSize) && isset($this->ScaledYSize))
301 {
302 $Command .= "-xysize ".$this->ScaledXSize." ".$this->ScaledYSize;
303 }
304 else
305 {
306 if (isset($this->ScaledXSize)) { $Command .= "-xsize ".$this->ScaledXSize." "; }
307 if (isset($this->ScaledYSize)) { $Command .= "-ysize ".$this->ScaledYSize." "; }
308 }
309 }
310
311 # build command (convert to final form)
312 switch ($NewImageType)
313 {
314 case IMGTYPE_BMP:
315 $Command .= "| ppmquant 256 | ppmtobmp -windows ";
316 break;
317
318 case IMGTYPE_GIF:
319 $Command .= "| ppmquant 256 | ppmtogif ";
320 break;
321
322 case IMGTYPE_PNG:
323 $Command .= "| ppmquant 256 | pnmtopng ";
324 break;
325
326 case IMGTYPE_JPEG:
327 default:
328 $Command .= "| cjpeg -quality ".$this->JpegSaveQuality." ";
329 break;
330 }
331
332 # build command (send output to new image file)
333 $Command .= "> ".$FileName;
334
335 # execute command
336
337 $CommandResult = system($Command);
338
339 # set error status if command failed
340 if ($CommandResult === FALSE)
341 {
342 $this->ErrorStatus = AI_PPMCMDFAILED;
343 $this->FailedCommand = $Command;
344 }
345 }
346 }
347
348 # report success or failure to caller
349 return $this->ErrorStatus;
350 }
351
352 # return the X (horizontal) image size in pixels
353 function XSize()
354 {
355 $this->ReadSize();
356 return $this->ImageXSize;
357 }
358
359 # return the Y (vertical) image size in pixels
360 function YSize()
361 {
362 $this->ReadSize();
363 return $this->ImageYSize;
364 }
365
366 # specify the size to scale the image to for the next SaveAs()
367 function ScaleTo($ScaledXSize, $ScaledYSize, $MaintainAspectRatio = FALSE)
368 {
369 # save size for scaling
370 $this->ScaledXSize = $ScaledXSize;
371 $this->ScaledYSize = $ScaledYSize;
372 $this->MaintainAspectRatio = $MaintainAspectRatio;
373 }
374
375 # specify the size to crop the image to for the next SaveAs()
376 function CropTo($CroppedXSize, $CroppedYSize, $CroppedXOrigin = 0, $CroppedYOrigin = 0)
377 {
378 # save origin and size for cropping
379 $this->CroppedXSize = $CroppedXSize;
380 $this->CroppedYSize = $CroppedYSize;
381 $this->CroppedXOrigin = $CroppedXOrigin;
382 $this->CroppedYOrigin = $CroppedYOrigin;
383 }
384
385 # return the image type
386 function Type($FileName = NULL)
387 {
388 if ($FileName == NULL) { $FileName = $this->SourceFileName; }
389 if (preg_match("/.*\\.jp[e]{0,1}g$/i", $FileName)) { return IMGTYPE_JPEG; }
390 elseif (preg_match("/.*\\.gif$/i", $FileName)) { return IMGTYPE_GIF; }
391 elseif (preg_match("/.*\\.bmp$/i", $FileName)) { return IMGTYPE_BMP; }
392 elseif (preg_match("/.*\\.png$/i", $FileName)) { return IMGTYPE_PNG; }
393 else { return IMGTYPE_UNKNOWN; }
394 }
395
396 # return the file name extension for the image
397 static function Extension($Type = NULL)
398 {
399 if ($Type === NULL)
400 {
401 return Image::$AxisImageFileExtensions[$this->Type()];
402 }
403 else
404 {
405 if (isset(Image::$AxisImageFileExtensions[$Type]))
406 {
407 return Image::$AxisImageFileExtensions[$Type];
408 }
409 else
410 {
411 return NULL;
412 }
413 }
414 }
415
416 # set/get the quality (0-100) for JPEG images created with SaveAs()
417 function JpegQuality($NewSetting = NULL)
418 {
419 if ($NewSetting != NULL) { $this->JpegSaveQuality = $NewSetting; }
420 return $this->JpegSaveQuality;
421 }
422
423 # check availability of external executables and return list of any that are not found
424 function MissingExternalExecutables()
425 {
426 # start with empty list of missing executables
427 $MissingExecutables = array();
428
429 # for each required executable
430 foreach (Image::RequiredExternalExecutables() as $Executable)
431 {
432 # if executable did not appear to be available
433 if (Image::ExternalExecutableIsAvailable($Executable) == FALSE)
434 {
435 # add executable to list of missing
436 $MissingExecutables[] = $Executable;
437 }
438 }
439
440 # return list of missing executables to caller
441 return $MissingExecutables;
442 }
443
444 # return list of all required external executables
445 function RequiredExternalExecutables($ImageTypes = NULL)
446 {
447 # start with the assumption that no executables are required
448 $RequiredExecutables = array();
449
450 # if no image types specified assume all image types
451 if ($ImageTypes == NULL)
452 {
453 $ImageTypes = IMGTYPE_JPEG | IMGTYPE_GIF | IMGTYPE_BMP | IMGTYPE_PNG;
454 }
455
456 # add format-specific executables that may or may not be needed
457 if (($ImageTypes & IMGTYPE_JPEG)
458 && (!function_exists("imagetypes") || !(imagetypes() & IMG_JPG)))
459 {
460 $RequiredExecutables[] = "djpeg";
461 $RequiredExecutables[] = "cjpeg";
462 }
463 if (($ImageTypes & IMGTYPE_GIF)
464 && (!function_exists("imagetypes") || !(imagetypes() & IMG_GIF)))
465 {
466 $RequiredExecutables[] = "giftopnm";
467 $RequiredExecutables[] = "ppmtogif";
468 }
469 if (($ImageTypes & IMGTYPE_PNG)
470 && (!function_exists("imagetypes") || !(imagetypes() & IMG_PNG)))
471 {
472 $RequiredExecutables[] = "pngtopnm";
473 $RequiredExecutables[] = "pnmtopng";
474 }
475 if ($ImageTypes & IMGTYPE_BMP)
476 {
477 $RequiredExecutables[] = "bmptoppm";
478 $RequiredExecutables[] = "ppmtobmp";
479 }
480
481 # if any format-specific executables needed
482 if (count($RequiredExecutables) != 0)
483 {
484 # add basic manipulation executables
485 $RequiredExecutables[] = "pnmcut";
486 $RequiredExecutables[] = "pnmscale";
487 $RequiredExecutables[] = "ppmquant";
488 $RequiredExecutables[] = "pnmfile";
489 }
490
491 # return list of required executables to caller
492 return $RequiredExecutables;
493 }
494
495 # return supported image formats
496 static function SupportedFormats()
497 {
498 # start out assuming no formats are supported
499 $Supported = 0;
500
501 # if JPEG is supported by PHP or needed external executables are available
502 if ((function_exists("imagetypes") && defined("IMG_JPG")
503 && (imagetypes() & IMG_JPG))
504 || (Image::ExternalExecutableIsAvailable("djpeg")
505 && Image::ExternalExecutableIsAvailable("cjpeg")))
506 {
507 # add JPEG to list of supported formats
508 $Supported |= IMGTYPE_JPEG;
509 }
510
511 # if GIF is supported by PHP or needed external executables are available
512 if ((function_exists("imagetypes") && defined("IMG_GIF")
513 && (imagetypes() & IMG_GIF))
514 || (Image::ExternalExecutableIsAvailable("giftopnm")
515 && Image::ExternalExecutableIsAvailable("ppmtogif")))
516 {
517 # add GIF to list of supported formats
518 $Supported |= IMGTYPE_GIF;
519 }
520
521 # if PNG is supported by PHP or needed external executables are available
522 if ((function_exists("imagetypes") && defined("IMG_PNG")
523 && (imagetypes() & IMG_PNG))
524 || (Image::ExternalExecutableIsAvailable("pngtopnm")
525 && Image::ExternalExecutableIsAvailable("pnmtopng")))
526 {
527 # add PNG to list of supported formats
528 $Supported |= IMGTYPE_PNG;
529 }
530
531 # if needed external executables are available for BMP
532 # needed executables being present is not sufficient for BMP support in PHP
533 # test is being shortcutted to false to reflect no PHP support for BMPs
534 if (0 && Image::ExternalExecutableIsAvailable("bmptoppm")
535 && Image::ExternalExecutableIsAvailable("ppmtobmp"))
536 {
537 # add BMP to list of supported formats
538 $Supported |= IMGTYPE_BMP;
539 }
540
541 # report to caller what formats are supported
542 return $Supported;
543 }
544
545 # return names (upper-case extensions) of supported image formats
546 static function SupportedFormatNames()
547 {
548 # assume that no formats are supported
549 $FormatNames = array();
550
551 # retrieve supported formats
552 $SupportedFormats = Image::SupportedFormats();
553
554 # for each possible supported format
555 foreach (Image::$AxisImageFileExtensions as $ImageType => $ImageExtension)
556 {
557 # if format is supported
558 if ($ImageType & $SupportedFormats)
559 {
560 # add format extension to list of supported image format names
561 $FormatNames[] = strtoupper($ImageExtension);
562 }
563 }
564
565 # return supported image format names to caller
566 return $FormatNames;
567 }
568
569 # return the error status set by the constructor or the last call to SaveAs()
570 function Status()
571 {
572 return $this->ErrorStatus;
573 }
574
575 # return string containing external command that failed
576 function FailedExternalCommand()
577 {
578 return $this->FailedCommand;
579 }
580
581
582 # ---- PRIVATE INTERFACE -------------------------------------------------
583
584 var $GDVersion;
585 var $ImageObj;
586 var $SourceFileName;
587 var $ImageXSize;
588 var $ImageYSize;
589 var $ScaledXSize;
590 var $ScaledYSize;
591 var $MaintainAspectRatio;
592 var $CroppedXSize;
593 var $CroppedYSize;
594 var $CroppedXOrigin;
595 var $CroppedYOrigin;
596 var $JpegSaveQuality;
597 var $DecodeCommand;
598 var $ErrorStatus;
599 var $FailedCommand;
600 var $DebugLevel;
601
602 # image file extensions
603 private static $AxisImageFileExtensions = array(
604 IMGTYPE_JPEG => "jpg",
605 IMGTYPE_GIF => "gif",
606 IMGTYPE_BMP => "bmp",
607 IMGTYPE_PNG => "png",
608 );
609
610 function ReadSize()
611 {
612 # if we do not already have image info
613 if (!isset($this->ImageXSize))
614 {
615 # if we are using internal image functions
616 if ($this->ImageFormatSupportedByPhp())
617 {
618 # read size information from image object
619 $this->ImageXSize = imagesx($this->ImageObj);
620 $this->ImageYSize = imagesy($this->ImageObj);
621 }
622 else
623 {
624 # retrieve image info string
625 $Command = $this->DecodeCommand."| pnmfile ";
626 $Result = exec("$Command");
627
628 # parse image info string
629 $Pieces = preg_split("/\s+/", $Result);
630 $this->ImageXSize = $Pieces[3];
631 $this->ImageYSize = $Pieces[5];
632 }
633 }
634 }
635
636 function ImageFormatSupportedByPhp($Format = NULL)
637 {
638 if ($Format == NULL) { $Format = $this->Type(); }
639
640 if (!function_exists("imagetypes")) { return FALSE; }
641
642 switch ($Format)
643 {
644 case IMGTYPE_JPEG:
645 return (imagetypes() & IMG_JPG) ? TRUE : FALSE;
646 break;
647
648 case IMGTYPE_GIF:
649 return (imagetypes() & IMG_GIF) ? TRUE : FALSE;
650 break;
651
652 case IMGTYPE_BMP:
653 return FALSE;
654 break;
655
656 case IMGTYPE_PNG:
657 return (imagetypes() & IMG_PNG) ? TRUE : FALSE;
658 break;
659
660 default:
661 return FALSE;
662 break;
663 }
664 }
665
666 # check whether external executable is available and report result back to caller
667 function ExternalExecutableIsAvailable($ExecutableName)
668 {
669 static $ExecutableAvailable;
670
671 if (!isset($ExecutableAvailable[$ExecutableName]))
672 {
673 $Result = exec("which ".$ExecutableName." 2>&1");
674 $ExecutableAvailable[$ExecutableName] =
675 (basename($Result) == $ExecutableName) ? TRUE : FALSE;
676 }
677 return $ExecutableAvailable[$ExecutableName];
678 }
679 }
680
681 # image type definitions (these are purposefully different from those defined by PHP GD lib)
682 define("IMGTYPE_UNKNOWN", 0);
683 define("IMGTYPE_JPEG", 1);
684 define("IMGTYPE_GIF", 2);
685 define("IMGTYPE_BMP", 4);
686 define("IMGTYPE_PNG", 8);
687
688 # error status definitions
689 define("AI_OKAY", 0);
690 define("AI_FILEUNREADABLE", 1);
691 define("AI_IMGOBJCREATEFAILED", 2);
692 define("AI_PPMCMDFAILED", 4);
693 define("AI_INTERNALERROR", 8);
694 define("AI_UNKNOWNTYPE", 16);
695 define("AI_UNSUPPORTEDFORMAT", 32);
696 define("AI_DESTINATIONUNWRITABLE", 64);
697
698 # supply imagetypes() function if not defined
699 if (!function_exists("imagetypes"))
700 {
701 # (returning 0 indicates no image types supported)
702 function imagetypes() { return 0; }
703 }
704
705 ?>