mambax7/extgallery

View on GitHub
class/pear/Image/Transform.php

Summary

Maintainability
F
4 days
Test Coverage
<?php

/* vim: set expandtab tabstop=4 shiftwidth=4: */

/**
 * Simple and cross-library package to doing image transformations and
 * manipulations.
 *
 * PHP versions 4 and 5
 *
 * LICENSE: This source file is subject to version 3.0 of the PHP license
 * that is available through the world-wide-web at the following URI:
 * http://www.php.net/license/3_0.txt.  If you did not receive a copy of
 * the PHP License and are unable to obtain it through the web, please
 * send a note to license@php.net so we can mail you a copy immediately.
 *
 * @category  Image
 * @package   Image_Transform
 * @author    Vincent Oostindie <vincent@sunlight.tmfweb.nl>
 * @author    Alan Knowles <alan@akbkhome.com>
 * @author    Peter Bowyer <peter@mapledesign.co.uk>
 * @author    Philippe Jausions <Philippe.Jausions@11abacus.com>
 * @copyright 2002-2007 The PHP Group
 * @license   http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version   CVS: $Id: Transform.php 322659 2012-01-24 11:56:22Z clockwerx $
 * @link      http://pear.php.net/package/Image_Transform
 */

/**
 * Include for error handling
 */
//require_once __DIR__ . '/PEAR.php';
require_once XOOPS_ROOT_PATH . '/modules/extgallery/class/pear/PEAR.php';

/**
 * Error code for unsupported library, image format or methods
 */
define('IMAGE_TRANSFORM_ERROR_UNSUPPORTED', 1);

/**
 * Error code for failed transformation operations
 */
define('IMAGE_TRANSFORM_ERROR_FAILED', 2);

/**
 * Error code for failed i/o (Input/Output) operations
 */
define('IMAGE_TRANSFORM_ERROR_IO', 3);

/**
 * Error code for invalid arguments
 */
define('IMAGE_TRANSFORM_ERROR_ARGUMENT', 4);

/**
 * Error code for out-of-bound related errors
 */
define('IMAGE_TRANSFORM_ERROR_OUTOFBOUND', 5);

/**
 * Error code for inexsitant driver errors
 */
define('IMAGE_TRANSFORM_DRIVER_FILE_MISSING', 6);

/**
 * Base class with factory method for backend driver
 *
 * The main "Image_Transform" class is a container and base class which
 * provides a static method for creating an Image object as well as
 * some utility functions (maths) common to all parts of Image_Transform.
 *
 * @category  Image
 * @package   Image_Transform
 * @author    Alan Knowles <alan@akbkhome.com>
 * @author    Peter Bowyer <peter@mapledesign.co.uk>
 * @author    Philippe Jausions <Philippe.Jausions@11abacus.com>
 * @copyright 2002-2007 The PHP Group
 * @license   http://www.php.net/license/3_0.txt  PHP License 3.0
 * @version   Release: @package_version@
 * @link      http://pear.php.net/package/Image_Transform
 * @since     PHP 4.0
 */
class Image_Transform
{
    /**
     * Name of the image file
     * @var string
     */
    public $image = '';
    /**
     * Type of the image file (eg. jpg, gif png ...)
     * @var string
     */
    public $type = '';
    /**
     * Original image width in x direction
     * @var int
     */
    public $img_x = '';
    /**
     * Original image width in y direction
     * @var int
     */
    public $img_y = '';
    /**
     * New image width in x direction
     * @var int
     */
    public $new_x = '';
    /**
     * New image width in y direction
     * @var int
     */
    public $new_y = '';
    /**
     * Path to the library used
     * e.g. /usr/local/ImageMagick/bin/ or
     * /usr/local/netpbm/
     */
    public $lib_path = '';
    /**
     * Flag to warn if image has been resized more than once before displaying
     * or saving.
     */
    public $resized = false;
    /**
     * @var array General options
     * @access protected
     */
    public $_options = [
        'quality'     => 75,
        'scaleMethod' => 'smooth',
        'canvasColor' => [255, 255, 255],
        'pencilColor' => [0, 0, 0],
        'textColor'   => [0, 0, 0],
    ];
    /**
     * Flag for whether settings should be discarded on saving/display of image
     * @var bool
     * @see Image_Transform::keepSettingsOnSave
     */
    public $keep_settings_on_save = false;
    /**
     * Supported image types
     * @var array
     * @access protected
     */
    public $_supported_image_types = [];
    /**
     * Initialization error tracking
     * @var object
     * @access private
     **/
    public $_error = null;
    /**
     * associative array that tracks existence of programs
     * (for drivers using shell interface and a tiny performance
     * improvement if the clearstatcache() is used)
     * @var array
     * @access protected
     */
    public $_programs = [];
    /**
     * Default parameters used in the addText methods.
     */
    public $default_text_params = [
        'text'         => 'Default text',
        'x'            => 10,
        'y'            => 20,
        'color'        => 'red',
        'font'         => 'Arial.ttf',
        'size'         => 12,
        'angle'        => 0,
        'resize_first' => false,
    ];

    /**
     * Creates a new Image_Transform object
     *
     * @param string $driver name of driver class to initialize. If no driver
     *                       is specified the factory will attempt to use 'Imagick' first
     *                       then 'GD' second, then 'Imlib' last
     *
     * @return object an Image_Transform object, or PEAR_Error on error
     *
     * @see PEAR::isError()
     * @see Image_Transform::setOption()
     */
    public function &factory($driver = '')
    {
        if ('' == $driver) {
            $extensions = [
                'imagick' => 'Imagick3',
                'gd'      => 'GD',
                'imlib'   => 'Imlib',
            ];
            if (version_compare(PHP_VERSION, '5.0.0', '<')) {
                //Imagick2 driver for php < 5
                $extensions['imagick'] = 'Imagick2';
            }

            foreach ($extensions as $ext => $ext_driver) {
                if (PEAR::loadExtension($ext)) {
                    $driver = $ext_driver;
                    break;
                }
            }
            if (!$driver) {
                return PEAR::raiseError('No image library specified and none can be found.' . ' You must specify driver in factory() call.', IMAGE_TRANSFORM_ERROR_ARGUMENT);
            }
        } else {
            switch (mb_strtolower($driver)) {
                case 'gd':
                    $driver = 'GD';
                    break;
                case 'imagick':
                    $driver = 'Imagick3';
                    if (version_compare(PHP_VERSION, '5.0.0', '<')) {
                        $driver = 'Imagick2';
                    }
                    break;
                case 'imlib':
                    $driver = 'Imlib';
                    break;
            }
        }

        //        $file = 'Image/Transform/Driver/' . $driver . '.php';
        $file = __DIR__ . '/Transform/Driver/' . $driver . '.php';
        if (!@fclose(@fopen($file, 'r', true))) {
            return PEAR::raiseError('Driver failed to load file ' . $file, IMAGE_TRANSFORM_DRIVER_FILE_MISSING);
        }

        $classname = 'Image_Transform_Driver_' . $driver;

        if (!class_exists($classname)) {
            require_once $file;

            if (!class_exists($classname)) {
                return PEAR::raiseError('Image library ' . $driver . ' not supported... aborting.', IMAGE_TRANSFORM_ERROR_UNSUPPORTED);
            }
        }
        $obj = new $classname();

        // Check startup error
        $error = &$obj->isError();
        if ($error) {
            $obj = &$error;
        }

        return $obj;
    }

    /**
     * Returns/sets an error when the instance couldn't initialize properly
     *
     * @param object $error PEAR_Error object when setting an error
     *
     * @return mixed FALSE or PEAR_Error object
     * @access protected
     */
    public function &isError($error = null)
    {
        if (null !== $error) {
            $this->_error = &$error;
        }

        return $this->_error;
    }

    /**
     * Resizes the image in the X and/or Y direction(s)
     *
     * If either is 0 it will keep the original size for that dimension
     *
     * @param mixed $new_x   (0, number, percentage 10% or 0.1)
     * @param mixed $new_y   (0, number, percentage 10% or 0.1)
     * @param array $options Options
     *
     * @return mixed TRUE or PEAR_Error object on error
     * @access public
     */
    public function resize($new_x = 0, $new_y = 0, $options = null)
    {
        // 0 means keep original size
        $new_x = (0 == $new_x) ? $this->img_x : $this->_parse_size($new_x, $this->img_x);
        $new_y = (0 == $new_y) ? $this->img_y : $this->_parse_size($new_y, $this->img_y);

        // Now do the library specific resizing.
        return $this->_resize($new_x, $new_y, $options);
    }

    // End resize

    /**
     * Scales the image to the specified width
     *
     * This method preserves the aspect ratio
     *
     * @param int $new_x Size to scale X-dimension to
     *
     * @return mixed TRUE or PEAR_Error object on error
     * @access public
     */
    public function scaleByX($new_x)
    {
        if ($new_x <= 0) {
            return PEAR::raiseError('New size must be strictly positive', IMAGE_TRANSFORM_ERROR_OUTOFBOUND);
        }
        $new_y = round(($new_x / $this->img_x) * $this->img_y, 0);

        return $this->_resize(max(1, $new_x), max(1, $new_y));
    }

    // End scaleByX

    /**
     * Alias for resize()
     *
     * @param int  $new_x
     * @param int  $new_y
     * @param null $options
     * @return mixed
     * @see resize()
     */
    public function scaleByXY($new_x = 0, $new_y = 0, $options = null)
    {
        return $this->resize($new_x, $new_y, $options);
    }

    // End scaleByXY

    /**
     * Scales the image to the specified height.
     *
     * This method preserves the aspect ratio
     *
     * @param int $new_y Size to scale Y-dimension to
     *
     * @return mixed TRUE or PEAR_Error object on error
     * @access public
     */
    public function scaleByY($new_y)
    {
        if ($new_y <= 0) {
            return PEAR::raiseError('New size must be strictly positive', IMAGE_TRANSFORM_ERROR_OUTOFBOUND);
        }
        $new_x = round(($new_y / $this->img_y) * $this->img_x, 0);

        return $this->_resize(max(1, $new_x), max(1, $new_y));
    }

    // End scaleByY

    /**
     * Scales an image by a percentage, factor or a given length
     *
     * This method preserves the aspect ratio
     *
     * @param mixed $size (number, percentage 10% or 0.1)
     *
     * @return mixed TRUE or PEAR_Error object on error
     * @access public
     * @see    scaleByPercentage, scaleByFactor, scaleByLength
     */
    public function scale($size)
    {
        if ((mb_strlen($size) > 1) && ('%' == mb_substr($size, -1))) {
            return $this->scaleByPercentage(mb_substr($size, 0, -1));
        } elseif ($size < 1) {
            return $this->scaleByFactor($size);
        }

        return $this->scaleByLength($size);
    }

    // End scale

    /**
     * Scales an image to a percentage of its original size.  For example, if
     * my image was 640x480 and I called scaleByPercentage(10) then the image
     * would be resized to 64x48
     *
     * @param int $size Percentage of original size to scale to
     *
     * @return mixed TRUE or PEAR_Error object on error
     * @access public
     */
    public function scaleByPercentage($size)
    {
        return $this->scaleByFactor($size / 100);
    }

    // End scaleByPercentage

    /**
     * Scales an image to a factor of its original size.  For example, if
     * my image was 640x480 and I called scaleByFactor(0.5) then the image
     * would be resized to 320x240.
     *
     * @param float $size Factor of original size to scale to
     *
     * @return mixed TRUE or PEAR_Error object on error
     * @access public
     */
    public function scaleByFactor($size)
    {
        if ($size <= 0) {
            return PEAR::raiseError('New size must be strictly positive', IMAGE_TRANSFORM_ERROR_OUTOFBOUND);
        }
        $new_x = round($size * $this->img_x, 0);
        $new_y = round($size * $this->img_y, 0);

        return $this->_resize(max(1, $new_x), max(1, $new_y));
    }

    // End scaleByFactor

    /**
     * Scales an image so that the longest side has the specified dimension.
     *
     * This method preserves the aspect ratio
     *
     * @param int $size Max dimension in pixels
     *
     * @return mixed TRUE or PEAR_Error object on error
     * @access public
     */
    public function scaleMaxLength($size)
    {
        if ($size <= 0) {
            return PEAR::raiseError('New size must be strictly positive', IMAGE_TRANSFORM_ERROR_OUTOFBOUND);
        }
        if ($this->img_x >= $this->img_y) {
            $new_x = $size;
            $new_y = round(($new_x / $this->img_x) * $this->img_y, 0);
        } else {
            $new_y = $size;
            $new_x = round(($new_y / $this->img_y) * $this->img_x, 0);
        }

        return $this->_resize(max(1, $new_x), max(1, $new_y));
    }

    // End scaleMaxLength

    /**
     * Alias for scaleMaxLength
     *
     * @param int $size Max dimension in pixels
     *
     * @return mixed TRUE or PEAR_Error object on error
     * @access public
     * @see    scaleMaxLength()
     */
    public function scaleByLength($size)
    {
        return $this->scaleMaxLength($size);
    }

    /**
     * Fits the image in the specified box size
     *
     * If the image is bigger than the box specified by $width and $height,
     * it will be scaled down to fit inside of it.
     * If the image is smaller, nothing is done.
     *
     * @param int $width  Width of the box in pixels
     * @param int $height Height of the box in pixels
     *
     * @return bool|PEAR_Error TRUE or PEAR_Error object on error
     * @access public
     */
    public function fit($width, $height)
    {
        if ($width <= 0 || $height <= 0) {
            return PEAR::raiseError('Invalid arguments.', IMAGE_TRANSFORM_ERROR_ARGUMENT);
        }
        $x = $this->img_x / $width;
        $y = $this->img_y / $height;
        if ($x <= 1 && $y <= 1) {
            return true;
        } elseif ($x > $y) {
            return $this->scaleByX($width);
        }

        return $this->scaleByY($height);
    }

    /**
     * This works as per fit, but creates the canvas of size $width x $height
     * and positions the resized image on it, by default in the centre.
     *
     * @param unknown_type $width
     * @param unknown_type $height
     * @param string       $posn
     *
     * @return unknown
     */
    public function fitOnCanvas($width, $height, $posn = 'center')
    {
        return PEAR::raiseError('fitOnCanvas() method not supported by driver', IMAGE_TRANSFORM_ERROR_UNSUPPORTED);
    }

    /**
     * Fits the image in the specified width
     *
     * If the image is wider than the width specified by $width,
     * it will be scaled down to fit inside of it.
     * If the image is smaller, nothing is done.
     *
     * @param int $width Maximum width in pixels
     *
     * @return bool|PEAR_Error TRUE or PEAR_Error object on error
     * @access public
     */
    public function fitX($width)
    {
        return ($this->img_x <= $width) ? true : $this->scaleByX($width);
    }

    /**
     * Fits the image in the specified height
     *
     * If the image is taller than the height specified by $height,
     * it will be scaled down to fit inside of it.
     * If the image is smaller, nothing is done.
     *
     * @param int $height Maximum height in pixels
     *
     * @return bool|PEAR_Error TRUE or PEAR_Error object on error
     * @access public
     */
    public function fitY($height)
    {
        return ($this->img_y <= $height) ? true : $this->scaleByY($height);
    }

    /**
     * Sets one options
     *
     * @param string $name  Name of option
     * @param mixed  $value Value of option
     *
     * @access public
     * @see    setOptions()
     */
    public function setOption($name, $value)
    {
        $this->_options[$name] = $value;
    }

    /**
     * Sets multiple options at once
     *
     * Associative array of options:
     *  - quality     (Integer: 0: poor - 100: best)
     *  - scaleMethod ('smooth', 'pixel')
     *
     * @param array $options Array of options
     *
     * @access public
     */
    public function setOptions($options)
    {
        $this->_options = array_merge($this->_options, $options);
    }

    /**
     * Sets the image type (in lowercase letters), the image height and width.
     *
     * @param string $image Image filename
     *
     * @return mixed TRUE or PEAR_error
     * @access protected
     * @see    PHP_Compat::image_type_to_mime_type()
     * @link   http://php.net/getimagesize
     */
    public function _get_image_details($image)
    {
        $data = @getimagesize($image);
        //  1 = GIF,   2 = JPG,  3 = PNG,  4 = SWF,  5 = PSD,  6 = BMP,
        //  7 = TIFF (intel byte order),   8 = TIFF (motorola byte order),
        //  9 = JPC,  10 = JP2, 11 = JPX, 12 = JB2, 13 = SWC, 14 = IFF,
        // 15 = WBMP, 16 = XBM
        if (!is_array($data)) {
            return PEAR::raiseError('Cannot fetch image or images details.', true);
        }

        switch ($data[2]) {
            case IMAGETYPE_GIF:
                $type = 'gif';
                break;
            case IMAGETYPE_JPEG:
                $type = 'jpeg';
                break;
            case IMAGETYPE_PNG:
                $type = 'png';
                break;
            case IMAGETYPE_SWF:
                $type = 'swf';
                break;
            case IMAGETYPE_PSD:
                $type = 'psd';
                break;
            case IMAGETYPE_BMP:
                $type = 'bmp';
                break;
            case IMAGETYPE_TIFF_II:
            case IMAGETYPE_TIFF_MM:
                $type = 'tiff';
                break;
            case IMAGETYPE_JPC:
                $type = 'jpc';
                break;
            case IMAGETYPE_JP2:
                $type = 'jp2';
                break;
            case IMAGETYPE_JPX:
                $type = 'jpx';
                break;
            case IMAGETYPE_JB2:
                $type = 'jb2';
                break;
            case IMAGETYPE_SWC:
                $type = 'swc';
                break;
            case IMAGETYPE_IFF:
                $type = 'iff';
                break;
            case IMAGETYPE_WBMP:
                $type = 'wbmp';
                break;
            case IMAGETYPE_XBM:
                $type = 'xbm';
                break;
            default:
                return PEAR::raiseError('Cannot recognize image format', IMAGE_TRANSFORM_ERROR_UNSUPPORTED);
        }
        $this->img_x = $this->new_x = $data[0];
        $this->img_y = $this->new_y = $data[1];
        $this->type  = $type;

        return true;
    }

    /**
     * Returns the matching IMAGETYPE_* constant for a given image type
     *
     * @param mixed $type String (GIF, JPG,...)
     *
     * @return mixed string or integer or input on error
     * @access protected
     * @see    PHP_Compat::image_type_to_mime_type()
     **/
    public function _convert_image_type($type)
    {
        switch (mb_strtolower($type)) {
            case 'gif':
                return IMAGETYPE_GIF;
            case 'jpeg':
            case 'jpg':
                return IMAGETYPE_JPEG;
            case 'png':
                return IMAGETYPE_PNG;
            case 'swf':
                return IMAGETYPE_SWF;
            case 'psd':
                return IMAGETYPE_PSD;
            case 'bmp':
                return IMAGETYPE_BMP;
            case 'tiff':
                return IMAGETYPE_TIFF_II;
            //IMAGETYPE_TIFF_MM;
            case 'jpc':
                return IMAGETYPE_JPC;
            case 'jp2':
                return IMAGETYPE_JP2;
            case 'jpx':
                return IMAGETYPE_JPX;
            case 'jb2':
                return IMAGETYPE_JB2;
            case 'swc':
                return IMAGETYPE_SWC;
            case 'iff':
                return IMAGETYPE_IFF;
            case 'wbmp':
                return IMAGETYPE_WBMP;
            case 'xbm':
                return IMAGETYPE_XBM;
            default:
                return $type;
        }

        return isset($types[$t = mb_strtolower($type)]) ? $types[$t] : $type;
    }

    /**
     * Parses input for number format and convert
     *
     * If either parameter is 0 it will be scaled proportionally
     *
     * @param mixed $new_size (0, number, percentage 10% or 0.1)
     * @param int   $old_size
     *
     * @return mixed Integer or PEAR_error
     * @access protected
     */
    public function _parse_size($new_size, $old_size)
    {
        if ('%' == mb_substr($new_size, -1)) {
            $new_size = mb_substr($new_size, 0, -1);
            $new_size = $new_size / 100;
        }
        if ($new_size > 1) {
            return (int)$new_size;
        } elseif (0 == $new_size) {
            return (int)$old_size;
        }

        return (int)round($new_size * $old_size, 0);
    }

    /**
     * Returns an angle between 0 and 360 from any angle value
     *
     * @param float $angle The angle to normalize
     *
     * @return float the angle
     * @access protected
     */
    public function _rotation_angle($angle)
    {
        $angle %= 360;

        return ($angle < 0) ? $angle + 360 : $angle;
    }

    /**
     * Returns the current value of $this->default_text_params.
     *
     * @return array $this->default_text_params The current text parameters
     * @access protected
     */
    public function _get_default_text_params()
    {
        return $this->default_text_params;
    }

    /**
     * Sets the image width
     *
     * @param int $size dimension to set
     *
     * @access protected
     * @since  29/05/02 13:36:31
     */
    public function _set_img_x($size)
    {
        $this->img_x = $size;
    }

    /**
     * Sets the image height
     *
     * @param int $size dimension to set
     *
     * @access protected
     * @since  29/05/02 13:36:31
     */
    public function _set_img_y($size)
    {
        $this->img_y = $size;
    }

    /**
     * Sets the new image width
     *
     * @param int $size dimension to set
     *
     * @access protected
     * @since  29/05/02 13:36:31
     */
    public function _set_new_x($size)
    {
        $this->new_x = $size;
    }

    /**
     * Sets the new image height
     *
     * @param int $size dimension to set
     *
     * @since  29/05/02 13:36:31
     * @access protected
     */
    public function _set_new_y($size)
    {
        $this->new_y = $size;
    }

    /**
     * Returns the image handle so that one can further try
     * to manipulate the image
     *
     * @return object
     *
     * @access public
     */
    public function getHandle()
    {
        return PEAR::raiseError('getHandle() method not supported by driver', IMAGE_TRANSFORM_ERROR_UNSUPPORTED);
    }

    //function getHandle()

    /**
     * Returns the type of the image being manipulated
     *
     * @return string the image type
     * @access public
     */
    public function getImageType()
    {
        return $this->type;
    }

    /**
     * Returns the MIME type of the image being manipulated
     *
     * @param string $type Image type to get MIME type for
     *
     * @return string The MIME type if available, or an empty string
     * @access public
     * @see    PHP_Compat::image_type_to_mime_type()
     * @link   http://php.net/image_type_to_mime_type
     */
    public function getMimeType($type = null)
    {
        return image_type_to_mime_type($this->_convert_image_type($type ?: $this->type));
    }

    /**
     * Returns the new image width
     *
     * This function returns the width
     * of the new image.
     *
     * @access public
     * @return int  The width of the new image.
     */
    public function getNewImageWidth()
    {
        if (isset($this->new_x)) {
            return (int)$this->new_x;
        }

        return false;
    }

    /**
     * Return new image Y
     *
     * This function will retrieve the
     * new image 'Y' and return it's value
     * if it's set.
     *
     * @access public
     * @return int  The new height of the image.
     */
    public function getNewImageHeight()
    {
        if (isset($this->new_y)) {
            return (int)$this->new_y;
        }

        return false;
    }

    /**
     * Returns the image width
     *
     * @return int the width of the image
     * @access public
     */
    public function getImageWidth()
    {
        return $this->img_x;
    }

    /**
     * Returns the image height
     *
     * @return int the width of the image
     * @access public
     */
    public function getImageHeight()
    {
        return $this->img_y;
    }

    /**
     * Returns the image size and extra format information
     *
     * @return array The width and height of the image
     * @access public
     * @see    PHP::getimagesize()
     */
    public function getImageSize()
    {
        return [
            $this->img_x,
            $this->img_y,
            $this->_convert_image_type($this->type),
            'height="' . $this->img_y . '" width="' . $this->img_x . '"',
            'mime' => $this->getMimeType(),
        ];
    }

    /**
     * This looks at the current image type and attempts to determine which
     * web-safe format will be most suited.  It does not work brilliantly with
     * *.png images, because it is very difficult to know whether they are
     * 8-bit or greater.  Guess I need to have fatter code here :-)
     *
     * @return string web-safe image type
     * @access public
     */
    public function getWebSafeFormat()
    {
        switch ($this->type) {
            case 'gif':
            case 'png':
                return 'png';
                break;
            default:
                return 'jpeg';
        } // switch
    }

    /**
     * Handles space in path and Windows/UNIX difference
     *
     * @param string $path    Base dir
     * @param string $command Command to execute
     * @param string $args    Arguments to pass to the command
     *
     * @return string A prepared string suitable for exec()
     * @access protected
     */
    public function _prepare_cmd($path, $command, $args = '')
    {
        if (!OS_WINDOWS
            || !preg_match('/\s/', $path)) {
            return $path . $command . ' ' . $args;
        }

        return 'start /D "' . $path . '" /B ' . $command . ' ' . $args;
    }

    /**
     * Place holder for the real resize method
     * used by extended methods to do the resizing
     *
     * @return PEAR_error
     * @access protected
     */
    public function _resize()
    {
        return PEAR::raiseError('Resize method not supported by driver', IMAGE_TRANSFORM_ERROR_UNSUPPORTED);
    }

    /**
     * Normalizes the colors, gamma and other properties of an image
     * (this should give a result equivalent to a Photoshop autolevels)
     *
     * @return PEAR_error
     * @access public
     */
    public function normalize()
    {
        return PEAR::raiseError('Normalize method not supported by driver', IMAGE_TRANSFORM_ERROR_UNSUPPORTED);
    }

    /**
     * Loads an image file to work with
     *
     * Place holder for the real load method
     * used by extended methods to do the resizing
     *
     * @param string $filename Full name of file
     *
     * @return PEAR_error
     * @access public
     */
    public function load($filename)
    {
        return PEAR::raiseError('load() method not supported by driver', IMAGE_TRANSFORM_ERROR_UNSUPPORTED);
    }

    /**
     * Outputs the image to standard output
     *
     * Place holder for the real display method
     * used by extended methods to do the resizing
     *
     * @param string $type    Format of image to save as
     * @param mixed  $quality Format-dependent
     *
     * @return PEAR_error
     * @access public
     */
    public function display($type, $quality = null)
    {
        return PEAR::raiseError('display() method not supported by driver', IMAGE_TRANSFORM_ERROR_UNSUPPORTED);
    }

    /**
     * Returns if the driver supports a given image type
     *
     * @param string $type Image type (GIF, PNG, JPEG...)
     * @param string $mode 'r' for read, 'w' for write, 'rw' for both
     *
     * @return true if type (and mode) is supported FALSE otherwise
     * @access public
     */
    public function supportsType($type, $mode = 'rw')
    {
        return (false === mb_strpos(@$this->_supported_image_types[mb_strtolower($type)], $mode)) ? false : true;
    }

    /**
     * Saves image to file
     *
     * Place holder for the real save method
     * used by extended methods to do the resizing
     *
     * @param string $filename Filename to save image to
     * @param string $type     Format of image to save as
     * @param mixed  $quality  Format-dependent
     *
     * @return PEAR_error
     * @access public
     */
    public function save($filename, $type, $quality = null)
    {
        return PEAR::raiseError('save() method not supported by driver', IMAGE_TRANSFORM_ERROR_UNSUPPORTED);
    }

    /**
     * Releases resource
     *
     * Place holder for the real free method
     * used by extended methods to do the resizing
     *
     * @return PEAR_error
     * @access public
     */
    public function free()
    {
        return PEAR::raiseError('free() method not supported by driver', IMAGE_TRANSFORM_ERROR_UNSUPPORTED);
    }

    /**
     * Converts a color string into an array of RGB values
     *
     * @param string $colorhex A color following the #FFFFFF format
     *
     * @return array 3-element array with 0-255 values
     * @access public
     *
     * @see    rgb2colorname
     * @see    colorarray2colorhex
     */
    public function colorhex2colorarray($colorhex)
    {
        $r = hexdec(mb_substr($colorhex, 1, 2));
        $g = hexdec(mb_substr($colorhex, 3, 2));
        $b = hexdec(mb_substr($colorhex, 5, 2));

        return [$r, $g, $b, 'type' => 'RGB'];
    }

    /**
     * @param $type
     */
    public function _send_display_headers($type)
    {
        // Find the filename of the original image:
        $filename = explode('.', basename($this->image));
        $filename = $filename[0];
        header('Content-type: ' . $this->getMimeType($type));
        header('Content-Disposition: inline; filename=' . $filename . '.' . $type);
    }

    /**
     * Converts an array of RGB value into a #FFFFFF format color.
     *
     * @param array $color 3-element array with 0-255 values
     *
     * @return mixed A color following the #FFFFFF format or FALSE
     *               if the array couldn't be converted
     * @access public
     *
     * @see    rgb2colorname
     * @see    colorhex2colorarray
     */
    public function colorarray2colorhex($color)
    {
        if (!is_array($color)) {
            return false;
        }
        $color = sprintf('#%02X%02X%02X', @$color[0], @$color[1], @$color[2]);

        return (7 != mb_strlen($color)) ? false : $color;
    }

    /**
     * Returns the temp directory according to either the TMP, TMPDIR, or TEMP env
     * variables. If these are not set it will also check for the existence of
     * /tmp, %WINDIR%\temp
     *
     * @access public
     * @return string The system tmp directory
     */
    public function getTempDir()
    {
        require_once __DIR__ . '/System.php';

        return System::tmpdir();
    }

    /**
     * Returns a temporary filename using tempnam() and the above getTmpDir() function.
     *
     * @param string $dirname Optional directory name for the tmp file
     *
     * @return string Filename and path of the tmp file
     * @access public
     */
    public function getTempFile($dirname = null)
    {
        if (null === $dirname) {
            require_once __DIR__ . '/System.php';
            $dirname = System::tmpdir();
        }

        return tempnam($dirname, 'temp.');
    }

    /**
     * @param $bool
     */
    public function keepSettingsOnSave($bool)
    {
        $this->keep_settings_on_save = $bool;
    }

    /**
     * Methods to add to the driver classes in the future
     *
     * @return object
     */
    public function addText()
    {
        return PEAR::raiseError('addText() method not supported by driver', IMAGE_TRANSFORM_ERROR_UNSUPPORTED);
    }

    /**
     * @return object
     */
    public function addDropShadow()
    {
        return PEAR::raiseError('addDropShadow() method not supported by driver', IMAGE_TRANSFORM_ERROR_UNSUPPORTED);
    }

    /**
     * @return object
     */
    public function addBorder()
    {
        return PEAR::raiseError('addBorder() method not supported by driver', IMAGE_TRANSFORM_ERROR_UNSUPPORTED);
    }

    /**
     * Crops an image
     *
     * @param int $width  Cropped image width
     * @param int $height Cropped image height
     * @param int $x      X-coordinate to crop at
     * @param int $y      Y-coordinate to crop at
     *
     * @return mixed TRUE or a PEAR_Error object on error
     * @access public
     **/
    public function crop($width, $height, $x = 0, $y = 0)
    {
        return PEAR::raiseError('crop() method not supported by driver', IMAGE_TRANSFORM_ERROR_UNSUPPORTED);
    }

    /**
     * @return object
     */
    public function canvasResize()
    {
        return PEAR::raiseError('canvasResize() method not supported by driver', IMAGE_TRANSFORM_ERROR_UNSUPPORTED);
    }

    /**
     * Corrects the gamma of an image
     *
     * @param float $outputgamma Gamma correction factor
     *
     * @return mixed TRUE or a PEAR_error object on error
     * @access public
     **/
    public function gamma($outputgamma = 1.0)
    {
        return PEAR::raiseError('gamma() method not supported by driver', IMAGE_TRANSFORM_ERROR_UNSUPPORTED);
    }

    /**
     * Rotates the image clockwise
     *
     * @param float $angle   Angle of rotation in degres
     * @param mixed $options Rotation options
     *
     * @return bool|PEAR_Error TRUE on success, PEAR_Error object on error
     * @access public
     */
    public function rotate($angle, $options = null)
    {
        return PEAR::raiseError('rotate() method not supported by driver', IMAGE_TRANSFORM_ERROR_UNSUPPORTED);
    }

    /**
     * Horizontal mirroring
     *
     * @return mixed TRUE or PEAR_Error object on error
     * @access public
     * @see    flip()
     **/
    public function mirror()
    {
        return PEAR::raiseError('mirror() method not supported by driver', IMAGE_TRANSFORM_ERROR_UNSUPPORTED);
    }

    /**
     * Vertical mirroring
     *
     * @return object or PEAR Error object on error
     * @access public
     * @see    mirror()
     */
    public function flip()
    {
        return PEAR::raiseError('flip() method not supported by driver', IMAGE_TRANSFORM_ERROR_UNSUPPORTED);
    }

    /**
     * Converts an image into greyscale colors
     *
     * @return mixed TRUE or a PEAR error object on error
     * @access public
     **/
    public function greyscale()
    {
        return PEAR::raiseError('greyscale() method not supported by driver', IMAGE_TRANSFORM_ERROR_UNSUPPORTED);
    }

    /**
     * Converts an image into greyscale colors
     *
     * @return mixed TRUE or a PEAR error object on error
     * @see greyscale()
     **/
    public function grayscale()
    {
        return $this->greyscale();
    }

    /**
     * Returns a color option
     *
     * @param string $colorOf one of 'canvasColor', 'pencilColor', 'fontColor'
     * @param array  $options configuration options
     * @param array  $default default value to return if color not found
     *
     * @return array an RGB color array
     * @access protected
     */
    public function _getColor($colorOf, $options = [], $default = [0, 0, 0])
    {
        $opt = array_merge($this->_options, (array)$options);
        if (isset($opt[$colorOf])) {
            $color = $opt[$colorOf];
            if (is_array($color)) {
                return $color;
            }
            if ('#' == $color[0]) {
                return $this->colorhex2colorarray($color);
            }
            static $colornames = [];
            require_once __DIR__ . '/Image/Transform/Driver/ColorsDefs.php';

            return $colornames[$color] ?? $default;
        }

        return $default;
    }

    /**
     * Returns an option
     *
     * @param string $name    name of option
     * @param array  $options local override option array
     * @param mixed  $default default value to return if option is not found
     *
     * @return mixed the option
     * @access protected
     */
    public function _getOption($name, $options = [], $default = null)
    {
        $opt = array_merge($this->_options, (array)$options);

        return $opt[$name] ?? $default;
    }

    /**
     * Checks if the rectangle passed intersects with the current image
     *
     * @param int $width  Width of rectangle
     * @param int $height Height of rectangle
     * @param int $x      X-coordinate
     * @param int $y      Y-coordinate
     *
     * @return bool|PEAR_Error TRUE if intersects, FALSE if not,
     *                         and PEAR_Error on error
     * @access public
     */
    public function intersects($width, $height, $x, $y)
    {
        $left  = $x;
        $right = $x + $width;
        if ($right < $left) {
            $left  = $right;
            $right = $x;
        }
        $top    = $y;
        $bottom = $y + $height;
        if ($bottom < $top) {
            $top    = $bottom;
            $bottom = $y;
        }

        return ($left < $this->new_x
                && $right >= 0
                && $top < $this->new_y
                && $bottom >= 0);
    }
}