2015-01-29 21:16:32 +01:00
< ? php
/**
* Resize and crop images on the fly , store generated images in a cache .
*
2015-02-02 12:39:58 +01:00
* This version is a all - in - one version of img . php , it is not dependant an any other file
2015-07-23 11:32:19 +02:00
* so you can simply copy it to any place you want it .
2015-02-02 12:39:58 +01:00
*
2015-01-29 21:16:32 +01:00
* @ author Mikael Roos mos @ dbwebb . se
* @ example http :// dbwebb . se / opensource / cimage
* @ link https :// github . com / mosbth / cimage
*
*/
2016-08-08 09:29:26 +02:00
define ( " CIMAGE_BUNDLE " , true );
2015-01-29 21:16:32 +01:00
/**
* Change configuration details in the array below or create a separate file
2015-07-23 11:32:19 +02:00
* where you store the configuration details .
2015-02-02 12:39:58 +01:00
*
2015-07-23 11:32:19 +02:00
* The configuration file should be named the same name as this file and then
2015-02-02 12:39:58 +01:00
* add '_config.php' . If this file is named 'img.php' then name the
* config file should be named 'img_config.php' .
*
2015-01-29 21:16:32 +01:00
* The settings below are only a few of the available ones . Check the file in
* webroot / img_config . php for a complete list of configuration options .
*/
$config = array (
2015-02-02 12:43:55 +01:00
'mode' => 'development' , // 'production', 'development', 'strict'
2015-01-29 21:16:32 +01:00
//'image_path' => __DIR__ . '/img/',
//'cache_path' => __DIR__ . '/../cache/',
//'alias_path' => __DIR__ . '/img/alias/',
2015-02-02 12:39:58 +01:00
//'remote_allow' => true,
//'password' => false, // "secret-password",
2015-01-29 21:16:32 +01:00
);
2016-08-08 09:29:26 +02:00
// Version of cimage and img.php
2020-05-06 09:51:12 +02:00
define ( " CIMAGE_VERSION " , " v0.7.23 (2020-05-06) " );
2016-08-08 09:29:26 +02:00
// For CRemoteImage
define ( " CIMAGE_USER_AGENT " , " CImage/ " . CIMAGE_VERSION );
2016-08-09 10:19:49 +02:00
// Image type IMG_WEBP is only defined from 5.6.25
if ( ! defined ( " IMG_WEBP " )) {
define ( " IMG_WEBP " , - 1 );
}
2016-08-08 09:29:26 +02:00
/**
* General functions to use in img . php .
*/
/**
* Trace and log execution to logfile , useful for debugging and development .
*
* @ param string $msg message to log to file .
*
* @ return void
*/
function trace ( $msg )
{
2016-08-08 10:39:11 +02:00
$file = CIMAGE_DEBUG_FILE ;
2016-08-08 09:29:26 +02:00
if ( ! is_writable ( $file )) {
2016-08-08 11:19:53 +02:00
return ;
2016-08-08 09:29:26 +02:00
}
2016-08-08 11:19:53 +02:00
$timer = number_format (( microtime ( true ) - $_SERVER [ " REQUEST_TIME_FLOAT " ]), 6 );
$details = " { $timer } ms " ;
2016-08-08 10:39:11 +02:00
$details .= " : " . round ( memory_get_peak_usage () / 1024 / 1024 , 3 ) . " MB " ;
$details .= " : " . count ( get_included_files ());
file_put_contents ( $file , " $details : $msg\n " , FILE_APPEND );
2016-08-08 09:29:26 +02:00
}
/**
* Display error message .
*
* @ param string $msg to display .
* @ param int $type of HTTP error to display .
*
* @ return void
*/
function errorPage ( $msg , $type = 500 )
{
global $mode ;
switch ( $type ) {
case 403 :
$header = " 403 Forbidden " ;
break ;
case 404 :
$header = " 404 Not Found " ;
break ;
default :
$header = " 500 Internal Server Error " ;
}
if ( $mode == " strict " ) {
$header = " 404 Not Found " ;
}
header ( " HTTP/1.0 $header " );
if ( $mode == " development " ) {
die ( " [img.php] $msg " );
}
error_log ( " [img.php] $msg " );
die ( " HTTP/1.0 $header " );
}
/**
* Get input from query string or return default value if not set .
*
* @ param mixed $key as string or array of string values to look for in $_GET .
* @ param mixed $default value to return when $key is not set in $_GET .
*
* @ return mixed value from $_GET or default value .
*/
function get ( $key , $default = null )
{
if ( is_array ( $key )) {
foreach ( $key as $val ) {
if ( isset ( $_GET [ $val ])) {
return $_GET [ $val ];
}
}
} elseif ( isset ( $_GET [ $key ])) {
return $_GET [ $key ];
}
return $default ;
}
/**
* Get input from query string and set to $defined if defined or else $undefined .
*
* @ param mixed $key as string or array of string values to look for in $_GET .
* @ param mixed $defined value to return when $key is set in $_GET .
* @ param mixed $undefined value to return when $key is not set in $_GET .
*
* @ return mixed value as $defined or $undefined .
*/
function getDefined ( $key , $defined , $undefined )
{
return get ( $key ) === null ? $undefined : $defined ;
}
/**
* Get value from config array or default if key is not set in config array .
*
* @ param string $key the key in the config array .
* @ param mixed $default value to be default if $key is not set in config .
*
* @ return mixed value as $config [ $key ] or $default .
*/
function getConfig ( $key , $default )
{
global $config ;
return isset ( $config [ $key ])
? $config [ $key ]
: $default ;
}
/**
* Log when verbose mode , when used without argument it returns the result .
*
* @ param string $msg to log .
*
* @ return void or array .
*/
function verbose ( $msg = null )
{
global $verbose , $verboseFile ;
static $log = array ();
if ( ! ( $verbose || $verboseFile )) {
return ;
}
if ( is_null ( $msg )) {
return $log ;
}
$log [] = $msg ;
}
2016-08-09 13:02:25 +02:00
/**
* Log when verbose mode , when used without argument it returns the result .
*
* @ param string $msg to log .
*
* @ return void or array .
*/
function checkExternalCommand ( $what , $enabled , $commandString )
{
$no = $enabled ? null : 'NOT' ;
$text = " Post processing $what is $no enabled.<br> " ;
list ( $command ) = explode ( " " , $commandString );
$no = is_executable ( $command ) ? null : 'NOT' ;
$text .= " The command for $what is $no an executable.<br> " ;
return $text ;
}
2015-01-29 21:16:32 +01:00
/**
* Get a image from a remote server using HTTP GET and If - Modified - Since .
*
*/
class CHttpGet
{
private $request = array ();
private $response = array ();
/**
* Constructor
*
*/
public function __construct ()
{
$this -> request [ 'header' ] = array ();
}
2015-08-17 20:35:25 +02:00
/**
* Build an encoded url .
*
* @ param string $baseUrl This is the original url which will be merged .
* @ param string $merge Thse parts should be merged into the baseUrl ,
* the format is as parse_url .
*
* @ return string $url as the modified url .
*/
public function buildUrl ( $baseUrl , $merge )
{
$parts = parse_url ( $baseUrl );
$parts = array_merge ( $parts , $merge );
$url = $parts [ 'scheme' ];
$url .= " :// " ;
$url .= $parts [ 'host' ];
$url .= isset ( $parts [ 'port' ])
? " : " . $parts [ 'port' ]
: " " ;
$url .= $parts [ 'path' ];
return $url ;
}
2015-01-29 21:16:32 +01:00
/**
* Set the url for the request .
*
* @ param string $url
*
* @ return $this
*/
public function setUrl ( $url )
{
2015-08-17 20:35:25 +02:00
$parts = parse_url ( $url );
$path = " " ;
if ( isset ( $parts [ 'path' ])) {
$pathParts = explode ( '/' , $parts [ 'path' ]);
unset ( $pathParts [ 0 ]);
foreach ( $pathParts as $value ) {
$path .= " / " . rawurlencode ( $value );
}
}
$url = $this -> buildUrl ( $url , array ( " path " => $path ));
2015-01-29 21:16:32 +01:00
$this -> request [ 'url' ] = $url ;
return $this ;
}
/**
* Set custom header field for the request .
*
* @ param string $field
* @ param string $value
*
* @ return $this
*/
public function setHeader ( $field , $value )
{
$this -> request [ 'header' ][] = " $field : $value " ;
return $this ;
}
/**
* Set header fields for the request .
*
* @ param string $field
* @ param string $value
*
* @ return $this
*/
public function parseHeader ()
{
2015-03-18 15:56:14 +01:00
//$header = explode("\r\n", rtrim($this->response['headerRaw'], "\r\n"));
$rawHeaders = rtrim ( $this -> response [ 'headerRaw' ], " \r \n " );
# Handle multiple responses e.g. with redirections (proxies too)
$headerGroups = explode ( " \r \n \r \n " , $rawHeaders );
# We're only interested in the last one
$header = explode ( " \r \n " , end ( $headerGroups ));
2015-01-29 21:16:32 +01:00
$output = array ();
if ( 'HTTP' === substr ( $header [ 0 ], 0 , 4 )) {
list ( $output [ 'version' ], $output [ 'status' ]) = explode ( ' ' , $header [ 0 ]);
unset ( $header [ 0 ]);
}
foreach ( $header as $entry ) {
$pos = strpos ( $entry , ':' );
$output [ trim ( substr ( $entry , 0 , $pos ))] = trim ( substr ( $entry , $pos + 1 ));
}
$this -> response [ 'header' ] = $output ;
return $this ;
}
/**
* Perform the request .
*
* @ param boolean $debug set to true to dump headers .
*
2015-08-17 20:35:25 +02:00
* @ throws Exception when curl fails to retrieve url .
*
2015-01-29 21:16:32 +01:00
* @ return boolean
*/
public function doGet ( $debug = false )
{
$options = array (
CURLOPT_URL => $this -> request [ 'url' ],
CURLOPT_HEADER => 1 ,
CURLOPT_HTTPHEADER => $this -> request [ 'header' ],
CURLOPT_AUTOREFERER => true ,
CURLOPT_RETURNTRANSFER => true ,
CURLINFO_HEADER_OUT => $debug ,
CURLOPT_CONNECTTIMEOUT => 5 ,
CURLOPT_TIMEOUT => 5 ,
2015-03-18 15:56:14 +01:00
CURLOPT_FOLLOWLOCATION => true ,
CURLOPT_MAXREDIRS => 2 ,
2015-01-29 21:16:32 +01:00
);
$ch = curl_init ();
curl_setopt_array ( $ch , $options );
$response = curl_exec ( $ch );
if ( ! $response ) {
2015-08-17 20:35:25 +02:00
throw new Exception ( " Failed retrieving url, details follows: " . curl_error ( $ch ));
2015-01-29 21:16:32 +01:00
}
$headerSize = curl_getinfo ( $ch , CURLINFO_HEADER_SIZE );
$this -> response [ 'headerRaw' ] = substr ( $response , 0 , $headerSize );
$this -> response [ 'body' ] = substr ( $response , $headerSize );
$this -> parseHeader ();
if ( $debug ) {
$info = curl_getinfo ( $ch );
echo " Request header<br><pre> " , var_dump ( $info [ 'request_header' ]), " </pre> " ;
echo " Response header (raw)<br><pre> " , var_dump ( $this -> response [ 'headerRaw' ]), " </pre> " ;
echo " Response header (parsed)<br><pre> " , var_dump ( $this -> response [ 'header' ]), " </pre> " ;
}
curl_close ( $ch );
return true ;
}
/**
* Get HTTP code of response .
*
* @ return integer as HTTP status code or null if not available .
*/
public function getStatus ()
{
return isset ( $this -> response [ 'header' ][ 'status' ])
? ( int ) $this -> response [ 'header' ][ 'status' ]
: null ;
}
/**
* Get file modification time of response .
*
* @ return int as timestamp .
*/
public function getLastModified ()
{
return isset ( $this -> response [ 'header' ][ 'Last-Modified' ])
? strtotime ( $this -> response [ 'header' ][ 'Last-Modified' ])
: null ;
}
/**
* Get content type .
*
* @ return string as the content type or null if not existing or invalid .
*/
public function getContentType ()
{
$type = isset ( $this -> response [ 'header' ][ 'Content-Type' ])
? $this -> response [ 'header' ][ 'Content-Type' ]
: null ;
return preg_match ( '#[a-z]+/[a-z]+#' , $type )
? $type
: null ;
}
/**
* Get file modification time of response .
*
* @ param mixed $default as default value ( int seconds ) if date is
* missing in response header .
*
* @ return int as timestamp or $default if Date is missing in
* response header .
*/
public function getDate ( $default = false )
{
return isset ( $this -> response [ 'header' ][ 'Date' ])
? strtotime ( $this -> response [ 'header' ][ 'Date' ])
: $default ;
}
/**
* Get max age of cachable item .
*
* @ param mixed $default as default value if date is missing in response
* header .
*
* @ return int as timestamp or false if not available .
*/
public function getMaxAge ( $default = false )
{
$cacheControl = isset ( $this -> response [ 'header' ][ 'Cache-Control' ])
? $this -> response [ 'header' ][ 'Cache-Control' ]
: null ;
$maxAge = null ;
if ( $cacheControl ) {
// max-age=2592000
$part = explode ( '=' , $cacheControl );
$maxAge = ( $part [ 0 ] == " max-age " )
? ( int ) $part [ 1 ]
: null ;
}
if ( $maxAge ) {
return $maxAge ;
}
$expire = isset ( $this -> response [ 'header' ][ 'Expires' ])
? strtotime ( $this -> response [ 'header' ][ 'Expires' ])
: null ;
return $expire ? $expire : $default ;
}
/**
* Get body of response .
*
* @ return string as body .
*/
public function getBody ()
{
return $this -> response [ 'body' ];
}
}
/**
* Get a image from a remote server using HTTP GET and If - Modified - Since .
*
*/
class CRemoteImage
{
/**
* Path to cache files .
*/
private $saveFolder = null ;
/**
* Use cache or not .
*/
private $useCache = true ;
/**
* HTTP object to aid in download file .
*/
private $http ;
/**
* Status of the HTTP request .
*/
private $status ;
/**
* Defalt age for cached items 60 * 60 * 24 * 7.
*/
private $defaultMaxAge = 604800 ;
/**
* Url of downloaded item .
*/
private $url ;
/**
2015-03-16 15:36:06 +01:00
* Base name of cache file for downloaded item and name of image .
2015-01-29 21:16:32 +01:00
*/
private $fileName ;
/**
* Filename for json - file with details of cached item .
*/
private $fileJson ;
/**
* Cache details loaded from file .
*/
private $cache ;
/**
* Get status of last HTTP request .
*
* @ return int as status
*/
public function getStatus ()
{
return $this -> status ;
}
/**
* Get JSON details for cache item .
*
* @ return array with json details on cache .
*/
public function getDetails ()
{
return $this -> cache ;
}
/**
* Set the path to the cache directory .
*
* @ param boolean $use true to use the cache and false to ignore cache .
*
* @ return $this
*/
public function setCache ( $path )
{
2015-12-07 16:02:17 +01:00
$this -> saveFolder = rtrim ( $path , " / " ) . " / " ;
2015-01-29 21:16:32 +01:00
return $this ;
}
/**
* Check if cache is writable or throw exception .
*
* @ return $this
*
* @ throws Exception if cahce folder is not writable .
*/
public function isCacheWritable ()
{
if ( ! is_writable ( $this -> saveFolder )) {
throw new Exception ( " Cache folder is not writable for downloaded files. " );
}
return $this ;
}
/**
* Decide if the cache should be used or not before trying to download
* a remote file .
*
* @ param boolean $use true to use the cache and false to ignore cache .
*
* @ return $this
*/
public function useCache ( $use = true )
{
$this -> useCache = $use ;
return $this ;
}
/**
* Set header fields .
*
* @ return $this
*/
2015-07-23 11:32:19 +02:00
public function setHeaderFields ()
{
2015-12-07 15:36:06 +01:00
$cimageVersion = " CImage " ;
if ( defined ( " CIMAGE_USER_AGENT " )) {
$cimageVersion = CIMAGE_USER_AGENT ;
}
$this -> http -> setHeader ( " User-Agent " , " $cimageVersion (PHP/ " . phpversion () . " cURL) " );
2015-01-29 21:16:32 +01:00
$this -> http -> setHeader ( " Accept " , " image/jpeg,image/png,image/gif " );
if ( $this -> useCache ) {
$this -> http -> setHeader ( " Cache-Control " , " max-age=0 " );
} else {
$this -> http -> setHeader ( " Cache-Control " , " no-cache " );
$this -> http -> setHeader ( " Pragma " , " no-cache " );
}
}
/**
* Save downloaded resource to cache .
*
* @ return string as path to saved file or false if not saved .
*/
2015-07-23 11:32:19 +02:00
public function save ()
{
2015-01-29 21:16:32 +01:00
$this -> cache = array ();
$date = $this -> http -> getDate ( time ());
$maxAge = $this -> http -> getMaxAge ( $this -> defaultMaxAge );
$lastModified = $this -> http -> getLastModified ();
$type = $this -> http -> getContentType ();
$this -> cache [ 'Date' ] = gmdate ( " D, d M Y H:i:s T " , $date );
$this -> cache [ 'Max-Age' ] = $maxAge ;
$this -> cache [ 'Content-Type' ] = $type ;
2015-03-16 15:36:06 +01:00
$this -> cache [ 'Url' ] = $this -> url ;
2015-01-29 21:16:32 +01:00
if ( $lastModified ) {
$this -> cache [ 'Last-Modified' ] = gmdate ( " D, d M Y H:i:s T " , $lastModified );
}
2015-03-16 15:36:06 +01:00
// Save only if body is a valid image
$body = $this -> http -> getBody ();
$img = imagecreatefromstring ( $body );
2015-01-29 21:16:32 +01:00
2015-03-16 15:36:06 +01:00
if ( $img !== false ) {
file_put_contents ( $this -> fileName , $body );
file_put_contents ( $this -> fileJson , json_encode ( $this -> cache ));
return $this -> fileName ;
2015-01-29 21:16:32 +01:00
}
return false ;
}
/**
* Got a 304 and updates cache with new age .
*
* @ return string as path to cached file .
*/
2015-07-23 11:32:19 +02:00
public function updateCacheDetails ()
{
2015-01-29 21:16:32 +01:00
$date = $this -> http -> getDate ( time ());
$maxAge = $this -> http -> getMaxAge ( $this -> defaultMaxAge );
$lastModified = $this -> http -> getLastModified ();
2015-03-16 15:36:06 +01:00
$this -> cache [ 'Date' ] = gmdate ( " D, d M Y H:i:s T " , $date );
$this -> cache [ 'Max-Age' ] = $maxAge ;
2015-01-29 21:16:32 +01:00
if ( $lastModified ) {
$this -> cache [ 'Last-Modified' ] = gmdate ( " D, d M Y H:i:s T " , $lastModified );
}
file_put_contents ( $this -> fileJson , json_encode ( $this -> cache ));
2015-03-16 15:36:06 +01:00
return $this -> fileName ;
2015-01-29 21:16:32 +01:00
}
/**
* Download a remote file and keep a cache of downloaded files .
*
* @ param string $url a remote url .
*
2015-08-17 20:35:25 +02:00
* @ throws Exception when status code does not match 200 or 304.
*
2015-01-29 21:16:32 +01:00
* @ return string as path to downloaded file or false if failed .
*/
2015-07-23 11:32:19 +02:00
public function download ( $url )
{
2015-01-29 21:16:32 +01:00
$this -> http = new CHttpGet ();
$this -> url = $url ;
// First check if the cache is valid and can be used
$this -> loadCacheDetails ();
if ( $this -> useCache ) {
$src = $this -> getCachedSource ();
if ( $src ) {
$this -> status = 1 ;
return $src ;
}
}
// Do a HTTP request to download item
$this -> setHeaderFields ();
$this -> http -> setUrl ( $this -> url );
$this -> http -> doGet ();
$this -> status = $this -> http -> getStatus ();
if ( $this -> status === 200 ) {
$this -> isCacheWritable ();
return $this -> save ();
2015-07-23 11:32:19 +02:00
} elseif ( $this -> status === 304 ) {
2015-01-29 21:16:32 +01:00
$this -> isCacheWritable ();
return $this -> updateCacheDetails ();
}
2015-08-17 20:35:25 +02:00
throw new Exception ( " Unknown statuscode when downloading remote image: " . $this -> status );
2015-01-29 21:16:32 +01:00
}
/**
* Get the path to the cached image file if the cache is valid .
*
* @ return $this
*/
public function loadCacheDetails ()
{
2015-03-16 15:36:06 +01:00
$cacheFile = md5 ( $this -> url );
2015-01-29 21:16:32 +01:00
$this -> fileName = $this -> saveFolder . $cacheFile ;
$this -> fileJson = $this -> fileName . " .json " ;
if ( is_readable ( $this -> fileJson )) {
$this -> cache = json_decode ( file_get_contents ( $this -> fileJson ), true );
}
}
/**
* Get the path to the cached image file if the cache is valid .
*
* @ return string as the path ot the image file or false if no cache .
*/
public function getCachedSource ()
{
2015-03-16 15:36:06 +01:00
$imageExists = is_readable ( $this -> fileName );
2015-01-29 21:16:32 +01:00
// Is cache valid?
$date = strtotime ( $this -> cache [ 'Date' ]);
$maxAge = $this -> cache [ 'Max-Age' ];
2015-03-16 15:36:06 +01:00
$now = time ();
2015-01-29 21:16:32 +01:00
if ( $imageExists && $date + $maxAge > $now ) {
2015-03-16 15:36:06 +01:00
return $this -> fileName ;
2015-01-29 21:16:32 +01:00
}
// Prepare for a 304 if available
if ( $imageExists && isset ( $this -> cache [ 'Last-Modified' ])) {
$this -> http -> setHeader ( " If-Modified-Since " , $this -> cache [ 'Last-Modified' ]);
}
return false ;
}
}
2015-09-01 16:53:19 +02:00
/**
* Act as whitelist ( or blacklist ) .
*
*/
class CWhitelist
{
/**
* Array to contain the whitelist options .
*/
private $whitelist = array ();
/**
* Set the whitelist from an array of strings , each item in the
* whitelist should be a regexp without the surrounding / or #.
*
* @ param array $whitelist with all valid options ,
* default is to clear the whitelist .
*
* @ return $this
*/
public function set ( $whitelist = array ())
{
if ( ! is_array ( $whitelist )) {
throw new Exception ( " Whitelist is not of a supported format. " );
}
$this -> whitelist = $whitelist ;
return $this ;
}
/**
* Check if item exists in the whitelist .
*
* @ param string $item string to check .
* @ param array $whitelist optional with all valid options , default is null .
*
* @ return boolean true if item is in whitelist , else false .
*/
public function check ( $item , $whitelist = null )
{
if ( $whitelist !== null ) {
$this -> set ( $whitelist );
}
if ( empty ( $item ) or empty ( $this -> whitelist )) {
return false ;
}
foreach ( $this -> whitelist as $regexp ) {
if ( preg_match ( " # $regexp # " , $item )) {
return true ;
}
}
return false ;
}
}
/**
* Create an ASCII version of an image .
*
*/
class CAsciiArt
{
/**
* Character set to use .
*/
private $characterSet = array (
'one' => " #0XT|:,.' " ,
'two' => " @%#*+=-:. " ,
'three' => " $ @B%8&WM#*oahkbdpqwmZO0QLCJUYXzcvunxrjft/ \ |()1 { }[]?-_+~<>i!lI;:, \" ^`'. "
);
/**
* Current character set .
*/
private $characters = null ;
/**
* Length of current character set .
*/
private $charCount = null ;
/**
* Scale of the area to swap to a character .
*/
private $scale = null ;
/**
* Strategy to calculate luminance .
*/
private $luminanceStrategy = null ;
/**
* Constructor which sets default options .
*/
public function __construct ()
{
$this -> setOptions ();
}
/**
* Add a custom character set .
*
* @ param string $key for the character set .
* @ param string $value for the character set .
*
* @ return $this
*/
public function addCharacterSet ( $key , $value )
{
$this -> characterSet [ $key ] = $value ;
return $this ;
}
/**
2015-09-15 17:26:04 +02:00
* Set options for processing , defaults are available .
2015-09-01 16:53:19 +02:00
*
* @ param array $options to use as default settings .
*
* @ return $this
*/
public function setOptions ( $options = array ())
{
$default = array (
" characterSet " => 'two' ,
" scale " => 14 ,
" luminanceStrategy " => 3 ,
" customCharacterSet " => null ,
);
$default = array_merge ( $default , $options );
if ( ! is_null ( $default [ 'customCharacterSet' ])) {
$this -> addCharacterSet ( 'custom' , $default [ 'customCharacterSet' ]);
$default [ 'characterSet' ] = 'custom' ;
}
$this -> scale = $default [ 'scale' ];
$this -> characters = $this -> characterSet [ $default [ 'characterSet' ]];
$this -> charCount = strlen ( $this -> characters );
$this -> luminanceStrategy = $default [ 'luminanceStrategy' ];
return $this ;
}
/**
* Create an Ascii image from an image file .
*
* @ param string $filename of the image to use .
*
* @ return string $ascii with the ASCII image .
*/
public function createFromFile ( $filename )
{
$img = imagecreatefromstring ( file_get_contents ( $filename ));
list ( $width , $height ) = getimagesize ( $filename );
$ascii = null ;
$incY = $this -> scale ;
$incX = $this -> scale / 2 ;
for ( $y = 0 ; $y < $height - 1 ; $y += $incY ) {
for ( $x = 0 ; $x < $width - 1 ; $x += $incX ) {
$toX = min ( $x + $this -> scale / 2 , $width - 1 );
$toY = min ( $y + $this -> scale , $height - 1 );
$luminance = $this -> luminanceAreaAverage ( $img , $x , $y , $toX , $toY );
$ascii .= $this -> luminance2character ( $luminance );
}
$ascii .= PHP_EOL ;
}
return $ascii ;
}
/**
* Get the luminance from a region of an image using average color value .
*
* @ param string $img the image .
* @ param integer $x1 the area to get pixels from .
* @ param integer $y1 the area to get pixels from .
* @ param integer $x2 the area to get pixels from .
* @ param integer $y2 the area to get pixels from .
*
* @ return integer $luminance with a value between 0 and 100.
*/
public function luminanceAreaAverage ( $img , $x1 , $y1 , $x2 , $y2 )
{
$numPixels = ( $x2 - $x1 + 1 ) * ( $y2 - $y1 + 1 );
$luminance = 0 ;
for ( $x = $x1 ; $x <= $x2 ; $x ++ ) {
for ( $y = $y1 ; $y <= $y2 ; $y ++ ) {
$rgb = imagecolorat ( $img , $x , $y );
$red = (( $rgb >> 16 ) & 0xFF );
$green = (( $rgb >> 8 ) & 0xFF );
$blue = ( $rgb & 0xFF );
$luminance += $this -> getLuminance ( $red , $green , $blue );
}
}
return $luminance / $numPixels ;
}
/**
* Calculate luminance value with different strategies .
*
* @ param integer $red The color red .
* @ param integer $green The color green .
* @ param integer $blue The color blue .
*
* @ return float $luminance with a value between 0 and 1.
*/
public function getLuminance ( $red , $green , $blue )
{
2015-12-07 15:36:06 +01:00
switch ( $this -> luminanceStrategy ) {
2015-09-01 16:53:19 +02:00
case 1 :
$luminance = ( $red * 0.2126 + $green * 0.7152 + $blue * 0.0722 ) / 255 ;
break ;
case 2 :
$luminance = ( $red * 0.299 + $green * 0.587 + $blue * 0.114 ) / 255 ;
break ;
case 3 :
$luminance = sqrt ( 0.299 * pow ( $red , 2 ) + 0.587 * pow ( $green , 2 ) + 0.114 * pow ( $blue , 2 )) / 255 ;
break ;
case 0 :
default :
$luminance = ( $red + $green + $blue ) / ( 255 * 3 );
}
return $luminance ;
}
/**
* Translate the luminance value to a character .
*
* @ param string $position a value between 0 - 100 representing the
* luminance .
*
* @ return string with the ascii character .
*/
public function luminance2character ( $luminance )
{
$position = ( int ) round ( $luminance * ( $this -> charCount - 1 ));
$char = $this -> characters [ $position ];
return $char ;
}
}
2015-01-29 21:16:32 +01:00
/**
* Resize and crop images on the fly , store generated images in a cache .
*
* @ author Mikael Roos mos @ dbwebb . se
* @ example http :// dbwebb . se / opensource / cimage
* @ link https :// github . com / mosbth / cimage
*/
class CImage
{
/**
* Constants type of PNG image
*/
const PNG_GREYSCALE = 0 ;
const PNG_RGB = 2 ;
const PNG_RGB_PALETTE = 3 ;
const PNG_GREYSCALE_ALPHA = 4 ;
const PNG_RGB_ALPHA = 6 ;
/**
* Constant for default image quality when not set
*/
const JPEG_QUALITY_DEFAULT = 60 ;
/**
* Quality level for JPEG images .
*/
private $quality ;
/**
* Is the quality level set from external use ( true ) or is it default ( false ) ?
*/
private $useQuality = false ;
/**
* Constant for default image quality when not set
*/
const PNG_COMPRESSION_DEFAULT = - 1 ;
/**
* Compression level for PNG images .
*/
private $compress ;
/**
* Is the compress level set from external use ( true ) or is it default ( false ) ?
*/
private $useCompress = false ;
2015-10-21 01:13:36 +02:00
/**
* Add HTTP headers for outputing image .
*/
private $HTTPHeader = array ();
2015-01-29 21:16:32 +01:00
/**
* Default background color , red , green , blue , alpha .
*
* @ todo remake when upgrading to PHP 5.5
*/
/*
const BACKGROUND_COLOR = array (
'red' => 0 ,
'green' => 0 ,
'blue' => 0 ,
'alpha' => null ,
); */
/**
* Default background color to use .
*
* @ todo remake when upgrading to PHP 5.5
*/
//private $bgColorDefault = self::BACKGROUND_COLOR;
private $bgColorDefault = array (
'red' => 0 ,
'green' => 0 ,
'blue' => 0 ,
'alpha' => null ,
);
/**
* Background color to use , specified as part of options .
*/
private $bgColor ;
/**
* Where to save the target file .
*/
private $saveFolder ;
/**
* The working image object .
*/
private $image ;
/**
* Image filename , may include subdirectory , relative from $imageFolder
*/
private $imageSrc ;
/**
* Actual path to the image , $imageFolder . '/' . $imageSrc
*/
private $pathToImage ;
/**
2015-03-06 13:00:21 +01:00
* File type for source image , as provided by getimagesize ()
2015-01-29 21:16:32 +01:00
*/
2015-03-06 13:00:21 +01:00
private $fileType ;
2015-01-29 21:16:32 +01:00
/**
* File extension to use when saving image .
*/
private $extension ;
/**
* Output format , supports null ( image ) or json .
*/
private $outputFormat = null ;
2016-08-09 13:02:25 +02:00
/**
* Do lossy output using external postprocessing tools .
*/
private $lossy = null ;
2015-01-29 21:16:32 +01:00
/**
* Verbose mode to print out a trace and display the created image
*/
private $verbose = false ;
/**
* Keep a log / trace on what happens
*/
private $log = array ();
/**
* Handle image as palette image
*/
private $palette ;
/**
* Target filename , with path , to save resulting image in .
*/
private $cacheFileName ;
/**
* Set a format to save image as , or null to use original format .
*/
private $saveAs ;
/**
2016-08-09 13:02:25 +02:00
* Path to command for lossy optimize , for example pngquant .
*/
private $pngLossy ;
private $pngLossyCmd ;
/**
* Path to command for filter optimize , for example optipng .
2015-01-29 21:16:32 +01:00
*/
private $pngFilter ;
2015-10-18 14:52:17 +02:00
private $pngFilterCmd ;
2015-01-29 21:16:32 +01:00
/**
2016-08-09 13:02:25 +02:00
* Path to command for deflate optimize , for example pngout .
2015-01-29 21:16:32 +01:00
*/
private $pngDeflate ;
2015-10-18 14:52:17 +02:00
private $pngDeflateCmd ;
2015-01-29 21:16:32 +01:00
/**
* Path to command to optimize jpeg images , for example jpegtran or null .
*/
2015-12-07 16:02:17 +01:00
private $jpegOptimize ;
private $jpegOptimizeCmd ;
2015-10-18 14:52:17 +02:00
2015-01-29 21:16:32 +01:00
/**
* Image dimensions , calculated from loaded image .
*/
private $width ; // Calculated from source image
private $height ; // Calculated from source image
/**
* New image dimensions , incoming as argument or calculated .
*/
private $newWidth ;
private $newWidthOrig ; // Save original value
private $newHeight ;
private $newHeightOrig ; // Save original value
/**
* Change target height & width when different dpr , dpr 2 means double image dimensions .
*/
private $dpr = 1 ;
/**
* Always upscale images , even if they are smaller than target image .
*/
const UPSCALE_DEFAULT = true ;
private $upscale = self :: UPSCALE_DEFAULT ;
/**
* Array with details on how to crop , incoming as argument and calculated .
*/
public $crop ;
public $cropOrig ; // Save original value
/**
* String with details on how to do image convolution . String
* should map a key in the $convolvs array or be a string of
* 11 float values separated by comma . The first nine builds
* up the matrix , then divisor and last offset .
*/
private $convolve ;
/**
* Custom convolution expressions , matrix 3 x3 , divisor and offset .
*/
private $convolves = array (
'lighten' => '0,0,0, 0,12,0, 0,0,0, 9, 0' ,
'darken' => '0,0,0, 0,6,0, 0,0,0, 9, 0' ,
'sharpen' => '-1,-1,-1, -1,16,-1, -1,-1,-1, 8, 0' ,
'sharpen-alt' => '0,-1,0, -1,5,-1, 0,-1,0, 1, 0' ,
'emboss' => '1,1,-1, 1,3,-1, 1,-1,-1, 3, 0' ,
'emboss-alt' => '-2,-1,0, -1,1,1, 0,1,2, 1, 0' ,
'blur' => '1,1,1, 1,15,1, 1,1,1, 23, 0' ,
'gblur' => '1,2,1, 2,4,2, 1,2,1, 16, 0' ,
'edge' => '-1,-1,-1, -1,8,-1, -1,-1,-1, 9, 0' ,
'edge-alt' => '0,1,0, 1,-4,1, 0,1,0, 1, 0' ,
'draw' => '0,-1,0, -1,5,-1, 0,-1,0, 0, 0' ,
'mean' => '1,1,1, 1,1,1, 1,1,1, 9, 0' ,
'motion' => '1,0,0, 0,1,0, 0,0,1, 3, 0' ,
);
/**
* Resize strategy to fill extra area with background color .
* True or false .
*/
private $fillToFit ;
2015-10-18 14:52:17 +02:00
/**
* To store value for option scale .
*/
private $scale ;
/**
* To store value for option .
*/
private $rotateBefore ;
/**
* To store value for option .
*/
private $rotateAfter ;
/**
* To store value for option .
*/
private $autoRotate ;
/**
* To store value for option .
*/
private $sharpen ;
/**
* To store value for option .
*/
private $emboss ;
/**
* To store value for option .
*/
private $blur ;
2015-01-29 21:16:32 +01:00
/**
* Used with option area to set which parts of the image to use .
*/
private $offset ;
/**
2015-12-07 16:02:17 +01:00
* Calculate target dimension for image when using fill - to - fit resize strategy .
*/
2015-01-29 21:16:32 +01:00
private $fillWidth ;
private $fillHeight ;
/**
2015-12-07 16:02:17 +01:00
* Allow remote file download , default is to disallow remote file download .
*/
2015-01-29 21:16:32 +01:00
private $allowRemote = false ;
2015-12-07 16:02:17 +01:00
/**
* Path to cache for remote download .
*/
private $remoteCache ;
2015-01-29 21:16:32 +01:00
/**
* Pattern to recognize a remote file .
*/
//private $remotePattern = '#^[http|https]://#';
private $remotePattern = '#^https?://#' ;
/**
* Use the cache if true , set to false to ignore the cached file .
*/
private $useCache = true ;
2016-08-08 09:29:26 +02:00
/**
* Disable the fasttrackCacke to start with , inject an object to enable it .
*/
private $fastTrackCache = null ;
2015-07-25 22:21:12 +02:00
/*
* Set whitelist for valid hostnames from where remote source can be
* downloaded .
*/
private $remoteHostWhitelist = null ;
/*
* Do verbose logging to file by setting this to a filename .
*/
private $verboseFileName = null ;
2015-09-01 16:53:19 +02:00
/*
2015-10-21 00:13:56 +02:00
* Output to ascii can take som options as an array .
2015-09-01 16:53:19 +02:00
*/
private $asciiOptions = array ();
2015-10-21 00:13:56 +02:00
/*
* Image copy strategy , defaults to RESAMPLE .
*/
const RESIZE = 1 ;
const RESAMPLE = 2 ;
2015-12-07 16:02:17 +01:00
private $copyStrategy = NULL ;
2015-10-21 00:13:56 +02:00
2015-01-29 21:16:32 +01:00
/**
* Properties , the class is mutable and the method setOptions ()
* decides ( partly ) what properties are created .
*
* @ todo Clean up these and check if and how they are used
*/
public $keepRatio ;
public $cropToFit ;
private $cropWidth ;
private $cropHeight ;
public $crop_x ;
public $crop_y ;
public $filters ;
private $attr ; // Calculated from source image
/**
* Constructor , can take arguments to init the object .
*
* @ param string $imageSrc filename which may contain subdirectory .
* @ param string $imageFolder path to root folder for images .
* @ param string $saveFolder path to folder where to save the new file or null to skip saving .
* @ param string $saveName name of target file when saveing .
*/
public function __construct ( $imageSrc = null , $imageFolder = null , $saveFolder = null , $saveName = null )
{
$this -> setSource ( $imageSrc , $imageFolder );
$this -> setTarget ( $saveFolder , $saveName );
}
2016-08-08 09:29:26 +02:00
/**
* Inject object and use it , must be available as member .
*
* @ param string $property to set as object .
* @ param object $object to set to property .
*
* @ return $this
*/
public function injectDependency ( $property , $object )
{
if ( ! property_exists ( $this , $property )) {
$this -> raiseError ( " Injecting unknown property. " );
}
$this -> $property = $object ;
return $this ;
}
2015-01-29 21:16:32 +01:00
/**
* Set verbose mode .
*
* @ param boolean $mode true or false to enable and disable verbose mode ,
* default is true .
*
* @ return $this
*/
public function setVerbose ( $mode = true )
{
$this -> verbose = $mode ;
return $this ;
}
/**
* Set save folder , base folder for saving cache files .
*
* @ todo clean up how $this -> saveFolder is used in other methods .
*
* @ param string $path where to store cached files .
*
* @ return $this
*/
public function setSaveFolder ( $path )
{
$this -> saveFolder = $path ;
return $this ;
}
/**
* Use cache or not .
*
2015-10-18 14:52:17 +02:00
* @ param boolean $use true or false to use cache .
2015-01-29 21:16:32 +01:00
*
* @ return $this
*/
public function useCache ( $use = true )
{
$this -> useCache = $use ;
return $this ;
}
2015-10-18 14:52:17 +02:00
/**
* Create and save a dummy image . Use dimensions as stated in
* $this -> newWidth , or $width or default to 100 ( same for height .
*
* @ param integer $width use specified width for image dimension .
* @ param integer $height use specified width for image dimension .
*
* @ return $this
*/
public function createDummyImage ( $width = null , $height = null )
{
$this -> newWidth = $this -> newWidth ? : $width ? : 100 ;
$this -> newHeight = $this -> newHeight ? : $height ? : 100 ;
$this -> image = $this -> CreateImageKeepTransparency ( $this -> newWidth , $this -> newHeight );
return $this ;
}
2015-01-29 21:16:32 +01:00
/**
* Allow or disallow remote image download .
*
* @ param boolean $allow true or false to enable and disable .
2015-12-07 15:36:06 +01:00
* @ param string $cache path to cache dir .
2015-01-29 21:16:32 +01:00
* @ param string $pattern to use to detect if its a remote file .
*
* @ return $this
*/
2015-12-07 15:36:06 +01:00
public function setRemoteDownload ( $allow , $cache , $pattern = null )
2015-01-29 21:16:32 +01:00
{
$this -> allowRemote = $allow ;
2015-12-07 15:36:06 +01:00
$this -> remoteCache = $cache ;
2015-03-04 11:29:10 +01:00
$this -> remotePattern = is_null ( $pattern ) ? $this -> remotePattern : $pattern ;
2015-01-29 21:16:32 +01:00
2015-07-25 22:21:12 +02:00
$this -> log (
" Set remote download to: "
2015-01-29 21:16:32 +01:00
. ( $this -> allowRemote ? " true " : " false " )
. " using pattern "
2015-07-25 22:21:12 +02:00
. $this -> remotePattern
);
2015-01-29 21:16:32 +01:00
return $this ;
}
/**
* Check if the image resource is a remote file or not .
*
* @ param string $src check if src is remote .
*
* @ return boolean true if $src is a remote file , else false .
*/
public function isRemoteSource ( $src )
{
$remote = preg_match ( $this -> remotePattern , $src );
$this -> log ( " Detected remote image: " . ( $remote ? " true " : " false " ));
2015-03-04 11:29:10 +01:00
return !! $remote ;
}
/**
2015-07-23 11:32:19 +02:00
* Set whitelist for valid hostnames from where remote source can be
2015-03-04 11:29:10 +01:00
* downloaded .
*
* @ param array $whitelist with regexp hostnames to allow download from .
*
* @ return $this
*/
public function setRemoteHostWhitelist ( $whitelist = null )
{
$this -> remoteHostWhitelist = $whitelist ;
2015-07-25 22:21:12 +02:00
$this -> log (
" Setting remote host whitelist to: "
. ( is_null ( $whitelist ) ? " null " : print_r ( $whitelist , 1 ))
);
2015-03-04 11:29:10 +01:00
return $this ;
}
/**
2015-07-23 11:32:19 +02:00
* Check if the hostname for the remote image , is on a whitelist ,
2015-03-04 11:29:10 +01:00
* if the whitelist is defined .
*
* @ param string $src the remote source .
*
* @ return boolean true if hostname on $src is in the whitelist , else false .
*/
public function isRemoteSourceOnWhitelist ( $src )
{
if ( is_null ( $this -> remoteHostWhitelist )) {
2015-07-25 22:21:12 +02:00
$this -> log ( " Remote host on whitelist not configured - allowing. " );
return true ;
2015-03-04 11:29:10 +01:00
}
2015-07-25 22:21:12 +02:00
$whitelist = new CWhitelist ();
$hostname = parse_url ( $src , PHP_URL_HOST );
$allow = $whitelist -> check ( $hostname , $this -> remoteHostWhitelist );
$this -> log (
" Remote host is on whitelist: "
. ( $allow ? " true " : " false " )
);
2015-03-04 11:29:10 +01:00
return $allow ;
2015-01-29 21:16:32 +01:00
}
/**
* Check if file extension is valid as a file extension .
*
* @ param string $extension of image file .
*
* @ return $this
*/
private function checkFileExtension ( $extension )
{
2016-08-09 10:19:49 +02:00
$valid = array ( 'jpg' , 'jpeg' , 'png' , 'gif' , 'webp' );
2015-01-29 21:16:32 +01:00
in_array ( strtolower ( $extension ), $valid )
or $this -> raiseError ( 'Not a valid file extension.' );
return $this ;
}
2015-10-21 00:13:56 +02:00
/**
* Normalize the file extension .
*
* @ param string $extension of image file or skip to use internal .
*
* @ return string $extension as a normalized file extension .
*/
private function normalizeFileExtension ( $extension = null )
{
$extension = strtolower ( $extension ? $extension : $this -> extension );
2015-12-06 12:40:54 +01:00
2015-10-21 00:13:56 +02:00
if ( $extension == 'jpeg' ) {
$extension = 'jpg' ;
2016-08-09 10:19:49 +02:00
}
2015-10-21 00:13:56 +02:00
return $extension ;
}
2015-01-29 21:16:32 +01:00
/**
* Download a remote image and return path to its local copy .
*
* @ param string $src remote path to image .
*
* @ return string as path to downloaded remote source .
*/
public function downloadRemoteSource ( $src )
{
2015-03-04 11:29:10 +01:00
if ( ! $this -> isRemoteSourceOnWhitelist ( $src )) {
throw new Exception ( " Hostname is not on whitelist for remote sources. " );
}
2015-12-06 12:40:54 +01:00
2015-01-29 21:16:32 +01:00
$remote = new CRemoteImage ();
2015-12-07 15:36:06 +01:00
if ( ! is_writable ( $this -> remoteCache )) {
2015-01-29 21:16:32 +01:00
$this -> log ( " The remote cache is not writable. " );
}
2015-12-07 15:36:06 +01:00
$remote -> setCache ( $this -> remoteCache );
2015-01-29 21:16:32 +01:00
$remote -> useCache ( $this -> useCache );
$src = $remote -> download ( $src );
$this -> log ( " Remote HTTP status: " . $remote -> getStatus ());
2015-03-16 15:36:06 +01:00
$this -> log ( " Remote item is in local cache: $src " );
2015-01-29 21:16:32 +01:00
$this -> log ( " Remote details on cache: " . print_r ( $remote -> getDetails (), true ));
return $src ;
}
/**
2015-10-18 14:52:17 +02:00
* Set source file to use as image source .
2015-01-29 21:16:32 +01:00
*
* @ param string $src of image .
2015-10-18 14:52:17 +02:00
* @ param string $dir as optional base directory where images are .
2015-01-29 21:16:32 +01:00
*
* @ return $this
*/
public function setSource ( $src , $dir = null )
{
if ( ! isset ( $src )) {
2015-10-18 14:52:17 +02:00
$this -> imageSrc = null ;
$this -> pathToImage = null ;
2015-01-29 21:16:32 +01:00
return $this ;
}
if ( $this -> allowRemote && $this -> isRemoteSource ( $src )) {
$src = $this -> downloadRemoteSource ( $src );
$dir = null ;
}
if ( ! isset ( $dir )) {
$dir = dirname ( $src );
$src = basename ( $src );
}
2015-10-18 14:52:17 +02:00
$this -> imageSrc = ltrim ( $src , '/' );
$imageFolder = rtrim ( $dir , '/' );
$this -> pathToImage = $imageFolder . '/' . $this -> imageSrc ;
2015-01-29 21:16:32 +01:00
return $this ;
}
/**
* Set target file .
*
* @ param string $src of target image .
2015-10-18 14:52:17 +02:00
* @ param string $dir as optional base directory where images are stored .
* Uses $this -> saveFolder if null .
2015-01-29 21:16:32 +01:00
*
* @ return $this
*/
public function setTarget ( $src = null , $dir = null )
{
2015-10-18 14:52:17 +02:00
if ( ! isset ( $src )) {
$this -> cacheFileName = null ;
2015-01-29 21:16:32 +01:00
return $this ;
}
2015-10-18 14:52:17 +02:00
if ( isset ( $dir )) {
$this -> saveFolder = rtrim ( $dir , '/' );
}
2015-01-29 21:16:32 +01:00
2015-10-18 14:52:17 +02:00
$this -> cacheFileName = $this -> saveFolder . '/' . $src ;
2015-01-29 21:16:32 +01:00
// Sanitize filename
$this -> cacheFileName = preg_replace ( '/^a-zA-Z0-9\.-_/' , '' , $this -> cacheFileName );
$this -> log ( " The cache file name is: " . $this -> cacheFileName );
return $this ;
}
2015-10-18 14:52:17 +02:00
/**
* Get filename of target file .
*
* @ return Boolean | String as filename of target or false if not set .
*/
public function getTarget ()
{
return $this -> cacheFileName ;
}
2015-01-29 21:16:32 +01:00
/**
* Set options to use when processing image .
*
* @ param array $args used when processing image .
*
* @ return $this
*/
public function setOptions ( $args )
{
$this -> log ( " Set new options for processing image. " );
$defaults = array (
// Options for calculate dimensions
'newWidth' => null ,
'newHeight' => null ,
'aspectRatio' => null ,
'keepRatio' => true ,
'cropToFit' => false ,
'fillToFit' => null ,
'crop' => null , //array('width'=>null, 'height'=>null, 'start_x'=>0, 'start_y'=>0),
'area' => null , //'0,0,0,0',
'upscale' => self :: UPSCALE_DEFAULT ,
// Options for caching or using original
'useCache' => true ,
'useOriginal' => true ,
// Pre-processing, before resizing is done
'scale' => null ,
'rotateBefore' => null ,
'autoRotate' => false ,
// General options
'bgColor' => null ,
// Post-processing, after resizing is done
'palette' => null ,
'filters' => null ,
'sharpen' => null ,
'emboss' => null ,
'blur' => null ,
'convolve' => null ,
'rotateAfter' => null ,
// Output format
'outputFormat' => null ,
'dpr' => 1 ,
2016-08-09 13:02:25 +02:00
// Postprocessing using external tools
'lossy' => null ,
2015-01-29 21:16:32 +01:00
);
// Convert crop settings from string to array
if ( isset ( $args [ 'crop' ]) && ! is_array ( $args [ 'crop' ])) {
$pices = explode ( ',' , $args [ 'crop' ]);
$args [ 'crop' ] = array (
'width' => $pices [ 0 ],
'height' => $pices [ 1 ],
'start_x' => $pices [ 2 ],
'start_y' => $pices [ 3 ],
);
}
// Convert area settings from string to array
if ( isset ( $args [ 'area' ]) && ! is_array ( $args [ 'area' ])) {
$pices = explode ( ',' , $args [ 'area' ]);
$args [ 'area' ] = array (
'top' => $pices [ 0 ],
'right' => $pices [ 1 ],
'bottom' => $pices [ 2 ],
'left' => $pices [ 3 ],
);
}
// Convert filter settings from array of string to array of array
if ( isset ( $args [ 'filters' ]) && is_array ( $args [ 'filters' ])) {
foreach ( $args [ 'filters' ] as $key => $filterStr ) {
$parts = explode ( ',' , $filterStr );
$filter = $this -> mapFilter ( $parts [ 0 ]);
$filter [ 'str' ] = $filterStr ;
for ( $i = 1 ; $i <= $filter [ 'argc' ]; $i ++ ) {
if ( isset ( $parts [ $i ])) {
$filter [ " arg { $i } " ] = $parts [ $i ];
} else {
throw new Exception (
' Missing arg to filter , review how many arguments are needed at
http :// php . net / manual / en / function . imagefilter . php '
);
}
}
$args [ 'filters' ][ $key ] = $filter ;
}
}
// Merge default arguments with incoming and set properties.
//$args = array_merge_recursive($defaults, $args);
$args = array_merge ( $defaults , $args );
foreach ( $defaults as $key => $val ) {
$this -> { $key } = $args [ $key ];
}
if ( $this -> bgColor ) {
$this -> setDefaultBackgroundColor ( $this -> bgColor );
}
// Save original values to enable re-calculating
$this -> newWidthOrig = $this -> newWidth ;
$this -> newHeightOrig = $this -> newHeight ;
$this -> cropOrig = $this -> crop ;
return $this ;
}
/**
* Map filter name to PHP filter and id .
*
* @ param string $name the name of the filter .
*
* @ return array with filter settings
* @ throws Exception
*/
private function mapFilter ( $name )
{
$map = array (
'negate' => array ( 'id' => 0 , 'argc' => 0 , 'type' => IMG_FILTER_NEGATE ),
'grayscale' => array ( 'id' => 1 , 'argc' => 0 , 'type' => IMG_FILTER_GRAYSCALE ),
'brightness' => array ( 'id' => 2 , 'argc' => 1 , 'type' => IMG_FILTER_BRIGHTNESS ),
'contrast' => array ( 'id' => 3 , 'argc' => 1 , 'type' => IMG_FILTER_CONTRAST ),
'colorize' => array ( 'id' => 4 , 'argc' => 4 , 'type' => IMG_FILTER_COLORIZE ),
'edgedetect' => array ( 'id' => 5 , 'argc' => 0 , 'type' => IMG_FILTER_EDGEDETECT ),
'emboss' => array ( 'id' => 6 , 'argc' => 0 , 'type' => IMG_FILTER_EMBOSS ),
'gaussian_blur' => array ( 'id' => 7 , 'argc' => 0 , 'type' => IMG_FILTER_GAUSSIAN_BLUR ),
'selective_blur' => array ( 'id' => 8 , 'argc' => 0 , 'type' => IMG_FILTER_SELECTIVE_BLUR ),
'mean_removal' => array ( 'id' => 9 , 'argc' => 0 , 'type' => IMG_FILTER_MEAN_REMOVAL ),
'smooth' => array ( 'id' => 10 , 'argc' => 1 , 'type' => IMG_FILTER_SMOOTH ),
'pixelate' => array ( 'id' => 11 , 'argc' => 2 , 'type' => IMG_FILTER_PIXELATE ),
);
if ( isset ( $map [ $name ])) {
return $map [ $name ];
} else {
throw new Exception ( 'No such filter.' );
}
}
/**
* Load image details from original image file .
*
* @ param string $file the file to load or null to use $this -> pathToImage .
*
* @ return $this
* @ throws Exception
*/
public function loadImageDetails ( $file = null )
{
$file = $file ? $file : $this -> pathToImage ;
is_readable ( $file )
or $this -> raiseError ( 'Image file does not exist.' );
2016-08-09 10:19:49 +02:00
$info = list ( $this -> width , $this -> height , $this -> fileType ) = getimagesize ( $file );
2015-03-06 13:00:21 +01:00
if ( empty ( $info )) {
2016-08-09 10:19:49 +02:00
// To support webp
$this -> fileType = false ;
if ( function_exists ( " exif_imagetype " )) {
$this -> fileType = exif_imagetype ( $file );
if ( $this -> fileType === false ) {
if ( function_exists ( " imagecreatefromwebp " )) {
$webp = imagecreatefromwebp ( $file );
if ( $webp !== false ) {
$this -> width = imagesx ( $webp );
$this -> height = imagesy ( $webp );
$this -> fileType = IMG_WEBP ;
}
}
}
}
}
if ( ! $this -> fileType ) {
throw new Exception ( " Loading image details, the file doesn't seem to be a valid image. " );
2015-07-23 11:32:19 +02:00
}
2015-01-29 21:16:32 +01:00
if ( $this -> verbose ) {
2015-03-06 13:00:21 +01:00
$this -> log ( " Loading image details for: { $file } " );
$this -> log ( " Image width x height (type): { $this -> width } x { $this -> height } ( { $this -> fileType } ). " );
$this -> log ( " Image filesize: " . filesize ( $file ) . " bytes. " );
2016-08-09 10:19:49 +02:00
$this -> log ( " Image mimetype: " . $this -> getMimeType ());
2015-01-29 21:16:32 +01:00
}
return $this ;
}
2016-08-09 10:19:49 +02:00
/**
* Get mime type for image type .
*
* @ return $this
* @ throws Exception
*/
protected function getMimeType ()
{
if ( $this -> fileType === IMG_WEBP ) {
return " image/webp " ;
}
return image_type_to_mime_type ( $this -> fileType );
}
2015-01-29 21:16:32 +01:00
/**
* Init new width and height and do some sanity checks on constraints , before any
* processing can be done .
*
* @ return $this
* @ throws Exception
*/
public function initDimensions ()
{
$this -> log ( " Init dimension (before) newWidth x newHeight is { $this -> newWidth } x { $this -> newHeight } . " );
// width as %
2020-01-15 16:51:39 +01:00
if ( $this -> newWidth
&& $this -> newWidth [ strlen ( $this -> newWidth ) - 1 ] == '%' ) {
2015-01-29 21:16:32 +01:00
$this -> newWidth = $this -> width * substr ( $this -> newWidth , 0 , - 1 ) / 100 ;
$this -> log ( " Setting new width based on % to { $this -> newWidth } " );
}
// height as %
2020-01-15 16:51:39 +01:00
if ( $this -> newHeight
&& $this -> newHeight [ strlen ( $this -> newHeight ) - 1 ] == '%' ) {
2015-01-29 21:16:32 +01:00
$this -> newHeight = $this -> height * substr ( $this -> newHeight , 0 , - 1 ) / 100 ;
$this -> log ( " Setting new height based on % to { $this -> newHeight } " );
}
is_null ( $this -> aspectRatio ) or is_numeric ( $this -> aspectRatio ) or $this -> raiseError ( 'Aspect ratio out of range' );
// width & height from aspect ratio
if ( $this -> aspectRatio && is_null ( $this -> newWidth ) && is_null ( $this -> newHeight )) {
if ( $this -> aspectRatio >= 1 ) {
$this -> newWidth = $this -> width ;
$this -> newHeight = $this -> width / $this -> aspectRatio ;
$this -> log ( " Setting new width & height based on width & aspect ratio (>=1) to (w x h) { $this -> newWidth } x { $this -> newHeight } " );
} else {
$this -> newHeight = $this -> height ;
$this -> newWidth = $this -> height * $this -> aspectRatio ;
$this -> log ( " Setting new width & height based on width & aspect ratio (<1) to (w x h) { $this -> newWidth } x { $this -> newHeight } " );
}
} elseif ( $this -> aspectRatio && is_null ( $this -> newWidth )) {
$this -> newWidth = $this -> newHeight * $this -> aspectRatio ;
$this -> log ( " Setting new width based on aspect ratio to { $this -> newWidth } " );
} elseif ( $this -> aspectRatio && is_null ( $this -> newHeight )) {
$this -> newHeight = $this -> newWidth / $this -> aspectRatio ;
$this -> log ( " Setting new height based on aspect ratio to { $this -> newHeight } " );
}
// Change width & height based on dpr
if ( $this -> dpr != 1 ) {
if ( ! is_null ( $this -> newWidth )) {
$this -> newWidth = round ( $this -> newWidth * $this -> dpr );
$this -> log ( " Setting new width based on dpr= { $this -> dpr } - w= { $this -> newWidth } " );
}
if ( ! is_null ( $this -> newHeight )) {
$this -> newHeight = round ( $this -> newHeight * $this -> dpr );
$this -> log ( " Setting new height based on dpr= { $this -> dpr } - h= { $this -> newHeight } " );
}
}
// Check values to be within domain
is_null ( $this -> newWidth )
or is_numeric ( $this -> newWidth )
or $this -> raiseError ( 'Width not numeric' );
is_null ( $this -> newHeight )
or is_numeric ( $this -> newHeight )
or $this -> raiseError ( 'Height not numeric' );
$this -> log ( " Init dimension (after) newWidth x newHeight is { $this -> newWidth } x { $this -> newHeight } . " );
return $this ;
}
/**
* Calculate new width and height of image , based on settings .
*
* @ return $this
*/
public function calculateNewWidthAndHeight ()
{
// Crop, use cropped width and height as base for calulations
$this -> log ( " Calculate new width and height. " );
$this -> log ( " Original width x height is { $this -> width } x { $this -> height } . " );
$this -> log ( " Target dimension (before calculating) newWidth x newHeight is { $this -> newWidth } x { $this -> newHeight } . " );
// Check if there is an area to crop off
if ( isset ( $this -> area )) {
$this -> offset [ 'top' ] = round ( $this -> area [ 'top' ] / 100 * $this -> height );
$this -> offset [ 'right' ] = round ( $this -> area [ 'right' ] / 100 * $this -> width );
$this -> offset [ 'bottom' ] = round ( $this -> area [ 'bottom' ] / 100 * $this -> height );
$this -> offset [ 'left' ] = round ( $this -> area [ 'left' ] / 100 * $this -> width );
$this -> offset [ 'width' ] = $this -> width - $this -> offset [ 'left' ] - $this -> offset [ 'right' ];
$this -> offset [ 'height' ] = $this -> height - $this -> offset [ 'top' ] - $this -> offset [ 'bottom' ];
$this -> width = $this -> offset [ 'width' ];
$this -> height = $this -> offset [ 'height' ];
$this -> log ( " The offset for the area to use is top { $this -> area [ 'top' ] } %, right { $this -> area [ 'right' ] } %, bottom { $this -> area [ 'bottom' ] } %, left { $this -> area [ 'left' ] } %. " );
$this -> log ( " The offset for the area to use is top { $this -> offset [ 'top' ] } px, right { $this -> offset [ 'right' ] } px, bottom { $this -> offset [ 'bottom' ] } px, left { $this -> offset [ 'left' ] } px, width { $this -> offset [ 'width' ] } px, height { $this -> offset [ 'height' ] } px. " );
}
$width = $this -> width ;
$height = $this -> height ;
// Check if crop is set
if ( $this -> crop ) {
$width = $this -> crop [ 'width' ] = $this -> crop [ 'width' ] <= 0 ? $this -> width + $this -> crop [ 'width' ] : $this -> crop [ 'width' ];
$height = $this -> crop [ 'height' ] = $this -> crop [ 'height' ] <= 0 ? $this -> height + $this -> crop [ 'height' ] : $this -> crop [ 'height' ];
if ( $this -> crop [ 'start_x' ] == 'left' ) {
$this -> crop [ 'start_x' ] = 0 ;
} elseif ( $this -> crop [ 'start_x' ] == 'right' ) {
$this -> crop [ 'start_x' ] = $this -> width - $width ;
} elseif ( $this -> crop [ 'start_x' ] == 'center' ) {
$this -> crop [ 'start_x' ] = round ( $this -> width / 2 ) - round ( $width / 2 );
}
if ( $this -> crop [ 'start_y' ] == 'top' ) {
$this -> crop [ 'start_y' ] = 0 ;
} elseif ( $this -> crop [ 'start_y' ] == 'bottom' ) {
$this -> crop [ 'start_y' ] = $this -> height - $height ;
} elseif ( $this -> crop [ 'start_y' ] == 'center' ) {
$this -> crop [ 'start_y' ] = round ( $this -> height / 2 ) - round ( $height / 2 );
}
$this -> log ( " Crop area is width { $width } px, height { $height } px, start_x { $this -> crop [ 'start_x' ] } px, start_y { $this -> crop [ 'start_y' ] } px. " );
}
// Calculate new width and height if keeping aspect-ratio.
if ( $this -> keepRatio ) {
$this -> log ( " Keep aspect ratio. " );
// Crop-to-fit and both new width and height are set.
if (( $this -> cropToFit || $this -> fillToFit ) && isset ( $this -> newWidth ) && isset ( $this -> newHeight )) {
// Use newWidth and newHeigh as width/height, image should fit in box.
$this -> log ( " Use newWidth and newHeigh as width/height, image should fit in box. " );
} elseif ( isset ( $this -> newWidth ) && isset ( $this -> newHeight )) {
// Both new width and height are set.
// Use newWidth and newHeigh as max width/height, image should not be larger.
$ratioWidth = $width / $this -> newWidth ;
$ratioHeight = $height / $this -> newHeight ;
$ratio = ( $ratioWidth > $ratioHeight ) ? $ratioWidth : $ratioHeight ;
$this -> newWidth = round ( $width / $ratio );
$this -> newHeight = round ( $height / $ratio );
$this -> log ( " New width and height was set. " );
} elseif ( isset ( $this -> newWidth )) {
// Use new width as max-width
$factor = ( float ) $this -> newWidth / ( float ) $width ;
$this -> newHeight = round ( $factor * $height );
$this -> log ( " New width was set. " );
} elseif ( isset ( $this -> newHeight )) {
// Use new height as max-hight
$factor = ( float ) $this -> newHeight / ( float ) $height ;
$this -> newWidth = round ( $factor * $width );
$this -> log ( " New height was set. " );
2016-06-01 09:34:30 +02:00
} else {
// Use existing width and height as new width and height.
$this -> newWidth = $width ;
$this -> newHeight = $height ;
2015-01-29 21:16:32 +01:00
}
2016-06-01 09:34:30 +02:00
2015-01-29 21:16:32 +01:00
// Get image dimensions for pre-resize image.
if ( $this -> cropToFit || $this -> fillToFit ) {
// Get relations of original & target image
$ratioWidth = $width / $this -> newWidth ;
$ratioHeight = $height / $this -> newHeight ;
if ( $this -> cropToFit ) {
// Use newWidth and newHeigh as defined width/height,
// image should fit the area.
$this -> log ( " Crop to fit. " );
$ratio = ( $ratioWidth < $ratioHeight ) ? $ratioWidth : $ratioHeight ;
$this -> cropWidth = round ( $width / $ratio );
$this -> cropHeight = round ( $height / $ratio );
$this -> log ( " Crop width, height, ratio: $this->cropWidth x $this->cropHeight ( $ratio ). " );
2015-07-23 11:32:19 +02:00
} elseif ( $this -> fillToFit ) {
2015-01-29 21:16:32 +01:00
// Use newWidth and newHeigh as defined width/height,
// image should fit the area.
$this -> log ( " Fill to fit. " );
$ratio = ( $ratioWidth < $ratioHeight ) ? $ratioHeight : $ratioWidth ;
$this -> fillWidth = round ( $width / $ratio );
$this -> fillHeight = round ( $height / $ratio );
$this -> log ( " Fill width, height, ratio: $this->fillWidth x $this->fillHeight ( $ratio ). " );
}
}
}
// Crop, ensure to set new width and height
if ( $this -> crop ) {
$this -> log ( " Crop. " );
$this -> newWidth = round ( isset ( $this -> newWidth ) ? $this -> newWidth : $this -> crop [ 'width' ]);
$this -> newHeight = round ( isset ( $this -> newHeight ) ? $this -> newHeight : $this -> crop [ 'height' ]);
}
// Fill to fit, ensure to set new width and height
/* if ( $this -> fillToFit ) {
$this -> log ( " FillToFit. " );
$this -> newWidth = round ( isset ( $this -> newWidth ) ? $this -> newWidth : $this -> crop [ 'width' ]);
$this -> newHeight = round ( isset ( $this -> newHeight ) ? $this -> newHeight : $this -> crop [ 'height' ]);
} */
// No new height or width is set, use existing measures.
$this -> newWidth = round ( isset ( $this -> newWidth ) ? $this -> newWidth : $this -> width );
$this -> newHeight = round ( isset ( $this -> newHeight ) ? $this -> newHeight : $this -> height );
$this -> log ( " Calculated new width x height as { $this -> newWidth } x { $this -> newHeight } . " );
return $this ;
}
/**
* Re - calculate image dimensions when original image dimension has changed .
*
* @ return $this
*/
public function reCalculateDimensions ()
{
$this -> log ( " Re-calculate image dimensions, newWidth x newHeigh was: " . $this -> newWidth . " x " . $this -> newHeight );
$this -> newWidth = $this -> newWidthOrig ;
$this -> newHeight = $this -> newHeightOrig ;
$this -> crop = $this -> cropOrig ;
$this -> initDimensions ()
-> calculateNewWidthAndHeight ();
return $this ;
}
/**
* Set extension for filename to save as .
*
* @ param string $saveas extension to save image as
*
* @ return $this
*/
public function setSaveAsExtension ( $saveAs = null )
{
if ( isset ( $saveAs )) {
$saveAs = strtolower ( $saveAs );
$this -> checkFileExtension ( $saveAs );
$this -> saveAs = $saveAs ;
$this -> extension = $saveAs ;
}
2015-03-06 13:00:21 +01:00
$this -> log ( " Prepare to save image as: " . $this -> extension );
2015-01-29 21:16:32 +01:00
return $this ;
}
/**
* Set JPEG quality to use when saving image
*
* @ param int $quality as the quality to set .
*
* @ return $this
*/
public function setJpegQuality ( $quality = null )
{
if ( $quality ) {
$this -> useQuality = true ;
}
$this -> quality = isset ( $quality )
? $quality
: self :: JPEG_QUALITY_DEFAULT ;
( is_numeric ( $this -> quality ) and $this -> quality > 0 and $this -> quality <= 100 )
or $this -> raiseError ( 'Quality not in range.' );
$this -> log ( " Setting JPEG quality to { $this -> quality } . " );
return $this ;
}
/**
* Set PNG compressen algorithm to use when saving image
*
* @ param int $compress as the algorithm to use .
*
* @ return $this
*/
public function setPngCompression ( $compress = null )
{
if ( $compress ) {
$this -> useCompress = true ;
}
$this -> compress = isset ( $compress )
? $compress
: self :: PNG_COMPRESSION_DEFAULT ;
( is_numeric ( $this -> compress ) and $this -> compress >= - 1 and $this -> compress <= 9 )
or $this -> raiseError ( 'Quality not in range.' );
$this -> log ( " Setting PNG compression level to { $this -> compress } . " );
return $this ;
}
/**
* Use original image if possible , check options which affects image processing .
*
* @ param boolean $useOrig default is to use original if possible , else set to false .
*
* @ return $this
*/
public function useOriginalIfPossible ( $useOrig = true )
{
if ( $useOrig
&& ( $this -> newWidth == $this -> width )
&& ( $this -> newHeight == $this -> height )
&& ! $this -> area
&& ! $this -> crop
&& ! $this -> cropToFit
&& ! $this -> fillToFit
&& ! $this -> filters
&& ! $this -> sharpen
&& ! $this -> emboss
&& ! $this -> blur
&& ! $this -> convolve
&& ! $this -> palette
&& ! $this -> useQuality
&& ! $this -> useCompress
&& ! $this -> saveAs
&& ! $this -> rotateBefore
&& ! $this -> rotateAfter
&& ! $this -> autoRotate
&& ! $this -> bgColor
&& ( $this -> upscale === self :: UPSCALE_DEFAULT )
2016-08-09 13:27:46 +02:00
&& ! $this -> lossy
2015-01-29 21:16:32 +01:00
) {
$this -> log ( " Using original image. " );
$this -> output ( $this -> pathToImage );
}
return $this ;
}
/**
* Generate filename to save file in cache .
*
2015-10-18 14:52:17 +02:00
* @ param string $base as optional basepath for storing file .
* @ param boolean $useSubdir use or skip the subdir part when creating the
* filename .
2015-12-06 12:18:33 +01:00
* @ param string $prefix to add as part of filename
2015-01-29 21:16:32 +01:00
*
* @ return $this
*/
2015-12-06 12:18:33 +01:00
public function generateFilename ( $base = null , $useSubdir = true , $prefix = null )
2015-01-29 21:16:32 +01:00
{
2015-10-18 14:52:17 +02:00
$filename = basename ( $this -> pathToImage );
2015-01-29 21:16:32 +01:00
$cropToFit = $this -> cropToFit ? '_cf' : null ;
$fillToFit = $this -> fillToFit ? '_ff' : null ;
$crop_x = $this -> crop_x ? " _x { $this -> crop_x } " : null ;
$crop_y = $this -> crop_y ? " _y { $this -> crop_y } " : null ;
$scale = $this -> scale ? " _s { $this -> scale } " : null ;
$bgColor = $this -> bgColor ? " _bgc { $this -> bgColor } " : null ;
$quality = $this -> quality ? " _q { $this -> quality } " : null ;
$compress = $this -> compress ? " _co { $this -> compress } " : null ;
$rotateBefore = $this -> rotateBefore ? " _rb { $this -> rotateBefore } " : null ;
$rotateAfter = $this -> rotateAfter ? " _ra { $this -> rotateAfter } " : null ;
2016-08-09 13:23:04 +02:00
$lossy = $this -> lossy ? " _l " : null ;
2015-01-29 21:16:32 +01:00
2015-10-21 00:13:56 +02:00
$saveAs = $this -> normalizeFileExtension ();
$saveAs = $saveAs ? " _ $saveAs " : null ;
$copyStrat = null ;
if ( $this -> copyStrategy === self :: RESIZE ) {
$copyStrat = " _rs " ;
}
2015-12-06 12:40:54 +01:00
2015-12-06 12:18:33 +01:00
$width = $this -> newWidth ? '_' . $this -> newWidth : null ;
$height = $this -> newHeight ? '_' . $this -> newHeight : null ;
2015-01-29 21:16:32 +01:00
$offset = isset ( $this -> offset )
? '_o' . $this -> offset [ 'top' ] . '-' . $this -> offset [ 'right' ] . '-' . $this -> offset [ 'bottom' ] . '-' . $this -> offset [ 'left' ]
: null ;
$crop = $this -> crop
? '_c' . $this -> crop [ 'width' ] . '-' . $this -> crop [ 'height' ] . '-' . $this -> crop [ 'start_x' ] . '-' . $this -> crop [ 'start_y' ]
: null ;
$filters = null ;
if ( isset ( $this -> filters )) {
foreach ( $this -> filters as $filter ) {
if ( is_array ( $filter )) {
$filters .= " _f { $filter [ 'id' ] } " ;
for ( $i = 1 ; $i <= $filter [ 'argc' ]; $i ++ ) {
2015-09-15 17:26:04 +02:00
$filters .= " - " . $filter [ " arg { $i } " ];
2015-01-29 21:16:32 +01:00
}
}
}
}
$sharpen = $this -> sharpen ? 's' : null ;
$emboss = $this -> emboss ? 'e' : null ;
$blur = $this -> blur ? 'b' : null ;
$palette = $this -> palette ? 'p' : null ;
$autoRotate = $this -> autoRotate ? 'ar' : null ;
2015-03-06 13:00:21 +01:00
$optimize = $this -> jpegOptimize ? 'o' : null ;
$optimize .= $this -> pngFilter ? 'f' : null ;
$optimize .= $this -> pngDeflate ? 'd' : null ;
2015-01-29 21:16:32 +01:00
$convolve = null ;
if ( $this -> convolve ) {
$convolve = '_conv' . preg_replace ( '/[^a-zA-Z0-9]/' , '' , $this -> convolve );
}
$upscale = null ;
if ( $this -> upscale !== self :: UPSCALE_DEFAULT ) {
$upscale = '_nu' ;
}
2015-10-18 14:52:17 +02:00
$subdir = null ;
if ( $useSubdir === true ) {
$subdir = str_replace ( '/' , '-' , dirname ( $this -> imageSrc ));
$subdir = ( $subdir == '.' ) ? '_.' : $subdir ;
$subdir .= '_' ;
}
2015-12-06 12:40:54 +01:00
$file = $prefix . $subdir . $filename . $width . $height
2015-12-06 12:18:33 +01:00
. $offset . $crop . $cropToFit . $fillToFit
2015-01-29 21:16:32 +01:00
. $crop_x . $crop_y . $upscale
2015-10-18 14:52:17 +02:00
. $quality . $filters . $sharpen . $emboss . $blur . $palette
. $optimize . $compress
. $scale . $rotateBefore . $rotateAfter . $autoRotate . $bgColor
2016-08-09 13:23:04 +02:00
. $convolve . $copyStrat . $lossy . $saveAs ;
2015-01-29 21:16:32 +01:00
return $this -> setTarget ( $file , $base );
}
/**
* Use cached version of image , if possible .
*
* @ param boolean $useCache is default true , set to false to avoid using cached object .
*
* @ return $this
*/
public function useCacheIfPossible ( $useCache = true )
{
if ( $useCache && is_readable ( $this -> cacheFileName )) {
$fileTime = filemtime ( $this -> pathToImage );
$cacheTime = filemtime ( $this -> cacheFileName );
if ( $fileTime <= $cacheTime ) {
if ( $this -> useCache ) {
if ( $this -> verbose ) {
$this -> log ( " Use cached file. " );
$this -> log ( " Cached image filesize: " . filesize ( $this -> cacheFileName ) . " bytes. " );
}
$this -> output ( $this -> cacheFileName , $this -> outputFormat );
} else {
$this -> log ( " Cache is valid but ignoring it by intention. " );
}
} else {
$this -> log ( " Original file is modified, ignoring cache. " );
}
} else {
$this -> log ( " Cachefile does not exists or ignoring it. " );
}
return $this ;
}
/**
2015-07-23 11:32:19 +02:00
* Load image from disk . Try to load image without verbose error message ,
2015-03-06 13:00:21 +01:00
* if fail , load again and display error messages .
2015-01-29 21:16:32 +01:00
*
* @ param string $src of image .
* @ param string $dir as base directory where images are .
*
* @ return $this
*
*/
public function load ( $src = null , $dir = null )
{
if ( isset ( $src )) {
$this -> setSource ( $src , $dir );
}
2016-08-09 10:19:49 +02:00
$this -> loadImageDetails ();
2015-01-29 21:16:32 +01:00
2016-08-09 10:19:49 +02:00
if ( $this -> fileType === IMG_WEBP ) {
$this -> image = imagecreatefromwebp ( $this -> pathToImage );
} else {
$imageAsString = file_get_contents ( $this -> pathToImage );
$this -> image = imagecreatefromstring ( $imageAsString );
}
2015-03-06 13:00:21 +01:00
if ( $this -> image === false ) {
throw new Exception ( " Could not load image. " );
}
2015-12-06 12:40:54 +01:00
2015-10-21 00:13:56 +02:00
/* Removed v0 . 7.7
2015-03-06 13:00:21 +01:00
if ( image_type_to_mime_type ( $this -> fileType ) == 'image/png' ) {
$type = $this -> getPngType ();
$hasFewColors = imagecolorstotal ( $this -> image );
2015-01-29 21:16:32 +01:00
2015-03-06 13:00:21 +01:00
if ( $type == self :: PNG_RGB_PALETTE || ( $hasFewColors > 0 && $hasFewColors <= 256 )) {
if ( $this -> verbose ) {
$this -> log ( " Handle this image as a palette image. " );
2015-01-29 21:16:32 +01:00
}
2015-03-06 13:00:21 +01:00
$this -> palette = true ;
}
2015-01-29 21:16:32 +01:00
}
2015-10-21 00:13:56 +02:00
*/
2015-01-29 21:16:32 +01:00
if ( $this -> verbose ) {
2015-10-21 00:13:56 +02:00
$this -> log ( " ### Image successfully loaded from file. " );
2015-03-06 13:00:21 +01:00
$this -> log ( " imageistruecolor() : " . ( imageistruecolor ( $this -> image ) ? 'true' : 'false' ));
$this -> log ( " imagecolorstotal() : " . imagecolorstotal ( $this -> image ));
$this -> log ( " Number of colors in image = " . $this -> colorsTotal ( $this -> image ));
2015-02-09 00:42:49 +01:00
$index = imagecolortransparent ( $this -> image );
2015-10-21 00:13:56 +02:00
$this -> log ( " Detected transparent color = " . ( $index >= 0 ? implode ( " , " , imagecolorsforindex ( $this -> image , $index )) : " NONE " ) . " at index = $index " );
2015-01-29 21:16:32 +01:00
}
return $this ;
}
/**
* Get the type of PNG image .
*
2015-10-21 00:13:56 +02:00
* @ param string $filename to use instead of default .
*
2015-01-29 21:16:32 +01:00
* @ return int as the type of the png - image
*
*/
2015-10-21 00:13:56 +02:00
public function getPngType ( $filename = null )
2015-01-29 21:16:32 +01:00
{
2015-10-21 00:13:56 +02:00
$filename = $filename ? $filename : $this -> pathToImage ;
2015-12-06 12:40:54 +01:00
2015-10-21 00:13:56 +02:00
$pngType = ord ( file_get_contents ( $filename , false , null , 25 , 1 ));
if ( $this -> verbose ) {
$this -> log ( " Checking png type of: " . $filename );
$this -> log ( $this -> getPngTypeAsString ( $pngType ));
}
2015-12-06 12:40:54 +01:00
2015-10-21 00:13:56 +02:00
return $pngType ;
}
/**
* Get the type of PNG image as a verbose string .
*
* @ param integer $type to use , default is to check the type .
* @ param string $filename to use instead of default .
*
* @ return int as the type of the png - image
*
*/
private function getPngTypeAsString ( $pngType = null , $filename = null )
{
if ( $filename || ! $pngType ) {
$pngType = $this -> getPngType ( $filename );
}
$index = imagecolortransparent ( $this -> image );
$transparent = null ;
if ( $index != - 1 ) {
2015-12-06 12:40:54 +01:00
$transparent = " (transparent) " ;
2015-10-21 00:13:56 +02:00
}
2015-01-29 21:16:32 +01:00
switch ( $pngType ) {
case self :: PNG_GREYSCALE :
2015-10-21 00:13:56 +02:00
$text = " PNG is type 0, Greyscale $transparent " ;
2015-01-29 21:16:32 +01:00
break ;
case self :: PNG_RGB :
2015-10-21 00:13:56 +02:00
$text = " PNG is type 2, RGB $transparent " ;
2015-01-29 21:16:32 +01:00
break ;
case self :: PNG_RGB_PALETTE :
2015-10-21 00:13:56 +02:00
$text = " PNG is type 3, RGB with palette $transparent " ;
2015-01-29 21:16:32 +01:00
break ;
case self :: PNG_GREYSCALE_ALPHA :
2015-10-21 00:13:56 +02:00
$text = " PNG is type 4, Greyscale with alpha channel " ;
2015-01-29 21:16:32 +01:00
break ;
case self :: PNG_RGB_ALPHA :
2015-10-21 00:13:56 +02:00
$text = " PNG is type 6, RGB with alpha channel (PNG 32-bit) " ;
2015-01-29 21:16:32 +01:00
break ;
default :
2015-10-21 00:13:56 +02:00
$text = " PNG is UNKNOWN type, is it really a PNG image? " ;
2015-01-29 21:16:32 +01:00
}
2015-10-21 00:13:56 +02:00
return $text ;
2015-01-29 21:16:32 +01:00
}
2015-10-21 00:13:56 +02:00
2015-01-29 21:16:32 +01:00
/**
* Calculate number of colors in an image .
*
* @ param resource $im the image .
*
* @ return int
*/
private function colorsTotal ( $im )
{
if ( imageistruecolor ( $im )) {
$this -> log ( " Colors as true color. " );
$h = imagesy ( $im );
$w = imagesx ( $im );
$c = array ();
for ( $x = 0 ; $x < $w ; $x ++ ) {
for ( $y = 0 ; $y < $h ; $y ++ ) {
@ $c [ 'c' . imagecolorat ( $im , $x , $y )] ++ ;
}
}
return count ( $c );
} else {
$this -> log ( " Colors as palette. " );
return imagecolorstotal ( $im );
}
}
/**
* Preprocess image before rezising it .
*
* @ return $this
*/
public function preResize ()
{
2015-10-21 00:13:56 +02:00
$this -> log ( " ### Pre-process before resizing " );
2015-01-29 21:16:32 +01:00
// Rotate image
if ( $this -> rotateBefore ) {
$this -> log ( " Rotating image. " );
$this -> rotate ( $this -> rotateBefore , $this -> bgColor )
-> reCalculateDimensions ();
}
// Auto-rotate image
if ( $this -> autoRotate ) {
$this -> log ( " Auto rotating image. " );
$this -> rotateExif ()
-> reCalculateDimensions ();
}
// Scale the original image before starting
if ( isset ( $this -> scale )) {
$this -> log ( " Scale by { $this -> scale } % " );
$newWidth = $this -> width * $this -> scale / 100 ;
$newHeight = $this -> height * $this -> scale / 100 ;
$img = $this -> CreateImageKeepTransparency ( $newWidth , $newHeight );
imagecopyresampled ( $img , $this -> image , 0 , 0 , 0 , 0 , $newWidth , $newHeight , $this -> width , $this -> height );
$this -> image = $img ;
$this -> width = $newWidth ;
$this -> height = $newHeight ;
}
return $this ;
}
2015-10-21 00:13:56 +02:00
/**
* Resize or resample the image while resizing .
*
* @ param int $strategy as CImage :: RESIZE or CImage :: RESAMPLE
*
* @ return $this
*/
2015-12-07 16:02:17 +01:00
public function setCopyResizeStrategy ( $strategy )
{
$this -> copyStrategy = $strategy ;
return $this ;
}
2015-10-21 00:13:56 +02:00
/**
* Resize and or crop the image .
*
* @ return void
*/
public function imageCopyResampled ( $dst_image , $src_image , $dst_x , $dst_y , $src_x , $src_y , $dst_w , $dst_h , $src_w , $src_h )
{
2015-12-07 16:02:17 +01:00
if ( $this -> copyStrategy == self :: RESIZE ) {
2015-10-21 00:13:56 +02:00
$this -> log ( " Copy by resize " );
imagecopyresized ( $dst_image , $src_image , $dst_x , $dst_y , $src_x , $src_y , $dst_w , $dst_h , $src_w , $src_h );
} else {
$this -> log ( " Copy by resample " );
imagecopyresampled ( $dst_image , $src_image , $dst_x , $dst_y , $src_x , $src_y , $dst_w , $dst_h , $src_w , $src_h );
}
}
2015-01-29 21:16:32 +01:00
/**
* Resize and or crop the image .
*
* @ return $this
*/
public function resize ()
{
2015-10-21 00:13:56 +02:00
$this -> log ( " ### Starting to Resize() " );
2015-01-29 21:16:32 +01:00
$this -> log ( " Upscale = ' $this->upscale ' " );
// Only use a specified area of the image, $this->offset is defining the area to use
if ( isset ( $this -> offset )) {
$this -> log ( " Offset for area to use, cropping it width= { $this -> offset [ 'width' ] } , height= { $this -> offset [ 'height' ] } , start_x= { $this -> offset [ 'left' ] } , start_y= { $this -> offset [ 'top' ] } " );
$img = $this -> CreateImageKeepTransparency ( $this -> offset [ 'width' ], $this -> offset [ 'height' ]);
imagecopy ( $img , $this -> image , 0 , 0 , $this -> offset [ 'left' ], $this -> offset [ 'top' ], $this -> offset [ 'width' ], $this -> offset [ 'height' ]);
$this -> image = $img ;
$this -> width = $this -> offset [ 'width' ];
$this -> height = $this -> offset [ 'height' ];
}
if ( $this -> crop ) {
// Do as crop, take only part of image
$this -> log ( " Cropping area width= { $this -> crop [ 'width' ] } , height= { $this -> crop [ 'height' ] } , start_x= { $this -> crop [ 'start_x' ] } , start_y= { $this -> crop [ 'start_y' ] } " );
$img = $this -> CreateImageKeepTransparency ( $this -> crop [ 'width' ], $this -> crop [ 'height' ]);
imagecopy ( $img , $this -> image , 0 , 0 , $this -> crop [ 'start_x' ], $this -> crop [ 'start_y' ], $this -> crop [ 'width' ], $this -> crop [ 'height' ]);
$this -> image = $img ;
$this -> width = $this -> crop [ 'width' ];
$this -> height = $this -> crop [ 'height' ];
}
if ( ! $this -> upscale ) {
// Consider rewriting the no-upscale code to fit within this if-statement,
// likely to be more readable code.
// The code is more or leass equal in below crop-to-fit, fill-to-fit and stretch
}
if ( $this -> cropToFit ) {
// Resize by crop to fit
$this -> log ( " Resizing using strategy - Crop to fit " );
2016-06-01 09:34:30 +02:00
if ( ! $this -> upscale
&& ( $this -> width < $this -> newWidth || $this -> height < $this -> newHeight )) {
2015-01-29 21:16:32 +01:00
$this -> log ( " Resizing - smaller image, do not upscale. " );
$posX = 0 ;
$posY = 0 ;
2016-06-01 09:34:30 +02:00
$cropX = 0 ;
$cropY = 0 ;
2015-01-29 21:16:32 +01:00
if ( $this -> newWidth > $this -> width ) {
$posX = round (( $this -> newWidth - $this -> width ) / 2 );
}
2016-06-01 09:34:30 +02:00
if ( $this -> newWidth < $this -> width ) {
$cropX = round (( $this -> width / 2 ) - ( $this -> newWidth / 2 ));
}
2015-01-29 21:16:32 +01:00
if ( $this -> newHeight > $this -> height ) {
$posY = round (( $this -> newHeight - $this -> height ) / 2 );
}
2016-06-01 09:34:30 +02:00
if ( $this -> newHeight < $this -> height ) {
$cropY = round (( $this -> height / 2 ) - ( $this -> newHeight / 2 ));
}
$this -> log ( " cwidth: $this->cropWidth " );
$this -> log ( " cheight: $this->cropHeight " );
$this -> log ( " nwidth: $this->newWidth " );
$this -> log ( " nheight: $this->newHeight " );
$this -> log ( " width: $this->width " );
$this -> log ( " height: $this->height " );
$this -> log ( " posX: $posX " );
$this -> log ( " posY: $posY " );
$this -> log ( " cropX: $cropX " );
$this -> log ( " cropY: $cropY " );
2015-01-29 21:16:32 +01:00
$imageResized = $this -> CreateImageKeepTransparency ( $this -> newWidth , $this -> newHeight );
2016-06-01 09:34:30 +02:00
imagecopy ( $imageResized , $this -> image , $posX , $posY , $cropX , $cropY , $this -> width , $this -> height );
2015-01-29 21:16:32 +01:00
} else {
$cropX = round (( $this -> cropWidth / 2 ) - ( $this -> newWidth / 2 ));
$cropY = round (( $this -> cropHeight / 2 ) - ( $this -> newHeight / 2 ));
$imgPreCrop = $this -> CreateImageKeepTransparency ( $this -> cropWidth , $this -> cropHeight );
$imageResized = $this -> CreateImageKeepTransparency ( $this -> newWidth , $this -> newHeight );
2015-10-21 00:13:56 +02:00
$this -> imageCopyResampled ( $imgPreCrop , $this -> image , 0 , 0 , 0 , 0 , $this -> cropWidth , $this -> cropHeight , $this -> width , $this -> height );
2015-01-29 21:16:32 +01:00
imagecopy ( $imageResized , $imgPreCrop , 0 , 0 , $cropX , $cropY , $this -> newWidth , $this -> newHeight );
}
$this -> image = $imageResized ;
$this -> width = $this -> newWidth ;
$this -> height = $this -> newHeight ;
2015-07-23 11:32:19 +02:00
} elseif ( $this -> fillToFit ) {
2015-01-29 21:16:32 +01:00
// Resize by fill to fit
$this -> log ( " Resizing using strategy - Fill to fit " );
$posX = 0 ;
$posY = 0 ;
$ratioOrig = $this -> width / $this -> height ;
$ratioNew = $this -> newWidth / $this -> newHeight ;
// Check ratio for landscape or portrait
if ( $ratioOrig < $ratioNew ) {
$posX = round (( $this -> newWidth - $this -> fillWidth ) / 2 );
} else {
$posY = round (( $this -> newHeight - $this -> fillHeight ) / 2 );
}
if ( ! $this -> upscale
2016-06-01 09:34:30 +02:00
&& ( $this -> width < $this -> newWidth && $this -> height < $this -> newHeight )
2015-01-29 21:16:32 +01:00
) {
$this -> log ( " Resizing - smaller image, do not upscale. " );
2016-06-01 09:34:30 +02:00
$posX = round (( $this -> newWidth - $this -> width ) / 2 );
$posY = round (( $this -> newHeight - $this -> height ) / 2 );
2015-01-29 21:16:32 +01:00
$imageResized = $this -> CreateImageKeepTransparency ( $this -> newWidth , $this -> newHeight );
2016-06-01 09:34:30 +02:00
imagecopy ( $imageResized , $this -> image , $posX , $posY , 0 , 0 , $this -> width , $this -> height );
2015-01-29 21:16:32 +01:00
} else {
$imgPreFill = $this -> CreateImageKeepTransparency ( $this -> fillWidth , $this -> fillHeight );
$imageResized = $this -> CreateImageKeepTransparency ( $this -> newWidth , $this -> newHeight );
2015-10-21 00:13:56 +02:00
$this -> imageCopyResampled ( $imgPreFill , $this -> image , 0 , 0 , 0 , 0 , $this -> fillWidth , $this -> fillHeight , $this -> width , $this -> height );
2015-01-29 21:16:32 +01:00
imagecopy ( $imageResized , $imgPreFill , $posX , $posY , 0 , 0 , $this -> fillWidth , $this -> fillHeight );
}
$this -> image = $imageResized ;
$this -> width = $this -> newWidth ;
$this -> height = $this -> newHeight ;
2015-07-23 11:32:19 +02:00
} elseif ( ! ( $this -> newWidth == $this -> width && $this -> newHeight == $this -> height )) {
2015-01-29 21:16:32 +01:00
// Resize it
$this -> log ( " Resizing, new height and/or width " );
if ( ! $this -> upscale
&& ( $this -> width < $this -> newWidth || $this -> height < $this -> newHeight )
) {
$this -> log ( " Resizing - smaller image, do not upscale. " );
if ( ! $this -> keepRatio ) {
$this -> log ( " Resizing - stretch to fit selected. " );
$posX = 0 ;
$posY = 0 ;
$cropX = 0 ;
$cropY = 0 ;
if ( $this -> newWidth > $this -> width && $this -> newHeight > $this -> height ) {
$posX = round (( $this -> newWidth - $this -> width ) / 2 );
$posY = round (( $this -> newHeight - $this -> height ) / 2 );
2015-07-23 11:32:19 +02:00
} elseif ( $this -> newWidth > $this -> width ) {
2015-01-29 21:16:32 +01:00
$posX = round (( $this -> newWidth - $this -> width ) / 2 );
$cropY = round (( $this -> height - $this -> newHeight ) / 2 );
2015-07-23 11:32:19 +02:00
} elseif ( $this -> newHeight > $this -> height ) {
2015-01-29 21:16:32 +01:00
$posY = round (( $this -> newHeight - $this -> height ) / 2 );
$cropX = round (( $this -> width - $this -> newWidth ) / 2 );
}
$imageResized = $this -> CreateImageKeepTransparency ( $this -> newWidth , $this -> newHeight );
2016-06-01 09:34:30 +02:00
imagecopy ( $imageResized , $this -> image , $posX , $posY , $cropX , $cropY , $this -> width , $this -> height );
2015-01-29 21:16:32 +01:00
$this -> image = $imageResized ;
$this -> width = $this -> newWidth ;
$this -> height = $this -> newHeight ;
}
} else {
$imageResized = $this -> CreateImageKeepTransparency ( $this -> newWidth , $this -> newHeight );
2015-10-21 00:13:56 +02:00
$this -> imageCopyResampled ( $imageResized , $this -> image , 0 , 0 , 0 , 0 , $this -> newWidth , $this -> newHeight , $this -> width , $this -> height );
2015-01-29 21:16:32 +01:00
$this -> image = $imageResized ;
$this -> width = $this -> newWidth ;
$this -> height = $this -> newHeight ;
}
}
return $this ;
}
/**
* Postprocess image after rezising image .
*
* @ return $this
*/
public function postResize ()
{
2015-10-21 00:13:56 +02:00
$this -> log ( " ### Post-process after resizing " );
2015-01-29 21:16:32 +01:00
// Rotate image
if ( $this -> rotateAfter ) {
$this -> log ( " Rotating image. " );
$this -> rotate ( $this -> rotateAfter , $this -> bgColor );
}
// Apply filters
if ( isset ( $this -> filters ) && is_array ( $this -> filters )) {
foreach ( $this -> filters as $filter ) {
$this -> log ( " Applying filter { $filter [ 'type' ] } . " );
switch ( $filter [ 'argc' ]) {
case 0 :
imagefilter ( $this -> image , $filter [ 'type' ]);
break ;
case 1 :
imagefilter ( $this -> image , $filter [ 'type' ], $filter [ 'arg1' ]);
break ;
case 2 :
imagefilter ( $this -> image , $filter [ 'type' ], $filter [ 'arg1' ], $filter [ 'arg2' ]);
break ;
case 3 :
imagefilter ( $this -> image , $filter [ 'type' ], $filter [ 'arg1' ], $filter [ 'arg2' ], $filter [ 'arg3' ]);
break ;
case 4 :
imagefilter ( $this -> image , $filter [ 'type' ], $filter [ 'arg1' ], $filter [ 'arg2' ], $filter [ 'arg3' ], $filter [ 'arg4' ]);
break ;
}
}
}
// Convert to palette image
if ( $this -> palette ) {
$this -> log ( " Converting to palette image. " );
$this -> trueColorToPalette ();
}
// Blur the image
if ( $this -> blur ) {
$this -> log ( " Blur. " );
$this -> blurImage ();
}
// Emboss the image
if ( $this -> emboss ) {
$this -> log ( " Emboss. " );
$this -> embossImage ();
}
// Sharpen the image
if ( $this -> sharpen ) {
$this -> log ( " Sharpen. " );
$this -> sharpenImage ();
}
// Custom convolution
if ( $this -> convolve ) {
//$this->log("Convolve: " . $this->convolve);
$this -> imageConvolution ();
}
return $this ;
}
/**
* Rotate image using angle .
*
* @ param float $angle to rotate image .
* @ param int $anglebgColor to fill image with if needed .
*
* @ return $this
*/
public function rotate ( $angle , $bgColor )
{
$this -> log ( " Rotate image " . $angle . " degrees with filler color. " );
$color = $this -> getBackgroundColor ();
$this -> image = imagerotate ( $this -> image , $angle , $color );
$this -> width = imagesx ( $this -> image );
$this -> height = imagesy ( $this -> image );
$this -> log ( " New image dimension width x height: " . $this -> width . " x " . $this -> height );
return $this ;
}
/**
* Rotate image using information in EXIF .
*
* @ return $this
*/
public function rotateExif ()
{
2015-03-06 13:00:21 +01:00
if ( ! in_array ( $this -> fileType , array ( IMAGETYPE_JPEG , IMAGETYPE_TIFF_II , IMAGETYPE_TIFF_MM ))) {
2015-01-29 21:16:32 +01:00
$this -> log ( " Autorotate ignored, EXIF not supported by this filetype. " );
return $this ;
}
$exif = exif_read_data ( $this -> pathToImage );
if ( ! empty ( $exif [ 'Orientation' ])) {
switch ( $exif [ 'Orientation' ]) {
case 3 :
$this -> log ( " Autorotate 180. " );
$this -> rotate ( 180 , $this -> bgColor );
break ;
case 6 :
$this -> log ( " Autorotate -90. " );
$this -> rotate ( - 90 , $this -> bgColor );
break ;
case 8 :
$this -> log ( " Autorotate 90. " );
$this -> rotate ( 90 , $this -> bgColor );
break ;
default :
$this -> log ( " Autorotate ignored, unknown value as orientation. " );
}
} else {
$this -> log ( " Autorotate ignored, no orientation in EXIF. " );
}
return $this ;
}
/**
* Convert true color image to palette image , keeping alpha .
* http :// stackoverflow . com / questions / 5752514 / how - to - convert - png - to - 8 - bit - png - using - php - gd - library
*
* @ return void
*/
public function trueColorToPalette ()
{
$img = imagecreatetruecolor ( $this -> width , $this -> height );
$bga = imagecolorallocatealpha ( $img , 0 , 0 , 0 , 127 );
imagecolortransparent ( $img , $bga );
imagefill ( $img , 0 , 0 , $bga );
imagecopy ( $img , $this -> image , 0 , 0 , 0 , 0 , $this -> width , $this -> height );
imagetruecolortopalette ( $img , false , 255 );
imagesavealpha ( $img , true );
if ( imageistruecolor ( $this -> image )) {
$this -> log ( " Matching colors with true color image. " );
imagecolormatch ( $this -> image , $img );
}
$this -> image = $img ;
}
/**
* Sharpen image using image convolution .
*
* @ return $this
*/
public function sharpenImage ()
{
$this -> imageConvolution ( 'sharpen' );
return $this ;
}
/**
* Emboss image using image convolution .
*
* @ return $this
*/
public function embossImage ()
{
$this -> imageConvolution ( 'emboss' );
return $this ;
}
/**
* Blur image using image convolution .
*
* @ return $this
*/
public function blurImage ()
{
$this -> imageConvolution ( 'blur' );
return $this ;
}
/**
* Create convolve expression and return arguments for image convolution .
*
* @ param string $expression constant string which evaluates to a list of
* 11 numbers separated by komma or such a list .
*
* @ return array as $matrix ( 3 x3 ), $divisor and $offset
*/
public function createConvolveArguments ( $expression )
{
// Check of matching constant
if ( isset ( $this -> convolves [ $expression ])) {
$expression = $this -> convolves [ $expression ];
}
$part = explode ( ',' , $expression );
$this -> log ( " Creating convolution expressen: $expression " );
// Expect list of 11 numbers, split by , and build up arguments
if ( count ( $part ) != 11 ) {
throw new Exception (
" Missmatch in argument convolve. Expected comma-separated string with
11 float values . Got $expression . "
);
}
array_walk ( $part , function ( $item , $key ) {
if ( ! is_numeric ( $item )) {
throw new Exception ( " Argument to convolve expression should be float but is not. " );
}
});
return array (
array (
array ( $part [ 0 ], $part [ 1 ], $part [ 2 ]),
array ( $part [ 3 ], $part [ 4 ], $part [ 5 ]),
array ( $part [ 6 ], $part [ 7 ], $part [ 8 ]),
),
$part [ 9 ],
$part [ 10 ],
);
}
/**
* Add custom expressions ( or overwrite existing ) for image convolution .
*
* @ param array $options Key value array with strings to be converted
* to convolution expressions .
*
* @ return $this
*/
public function addConvolveExpressions ( $options )
{
$this -> convolves = array_merge ( $this -> convolves , $options );
return $this ;
}
/**
* Image convolution .
*
* @ param string $options A string with 11 float separated by comma .
*
* @ return $this
*/
public function imageConvolution ( $options = null )
{
// Use incoming options or use $this.
$options = $options ? $options : $this -> convolve ;
// Treat incoming as string, split by +
$this -> log ( " Convolution with ' $options ' " );
$options = explode ( " : " , $options );
// Check each option if it matches constant value
foreach ( $options as $option ) {
list ( $matrix , $divisor , $offset ) = $this -> createConvolveArguments ( $option );
imageconvolution ( $this -> image , $matrix , $divisor , $offset );
}
return $this ;
}
/**
* Set default background color between 000000 - FFFFFF or if using
* alpha 00000000 - FFFFFF7F .
*
* @ param string $color as hex value .
*
* @ return $this
*/
public function setDefaultBackgroundColor ( $color )
{
$this -> log ( " Setting default background color to ' $color '. " );
if ( ! ( strlen ( $color ) == 6 || strlen ( $color ) == 8 )) {
throw new Exception (
" Background color needs a hex value of 6 or 8
digits . 000000 - FFFFFF or 00000000 - FFFFFF7F .
Current value was : '$color' . "
);
}
$red = hexdec ( substr ( $color , 0 , 2 ));
$green = hexdec ( substr ( $color , 2 , 2 ));
$blue = hexdec ( substr ( $color , 4 , 2 ));
$alpha = ( strlen ( $color ) == 8 )
? hexdec ( substr ( $color , 6 , 2 ))
: null ;
if (( $red < 0 || $red > 255 )
|| ( $green < 0 || $green > 255 )
|| ( $blue < 0 || $blue > 255 )
|| ( $alpha < 0 || $alpha > 127 )
) {
throw new Exception (
" Background color out of range. Red, green blue
should be 00 - FF and alpha should be 00 - 7 F .
Current value was : '$color' . "
);
}
$this -> bgColor = strtolower ( $color );
$this -> bgColorDefault = array (
'red' => $red ,
'green' => $green ,
'blue' => $blue ,
'alpha' => $alpha
);
return $this ;
}
/**
* Get the background color .
*
* @ param resource $img the image to work with or null if using $this -> image .
*
* @ return color value or null if no background color is set .
*/
private function getBackgroundColor ( $img = null )
{
$img = isset ( $img ) ? $img : $this -> image ;
if ( $this -> bgColorDefault ) {
$red = $this -> bgColorDefault [ 'red' ];
$green = $this -> bgColorDefault [ 'green' ];
$blue = $this -> bgColorDefault [ 'blue' ];
$alpha = $this -> bgColorDefault [ 'alpha' ];
if ( $alpha ) {
$color = imagecolorallocatealpha ( $img , $red , $green , $blue , $alpha );
} else {
$color = imagecolorallocate ( $img , $red , $green , $blue );
}
return $color ;
} else {
return 0 ;
}
}
/**
* Create a image and keep transparency for png and gifs .
*
* @ param int $width of the new image .
* @ param int $height of the new image .
*
* @ return image resource .
*/
private function createImageKeepTransparency ( $width , $height )
{
$this -> log ( " Creating a new working image width= { $width } px, height= { $height } px. " );
$img = imagecreatetruecolor ( $width , $height );
imagealphablending ( $img , false );
imagesavealpha ( $img , true );
2015-10-18 14:52:17 +02:00
$index = $this -> image
? imagecolortransparent ( $this -> image )
: - 1 ;
2015-12-06 12:40:54 +01:00
2015-02-09 00:42:49 +01:00
if ( $index != - 1 ) {
imagealphablending ( $img , true );
$transparent = imagecolorsforindex ( $this -> image , $index );
$color = imagecolorallocatealpha ( $img , $transparent [ 'red' ], $transparent [ 'green' ], $transparent [ 'blue' ], $transparent [ 'alpha' ]);
imagefill ( $img , 0 , 0 , $color );
$index = imagecolortransparent ( $img , $color );
$this -> Log ( " Detected transparent color = " . implode ( " , " , $transparent ) . " at index = $index " );
} elseif ( $this -> bgColorDefault ) {
2015-01-29 21:16:32 +01:00
$color = $this -> getBackgroundColor ( $img );
imagefill ( $img , 0 , 0 , $color );
$this -> Log ( " Filling image with background color. " );
}
return $img ;
}
/**
* Set optimizing and post - processing options .
*
* @ param array $options with config for postprocessing with external tools .
*
* @ return $this
*/
public function setPostProcessingOptions ( $options )
{
if ( isset ( $options [ 'jpeg_optimize' ]) && $options [ 'jpeg_optimize' ]) {
$this -> jpegOptimizeCmd = $options [ 'jpeg_optimize_cmd' ];
} else {
$this -> jpegOptimizeCmd = null ;
}
2016-08-09 13:02:25 +02:00
if ( array_key_exists ( " png_lossy " , $options )
&& $options [ 'png_lossy' ] !== false ) {
$this -> pngLossy = $options [ 'png_lossy' ];
$this -> pngLossyCmd = $options [ 'png_lossy_cmd' ];
} else {
$this -> pngLossyCmd = null ;
}
2015-01-29 21:16:32 +01:00
if ( isset ( $options [ 'png_filter' ]) && $options [ 'png_filter' ]) {
$this -> pngFilterCmd = $options [ 'png_filter_cmd' ];
} else {
$this -> pngFilterCmd = null ;
}
if ( isset ( $options [ 'png_deflate' ]) && $options [ 'png_deflate' ]) {
$this -> pngDeflateCmd = $options [ 'png_deflate_cmd' ];
} else {
$this -> pngDeflateCmd = null ;
}
return $this ;
}
2015-03-06 13:00:21 +01:00
/**
* Find out the type ( file extension ) for the image to be saved .
*
* @ return string as image extension .
*/
protected function getTargetImageExtension ()
{
2015-10-21 00:13:56 +02:00
// switch on mimetype
2015-03-06 13:00:21 +01:00
if ( isset ( $this -> extension )) {
return strtolower ( $this -> extension );
2016-08-09 10:19:49 +02:00
} elseif ( $this -> fileType === IMG_WEBP ) {
return " webp " ;
2015-03-06 13:00:21 +01:00
}
2016-08-09 10:19:49 +02:00
return substr ( image_type_to_extension ( $this -> fileType ), 1 );
2015-03-06 13:00:21 +01:00
}
2015-12-06 12:40:54 +01:00
2015-03-06 13:00:21 +01:00
2015-01-29 21:16:32 +01:00
/**
* Save image .
*
2015-10-18 14:52:17 +02:00
* @ param string $src as target filename .
* @ param string $base as base directory where to store images .
* @ param boolean $overwrite or not , default to always overwrite file .
2015-01-29 21:16:32 +01:00
*
* @ return $this or false if no folder is set .
*/
2015-10-18 14:52:17 +02:00
public function save ( $src = null , $base = null , $overwrite = true )
2015-01-29 21:16:32 +01:00
{
if ( isset ( $src )) {
$this -> setTarget ( $src , $base );
}
2015-10-18 14:52:17 +02:00
if ( $overwrite === false && is_file ( $this -> cacheFileName )) {
$this -> Log ( " Not overwriting file since its already exists and \$ overwrite if false. " );
return ;
}
2015-01-29 21:16:32 +01:00
is_writable ( $this -> saveFolder )
or $this -> raiseError ( 'Target directory is not writable.' );
2015-10-21 00:13:56 +02:00
$type = $this -> getTargetImageExtension ();
$this -> Log ( " Saving image as " . $type );
2015-12-07 16:02:17 +01:00
switch ( $type ) {
2015-01-29 21:16:32 +01:00
case 'jpeg' :
case 'jpg' :
$this -> Log ( " Saving image as JPEG to cache using quality = { $this -> quality } . " );
imagejpeg ( $this -> image , $this -> cacheFileName , $this -> quality );
// Use JPEG optimize if defined
if ( $this -> jpegOptimizeCmd ) {
if ( $this -> verbose ) {
clearstatcache ();
$this -> log ( " Filesize before optimize: " . filesize ( $this -> cacheFileName ) . " bytes. " );
}
$res = array ();
$cmd = $this -> jpegOptimizeCmd . " -outfile $this->cacheFileName $this->cacheFileName " ;
exec ( $cmd , $res );
$this -> log ( $cmd );
$this -> log ( $res );
}
break ;
case 'gif' :
$this -> Log ( " Saving image as GIF to cache. " );
imagegif ( $this -> image , $this -> cacheFileName );
break ;
2016-08-09 10:19:49 +02:00
case 'webp' :
$this -> Log ( " Saving image as WEBP to cache using quality = { $this -> quality } . " );
imagewebp ( $this -> image , $this -> cacheFileName , $this -> quality );
break ;
2015-01-29 21:16:32 +01:00
case 'png' :
2015-03-06 13:00:21 +01:00
default :
2015-01-29 21:16:32 +01:00
$this -> Log ( " Saving image as PNG to cache using compression = { $this -> compress } . " );
// Turn off alpha blending and set alpha flag
imagealphablending ( $this -> image , false );
imagesavealpha ( $this -> image , true );
imagepng ( $this -> image , $this -> cacheFileName , $this -> compress );
2016-08-09 13:02:25 +02:00
// Use external program to process lossy PNG, if defined
$lossyEnabled = $this -> pngLossy === true ;
$lossySoftEnabled = $this -> pngLossy === null ;
$lossyActiveEnabled = $this -> lossy === true ;
if ( $lossyEnabled || ( $lossySoftEnabled && $lossyActiveEnabled )) {
if ( $this -> verbose ) {
clearstatcache ();
$this -> log ( " Lossy enabled: $lossyEnabled " );
$this -> log ( " Lossy soft enabled: $lossySoftEnabled " );
$this -> Log ( " Filesize before lossy optimize: " . filesize ( $this -> cacheFileName ) . " bytes. " );
}
$res = array ();
$cmd = $this -> pngLossyCmd . " $this->cacheFileName $this->cacheFileName " ;
exec ( $cmd , $res );
$this -> Log ( $cmd );
$this -> Log ( $res );
}
2015-01-29 21:16:32 +01:00
// Use external program to filter PNG, if defined
if ( $this -> pngFilterCmd ) {
if ( $this -> verbose ) {
clearstatcache ();
$this -> Log ( " Filesize before filter optimize: " . filesize ( $this -> cacheFileName ) . " bytes. " );
}
$res = array ();
$cmd = $this -> pngFilterCmd . " $this->cacheFileName " ;
exec ( $cmd , $res );
$this -> Log ( $cmd );
$this -> Log ( $res );
}
// Use external program to deflate PNG, if defined
if ( $this -> pngDeflateCmd ) {
if ( $this -> verbose ) {
clearstatcache ();
$this -> Log ( " Filesize before deflate optimize: " . filesize ( $this -> cacheFileName ) . " bytes. " );
}
$res = array ();
$cmd = $this -> pngDeflateCmd . " $this->cacheFileName " ;
exec ( $cmd , $res );
$this -> Log ( $cmd );
$this -> Log ( $res );
}
break ;
}
if ( $this -> verbose ) {
clearstatcache ();
2015-03-06 13:00:21 +01:00
$this -> log ( " Saved image to cache. " );
$this -> log ( " Cached image filesize: " . filesize ( $this -> cacheFileName ) . " bytes. " );
$this -> log ( " imageistruecolor() : " . ( imageistruecolor ( $this -> image ) ? 'true' : 'false' ));
$this -> log ( " imagecolorstotal() : " . imagecolorstotal ( $this -> image ));
$this -> log ( " Number of colors in image = " . $this -> ColorsTotal ( $this -> image ));
2015-02-09 00:42:49 +01:00
$index = imagecolortransparent ( $this -> image );
2015-03-06 13:00:21 +01:00
$this -> log ( " Detected transparent color = " . ( $index > 0 ? implode ( " , " , imagecolorsforindex ( $this -> image , $index )) : " NONE " ) . " at index = $index " );
2015-01-29 21:16:32 +01:00
}
return $this ;
}
2015-12-06 12:18:33 +01:00
/**
* Convert image from one colorpsace / color profile to sRGB without
* color profile .
*
* @ param string $src of image .
* @ param string $dir as base directory where images are .
* @ param string $cache as base directory where to store images .
* @ param string $iccFile filename of colorprofile .
* @ param boolean $useCache or not , default to always use cache .
*
* @ return string | boolean false if no conversion else the converted
* filename .
*/
public function convert2sRGBColorSpace ( $src , $dir , $cache , $iccFile , $useCache = true )
{
if ( $this -> verbose ) {
$this -> log ( " # Converting image to sRGB colorspace. " );
}
if ( ! class_exists ( " Imagick " )) {
$this -> log ( " Ignoring since Imagemagick is not installed. " );
return false ;
}
// Prepare
$this -> setSaveFolder ( $cache )
-> setSource ( $src , $dir )
-> generateFilename ( null , false , 'srgb_' );
// Check if the cached version is accurate.
if ( $useCache && is_readable ( $this -> cacheFileName )) {
$fileTime = filemtime ( $this -> pathToImage );
$cacheTime = filemtime ( $this -> cacheFileName );
if ( $fileTime <= $cacheTime ) {
$this -> log ( " Using cached version: " . $this -> cacheFileName );
return $this -> cacheFileName ;
}
}
// Only covert if cachedir is writable
if ( is_writable ( $this -> saveFolder )) {
// Load file and check if conversion is needed
$image = new Imagick ( $this -> pathToImage );
$colorspace = $image -> getImageColorspace ();
$this -> log ( " Current colorspace: " . $colorspace );
2015-12-06 12:40:54 +01:00
$profiles = $image -> getImageProfiles ( '*' , false );
2015-12-06 12:18:33 +01:00
$hasICCProfile = ( array_search ( 'icc' , $profiles ) !== false );
$this -> log ( " Has ICC color profile: " . ( $hasICCProfile ? " YES " : " NO " ));
if ( $colorspace != Imagick :: COLORSPACE_SRGB || $hasICCProfile ) {
$this -> log ( " Converting to sRGB. " );
$sRGBicc = file_get_contents ( $iccFile );
$image -> profileImage ( 'icc' , $sRGBicc );
2015-12-06 12:40:54 +01:00
2015-12-06 12:18:33 +01:00
$image -> transformImageColorspace ( Imagick :: COLORSPACE_SRGB );
$image -> writeImage ( $this -> cacheFileName );
return $this -> cacheFileName ;
}
}
2015-12-06 12:40:54 +01:00
2015-12-06 12:18:33 +01:00
return false ;
}
2015-01-29 21:16:32 +01:00
/**
* Create a hard link , as an alias , to the cached file .
*
* @ param string $alias where to store the link ,
* filename without extension .
*
* @ return $this
*/
public function linkToCacheFile ( $alias )
{
if ( $alias === null ) {
$this -> log ( " Ignore creating alias. " );
return $this ;
}
if ( is_readable ( $alias )) {
unlink ( $alias );
}
$res = link ( $this -> cacheFileName , $alias );
if ( $res ) {
$this -> log ( " Created an alias as: $alias " );
} else {
$this -> log ( " Failed to create the alias: $alias " );
}
return $this ;
}
2015-10-21 01:13:36 +02:00
/**
2016-08-08 09:29:26 +02:00
* Add HTTP header for output together with image .
2015-10-21 01:13:36 +02:00
*
* @ param string $type the header type such as " Cache-Control "
* @ param string $value the value to use
*
* @ return void
*/
public function addHTTPHeader ( $type , $value )
{
$this -> HTTPHeader [ $type ] = $value ;
}
2015-01-29 21:16:32 +01:00
/**
* Output image to browser using caching .
*
2015-10-21 01:13:36 +02:00
* @ param string $file to read and output , default is to
* use $this -> cacheFileName
* @ param string $format set to json to output file as json
* object with details
2015-01-29 21:16:32 +01:00
*
* @ return void
*/
public function output ( $file = null , $format = null )
{
if ( is_null ( $file )) {
$file = $this -> cacheFileName ;
}
if ( is_null ( $format )) {
$format = $this -> outputFormat ;
}
2016-08-09 10:19:49 +02:00
$this -> log ( " ### Output " );
2015-01-29 21:16:32 +01:00
$this -> log ( " Output format is: $format " );
if ( ! $this -> verbose && $format == 'json' ) {
header ( 'Content-type: application/json' );
echo $this -> json ( $file );
exit ;
2015-09-01 16:53:19 +02:00
} elseif ( $format == 'ascii' ) {
header ( 'Content-type: text/plain' );
echo $this -> ascii ( $file );
exit ;
2015-01-29 21:16:32 +01:00
}
$this -> log ( " Outputting image: $file " );
// Get image modification time
clearstatcache ();
$lastModified = filemtime ( $file );
2016-08-08 09:29:26 +02:00
$lastModifiedFormat = " D, d M Y H:i:s " ;
$gmdate = gmdate ( $lastModifiedFormat , $lastModified );
2015-01-29 21:16:32 +01:00
if ( ! $this -> verbose ) {
2016-08-08 09:29:26 +02:00
$header = " Last-Modified: $gmdate GMT " ;
header ( $header );
$this -> fastTrackCache -> addHeader ( $header );
$this -> fastTrackCache -> setLastModified ( $lastModified );
2015-01-29 21:16:32 +01:00
}
2016-08-08 09:29:26 +02:00
foreach ( $this -> HTTPHeader as $key => $val ) {
$header = " $key : $val " ;
header ( $header );
$this -> fastTrackCache -> addHeader ( $header );
2015-10-21 01:13:36 +02:00
}
2016-08-09 10:19:49 +02:00
if ( isset ( $_SERVER [ 'HTTP_IF_MODIFIED_SINCE' ])
&& strtotime ( $_SERVER [ 'HTTP_IF_MODIFIED_SINCE' ]) == $lastModified ) {
2015-01-29 21:16:32 +01:00
if ( $this -> verbose ) {
$this -> log ( " 304 not modified " );
$this -> verboseOutput ();
exit ;
}
header ( " HTTP/1.0 304 Not Modified " );
2016-08-08 10:39:11 +02:00
if ( CIMAGE_DEBUG ) {
trace ( __CLASS__ . " 304 " );
}
2015-01-29 21:16:32 +01:00
} else {
2016-08-09 10:19:49 +02:00
$this -> loadImageDetails ( $file );
$mime = $this -> getMimeType ();
2015-10-21 00:13:56 +02:00
$size = filesize ( $file );
2015-01-29 21:16:32 +01:00
if ( $this -> verbose ) {
2015-10-21 00:13:56 +02:00
$this -> log ( " Last-Modified: " . $gmdate . " GMT " );
$this -> log ( " Content-type: " . $mime );
$this -> log ( " Content-length: " . $size );
2015-01-29 21:16:32 +01:00
$this -> verboseOutput ();
2015-12-06 12:40:54 +01:00
2015-07-25 22:21:12 +02:00
if ( is_null ( $this -> verboseFileName )) {
exit ;
}
2015-01-29 21:16:32 +01:00
}
2016-08-08 09:29:26 +02:00
$header = " Content-type: $mime " ;
header ( $header );
$this -> fastTrackCache -> addHeaderOnOutput ( $header );
$header = " Content-length: $size " ;
header ( $header );
$this -> fastTrackCache -> addHeaderOnOutput ( $header );
$this -> fastTrackCache -> setSource ( $file );
$this -> fastTrackCache -> writeToCache ();
2016-08-08 10:39:11 +02:00
if ( CIMAGE_DEBUG ) {
trace ( __CLASS__ . " 200 " );
}
2015-01-29 21:16:32 +01:00
readfile ( $file );
}
exit ;
}
/**
* Create a JSON object from the image details .
*
* @ param string $file the file to output .
*
* @ return string json - encoded representation of the image .
*/
public function json ( $file = null )
{
$file = $file ? $file : $this -> cacheFileName ;
$details = array ();
clearstatcache ();
2015-03-06 13:00:21 +01:00
$details [ 'src' ] = $this -> imageSrc ;
$lastModified = filemtime ( $this -> pathToImage );
2015-01-29 21:16:32 +01:00
$details [ 'srcGmdate' ] = gmdate ( " D, d M Y H:i:s " , $lastModified );
2015-03-06 13:00:21 +01:00
$details [ 'cache' ] = basename ( $this -> cacheFileName );
$lastModified = filemtime ( $this -> cacheFileName );
2015-01-29 21:16:32 +01:00
$details [ 'cacheGmdate' ] = gmdate ( " D, d M Y H:i:s " , $lastModified );
2015-03-06 13:00:21 +01:00
$this -> load ( $file );
2015-01-29 21:16:32 +01:00
2015-03-06 13:00:21 +01:00
$details [ 'filename' ] = basename ( $file );
2016-08-09 10:19:49 +02:00
$details [ 'mimeType' ] = $this -> getMimeType ( $this -> fileType );
2015-03-06 13:00:21 +01:00
$details [ 'width' ] = $this -> width ;
$details [ 'height' ] = $this -> height ;
2015-01-29 21:16:32 +01:00
$details [ 'aspectRatio' ] = round ( $this -> width / $this -> height , 3 );
2015-03-06 13:00:21 +01:00
$details [ 'size' ] = filesize ( $file );
2015-01-29 21:16:32 +01:00
$details [ 'colors' ] = $this -> colorsTotal ( $this -> image );
2015-10-21 00:13:56 +02:00
$details [ 'includedFiles' ] = count ( get_included_files ());
$details [ 'memoryPeek' ] = round ( memory_get_peak_usage () / 1024 / 1024 , 3 ) . " MB " ;
$details [ 'memoryCurrent' ] = round ( memory_get_usage () / 1024 / 1024 , 3 ) . " MB " ;
$details [ 'memoryLimit' ] = ini_get ( 'memory_limit' );
2015-12-06 12:40:54 +01:00
2015-10-21 00:13:56 +02:00
if ( isset ( $_SERVER [ 'REQUEST_TIME_FLOAT' ])) {
$details [ 'loadTime' ] = ( string ) round (( microtime ( true ) - $_SERVER [ 'REQUEST_TIME_FLOAT' ]), 3 ) . " s " ;
}
if ( $details [ 'mimeType' ] == 'image/png' ) {
$details [ 'pngType' ] = $this -> getPngTypeAsString ( null , $file );
}
2015-01-29 21:16:32 +01:00
$options = null ;
if ( defined ( " JSON_PRETTY_PRINT " ) && defined ( " JSON_UNESCAPED_SLASHES " )) {
$options = JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES ;
}
return json_encode ( $details , $options );
}
2015-09-01 16:53:19 +02:00
/**
* Set options for creating ascii version of image .
*
* @ param array $options empty to use default or set options to change .
*
* @ return void .
*/
public function setAsciiOptions ( $options = array ())
{
$this -> asciiOptions = $options ;
}
/**
* Create an ASCII version from the image details .
*
* @ param string $file the file to output .
*
* @ return string ASCII representation of the image .
*/
public function ascii ( $file = null )
{
$file = $file ? $file : $this -> cacheFileName ;
$asciiArt = new CAsciiArt ();
$asciiArt -> setOptions ( $this -> asciiOptions );
return $asciiArt -> createFromFile ( $file );
}
2015-01-29 21:16:32 +01:00
/**
* Log an event if verbose mode .
*
* @ param string $message to log .
*
* @ return this
*/
public function log ( $message )
{
if ( $this -> verbose ) {
$this -> log [] = $message ;
}
return $this ;
}
2015-07-25 22:21:12 +02:00
/**
* Do verbose output to a file .
*
* @ param string $fileName where to write the verbose output .
*
* @ return void
*/
public function setVerboseToFile ( $fileName )
{
$this -> log ( " Setting verbose output to file. " );
$this -> verboseFileName = $fileName ;
}
2015-01-29 21:16:32 +01:00
/**
* Do verbose output and print out the log and the actual images .
*
* @ return void
*/
private function verboseOutput ()
{
$log = null ;
2016-08-09 10:19:49 +02:00
$this -> log ( " ### Summary of verbose log " );
2015-01-29 21:16:32 +01:00
$this -> log ( " As JSON: \n " . $this -> json ());
$this -> log ( " Memory peak: " . round ( memory_get_peak_usage () / 1024 / 1024 ) . " M " );
$this -> log ( " Memory limit: " . ini_get ( 'memory_limit' ));
$included = get_included_files ();
$this -> log ( " Included files: " . count ( $included ));
foreach ( $this -> log as $val ) {
if ( is_array ( $val )) {
foreach ( $val as $val1 ) {
$log .= htmlentities ( $val1 ) . '<br/>' ;
}
} else {
$log .= htmlentities ( $val ) . '<br/>' ;
}
}
2015-07-25 22:21:12 +02:00
if ( ! is_null ( $this -> verboseFileName )) {
file_put_contents (
$this -> verboseFileName ,
str_replace ( " <br/> " , " \n " , $log )
);
} else {
echo <<< EOD
2015-01-29 21:16:32 +01:00
< h1 > CImage Verbose Output </ h1 >
< pre > { $log } </ pre >
EOD ;
2015-07-25 22:21:12 +02:00
}
2015-01-29 21:16:32 +01:00
}
/**
* Raise error , enables to implement a selection of error methods .
*
* @ param string $message the error message to display .
*
* @ return void
* @ throws Exception
*/
private function raiseError ( $message )
{
throw new Exception ( $message );
}
}
2015-12-07 15:36:06 +01:00
/**
* Deal with the cache directory and cached items .
*
*/
class CCache
{
/**
* Path to the cache directory .
*/
private $path ;
/**
* Set the path to the cache dir which must exist .
*
* @ param string path to the cache dir .
*
* @ throws Exception when $path is not a directory .
*
* @ return $this
*/
public function setDir ( $path )
{
if ( ! is_dir ( $path )) {
throw new Exception ( " Cachedir is not a directory. " );
}
$this -> path = $path ;
return $this ;
}
/**
* Get the path to the cache subdir and try to create it if its not there .
*
* @ param string $subdir name of subdir
* @ param array $create default is to try to create the subdir
*
* @ return string | boolean as real path to the subdir or
* false if it does not exists
*/
public function getPathToSubdir ( $subdir , $create = true )
{
$path = realpath ( $this -> path . " / " . $subdir );
if ( is_dir ( $path )) {
return $path ;
}
if ( $create && is_writable ( $this -> path )) {
$path = $this -> path . " / " . $subdir ;
if ( mkdir ( $path )) {
return realpath ( $path );
}
}
return false ;
}
/**
* Get status of the cache subdir .
*
* @ param string $subdir name of subdir
*
* @ return string with status
*/
public function getStatusOfSubdir ( $subdir )
{
$path = realpath ( $this -> path . " / " . $subdir );
$exists = is_dir ( $path );
$res = $exists ? " exists " : " does not exist " ;
if ( $exists ) {
$res .= is_writable ( $path ) ? " , writable " : " , not writable " ;
}
return $res ;
}
/**
* Remove the cache subdir .
*
* @ param string $subdir name of subdir
*
* @ return null | boolean true if success else false , null if no operation
*/
public function removeSubdir ( $subdir )
{
$path = realpath ( $this -> path . " / " . $subdir );
if ( is_dir ( $path )) {
return rmdir ( $path );
}
return null ;
}
}
2015-01-29 21:16:32 +01:00
/**
2016-08-08 09:29:26 +02:00
* Enable a fast track cache with a json representation of the image delivery .
2015-01-29 21:16:32 +01:00
*
*/
2016-08-08 09:29:26 +02:00
class CFastTrackCache
{
/**
* Cache is disabled to start with .
*/
private $enabled = false ;
2015-01-29 21:16:32 +01:00
2015-12-07 15:36:06 +01:00
2015-02-10 22:49:17 +01:00
2016-08-08 09:29:26 +02:00
/**
* Path to the cache directory .
*/
private $path ;
2015-01-29 21:16:32 +01:00
2016-08-08 09:29:26 +02:00
/**
* Filename of current cache item .
*/
private $filename ;
2015-12-06 12:18:33 +01:00
2015-12-07 17:40:00 +01:00
2015-01-29 21:16:32 +01:00
2016-08-08 09:29:26 +02:00
/**
* Container with items to store as cached item .
*/
private $container ;
/**
* Enable or disable cache .
*
* @ param boolean $enable set to true to enable , false to disable
*
* @ return $this
*/
public function enable ( $enabled )
{
$this -> enabled = $enabled ;
return $this ;
2015-01-29 21:16:32 +01:00
}
2015-07-25 22:21:12 +02:00
2015-01-29 21:16:32 +01:00
2016-08-08 09:29:26 +02:00
/**
* Set the path to the cache dir which must exist .
*
* @ param string $path to the cache dir .
*
* @ throws Exception when $path is not a directory .
*
* @ return $this
*/
public function setCacheDir ( $path )
{
if ( ! is_dir ( $path )) {
throw new Exception ( " Cachedir is not a directory. " );
}
2015-01-29 21:16:32 +01:00
2016-08-08 09:29:26 +02:00
$this -> path = rtrim ( $path , " / " );
return $this ;
}
2015-01-29 21:16:32 +01:00
2016-08-08 09:29:26 +02:00
/**
* Set the filename to store in cache , use the querystring to create that
* filename .
*
* @ param array $clear items to clear in $_GET when creating the filename .
*
* @ return string as filename created .
*/
public function setFilename ( $clear )
{
$query = $_GET ;
// Remove parts from querystring that should not be part of filename
foreach ( $clear as $value ) {
unset ( $query [ $value ]);
2015-01-29 21:16:32 +01:00
}
2016-08-08 09:29:26 +02:00
arsort ( $query );
$queryAsString = http_build_query ( $query );
$this -> filename = md5 ( $queryAsString );
2016-08-08 10:39:11 +02:00
if ( CIMAGE_DEBUG ) {
$this -> container [ " query-string " ] = $queryAsString ;
}
2016-08-08 09:29:26 +02:00
return $this -> filename ;
2015-01-29 21:16:32 +01:00
}
2016-08-08 09:29:26 +02:00
/**
* Add header items .
*
* @ param string $header add this as header .
*
* @ return $this
*/
public function addHeader ( $header )
{
$this -> container [ " header " ][] = $header ;
return $this ;
}
2015-01-29 21:16:32 +01:00
2016-08-08 09:29:26 +02:00
/**
* Add header items on output , these are not output when 304.
*
* @ param string $header add this as header .
*
* @ return $this
*/
public function addHeaderOnOutput ( $header )
{
$this -> container [ " header-output " ][] = $header ;
return $this ;
}
2015-01-29 21:16:32 +01:00
2016-08-08 09:29:26 +02:00
/**
* Set path to source image to .
*
* @ param string $source path to source image file .
*
* @ return $this
*/
public function setSource ( $source )
{
$this -> container [ " source " ] = $source ;
return $this ;
}
2015-01-29 21:16:32 +01:00
2016-08-08 09:29:26 +02:00
/**
* Set last modified of source image , use to check for 304.
*
* @ param string $lastModified
*
* @ return $this
*/
public function setLastModified ( $lastModified )
{
$this -> container [ " last-modified " ] = $lastModified ;
return $this ;
2015-01-29 21:16:32 +01:00
}
2016-08-08 09:29:26 +02:00
/**
* Get filename of cached item .
*
* @ return string as filename .
*/
public function getFilename ()
{
return $this -> path . " / " . $this -> filename ;
2015-01-29 21:16:32 +01:00
}
2016-08-08 09:29:26 +02:00
/**
* Write current item to cache .
*
* @ return boolean if cache file was written .
*/
public function writeToCache ()
{
if ( ! $this -> enabled ) {
return false ;
}
if ( is_dir ( $this -> path ) && is_writable ( $this -> path )) {
$filename = $this -> getFilename ();
return file_put_contents ( $filename , json_encode ( $this -> container )) !== false ;
}
return false ;
}
/**
* Output current item from cache , if available .
*
* @ return void
*/
public function output ()
{
$filename = $this -> getFilename ();
if ( ! is_readable ( $filename )) {
return ;
}
$item = json_decode ( file_get_contents ( $filename ), true );
if ( ! is_readable ( $item [ " source " ])) {
return ;
}
foreach ( $item [ " header " ] as $value ) {
header ( $value );
}
if ( isset ( $_SERVER [ " HTTP_IF_MODIFIED_SINCE " ])
&& strtotime ( $_SERVER [ " HTTP_IF_MODIFIED_SINCE " ]) == $item [ " last-modified " ]) {
header ( " HTTP/1.0 304 Not Modified " );
2016-08-08 10:39:11 +02:00
if ( CIMAGE_DEBUG ) {
trace ( __CLASS__ . " 304 " );
}
2016-08-08 09:29:26 +02:00
exit ;
}
foreach ( $item [ " header-output " ] as $value ) {
header ( $value );
}
2016-08-08 10:39:11 +02:00
if ( CIMAGE_DEBUG ) {
trace ( __CLASS__ . " 200 " );
}
2016-08-08 09:29:26 +02:00
readfile ( $item [ " source " ]);
exit ;
}
2015-01-29 21:16:32 +01:00
}
2016-08-08 09:29:26 +02:00
/**
* Resize and crop images on the fly , store generated images in a cache .
*
* @ author Mikael Roos mos @ dbwebb . se
* @ example http :// dbwebb . se / opensource / cimage
* @ link https :// github . com / mosbth / cimage
*
*/
2017-03-31 00:47:55 +02:00
/**
* Custom exception handler .
*/
set_exception_handler ( function ( $exception ) {
errorPage (
" <p><b>img.php: Uncaught exception:</b> <p> "
. $exception -> getMessage ()
. " </p><pre> "
. $exception -> getTraceAsString ()
. " </pre> " ,
500
);
});
2015-01-29 21:16:32 +01:00
/**
* Get configuration options from file , if the file exists , else use $config
* if its defined or create an empty $config .
*/
$configFile = __DIR__ . '/' . basename ( __FILE__ , '.php' ) . '_config.php' ;
if ( is_file ( $configFile )) {
$config = require $configFile ;
2015-07-23 11:32:19 +02:00
} elseif ( ! isset ( $config )) {
2015-01-29 21:16:32 +01:00
$config = array ();
}
2016-08-08 11:19:53 +02:00
// Make CIMAGE_DEBUG false by default, if not already defined
if ( ! defined ( " CIMAGE_DEBUG " )) {
define ( " CIMAGE_DEBUG " , false );
}
2015-01-29 21:16:32 +01:00
2016-08-08 09:29:26 +02:00
/**
* Setup the autoloader , but not when using a bundle .
*/
if ( ! defined ( " CIMAGE_BUNDLE " )) {
if ( ! isset ( $config [ " autoloader " ])) {
die ( " CImage: Missing autoloader. " );
}
require $config [ " autoloader " ];
}
2015-01-29 21:16:32 +01:00
/**
* verbose , v - do a verbose dump of what happens
2015-07-25 22:21:12 +02:00
* vf - do verbose dump to file
2015-01-29 21:16:32 +01:00
*/
$verbose = getDefined ( array ( 'verbose' , 'v' ), true , false );
2015-07-25 22:21:12 +02:00
$verboseFile = getDefined ( 'vf' , true , false );
2016-08-08 09:29:26 +02:00
verbose ( " img.php version = " . CIMAGE_VERSION );
2015-01-29 21:16:32 +01:00
2015-10-21 00:13:56 +02:00
/**
* status - do a verbose dump of the configuration
*/
$status = getDefined ( 'status' , true , false );
2015-01-29 21:16:32 +01:00
/**
* Set mode as strict , production or development .
* Default is production environment .
*/
$mode = getConfig ( 'mode' , 'production' );
// Settings for any mode
set_time_limit ( 20 );
ini_set ( 'gd.jpeg_ignore_warning' , 1 );
if ( ! extension_loaded ( 'gd' )) {
2015-12-06 12:18:33 +01:00
errorPage ( " Extension gd is not loaded. " , 500 );
2015-01-29 21:16:32 +01:00
}
// Specific settings for each mode
if ( $mode == 'strict' ) {
error_reporting ( 0 );
ini_set ( 'display_errors' , 0 );
ini_set ( 'log_errors' , 1 );
$verbose = false ;
2015-10-21 00:13:56 +02:00
$status = false ;
2015-07-25 22:21:12 +02:00
$verboseFile = false ;
2015-12-06 12:18:33 +01:00
2015-07-23 11:32:19 +02:00
} elseif ( $mode == 'production' ) {
2015-01-29 21:16:32 +01:00
error_reporting ( - 1 );
ini_set ( 'display_errors' , 0 );
ini_set ( 'log_errors' , 1 );
$verbose = false ;
2015-10-21 00:13:56 +02:00
$status = false ;
2015-07-25 22:21:12 +02:00
$verboseFile = false ;
2015-01-29 21:16:32 +01:00
2015-07-23 11:32:19 +02:00
} elseif ( $mode == 'development' ) {
2015-01-29 21:16:32 +01:00
error_reporting ( - 1 );
ini_set ( 'display_errors' , 1 );
ini_set ( 'log_errors' , 0 );
2015-07-25 22:21:12 +02:00
$verboseFile = false ;
} elseif ( $mode == 'test' ) {
error_reporting ( - 1 );
ini_set ( 'display_errors' , 1 );
ini_set ( 'log_errors' , 0 );
2015-01-29 21:16:32 +01:00
} else {
2015-12-06 12:18:33 +01:00
errorPage ( " Unknown mode: $mode " , 500 );
2015-01-29 21:16:32 +01:00
}
verbose ( " mode = $mode " );
verbose ( " error log = " . ini_get ( 'error_log' ));
/**
* Set default timezone if not set or if its set in the config - file .
*/
$defaultTimezone = getConfig ( 'default_timezone' , null );
if ( $defaultTimezone ) {
date_default_timezone_set ( $defaultTimezone );
2015-07-23 11:32:19 +02:00
} elseif ( ! ini_get ( 'default_timezone' )) {
2015-01-29 21:16:32 +01:00
date_default_timezone_set ( 'UTC' );
}
/**
* Check if passwords are configured , used and match .
* Options decide themself if they require passwords to be used .
*/
$pwdConfig = getConfig ( 'password' , false );
2015-02-10 22:49:17 +01:00
$pwdAlways = getConfig ( 'password_always' , false );
2015-07-25 22:58:04 +02:00
$pwdType = getConfig ( 'password_type' , 'text' );
2015-01-29 21:16:32 +01:00
$pwd = get ( array ( 'password' , 'pwd' ), null );
// Check if passwords match, if configured to use passwords
2015-07-25 23:20:21 +02:00
$passwordMatch = null ;
2015-07-25 22:58:04 +02:00
if ( $pwd ) {
2015-12-07 15:36:06 +01:00
switch ( $pwdType ) {
2015-07-25 22:58:04 +02:00
case 'md5' :
$passwordMatch = ( $pwdConfig === md5 ( $pwd ));
break ;
case 'hash' :
$passwordMatch = password_verify ( $pwd , $pwdConfig );
break ;
case 'text' :
$passwordMatch = ( $pwdConfig === $pwd );
break ;
2015-07-25 23:20:21 +02:00
default :
$passwordMatch = false ;
2015-02-10 22:49:17 +01:00
}
2015-07-25 22:58:04 +02:00
}
2015-02-10 22:49:17 +01:00
2015-07-25 22:58:04 +02:00
if ( $pwdAlways && $passwordMatch !== true ) {
2015-12-06 12:18:33 +01:00
errorPage ( " Password required and does not match or exists. " , 403 );
2015-01-29 21:16:32 +01:00
}
verbose ( " password match = $passwordMatch " );
/**
* Prevent hotlinking , leeching , of images by controlling who access them
* from where .
*
*/
$allowHotlinking = getConfig ( 'allow_hotlinking' , true );
$hotlinkingWhitelist = getConfig ( 'hotlinking_whitelist' , array ());
$serverName = isset ( $_SERVER [ 'SERVER_NAME' ]) ? $_SERVER [ 'SERVER_NAME' ] : null ;
$referer = isset ( $_SERVER [ 'HTTP_REFERER' ]) ? $_SERVER [ 'HTTP_REFERER' ] : null ;
$refererHost = parse_url ( $referer , PHP_URL_HOST );
if ( ! $allowHotlinking ) {
if ( $passwordMatch ) {
; // Always allow when password match
2015-07-25 22:21:12 +02:00
verbose ( " Hotlinking since passwordmatch " );
2015-07-23 11:32:19 +02:00
} elseif ( $passwordMatch === false ) {
2015-12-06 12:18:33 +01:00
errorPage ( " Hotlinking/leeching not allowed when password missmatch. " , 403 );
2015-07-23 11:32:19 +02:00
} elseif ( ! $referer ) {
2015-12-06 12:18:33 +01:00
errorPage ( " Hotlinking/leeching not allowed and referer is missing. " , 403 );
2015-07-23 11:32:19 +02:00
} elseif ( strcmp ( $serverName , $refererHost ) == 0 ) {
2015-01-29 21:16:32 +01:00
; // Allow when serverName matches refererHost
2015-07-25 22:21:12 +02:00
verbose ( " Hotlinking disallowed but serverName matches refererHost. " );
2015-07-23 11:32:19 +02:00
} elseif ( ! empty ( $hotlinkingWhitelist )) {
2015-07-25 22:21:12 +02:00
$whitelist = new CWhitelist ();
$allowedByWhitelist = $whitelist -> check ( $refererHost , $hotlinkingWhitelist );
2015-01-29 21:16:32 +01:00
2015-07-25 22:21:12 +02:00
if ( $allowedByWhitelist ) {
verbose ( " Hotlinking/leeching allowed by whitelist. " );
} else {
2015-12-06 12:18:33 +01:00
errorPage ( " Hotlinking/leeching not allowed by whitelist. Referer: $referer . " , 403 );
2015-01-29 21:16:32 +01:00
}
} else {
2015-12-06 12:18:33 +01:00
errorPage ( " Hotlinking/leeching not allowed. " , 403 );
2015-01-29 21:16:32 +01:00
}
}
verbose ( " allow_hotlinking = $allowHotlinking " );
verbose ( " referer = $referer " );
verbose ( " referer host = $refererHost " );
/**
2016-08-08 09:29:26 +02:00
* Create the class for the image .
2015-01-29 21:16:32 +01:00
*/
2016-08-08 09:29:26 +02:00
$CImage = getConfig ( 'CImage' , 'CImage' );
$img = new $CImage ();
$img -> setVerbose ( $verbose || $verboseFile );
2015-01-29 21:16:32 +01:00
2016-08-08 09:29:26 +02:00
/**
* Get the cachepath from config .
*/
$CCache = getConfig ( 'CCache' , 'CCache' );
$cachePath = getConfig ( 'cache_path' , __DIR__ . '/../cache/' );
$cache = new $CCache ();
$cache -> setDir ( $cachePath );
2015-01-29 21:16:32 +01:00
/**
2016-08-08 09:29:26 +02:00
* no - cache , nc - skip the cached version and process and create a new version in cache .
2015-01-29 21:16:32 +01:00
*/
2016-08-08 09:29:26 +02:00
$useCache = getDefined ( array ( 'no-cache' , 'nc' ), false , true );
verbose ( " use cache = $useCache " );
2015-01-29 21:16:32 +01:00
2015-12-07 15:36:06 +01:00
/**
2016-08-08 09:29:26 +02:00
* Prepare fast track cache for swriting cache items .
2015-12-07 15:36:06 +01:00
*/
2016-08-08 09:29:26 +02:00
$fastTrackCache = " fasttrack " ;
$allowFastTrackCache = getConfig ( 'fast_track_allow' , false );
2015-12-07 15:36:06 +01:00
2016-08-08 09:29:26 +02:00
$CFastTrackCache = getConfig ( 'CFastTrackCache' , 'CFastTrackCache' );
$ftc = new $CFastTrackCache ();
$ftc -> setCacheDir ( $cache -> getPathToSubdir ( $fastTrackCache ))
-> enable ( $allowFastTrackCache )
-> setFilename ( array ( 'no-cache' , 'nc' ));
$img -> injectDependency ( " fastTrackCache " , $ftc );
/**
* Load and output images from fast track cache , if items are available
* in cache .
*/
if ( $useCache && $allowFastTrackCache ) {
2016-08-08 10:39:11 +02:00
if ( CIMAGE_DEBUG ) {
trace ( " img.php fast track cache enabled and used " );
}
2016-08-08 09:29:26 +02:00
$ftc -> output ();
}
2015-12-07 15:36:06 +01:00
2015-01-29 21:16:32 +01:00
/**
* Allow or disallow remote download of images from other servers .
* Passwords apply if used .
*
*/
$allowRemote = getConfig ( 'remote_allow' , false );
if ( $allowRemote && $passwordMatch !== false ) {
2015-12-07 15:36:06 +01:00
$cacheRemote = $cache -> getPathToSubdir ( " remote " );
2015-01-29 21:16:32 +01:00
$pattern = getConfig ( 'remote_pattern' , null );
2015-12-07 15:36:06 +01:00
$img -> setRemoteDownload ( $allowRemote , $cacheRemote , $pattern );
2015-03-04 11:29:10 +01:00
$whitelist = getConfig ( 'remote_whitelist' , null );
$img -> setRemoteHostWhitelist ( $whitelist );
2015-01-29 21:16:32 +01:00
}
/**
* shortcut , sc - extend arguments with a constant value , defined
* in config - file .
*/
$shortcut = get ( array ( 'shortcut' , 'sc' ), null );
$shortcutConfig = getConfig ( 'shortcut' , array (
'sepia' => " &f=grayscale&f0=brightness,-10&f1=contrast,-20&f2=colorize,120,60,0,0&sharpen " ,
));
verbose ( " shortcut = $shortcut " );
if ( isset ( $shortcut )
&& isset ( $shortcutConfig [ $shortcut ])) {
parse_str ( $shortcutConfig [ $shortcut ], $get );
verbose ( " shortcut-constant = { $shortcutConfig [ $shortcut ] } " );
$_GET = array_merge ( $_GET , $get );
}
/**
* src - the source image file .
*/
2015-04-28 14:22:38 +02:00
$srcImage = urldecode ( get ( 'src' ))
2015-12-06 12:18:33 +01:00
or errorPage ( 'Must set src-attribute.' , 404 );
2015-01-29 21:16:32 +01:00
2016-04-01 10:27:33 +02:00
// Get settings for src-alt as backup image
$srcAltImage = urldecode ( get ( 'src-alt' , null ));
$srcAltConfig = getConfig ( 'src_alt' , null );
if ( empty ( $srcAltImage )) {
$srcAltImage = $srcAltConfig ;
}
2015-01-29 21:16:32 +01:00
// Check for valid/invalid characters
$imagePath = getConfig ( 'image_path' , __DIR__ . '/img/' );
$imagePathConstraint = getConfig ( 'image_path_constraint' , true );
2015-04-28 14:22:38 +02:00
$validFilename = getConfig ( 'valid_filename' , '#^[a-z0-9A-Z-/_ \.:]+$#' );
2015-01-29 21:16:32 +01:00
2016-04-01 10:27:33 +02:00
// Source is remote
$remoteSource = false ;
2015-10-18 15:43:43 +02:00
// Dummy image feature
2015-10-18 14:52:17 +02:00
$dummyEnabled = getConfig ( 'dummy_enabled' , true );
$dummyFilename = getConfig ( 'dummy_filename' , 'dummy' );
$dummyImage = false ;
2015-01-29 21:16:32 +01:00
preg_match ( $validFilename , $srcImage )
2016-04-01 10:27:33 +02:00
or errorPage ( 'Source filename contains invalid characters.' , 404 );
2015-01-29 21:16:32 +01:00
2015-10-18 14:52:17 +02:00
if ( $dummyEnabled && $srcImage === $dummyFilename ) {
// Prepare to create a dummy image and use it as the source image.
$dummyImage = true ;
} elseif ( $allowRemote && $img -> isRemoteSource ( $srcImage )) {
2015-01-29 21:16:32 +01:00
// If source is a remote file, ignore local file checks.
2016-04-01 10:27:33 +02:00
$remoteSource = true ;
2015-01-29 21:16:32 +01:00
2016-04-01 10:27:33 +02:00
} else {
2015-01-29 21:16:32 +01:00
2016-04-01 10:27:33 +02:00
// Check if file exists on disk or try using src-alt
2015-01-29 21:16:32 +01:00
$pathToImage = realpath ( $imagePath . $srcImage );
2016-04-01 10:27:33 +02:00
if ( ! is_file ( $pathToImage ) && ! empty ( $srcAltImage )) {
// Try using the src-alt instead
$srcImage = $srcAltImage ;
$pathToImage = realpath ( $imagePath . $srcImage );
preg_match ( $validFilename , $srcImage )
or errorPage ( 'Source (alt) filename contains invalid characters.' , 404 );
if ( $dummyEnabled && $srcImage === $dummyFilename ) {
// Check if src-alt is the dummy image
$dummyImage = true ;
}
}
if ( ! $dummyImage ) {
is_file ( $pathToImage )
or errorPage (
' Source image is not a valid file , check the filename and that a
matching file exists on the filesystem . ' ,
404
);
}
}
if ( $imagePathConstraint && ! $dummyImage && ! $remoteSource ) {
// Check that the image is a file below the directory 'image_path'.
$imageDir = realpath ( $imagePath );
2015-01-29 21:16:32 +01:00
substr_compare ( $imageDir , $pathToImage , 0 , strlen ( $imageDir )) == 0
or errorPage (
' Security constraint : Source image is not below the directory " image_path "
2015-12-07 15:36:06 +01:00
as specified in the config file img_config . php . ' ,
404
);
2015-01-29 21:16:32 +01:00
}
verbose ( " src = $srcImage " );
/**
* Manage size constants from config file , use constants to replace values
* for width and height .
*/
$sizeConstant = getConfig ( 'size_constant' , function () {
// Set sizes to map constant to value, easier to use with width or height
$sizes = array (
'w1' => 613 ,
'w2' => 630 ,
);
// Add grid column width, useful for use as predefined size for width (or height).
$gridColumnWidth = 30 ;
$gridGutterWidth = 10 ;
$gridColumns = 24 ;
for ( $i = 1 ; $i <= $gridColumns ; $i ++ ) {
$sizes [ 'c' . $i ] = ( $gridColumnWidth + $gridGutterWidth ) * $i - $gridGutterWidth ;
}
return $sizes ;
});
$sizes = call_user_func ( $sizeConstant );
/**
* width , w - set target width , affecting the resulting image width , height and resize options
*/
$newWidth = get ( array ( 'width' , 'w' ));
$maxWidth = getConfig ( 'max_width' , 2000 );
// Check to replace predefined size
if ( isset ( $sizes [ $newWidth ])) {
$newWidth = $sizes [ $newWidth ];
}
// Support width as % of original width
2020-01-15 16:51:39 +01:00
if ( $newWidth && $newWidth [ strlen ( $newWidth ) - 1 ] == '%' ) {
2015-01-29 21:16:32 +01:00
is_numeric ( substr ( $newWidth , 0 , - 1 ))
2015-12-06 12:18:33 +01:00
or errorPage ( 'Width % not numeric.' , 404 );
2015-01-29 21:16:32 +01:00
} else {
is_null ( $newWidth )
or ( $newWidth > 10 && $newWidth <= $maxWidth )
2015-12-06 12:18:33 +01:00
or errorPage ( 'Width out of range.' , 404 );
2015-01-29 21:16:32 +01:00
}
verbose ( " new width = $newWidth " );
/**
* height , h - set target height , affecting the resulting image width , height and resize options
*/
$newHeight = get ( array ( 'height' , 'h' ));
$maxHeight = getConfig ( 'max_height' , 2000 );
// Check to replace predefined size
if ( isset ( $sizes [ $newHeight ])) {
$newHeight = $sizes [ $newHeight ];
}
// height
2020-01-15 16:51:39 +01:00
if ( $newHeight && $newHeight [ strlen ( $newHeight ) - 1 ] == '%' ) {
2015-01-29 21:16:32 +01:00
is_numeric ( substr ( $newHeight , 0 , - 1 ))
2015-12-06 12:18:33 +01:00
or errorPage ( 'Height % out of range.' , 404 );
2015-01-29 21:16:32 +01:00
} else {
is_null ( $newHeight )
or ( $newHeight > 10 && $newHeight <= $maxHeight )
2015-12-06 12:18:33 +01:00
or errorPage ( 'Height out of range.' , 404 );
2015-01-29 21:16:32 +01:00
}
verbose ( " new height = $newHeight " );
/**
* aspect - ratio , ar - affecting the resulting image width , height and resize options
*/
$aspectRatio = get ( array ( 'aspect-ratio' , 'ar' ));
$aspectRatioConstant = getConfig ( 'aspect_ratio_constant' , function () {
return array (
'3:1' => 3 / 1 ,
'3:2' => 3 / 2 ,
'4:3' => 4 / 3 ,
'8:5' => 8 / 5 ,
'16:10' => 16 / 10 ,
'16:9' => 16 / 9 ,
'golden' => 1.618 ,
);
});
// Check to replace predefined aspect ratio
$aspectRatios = call_user_func ( $aspectRatioConstant );
2020-01-15 16:51:39 +01:00
$negateAspectRatio = ( $aspectRatio && $aspectRatio [ 0 ] == '!' ) ? true : false ;
2015-01-29 21:16:32 +01:00
$aspectRatio = $negateAspectRatio ? substr ( $aspectRatio , 1 ) : $aspectRatio ;
if ( isset ( $aspectRatios [ $aspectRatio ])) {
$aspectRatio = $aspectRatios [ $aspectRatio ];
}
if ( $negateAspectRatio ) {
$aspectRatio = 1 / $aspectRatio ;
}
is_null ( $aspectRatio )
or is_numeric ( $aspectRatio )
2015-12-06 12:18:33 +01:00
or errorPage ( 'Aspect ratio out of range' , 404 );
2015-01-29 21:16:32 +01:00
verbose ( " aspect ratio = $aspectRatio " );
/**
* crop - to - fit , cf - affecting the resulting image width , height and resize options
*/
$cropToFit = getDefined ( array ( 'crop-to-fit' , 'cf' ), true , false );
verbose ( " crop to fit = $cropToFit " );
/**
* Set default background color from config file .
*/
$backgroundColor = getConfig ( 'background_color' , null );
if ( $backgroundColor ) {
$img -> setDefaultBackgroundColor ( $backgroundColor );
verbose ( " Using default background_color = $backgroundColor " );
}
/**
* bgColor - Default background color to use
*/
$bgColor = get ( array ( 'bgColor' , 'bg-color' , 'bgc' ), null );
verbose ( " bgColor = $bgColor " );
2015-10-21 00:13:56 +02:00
/**
* Do or do not resample image when resizing .
*/
$resizeStrategy = getDefined ( array ( 'no-resample' ), true , false );
if ( $resizeStrategy ) {
$img -> setCopyResizeStrategy ( $img :: RESIZE );
verbose ( " Setting = Resize instead of resample " );
}
2015-01-29 21:16:32 +01:00
/**
* fill - to - fit , ff - affecting the resulting image width , height and resize options
*/
$fillToFit = get ( array ( 'fill-to-fit' , 'ff' ), null );
verbose ( " fill-to-fit = $fillToFit " );
if ( $fillToFit !== null ) {
if ( ! empty ( $fillToFit )) {
$bgColor = $fillToFit ;
verbose ( " fillToFit changed bgColor to = $bgColor " );
}
$fillToFit = true ;
verbose ( " fill-to-fit (fixed) = $fillToFit " );
}
/**
* no - ratio , nr , stretch - affecting the resulting image width , height and resize options
*/
$keepRatio = getDefined ( array ( 'no-ratio' , 'nr' , 'stretch' ), false , true );
verbose ( " keep ratio = $keepRatio " );
/**
* crop , c - affecting the resulting image width , height and resize options
*/
$crop = get ( array ( 'crop' , 'c' ));
verbose ( " crop = $crop " );
/**
* area , a - affecting the resulting image width , height and resize options
*/
$area = get ( array ( 'area' , 'a' ));
verbose ( " area = $area " );
/**
* skip - original , so - skip the original image and always process a new image
*/
$useOriginal = getDefined ( array ( 'skip-original' , 'so' ), false , true );
2015-12-06 12:18:33 +01:00
$useOriginalDefault = getConfig ( 'skip_original' , false );
if ( $useOriginalDefault === true ) {
2016-04-18 15:58:39 +02:00
verbose ( " skip original is default ON " );
$useOriginal = false ;
2015-12-06 12:18:33 +01:00
}
2015-01-29 21:16:32 +01:00
verbose ( " use original = $useOriginal " );
/**
* quality , q - set level of quality for jpeg images
*/
$quality = get ( array ( 'quality' , 'q' ));
2015-10-21 01:13:36 +02:00
$qualityDefault = getConfig ( 'jpg_quality' , null );
2015-01-29 21:16:32 +01:00
is_null ( $quality )
or ( $quality > 0 and $quality <= 100 )
2015-12-06 12:18:33 +01:00
or errorPage ( 'Quality out of range' , 404 );
2015-01-29 21:16:32 +01:00
2015-10-21 01:13:36 +02:00
if ( is_null ( $quality ) && ! is_null ( $qualityDefault )) {
$quality = $qualityDefault ;
}
2015-01-29 21:16:32 +01:00
verbose ( " quality = $quality " );
/**
* compress , co - what strategy to use when compressing png images
*/
$compress = get ( array ( 'compress' , 'co' ));
2015-10-21 01:13:36 +02:00
$compressDefault = getConfig ( 'png_compression' , null );
2015-01-29 21:16:32 +01:00
is_null ( $compress )
or ( $compress > 0 and $compress <= 9 )
2015-12-06 12:18:33 +01:00
or errorPage ( 'Compress out of range' , 404 );
2015-01-29 21:16:32 +01:00
2015-10-21 01:13:36 +02:00
if ( is_null ( $compress ) && ! is_null ( $compressDefault )) {
$compress = $compressDefault ;
}
2015-01-29 21:16:32 +01:00
verbose ( " compress = $compress " );
/**
* save - as , sa - what type of image to save
*/
$saveAs = get ( array ( 'save-as' , 'sa' ));
verbose ( " save as = $saveAs " );
/**
* scale , s - Processing option , scale up or down the image prior actual resize
*/
$scale = get ( array ( 'scale' , 's' ));
is_null ( $scale )
or ( $scale >= 0 and $scale <= 400 )
2015-12-06 12:18:33 +01:00
or errorPage ( 'Scale out of range' , 404 );
2015-01-29 21:16:32 +01:00
verbose ( " scale = $scale " );
/**
* palette , p - Processing option , create a palette version of the image
*/
$palette = getDefined ( array ( 'palette' , 'p' ), true , false );
verbose ( " palette = $palette " );
/**
* sharpen - Processing option , post filter for sharpen effect
*/
$sharpen = getDefined ( 'sharpen' , true , null );
verbose ( " sharpen = $sharpen " );
/**
* emboss - Processing option , post filter for emboss effect
*/
$emboss = getDefined ( 'emboss' , true , null );
verbose ( " emboss = $emboss " );
/**
* blur - Processing option , post filter for blur effect
*/
$blur = getDefined ( 'blur' , true , null );
verbose ( " blur = $blur " );
/**
* rotateBefore - Rotate the image with an angle , before processing
*/
$rotateBefore = get ( array ( 'rotateBefore' , 'rotate-before' , 'rb' ));
is_null ( $rotateBefore )
or ( $rotateBefore >= - 360 and $rotateBefore <= 360 )
2015-12-06 12:18:33 +01:00
or errorPage ( 'RotateBefore out of range' , 404 );
2015-01-29 21:16:32 +01:00
verbose ( " rotateBefore = $rotateBefore " );
/**
* rotateAfter - Rotate the image with an angle , before processing
*/
$rotateAfter = get ( array ( 'rotateAfter' , 'rotate-after' , 'ra' , 'rotate' , 'r' ));
is_null ( $rotateAfter )
or ( $rotateAfter >= - 360 and $rotateAfter <= 360 )
2015-12-06 12:18:33 +01:00
or errorPage ( 'RotateBefore out of range' , 404 );
2015-01-29 21:16:32 +01:00
verbose ( " rotateAfter = $rotateAfter " );
/**
* autoRotate - Auto rotate based on EXIF information
*/
$autoRotate = getDefined ( array ( 'autoRotate' , 'auto-rotate' , 'aro' ), true , false );
verbose ( " autoRotate = $autoRotate " );
/**
* filter , f , f0 - f9 - Processing option , post filter for various effects using imagefilter ()
*/
$filters = array ();
$filter = get ( array ( 'filter' , 'f' ));
if ( $filter ) {
$filters [] = $filter ;
}
for ( $i = 0 ; $i < 10 ; $i ++ ) {
$filter = get ( array ( " filter { $i } " , " f { $i } " ));
if ( $filter ) {
$filters [] = $filter ;
}
}
verbose ( " filters = " . print_r ( $filters , 1 ));
/**
2015-09-01 16:53:19 +02:00
* json - output the image as a JSON object with details on the image .
* ascii - output the image as ASCII art .
2015-01-29 21:16:32 +01:00
*/
$outputFormat = getDefined ( 'json' , 'json' , null );
2015-09-01 16:53:19 +02:00
$outputFormat = getDefined ( 'ascii' , 'ascii' , $outputFormat );
verbose ( " outputformat = $outputFormat " );
if ( $outputFormat == 'ascii' ) {
$defaultOptions = getConfig (
'ascii-options' ,
array (
" characterSet " => 'two' ,
" scale " => 14 ,
" luminanceStrategy " => 3 ,
" customCharacterSet " => null ,
)
);
$options = get ( 'ascii' );
$options = explode ( ',' , $options );
if ( isset ( $options [ 0 ]) && ! empty ( $options [ 0 ])) {
$defaultOptions [ 'characterSet' ] = $options [ 0 ];
}
if ( isset ( $options [ 1 ]) && ! empty ( $options [ 1 ])) {
$defaultOptions [ 'scale' ] = $options [ 1 ];
}
if ( isset ( $options [ 2 ]) && ! empty ( $options [ 2 ])) {
$defaultOptions [ 'luminanceStrategy' ] = $options [ 2 ];
}
if ( count ( $options ) > 3 ) {
// Last option is custom character string
unset ( $options [ 0 ]);
unset ( $options [ 1 ]);
unset ( $options [ 2 ]);
$characterString = implode ( $options );
$defaultOptions [ 'customCharacterSet' ] = $characterString ;
}
$img -> setAsciiOptions ( $defaultOptions );
}
2015-01-29 21:16:32 +01:00
/**
* dpr - change to get larger image to easier support larger dpr , such as retina .
*/
$dpr = get ( array ( 'ppi' , 'dpr' , 'device-pixel-ratio' ), 1 );
verbose ( " dpr = $dpr " );
/**
* convolve - image convolution as in http :// php . net / manual / en / function . imageconvolution . php
*/
$convolve = get ( 'convolve' , null );
$convolutionConstant = getConfig ( 'convolution_constant' , array ());
// Check if the convolve is matching an existing constant
if ( $convolve && isset ( $convolutionConstant )) {
$img -> addConvolveExpressions ( $convolutionConstant );
verbose ( " convolve constant = " . print_r ( $convolutionConstant , 1 ));
}
verbose ( " convolve = " . print_r ( $convolve , 1 ));
/**
* no - upscale , nu - Do not upscale smaller image to larger dimension .
*/
$upscale = getDefined ( array ( 'no-upscale' , 'nu' ), false , true );
verbose ( " upscale = $upscale " );
/**
* Get details for post processing
*/
$postProcessing = getConfig ( 'postprocessing' , array (
2016-08-09 13:02:25 +02:00
'png_lossy' => false ,
'png_lossy_cmd' => '/usr/local/bin/pngquant --force --output' ,
2015-01-29 21:16:32 +01:00
'png_filter' => false ,
'png_filter_cmd' => '/usr/local/bin/optipng -q' ,
'png_deflate' => false ,
'png_deflate_cmd' => '/usr/local/bin/pngout -q' ,
'jpeg_optimize' => false ,
'jpeg_optimize_cmd' => '/usr/local/bin/jpegtran -copy none -optimize' ,
));
2016-08-09 13:02:25 +02:00
/**
* lossy - Do lossy postprocessing , if available .
*/
$lossy = getDefined ( array ( 'lossy' ), true , null );
verbose ( " lossy = $lossy " );
2015-01-29 21:16:32 +01:00
/**
* alias - Save resulting image to another alias name .
* Password always apply , must be defined .
*/
$alias = get ( 'alias' , null );
$aliasPath = getConfig ( 'alias_path' , null );
$validAliasname = getConfig ( 'valid_aliasname' , '#^[a-z0-9A-Z-_]+$#' );
$aliasTarget = null ;
if ( $alias && $aliasPath && $passwordMatch ) {
$aliasTarget = $aliasPath . $alias ;
$useCache = false ;
is_writable ( $aliasPath )
2015-12-06 12:18:33 +01:00
or errorPage ( " Directory for alias is not writable. " , 403 );
2015-01-29 21:16:32 +01:00
preg_match ( $validAliasname , $alias )
2015-12-06 12:18:33 +01:00
or errorPage ( 'Filename for alias contains invalid characters. Do not add extension.' , 404 );
2015-01-29 21:16:32 +01:00
2015-07-23 11:32:19 +02:00
} elseif ( $alias ) {
2015-12-06 12:18:33 +01:00
errorPage ( 'Alias is not enabled in the config file or password not matching.' , 403 );
2015-01-29 21:16:32 +01:00
}
verbose ( " alias = $alias " );
2015-07-25 22:21:12 +02:00
/**
2015-12-07 15:36:06 +01:00
* Add cache control HTTP header .
2015-10-21 01:13:36 +02:00
*/
$cacheControl = getConfig ( 'cache_control' , null );
if ( $cacheControl ) {
verbose ( " cacheControl = $cacheControl " );
$img -> addHTTPHeader ( " Cache-Control " , $cacheControl );
}
2015-10-21 00:13:56 +02:00
/**
* Prepare a dummy image and use it as source image .
*/
if ( $dummyImage === true ) {
2015-12-07 15:36:06 +01:00
$dummyDir = $cache -> getPathToSubdir ( " dummy " );
2015-10-21 00:13:56 +02:00
$img -> setSaveFolder ( $dummyDir )
-> setSource ( $dummyFilename , $dummyDir )
-> setOptions (
array (
'newWidth' => $newWidth ,
'newHeight' => $newHeight ,
'bgColor' => $bgColor ,
)
)
-> setJpegQuality ( $quality )
-> setPngCompression ( $compress )
-> createDummyImage ()
-> generateFilename ( null , false )
-> save ( null , null , false );
$srcImage = $img -> getTarget ();
$imagePath = null ;
2015-12-06 12:18:33 +01:00
2015-10-21 00:13:56 +02:00
verbose ( " src (updated) = $srcImage " );
}
2015-12-06 12:18:33 +01:00
/**
* Prepare a sRGB version of the image and use it as source image .
*/
$srgbDefault = getConfig ( 'srgb_default' , false );
$srgbColorProfile = getConfig ( 'srgb_colorprofile' , __DIR__ . '/../icc/sRGB_IEC61966-2-1_black_scaled.icc' );
$srgb = getDefined ( 'srgb' , true , null );
if ( $srgb || $srgbDefault ) {
$filename = $img -> convert2sRGBColorSpace (
$srcImage ,
$imagePath ,
2015-12-07 15:36:06 +01:00
$cache -> getPathToSubdir ( " srgb " ),
2015-12-06 12:18:33 +01:00
$srgbColorProfile ,
$useCache
);
if ( $filename ) {
$srcImage = $img -> getTarget ();
$imagePath = null ;
verbose ( " srgb conversion and saved to cache = $srcImage " );
} else {
verbose ( " srgb not op " );
}
}
2015-10-21 00:13:56 +02:00
/**
* Display status
*/
if ( $status ) {
2016-08-08 09:29:26 +02:00
$text = " img.php version = " . CIMAGE_VERSION . " \n " ;
2015-10-21 00:13:56 +02:00
$text .= " PHP version = " . PHP_VERSION . " \n " ;
$text .= " Running on: " . $_SERVER [ 'SERVER_SOFTWARE' ] . " \n " ;
$text .= " Allow remote images = $allowRemote\n " ;
2015-12-07 15:36:06 +01:00
$res = $cache -> getStatusOfSubdir ( " " );
$text .= " Cache $res\n " ;
$res = $cache -> getStatusOfSubdir ( " remote " );
$text .= " Cache remote $res\n " ;
$res = $cache -> getStatusOfSubdir ( " dummy " );
$text .= " Cache dummy $res\n " ;
$res = $cache -> getStatusOfSubdir ( " srgb " );
$text .= " Cache srgb $res\n " ;
2016-08-09 13:02:25 +02:00
$res = $cache -> getStatusOfSubdir ( $fastTrackCache );
2016-08-08 09:29:26 +02:00
$text .= " Cache fasttrack $res\n " ;
2015-10-21 00:13:56 +02:00
$text .= " Alias path writable = " . is_writable ( $aliasPath ) . " \n " ;
$no = extension_loaded ( 'exif' ) ? null : 'NOT' ;
$text .= " Extension exif is $no loaded.<br> " ;
$no = extension_loaded ( 'curl' ) ? null : 'NOT' ;
$text .= " Extension curl is $no loaded.<br> " ;
2015-12-06 12:18:33 +01:00
$no = extension_loaded ( 'imagick' ) ? null : 'NOT' ;
$text .= " Extension imagick is $no loaded.<br> " ;
2015-10-21 00:13:56 +02:00
$no = extension_loaded ( 'gd' ) ? null : 'NOT' ;
$text .= " Extension gd is $no loaded.<br> " ;
2016-08-09 13:02:25 +02:00
$text .= checkExternalCommand ( " PNG LOSSY " , $postProcessing [ " png_lossy " ], $postProcessing [ " png_lossy_cmd " ]);
$text .= checkExternalCommand ( " PNG FILTER " , $postProcessing [ " png_filter " ], $postProcessing [ " png_filter_cmd " ]);
$text .= checkExternalCommand ( " PNG DEFLATE " , $postProcessing [ " png_deflate " ], $postProcessing [ " png_deflate_cmd " ]);
$text .= checkExternalCommand ( " JPEG OPTIMIZE " , $postProcessing [ " jpeg_optimize " ], $postProcessing [ " jpeg_optimize_cmd " ]);
2015-10-21 00:13:56 +02:00
if ( ! $no ) {
$text .= print_r ( gd_info (), 1 );
}
echo <<< EOD
<! doctype html >
< html lang = en >
< meta charset = utf - 8 >
< title > CImage status </ title >
< pre > $text </ pre >
EOD ;
exit ;
}
2015-10-21 01:13:36 +02:00
/**
* Log verbose details to file
*/
if ( $verboseFile ) {
$img -> setVerboseToFile ( " $cachePath /log.txt " );
}
/**
* Hook after img . php configuration and before processing with CImage
*/
$hookBeforeCImage = getConfig ( 'hook_before_CImage' , null );
if ( is_callable ( $hookBeforeCImage )) {
verbose ( " hookBeforeCImage activated " );
2015-12-06 12:18:33 +01:00
2015-10-21 01:13:36 +02:00
$allConfig = $hookBeforeCImage ( $img , array (
// Options for calculate dimensions
'newWidth' => $newWidth ,
'newHeight' => $newHeight ,
'aspectRatio' => $aspectRatio ,
'keepRatio' => $keepRatio ,
'cropToFit' => $cropToFit ,
'fillToFit' => $fillToFit ,
'crop' => $crop ,
'area' => $area ,
'upscale' => $upscale ,
// Pre-processing, before resizing is done
'scale' => $scale ,
'rotateBefore' => $rotateBefore ,
'autoRotate' => $autoRotate ,
// General processing options
'bgColor' => $bgColor ,
// Post-processing, after resizing is done
'palette' => $palette ,
'filters' => $filters ,
'sharpen' => $sharpen ,
'emboss' => $emboss ,
'blur' => $blur ,
'convolve' => $convolve ,
'rotateAfter' => $rotateAfter ,
// Output format
'outputFormat' => $outputFormat ,
'dpr' => $dpr ,
2015-12-06 12:18:33 +01:00
2015-10-21 01:13:36 +02:00
// Other
'postProcessing' => $postProcessing ,
2016-08-09 13:02:25 +02:00
'lossy' => $lossy ,
2015-10-21 01:13:36 +02:00
));
verbose ( print_r ( $allConfig , 1 ));
extract ( $allConfig );
}
2015-01-29 21:16:32 +01:00
/**
* Display image if verbose mode
*/
if ( $verbose ) {
$query = array ();
parse_str ( $_SERVER [ 'QUERY_STRING' ], $query );
unset ( $query [ 'verbose' ]);
unset ( $query [ 'v' ]);
unset ( $query [ 'nocache' ]);
unset ( $query [ 'nc' ]);
unset ( $query [ 'json' ]);
$url1 = '?' . htmlentities ( urldecode ( http_build_query ( $query )));
$url2 = '?' . urldecode ( http_build_query ( $query ));
echo <<< EOD
2015-03-06 13:00:21 +01:00
<! doctype html >
< html lang = en >
< meta charset = utf - 8 >
< title > CImage verbose output </ title >
< style > body { background - color : #ddd}</style>
2015-01-29 21:16:32 +01:00
< a href = $url1 >< code > $url1 </ code ></ a >< br >
< img src = '{$url1}' />
< pre id = " json " ></ pre >
< script src = " https://code.jquery.com/jquery-2.1.1.min.js " ></ script >
< script type = " text/javascript " >
window . getDetails = function ( url , id ) {
$ . getJSON ( url , function ( data ) {
element = document . getElementById ( id );
2015-10-21 00:13:56 +02:00
element . innerHTML = " filename: " + data . filename + " \\ nmime type: " + data . mimeType + " \\ ncolors: " + data . colors + " \\ nsize: " + data . size + " \\ nwidth: " + data . width + " \\ nheigh: " + data . height + " \\ naspect-ratio: " + data . aspectRatio + ( data . pngType ? " \\ npng-type: " + data . pngType : '' );
2015-01-29 21:16:32 +01:00
});
}
</ script >
< script type = " text/javascript " > window . getDetails ( " { $url2 } &json " , " json " ) </ script >
EOD ;
}
2015-10-18 14:52:17 +02:00
/**
* Load , process and output the image
*/
2015-01-29 21:16:32 +01:00
$img -> log ( " Incoming arguments: " . print_r ( verbose (), 1 ))
-> setSaveFolder ( $cachePath )
-> useCache ( $useCache )
-> setSource ( $srcImage , $imagePath )
-> setOptions (
array (
// Options for calculate dimensions
'newWidth' => $newWidth ,
'newHeight' => $newHeight ,
'aspectRatio' => $aspectRatio ,
'keepRatio' => $keepRatio ,
'cropToFit' => $cropToFit ,
'fillToFit' => $fillToFit ,
'crop' => $crop ,
'area' => $area ,
'upscale' => $upscale ,
// Pre-processing, before resizing is done
'scale' => $scale ,
'rotateBefore' => $rotateBefore ,
'autoRotate' => $autoRotate ,
// General processing options
'bgColor' => $bgColor ,
// Post-processing, after resizing is done
'palette' => $palette ,
'filters' => $filters ,
'sharpen' => $sharpen ,
'emboss' => $emboss ,
'blur' => $blur ,
'convolve' => $convolve ,
'rotateAfter' => $rotateAfter ,
// Output format
'outputFormat' => $outputFormat ,
'dpr' => $dpr ,
2016-08-09 13:02:25 +02:00
// Postprocessing using external tools
'lossy' => $lossy ,
2015-01-29 21:16:32 +01:00
)
)
-> loadImageDetails ()
-> initDimensions ()
-> calculateNewWidthAndHeight ()
-> setSaveAsExtension ( $saveAs )
-> setJpegQuality ( $quality )
-> setPngCompression ( $compress )
-> useOriginalIfPossible ( $useOriginal )
-> generateFilename ( $cachePath )
-> useCacheIfPossible ( $useCache )
-> load ()
-> preResize ()
-> resize ()
-> postResize ()
-> setPostProcessingOptions ( $postProcessing )
-> save ()
-> linkToCacheFile ( $aliasTarget )
-> output ();