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 ?>