00001 <?PHP 00002 00003 # 00004 # Axis--Image.php 00005 # A PHP Object to Support Image File Manipulation 00006 # 00007 # NOTE: To use this object, either PHP must have internal support for the image formats 00008 # to be manipulated (can be checked with the imagetypes() function) or the web server 00009 # must be running with safe_mode off and certain external executables must be available 00010 # to be invoked by the server. (A list of the needed executables can be obtained via 00011 # the Image::RequiredExternalExecutables() method.) 00012 # 00013 # Copyright 2002-2004 Axis Data 00014 # This code is free software that can be used or redistributed under the 00015 # terms of Version 2 of the GNU General Public License, as published by the 00016 # Free Software Foundation (http://www.fsf.org). 00017 # 00018 # Author: Edward Almasy (ealmasy@axisdata.com) 00019 # 00020 # Part of the AxisPHP library v1.2.5 00021 # For more information see http://www.axisdata.com/AxisPHP/ 00022 # 00023 00024 class Image { 00025 00026 # ---- PUBLIC INTERFACE -------------------------------------------------- 00027 00028 function Image($SourceFileName, $DebugLevel = 0) 00029 { 00030 # set debug level 00031 $this->DebugLevel = $DebugLevel; 00032 00033 # save source file name 00034 $this->SourceFileName = $SourceFileName; 00035 00036 # set default values 00037 $this->JpegSaveQuality = 80; 00038 $this->ErrorStatus = AI_OKAY; 00039 $this->FailedCommand = ""; 00040 00041 # get GD library version 00042 if (extension_loaded("gd")) 00043 { 00044 if (in_array("imagecreatetruecolor", get_extension_funcs("gd"))) 00045 { 00046 $this->GDVersion = 2; 00047 } 00048 else 00049 { 00050 $this->GDVersion = 1; 00051 } 00052 } 00053 else 00054 { 00055 $this->GDVersion = 0; 00056 } 00057 00058 # if source file is readable 00059 if (is_readable(realpath($SourceFileName))) 00060 { 00061 # if internal support is available for this image type 00062 if ($this->ImageFormatSupportedByPhp()) 00063 { 00064 if ($this->DebugLevel > 0) { print("AI: using internal funcs for decoding image<br>\n"); } 00065 00066 # create PHP image object 00067 switch ($this->Type()) 00068 { 00069 case IMGTYPE_JPEG: 00070 if ($this->DebugLevel > 1) { print("AI: file format is JPEG<br>\n"); } 00071 $this->ImageObj = imagecreatefromjpeg($this->SourceFileName); 00072 break; 00073 00074 case IMGTYPE_GIF: 00075 if ($this->DebugLevel > 1) { print("AI: file format is GIF<br>\n"); } 00076 $this->ImageObj = imagecreatefromgif($this->SourceFileName); 00077 break; 00078 00079 case IMGTYPE_BMP: 00080 if ($this->DebugLevel > 1) { print("AI: file format is BMP<br>\n"); } 00081 $this->ImageObj = imagecreatefrombmp($this->SourceFileName); 00082 break; 00083 00084 case IMGTYPE_PNG: 00085 if ($this->DebugLevel > 1) { print("AI: file format is PNG<br>\n"); } 00086 $this->ImageObj = imagecreatefrompng($this->SourceFileName); 00087 break; 00088 00089 default: 00090 $this->ErrorStatus = AI_INTERNALERROR; 00091 break; 00092 } 00093 00094 # if PHP image object creation failed 00095 if (strlen($this->ImageObj) == 0) 00096 { 00097 # set error status 00098 $this->ErrorStatus = AI_IMGOBJCREATEFAILED; 00099 } 00100 } 00101 00102 # if external command execution possible 00103 if (ini_get("safe_mode") != "1") 00104 { 00105 # determine external save command to convert image to portable format 00106 switch ($this->Type()) 00107 { 00108 case IMGTYPE_BMP: 00109 $this->DecodeCommand = "bmptoppm "; 00110 break; 00111 00112 case IMGTYPE_GIF: 00113 $this->DecodeCommand = "giftopnm "; 00114 break; 00115 00116 case IMGTYPE_PNG: 00117 $this->DecodeCommand = "pngtopnm "; 00118 break; 00119 00120 case IMGTYPE_JPEG: 00121 $this->DecodeCommand = "djpeg "; 00122 break; 00123 00124 default: 00125 $this->ErrorStatus = AI_UNKNOWNTYPE; 00126 break; 00127 } 00128 $this->DecodeCommand .= realpath($this->SourceFileName)." "; 00129 } 00130 else 00131 { 00132 # if format wasn't supported internally 00133 if (!$this->ImageFormatSupportedByPhp()) 00134 { 00135 # set error status to indicate unsupported image format 00136 $this->ErrorStatus = AI_UNSUPPORTEDFORMAT; 00137 } 00138 } 00139 } 00140 else 00141 { 00142 # set error status 00143 $this->ErrorStatus = AI_FILEUNREADABLE; 00144 } 00145 } 00146 00147 # save image with a new name and (optionally) a new type 00148 function SaveAs($FileName, $NewImageType = NULL) 00149 { 00150 # assume we will succeed 00151 $this->ErrorStatus = AI_OKAY; 00152 00153 # if destination file exists and is not writable 00154 if (file_exists($FileName) && (is_writable($FileName) != TRUE)) 00155 { 00156 # set error code 00157 $this->ErrorStatus = AI_DESTINATIONUNWRITABLE; 00158 } 00159 # else if destination directory is not writable 00160 elseif (is_writable(dirname($FileName)) != TRUE) 00161 { 00162 # set error code 00163 $this->ErrorStatus = AI_DESTINATIONUNWRITABLE; 00164 } 00165 else 00166 { 00167 # if no image type specified try to determine based on file name or use source file type 00168 if ($NewImageType == NULL) 00169 { 00170 if ($this->Type($FileName) != IMGTYPE_UNKNOWN) 00171 { $NewImageType = $this->Type($FileName); } 00172 else 00173 { $NewImageType = $this->Type(); } 00174 } 00175 00176 # if input and output types both supported by internal functions 00177 if ($this->ImageFormatSupportedByPhp() && $this->ImageFormatSupportedByPhp($NewImageType)) 00178 { 00179 # if image cropping or scaling was requested 00180 if (isset($this->CroppedXSize) 00181 || isset($this->ScaledXSize) 00182 || isset($this->ScaledYSize)) 00183 { 00184 # determine destination image size 00185 if (isset($this->ScaledXSize) && isset($this->ScaledYSize) 00186 && ($this->MaintainAspectRatio != TRUE)) 00187 { 00188 $DstXSize = $this->ScaledXSize; 00189 $DstYSize = $this->ScaledYSize; 00190 } 00191 elseif (isset($this->ScaledXSize) 00192 || ($this->ScaledXSize > $this->ScaledYSize)) 00193 { 00194 $DstXSize = $this->ScaledXSize; 00195 $DstYSize = ($this->ScaledXSize * $this->YSize()) 00196 / $this->XSize(); 00197 } 00198 elseif (isset($this->ScaledYSize)) 00199 { 00200 $DstXSize = ($this->ScaledYSize * $this->XSize()) 00201 / $this->YSize(); 00202 $DstYSize = $this->ScaledYSize; 00203 } 00204 elseif (isset($this->CroppedXSize)) 00205 { 00206 $DstXSize = $this->CroppedXSize; 00207 $DstYSize = $this->CroppedYSize; 00208 } 00209 else 00210 { 00211 $DstXSize = $this->XSize(); 00212 $DstYSize = $this->YSize(); 00213 } 00214 00215 # create destination image object 00216 if (($NewImageType == IMGTYPE_GIF) || ($this->GDVersion < 2)) 00217 { 00218 $DstImage = imagecreate($DstXSize, $DstYSize); 00219 } 00220 else 00221 { 00222 $DstImage = imagecreatetruecolor($DstXSize, $DstYSize); 00223 } 00224 00225 # determine area of source image to use 00226 if (isset($this->CroppedXSize)) 00227 { 00228 $SrcXSize = $this->CroppedXSize; 00229 $SrcYSize = $this->CroppedYSize; 00230 } 00231 else 00232 { 00233 $SrcXSize = $this->XSize(); 00234 $SrcYSize = $this->YSize(); 00235 } 00236 00237 # copy/scale portion of original image to destination image 00238 if ($this->GDVersion >= 2) 00239 { 00240 imagecopyresampled($DstImage, $this->ImageObj, 00241 0, 0, 00242 $this->CroppedXOrigin, $this->CroppedYOrigin, 00243 $DstXSize, $DstYSize, 00244 $SrcXSize, $SrcYSize); 00245 } 00246 else 00247 { 00248 imagecopyresized($DstImage, $this->ImageObj, 00249 0, 0, 00250 $this->CroppedXOrigin, $this->CroppedYOrigin, 00251 $DstXSize, $DstYSize, 00252 $SrcXSize, $SrcYSize); 00253 } 00254 } 00255 else 00256 { 00257 $DstImage =& $this->ImageObj; 00258 } 00259 00260 # save image to new file 00261 switch ($NewImageType) 00262 { 00263 case IMGTYPE_GIF: 00264 imagegif($DstImage, $FileName); 00265 break; 00266 00267 case IMGTYPE_JPEG: 00268 imagejpeg($DstImage, $FileName, $this->JpegSaveQuality); 00269 break; 00270 00271 case IMGTYPE_PNG: 00272 imagepng($DstImage, $FileName); 00273 break; 00274 00275 case IMGTYPE_BMP: 00276 imagewbmp($DstImage, $FileName); 00277 00278 default: 00279 $this->ErrorStatus = AI_INTERNALERROR; 00280 break; 00281 } 00282 } 00283 else 00284 { 00285 # build command (convert image to intermediate form) 00286 $Command = $this->DecodeCommand; 00287 00288 # build command (crop if requested) 00289 if (isset($this->CroppedXSize)) 00290 { 00291 $Command .= "| pnmcut ".$this->CroppedXOrigin." ".$this->CroppedYOrigin." " 00292 .$this->CroppedXSize." ".$this->CroppedYSize." "; 00293 } 00294 00295 # build command (scale if requested) 00296 if (isset($this->ScaledXSize) || isset($this->ScaledYSize)) 00297 { 00298 $Command .= "| pnmscale "; 00299 if ($this->MaintainAspectRatio 00300 && isset($this->ScaledXSize) && isset($this->ScaledYSize)) 00301 { 00302 $Command .= "-xysize ".$this->ScaledXSize." ".$this->ScaledYSize; 00303 } 00304 else 00305 { 00306 if (isset($this->ScaledXSize)) { $Command .= "-xsize ".$this->ScaledXSize." "; } 00307 if (isset($this->ScaledYSize)) { $Command .= "-ysize ".$this->ScaledYSize." "; } 00308 } 00309 } 00310 00311 # build command (convert to final form) 00312 switch ($NewImageType) 00313 { 00314 case IMGTYPE_BMP: 00315 $Command .= "| ppmquant 256 | ppmtobmp -windows "; 00316 break; 00317 00318 case IMGTYPE_GIF: 00319 $Command .= "| ppmquant 256 | ppmtogif "; 00320 break; 00321 00322 case IMGTYPE_PNG: 00323 $Command .= "| ppmquant 256 | pnmtopng "; 00324 break; 00325 00326 case IMGTYPE_JPEG: 00327 default: 00328 $Command .= "| cjpeg -quality ".$this->JpegSaveQuality." "; 00329 break; 00330 } 00331 00332 # build command (send output to new image file) 00333 $Command .= "> ".$FileName; 00334 00335 # execute command 00336 00337 $CommandResult = system($Command); 00338 00339 # set error status if command failed 00340 if ($CommandResult === FALSE) 00341 { 00342 $this->ErrorStatus = AI_PPMCMDFAILED; 00343 $this->FailedCommand = $Command; 00344 } 00345 } 00346 } 00347 00348 # report success or failure to caller 00349 return $this->ErrorStatus; 00350 } 00351 00352 # return the X (horizontal) image size in pixels 00353 function XSize() 00354 { 00355 $this->ReadSize(); 00356 return $this->ImageXSize; 00357 } 00358 00359 # return the Y (vertical) image size in pixels 00360 function YSize() 00361 { 00362 $this->ReadSize(); 00363 return $this->ImageYSize; 00364 } 00365 00366 # specify the size to scale the image to for the next SaveAs() 00367 function ScaleTo($ScaledXSize, $ScaledYSize, $MaintainAspectRatio = FALSE) 00368 { 00369 # save size for scaling 00370 $this->ScaledXSize = $ScaledXSize; 00371 $this->ScaledYSize = $ScaledYSize; 00372 $this->MaintainAspectRatio = $MaintainAspectRatio; 00373 } 00374 00375 # specify the size to crop the image to for the next SaveAs() 00376 function CropTo($CroppedXSize, $CroppedYSize, $CroppedXOrigin = 0, $CroppedYOrigin = 0) 00377 { 00378 # save origin and size for cropping 00379 $this->CroppedXSize = $CroppedXSize; 00380 $this->CroppedYSize = $CroppedYSize; 00381 $this->CroppedXOrigin = $CroppedXOrigin; 00382 $this->CroppedYOrigin = $CroppedYOrigin; 00383 } 00384 00385 # return the image type 00386 function Type($FileName = NULL) 00387 { 00388 if ($FileName == NULL) { $FileName = $this->SourceFileName; } 00389 if (preg_match("/.*\\.jp[e]{0,1}g$/i", $FileName)) { return IMGTYPE_JPEG; } 00390 elseif (preg_match("/.*\\.gif$/i", $FileName)) { return IMGTYPE_GIF; } 00391 elseif (preg_match("/.*\\.bmp$/i", $FileName)) { return IMGTYPE_BMP; } 00392 elseif (preg_match("/.*\\.png$/i", $FileName)) { return IMGTYPE_PNG; } 00393 else { return IMGTYPE_UNKNOWN; } 00394 } 00395 00396 # return the file name extension for the image 00397 static function Extension($Type = NULL) 00398 { 00399 if ($Type === NULL) 00400 { 00401 return Image::$AxisImageFileExtensions[$this->Type()]; 00402 } 00403 else 00404 { 00405 if (isset(Image::$AxisImageFileExtensions[$Type])) 00406 { 00407 return Image::$AxisImageFileExtensions[$Type]; 00408 } 00409 else 00410 { 00411 return NULL; 00412 } 00413 } 00414 } 00415 00416 # set/get the quality (0-100) for JPEG images created with SaveAs() 00417 function JpegQuality($NewSetting = NULL) 00418 { 00419 if ($NewSetting != NULL) { $this->JpegSaveQuality = $NewSetting; } 00420 return $this->JpegSaveQuality; 00421 } 00422 00423 # check availability of external executables and return list of any that are not found 00424 function MissingExternalExecutables() 00425 { 00426 # start with empty list of missing executables 00427 $MissingExecutables = array(); 00428 00429 # for each required executable 00430 foreach (Image::RequiredExternalExecutables() as $Executable) 00431 { 00432 # if executable did not appear to be available 00433 if (Image::ExternalExecutableIsAvailable($Executable) == FALSE) 00434 { 00435 # add executable to list of missing 00436 $MissingExecutables[] = $Executable; 00437 } 00438 } 00439 00440 # return list of missing executables to caller 00441 return $MissingExecutables; 00442 } 00443 00444 # return list of all required external executables 00445 function RequiredExternalExecutables($ImageTypes = NULL) 00446 { 00447 # start with the assumption that no executables are required 00448 $RequiredExecutables = array(); 00449 00450 # if no image types specified assume all image types 00451 if ($ImageTypes == NULL) 00452 { 00453 $ImageTypes = IMGTYPE_JPEG | IMGTYPE_GIF | IMGTYPE_BMP | IMGTYPE_PNG; 00454 } 00455 00456 # add format-specific executables that may or may not be needed 00457 if (($ImageTypes & IMGTYPE_JPEG) 00458 && (!function_exists("imagetypes") || !(imagetypes() & IMG_JPG))) 00459 { 00460 $RequiredExecutables[] = "djpeg"; 00461 $RequiredExecutables[] = "cjpeg"; 00462 } 00463 if (($ImageTypes & IMGTYPE_GIF) 00464 && (!function_exists("imagetypes") || !(imagetypes() & IMG_GIF))) 00465 { 00466 $RequiredExecutables[] = "giftopnm"; 00467 $RequiredExecutables[] = "ppmtogif"; 00468 } 00469 if (($ImageTypes & IMGTYPE_PNG) 00470 && (!function_exists("imagetypes") || !(imagetypes() & IMG_PNG))) 00471 { 00472 $RequiredExecutables[] = "pngtopnm"; 00473 $RequiredExecutables[] = "pnmtopng"; 00474 } 00475 if ($ImageTypes & IMGTYPE_BMP) 00476 { 00477 $RequiredExecutables[] = "bmptoppm"; 00478 $RequiredExecutables[] = "ppmtobmp"; 00479 } 00480 00481 # if any format-specific executables needed 00482 if (count($RequiredExecutables) != 0) 00483 { 00484 # add basic manipulation executables 00485 $RequiredExecutables[] = "pnmcut"; 00486 $RequiredExecutables[] = "pnmscale"; 00487 $RequiredExecutables[] = "ppmquant"; 00488 $RequiredExecutables[] = "pnmfile"; 00489 } 00490 00491 # return list of required executables to caller 00492 return $RequiredExecutables; 00493 } 00494 00495 # return supported image formats 00496 static function SupportedFormats() 00497 { 00498 # start out assuming no formats are supported 00499 $Supported = 0; 00500 00501 # if JPEG is supported by PHP or needed external executables are available 00502 if ((function_exists("imagetypes") && defined("IMG_JPG") 00503 && (imagetypes() & IMG_JPG)) 00504 || (Image::ExternalExecutableIsAvailable("djpeg") 00505 && Image::ExternalExecutableIsAvailable("cjpeg"))) 00506 { 00507 # add JPEG to list of supported formats 00508 $Supported |= IMGTYPE_JPEG; 00509 } 00510 00511 # if GIF is supported by PHP or needed external executables are available 00512 if ((function_exists("imagetypes") && defined("IMG_GIF") 00513 && (imagetypes() & IMG_GIF)) 00514 || (Image::ExternalExecutableIsAvailable("giftopnm") 00515 && Image::ExternalExecutableIsAvailable("ppmtogif"))) 00516 { 00517 # add GIF to list of supported formats 00518 $Supported |= IMGTYPE_GIF; 00519 } 00520 00521 # if PNG is supported by PHP or needed external executables are available 00522 if ((function_exists("imagetypes") && defined("IMG_PNG") 00523 && (imagetypes() & IMG_PNG)) 00524 || (Image::ExternalExecutableIsAvailable("pngtopnm") 00525 && Image::ExternalExecutableIsAvailable("pnmtopng"))) 00526 { 00527 # add PNG to list of supported formats 00528 $Supported |= IMGTYPE_PNG; 00529 } 00530 00531 # if needed external executables are available for BMP 00532 # needed executables being present is not sufficient for BMP support in PHP 00533 # test is being shortcutted to false to reflect no PHP support for BMPs 00534 if (0 && Image::ExternalExecutableIsAvailable("bmptoppm") 00535 && Image::ExternalExecutableIsAvailable("ppmtobmp")) 00536 { 00537 # add BMP to list of supported formats 00538 $Supported |= IMGTYPE_BMP; 00539 } 00540 00541 # report to caller what formats are supported 00542 return $Supported; 00543 } 00544 00545 # return names (upper-case extensions) of supported image formats 00546 static function SupportedFormatNames() 00547 { 00548 # assume that no formats are supported 00549 $FormatNames = array(); 00550 00551 # retrieve supported formats 00552 $SupportedFormats = Image::SupportedFormats(); 00553 00554 # for each possible supported format 00555 foreach (Image::$AxisImageFileExtensions as $ImageType => $ImageExtension) 00556 { 00557 # if format is supported 00558 if ($ImageType & $SupportedFormats) 00559 { 00560 # add format extension to list of supported image format names 00561 $FormatNames[] = strtoupper($ImageExtension); 00562 } 00563 } 00564 00565 # return supported image format names to caller 00566 return $FormatNames; 00567 } 00568 00569 # return the error status set by the constructor or the last call to SaveAs() 00570 function Status() 00571 { 00572 return $this->ErrorStatus; 00573 } 00574 00575 # return string containing external command that failed 00576 function FailedExternalCommand() 00577 { 00578 return $this->FailedCommand; 00579 } 00580 00581 00582 # ---- PRIVATE INTERFACE ------------------------------------------------- 00583 00584 var $GDVersion; 00585 var $ImageObj; 00586 var $SourceFileName; 00587 var $ImageXSize; 00588 var $ImageYSize; 00589 var $ScaledXSize; 00590 var $ScaledYSize; 00591 var $MaintainAspectRatio; 00592 var $CroppedXSize; 00593 var $CroppedYSize; 00594 var $CroppedXOrigin; 00595 var $CroppedYOrigin; 00596 var $JpegSaveQuality; 00597 var $DecodeCommand; 00598 var $ErrorStatus; 00599 var $FailedCommand; 00600 var $DebugLevel; 00601 00602 # image file extensions 00603 private static $AxisImageFileExtensions = array( 00604 IMGTYPE_JPEG => "jpg", 00605 IMGTYPE_GIF => "gif", 00606 IMGTYPE_BMP => "bmp", 00607 IMGTYPE_PNG => "png", 00608 ); 00609 00610 function ReadSize() 00611 { 00612 # if we do not already have image info 00613 if (!isset($this->ImageXSize)) 00614 { 00615 # if we are using internal image functions 00616 if ($this->ImageFormatSupportedByPhp()) 00617 { 00618 # read size information from image object 00619 $this->ImageXSize = imagesx($this->ImageObj); 00620 $this->ImageYSize = imagesy($this->ImageObj); 00621 } 00622 else 00623 { 00624 # retrieve image info string 00625 $Command = $this->DecodeCommand."| pnmfile "; 00626 $Result = exec("$Command"); 00627 00628 # parse image info string 00629 $Pieces = preg_split("/\s+/", $Result); 00630 $this->ImageXSize = $Pieces[3]; 00631 $this->ImageYSize = $Pieces[5]; 00632 } 00633 } 00634 } 00635 00636 function ImageFormatSupportedByPhp($Format = NULL) 00637 { 00638 if ($Format == NULL) { $Format = $this->Type(); } 00639 00640 if (!function_exists("imagetypes")) { return FALSE; } 00641 00642 switch ($Format) 00643 { 00644 case IMGTYPE_JPEG: 00645 return (imagetypes() & IMG_JPG) ? TRUE : FALSE; 00646 break; 00647 00648 case IMGTYPE_GIF: 00649 return (imagetypes() & IMG_GIF) ? TRUE : FALSE; 00650 break; 00651 00652 case IMGTYPE_BMP: 00653 return FALSE; 00654 break; 00655 00656 case IMGTYPE_PNG: 00657 return (imagetypes() & IMG_PNG) ? TRUE : FALSE; 00658 break; 00659 00660 default: 00661 return FALSE; 00662 break; 00663 } 00664 } 00665 00666 # check whether external executable is available and report result back to caller 00667 function ExternalExecutableIsAvailable($ExecutableName) 00668 { 00669 static $ExecutableAvailable; 00670 00671 if (!isset($ExecutableAvailable[$ExecutableName])) 00672 { 00673 $Result = exec("which ".$ExecutableName." 2>&1"); 00674 $ExecutableAvailable[$ExecutableName] = 00675 (basename($Result) == $ExecutableName) ? TRUE : FALSE; 00676 } 00677 return $ExecutableAvailable[$ExecutableName]; 00678 } 00679 } 00680 00681 # image type definitions (these are purposefully different from those defined by PHP GD lib) 00682 define("IMGTYPE_UNKNOWN", 0); 00683 define("IMGTYPE_JPEG", 1); 00684 define("IMGTYPE_GIF", 2); 00685 define("IMGTYPE_BMP", 4); 00686 define("IMGTYPE_PNG", 8); 00687 00688 # error status definitions 00689 define("AI_OKAY", 0); 00690 define("AI_FILEUNREADABLE", 1); 00691 define("AI_IMGOBJCREATEFAILED", 2); 00692 define("AI_PPMCMDFAILED", 4); 00693 define("AI_INTERNALERROR", 8); 00694 define("AI_UNKNOWNTYPE", 16); 00695 define("AI_UNSUPPORTEDFORMAT", 32); 00696 define("AI_DESTINATIONUNWRITABLE", 64); 00697 00698 # supply imagetypes() function if not defined 00699 if (!function_exists("imagetypes")) 00700 { 00701 # (returning 0 indicates no image types supported) 00702 function imagetypes() { return 0; } 00703 } 00704 00705 ?>