cakebook / branches / master / models / behaviors / image_upload.php
history
<?php
/**
* Short description for image_upload.php
*
* Long description for image_upload.php
*
* PHP versions 4 and 5
*
* Copyright (c) 2008, Andy Dawson
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @filesource
* @copyright Copyright (c) 2008, Andy Dawson
* @link www.ad7six.com
* @package base
* @subpackage base.models.behaviors
* @since v 1.0
* @license http://www.opensource.org/licenses/mit-license.php The MIT License
*/
require_once('upload.php');
/**
* ImageUploadBehavior class
*
* Extending the Upload behavior, adding image specific logic uses directy system calls to
* imagemagick - which naturally must be installed for it to work
*
* @uses UploadBehavior
* @package base
* @subpackage base.models.behaviors
*/
class ImageUploadBehavior extends UploadBehavior {
/**
* name property
*
* @var string 'ImageUpload'
* @access public
*/
var $name = 'ImageUpload';
/**
* mapMethods property
*
* Map "none-file" calls to the __passThru method which will pickup the filename and call the file version
* of the method.
* For Flip/Flop/Rotate - operate on the original file and reprocess the versions
*
* @var array
* @access public
*/
var $mapMethods = array(
'/convert|crop|polaroid|perspective|reflection|resize|shear|thumbnail|trim/' => '__passThru',
'/flip|flop|rotate/' => '__passThruReprocess',
);
/**
* current property
*
* The file currently being processed. stored to allow consecutive operations without deleting the original file
*
* @var bool false
* @access private
*/
var $__current = false;
/**
* setup method
*
* Override defaults inherited from the upload behavior
* Set allowed mimes to image types
* Set allowed extension to matching mimes
* Set versions to create tiny, small, medium and large thumbs/versions
*
* @param mixed $model
* @param array $config
* @return void
* @access public
*/
function setup (&$model, $config = array()) {
$this->_defaultSettings['allowedMime'] = array('image/jpeg', 'image/gif', 'image/png', 'image/bmp');
$this->_defaultSettings['allowedExt'] = array('jpeg', 'jpg', 'gif', 'png', 'bmp');
$this->_defaultSettings['versions'] = array(
'thumb' => array(
'callback' => array('resize', 50, 50)
),
'small' => array(
'callback' => array('resize', 75, 75)
),
'medium' => array(
'callback' => array('resize', 150, 150)
),
'large' => array(
'callback' => array(
array('resize', 600, 400)
)
),
);
parent::setup($model, $config);
}
/**
* convertFile method
*
* Directly call convert
*
* @param mixed $model
* @param mixed $fullpath
* @param array $params
* @return void
* @access public
*/
function convertFile(&$model, $fullpath = null, $params = array()) {
if ($params) {
return $this->__imConvert($model, $fullpath, $params);
}
return false;
}
/**
* cropFile method
*
* @param mixed $model
* @param mixed $fullpath
* @param array $params
* @return void
* @access public
*/
function cropFile(&$model, $fullpath = null, $cropTo = null, $params = array()) {
if (!$cropTo) {
$params['gravity'] = 'Center';
$cropTo = '50%\!';
}
$params = am($params, array('-crop' => $cropTo));
return $this->__imConvert($model, $fullpath, $params);
}
/**
* flipFile method
*
* Flip (mirror) vertically
*
* @param mixed $model
* @param mixed $fullpath
* @param array $params
* @return void
* @access public
*/
function flipFile(&$model, $fullpath = null, $params = array()) {
$params = am($params, array('-flip'));
return $this->__imConvert($model, $fullpath, $params);
}
/**
* flopFile method
*
* Flop (mirror) horizontally
*
* @param mixed $model
* @param mixed $fullpath
* @param array $params
* @return void
* @access public
*/
function flopFile(&$model, $fullpath = null, $params = array()) {
$params = am($params, array('-flop'));
return $this->__imConvert($model, $fullpath, $params);
}
/**
* perspectiveFile method
*
* @param mixed $model
* @param mixed $fullpath
* @param mixed $tl
* @param mixed $tr
* @param mixed $bl
* @param mixed $br
* @param array $params
* @return void
* @access public
*/
function perspectiveFile(&$model, $fullpath = null, $tl = null, $tr = null, $bl = null, $br = null, $params = array()) {
$dimensions = $this->__imIdentify($model, $fullpath, array('format' => '%wx%h'));
if (!$dimensions) {
return false;
}
list($width, $height) = explode('x', $dimensions);
if (!$tl) {
$tl = '0,0';
}
if (!$tr) {
$tr = $width . ',0';
}
if (!$bl) {
$bl = '0,' . $height;
}
if (!$br) {
$br = $width . ',' . $height;
}
foreach (array('tl', 'tr', 'bl', 'br') as $var) {
if (strpos($$var, '%')) {
list($w, $h) = explode(',', $$var);
if (strpos($w, '%')) {
$w = str_replace('%', '', $w);
$w = round($width * $w / 100);
}
if (strpos($h, '%')) {
$h = str_replace('%', '', $h);
$h = round($height * $h / 100);
}
$$var = $w . ',' . $h;
}
}
$params = am($params, array(
'-matte',
'virtual-pixel' => 'transparent',
'distort' => "Perspective '0,0 $tl $width,0 $tr 0,$height $bl $width,$height $br'",
));
$return = $this->__imConvert($model, $fullpath, $params);
}
/**
* polaroidFile method
*
* @param mixed $model
* @param mixed $fullpath
* @param int $width
* @param int $height
* @param array $params
* @return void
* @access public
*/
function polaroidFile(&$model, $fullpath = null, $width = 100, $height = 100, $params = array()) {
$params = am($params, array('thumbnail' => $width . 'x' . $height, '+polaroid'));
return $this->__imConvert($model, $fullpath, $params);
}
/**
* reflectionFile method
*
* @TODO not fully implemented/tested
* @param mixed $model
* @param mixed $fullpath
* @param mixed $reflectionDepth
* @param array $params
* @return void
* @access public
*/
function reflectionFile(&$model, $fullpath, $reflectionDepth = 0.5, $params = array()) {
$upsideDown = dirname($fullpath) . DS . rand();
$rightWayUp = dirname($fullpath) . DS . rand();
copy($fullpath, $rightWayUp);
copy($fullpath, $upsideDown);
$this->resizeFile($model, $upsideDown, '100%', 200 * $reflectionDepth . '%');
$this->flipFile($model, $upsideDown);
$dimensions = $this->__imIdentify($model, $upsideDown, array('format' => '%wx%h'));
list($width, $height) = explode('x', $dimensions);
$height = $height * $reflectionDepth;
$this->cropFile($model, $upsideDown, "{$width}x$height");
$params = am($params, array('geometry' => '+1+1', 'tile' => '1x2'));
$return = $this->__imMontage($model, array($rightWayUp, $upsideDown), $params, $fullpath);
}
/**
* resizeFile method
*
* @param mixed $model
* @param mixed $fullpath
* @param int $width
* @param int $height
* @return void
* @access public
*/
function resizeFile(&$model, $fullpath = null, $width = 600, $height = 400, $params = array()) {
$params = am($params, array('resize' => $width . 'x' . $height . '>'));
return $this->__imConvert($model, $fullpath, $params);
}
/**
* rotateFile method
*
* @param mixed $model
* @param mixed $fullpath
* @param int $angle
* @param array $params
* @return void
* @access public
*/
function rotateFile(&$model, $fullpath = null, $angle = 90, $params = array()) {
$params = am($params, array('rotate' => $angle));
return $this->__imConvert($model, $fullpath, $params);
}
/**
* thumbnailFile method
*
* @param mixed $model
* @param mixed $fullpath
* @param int $width
* @param int $height
* @return void
* @access public
*/
function thumbnailFile(&$model, $fullpath = null, $width = 100, $height = 100, $params = array()) {
$params = am($params, array('thumbnail' => $width . 'x' . $height . '^', '-auto-orient'));
return $this->__imConvert($model, $fullpath, $params);
}
/**
* shearFile method
*
* @param mixed $model
* @param mixed $fullpath
* @param int $x
* @param int $y
* @return void
* @access public
*/
function shearFile(&$model, $fullpath = null, $x = 0 , $y = 0, $params = array()) {
if ($y === false && $x) {
$params = am($params, array('shear' => $x));
} else {
$params = am($params, array('shear' => $x . 'x' . $y));
}
return $this->__imConvert($model, $fullpath, $params);
}
/**
* trimFile method
*
* @param mixed $model
* @param mixed $fullpath
* @param array $params
* @return void
* @access public
*/
function trimFile(&$model, $fullpath = null, $params = array()) {
$params = am($params, array('-trim', '+repage'));
return $this->__imConvert($model, $fullpath, $params);
}
/**
* beforeProcessUpload method
*
* Add width and height to the data to be saved
*
* @param mixed $model
* @param mixed $data
* @return void
* @access protected
*/
function _beforeProcessUpload(&$model, &$data) {
parent::_beforeProcessUpload($model, $data);
extract($this->settings[$model->alias]);
list($width, $height) = getimagesize($data[$model->alias]['tempFile']);
$data[$model->alias]['width'] = $width;
$data[$model->alias]['height'] = $height;
return !$this->errors;
}
/**
* passThru method
*
* @param mixed $model
* @param mixed $method
* @param mixed $firstParam
* @return void
* @access private
*/
function __passThru(&$model, $method, $firstParam = null) {
if (!$firstParam) {
return $this->$method($model);
}
$params = func_get_args();
array_shift($params);
array_shift($params);
$path = '';
if (isset($this->settings[$model->alias]['versions'][$params[0]])) {
$path = $model->absolutePath($params[0]);
} elseif (is_numeric($params[0]) && $model->hasAny(array($model->primaryKey => $params[0]))) {
$path = $model->absolutePath($params[0]);
} else {
$idSchema = $model->schema($model->primaryKey);
if ($idSchema['length'] == 36 && $model->hasAny(array($model->primaryKey => $params[0]))) {
$path = $model->absolutePath($params[0]);
}
}
if ($path) {
$params[0] = $path;
} else {
$this->errors[] = "method {$method} called but impossible to pass to {$method}File - couldn't determine the file to process";
return false;
}
$method = $method . 'File';
switch (count($params)) {
case 1:
return $this->{$method}($model, $params[0]);
case 2:
return $this->{$method}($model, $params[0], $params[1]);
case 3:
return $this->{$method}($model, $params[0], $params[1], $params[2]);
case 4:
return $this->{$method}($model, $params[0], $params[1], $params[2], $params[3]);
case 5:
return $this->{$method}($model, $params[0], $params[1], $params[2], $params[3], $params[4]);
default:
array_unshift($params, $model);
return call_user_func_array(array(&$this, $method), $params);
break;
}
}
/**
* passThruReprocess method
*
* @param mixed $model
* @param mixed $method
* @param mixed $id
* @param array $params
* @return void
* @access private
*/
function __passThruReprocess(&$model, $method, $id = null) {
if (!$id) {
if (!$model->id) {
return false;
}
$id = $model->id;
}
$model->id = $id;
$method = $method . 'File';
$params = func_get_args();
$params[1] = $this->absolutePath($model);
$return = call_user_func_array(array(&$this, $method), $params);
if ($return) {
$return = $this->reprocess($model, true);
return $return;
}
return false;
}
/**
* imConvert method
*
* @param mixed $input (filename)
* @param array $params
* @param mixed $output (filename)
* @param bool $return
* @return void
* @access private
*/
function __imConvert(&$model, $input, $params = array(), $output = null, $return = false) {
if (isset($params['return'])) {
$return = $params['return'];
unset($params['return']);
}
if (!$output) {
$output = $input;
}
if ($this->__current != $output) {
$this->__current = $output;
if (file_exists($output) && $output != $input) {
unlink($output);
}
$input = $this->absolutePath($model);
}
$dir = dirname($output);
if (!file_exists($dir)) {
new Folder($dir, true);
}
$params = $this->__imParseParams($params);
$command = "convert $input $params $output";
if ($return) {
return $command;
}
$return = exec($command, $exitCode);
//return $return;
return true;
}
/**
* imMontage method
*
* @param mixed $model
* @param mixed $input
* @param array $params
* @param mixed $output
* @return void
* @access private
*/
function __imMontage(&$model, $input, $params = array(), $output = null) {
if (!$output) {
if (is_array($input)) {
$output = $input[0];
} else {
$output = $input;
}
}
if (is_array($input)) {
$input = implode($input, ' ');
}
if ($this->__current != $output) {
$this->__current = $output;
if (file_exists($output) && $output != $input) {
unlink($output);
}
}
$dir = dirname($output);
if (!file_exists($dir)) {
new Folder($dir, true);
}
$params = $this->__imParseParams($params);
$command = "montage $input $params $output";
$return = exec($command, $exitCode);
//return $return;
return true;
}
/**
* imInfo method
*
* @param mixed $model
* @param mixed $path
* @param array $params
* @return void
* @access private
*/
function __imIdentify(&$model, $path = null, $params = array()) {
if (!$path || !file_exists($path)) {
$path = $this->absolutePath($model);
}
$params = $this->__imParseParams($params);
$return = exec('identify ' . $params . ' ' . $path );
return $return;
}
/**
* imParseParams method
*
* @param mixed $params
* @param mixed $key
* @return void
* @access private
*/
function __imParseParams($params, $key = null) {
foreach ($params as $key => $value) {
if (is_array($value)) {
$params[$key] = $this->__imParseParams($value, $key);
} elseif (is_numeric(($key))) {
$params[$key] = $value;
} elseif (!in_array($key[0], array('-', '+'))) {
$this->__imEscape($value);
$params[$key] = '-' . $key . ' ' . $value;
} else {
$this->__imEscape($value);
$params[$key] = $key . ' ' . $value;
}
}
return implode($params, ' ');
}
/**
* imEscape method
*
* @param mixed $value
* @return void
* @access private
*/
function __imEscape(&$value) {
$value = str_replace('>', '\>', $value);
}
}
?>