Files
server/usr/share/psa-pear/pear/php/Horde/Image/Base.php
2026-01-07 20:52:11 +01:00

543 lines
15 KiB
PHP

<?php
/**
* Copyright 2002-2017 Horde LLC (http://www.horde.org/)
*
* See the enclosed file LICENSE for license information (LGPL). If you
* did not receive this file, see http://www.horde.org/licenses/lgpl21.
*
* @author Chuck Hagenbuch <chuck@horde.org>
* @author Michael J. Rubinsky <mrubinsk@horde.org>
* @author Jan Schneider <jan@horde.org>
* @category Horde
* @license http://www.horde.org/licenses/lgpl21 LGPL-2.1
* @package Image
*/
/**
* This class defines the Horde_Image API, and also provides some utility
* functions, such as generating highlights of a color.
*
* @author Chuck Hagenbuch <chuck@horde.org>
* @author Michael J. Rubinsky <mrubinsk@horde.org>
* @author Jan Schneider <jan@horde.org>
* @category Horde
* @copyright 2002-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL-2.1
* @package Image
*/
abstract class Horde_Image_Base extends EmptyIterator
{
/**
* Background color.
*
* @var string
*/
protected $_background = 'white';
/**
* Capabilites of this driver.
*
* @var array
*/
protected $_capabilities = array();
/**
* The current image data.
*
* @var Horde_Stream
*/
protected $_data;
/**
* Logger.
*/
protected $_logger;
/**
* The current width of the image data.
*
* @var integer
*/
protected $_width = 0;
/**
* The current height of the image data.
*
* @var integer
*/
protected $_height = 0;
/**
* A directory for temporary files.
*
* @var string
*/
protected $_tmpdir;
/**
* Array containing available Effects
*
* @var array
*/
protected $_loadedEffects = array();
/**
* What kind of images should ImageMagick generate? Defaults to 'png'.
*
* @var string
*/
protected $_type = 'png';
/**
* Cache the context
*
* @param array
*/
protected $_context;
/**
* Constructor.
*
* @param array $params The image object parameters. Values include:
* - background: (string) The background color.
* DEFAULT: white.
* - data: (string) The image binary data.
* - height: (integer) The desired image height.
* - type: (string) The output image type (png, jpeg
* etc.). DEFAULT: png.
* - width: (integer) The desired image width.
* @param array $context The object context - configuration, injected
* objects:
* - logger: (Horde_Log_Logger) A logger.
* - tmpdir: [REQUIRED] (string) Temporary directory.
*
* @throws InvalidArgumentException
*/
protected function __construct($params, $context = array())
{
$this->_params = $params;
$this->_context = $context;
if (empty($context['tmpdir'])) {
throw new InvalidArgumentException(
'A path to a temporary directory is required.'
);
}
$this->_tmpdir = $context['tmpdir'];
$this->_logger = !empty($context['logger'])
? $context['logger']
: new Horde_Support_Stub();
if (isset($params['width'])) {
$this->_width = (integer)$params['width'];
}
if (isset($params['height'])) {
$this->_height = (integer)$params['height'];
}
if (!empty($params['type'])) {
// We only want the extension, not the full mimetype.
if (strpos($params['type'], 'image/') !== false) {
$params['type'] = substr($params['type'], 6);
}
$this->_type = $params['type'];
}
if (!empty($params['background'])) {
$this->_background = $params['background'];
}
}
/**
* Catch-all method so that we don't error out when calling an unsupported
* manipulation method.
*/
public function __call($method, $args)
{
}
/**
* Returns the capabilities.
*
* @return array A list of backend capabilities.
*/
public function getCapabilities()
{
return $this->_capabilities;
}
/**
* Checks the existence of a particular capability.
*
* @param string $capability The capability to check for.
*
* @return boolean True if the backend has this capability.
*/
public function hasCapability($capability)
{
return in_array($capability, $this->_capabilities);
}
/**
* Sends HTTP headers for the image.
*/
public function headers()
{
header('Content-type: ' . $this->getContentType());
}
/**
* Returns the MIME type for this image.
*
* @return string The MIME type for this image.
*/
public function getContentType()
{
return 'image/' . $this->_type;
}
/**
* Returns the image type.
*
* @return string The type of this image (png, jpg, etc.).
*/
public function getType()
{
return $this->_type;
}
/**
* Sets the output image type.
*
* @param string $type An image type (png, jpg, etc.)
*
* @return string The previous image type.
*/
public function setType($type)
{
// We only want the extension, not the full mimetype.
if (strpos($type, 'image/') !== false) {
$type = substr($type, 6);
}
$old = $this->_type;
$this->_type = $type;
return $old;
}
/**
* Draws a shaped point at the specified (x,y) point.
*
* Useful for scatter diagrams, debug points, etc. Draws squares, circles,
* diamonds, and triangles.
*
* @param integer $x The x coordinate of the point to brush.
* @param integer $y The y coordinate of the point to brush.
* @param string $color The color to brush the point with.
* @param string $shape What brush to use? Defaults to a square.
*/
public function brush($x, $y, $color = 'black', $shape = 'square')
{
switch ($shape) {
case 'triangle':
$verts[0] = array('x' => $x + 3, 'y' => $y + 3);
$verts[1] = array('x' => $x, 'y' => $y - 3);
$verts[2] = array('x' => $x - 3, 'y' => $y + 3);
$this->polygon($verts, $color, $color);
break;
case 'circle':
$this->circle($x, $y, 3, $color, $color);
break;
case 'diamond':
$verts[0] = array('x' => $x - 3, 'y' => $y);
$verts[1] = array('x' => $x, 'y' => $y + 3);
$verts[2] = array('x' => $x + 3, 'y' => $y);
$verts[3] = array('x' => $x, 'y' => $y - 3);
$this->polygon($verts, $color, $color);
break;
case 'square':
default:
$this->rectangle($x - 2, $y - 2, 4, 4, $color, $color);
break;
}
}
/**
* Resets the image data to defaults.
*/
public function reset()
{
if ($this->_data) {
$this->_data->close();
}
$this->_data = null;
$this->_width = null;
$this->_height = null;
$this->_background = 'white';
}
/**
* Returns the height and width of the current image data.
*
* @return array An hash with 'width' containing the width,
* 'height' containing the height of the image.
*/
public function getDimensions()
{
// Check if we know it already
if ($this->_width == 0 && $this->_height == 0) {
$tmp = $this->toFile();
$details = @getimagesize($tmp);
list($this->_width, $this->_height) = $details;
unlink($tmp);
}
return array('width' => $this->_width, 'height' => $this->_height);
}
/**
* Loads the image data from a string.
*
* @param mixed $image_data The data to use for the image as a string,
* Horde_Stream, or stream resource.
*/
public function loadString($image_data)
{
$this->reset();
$this->_data = new Horde_Stream_Temp();
$this->_data->add($image_data, true);
}
/**
* Loads the image data from a file.
*
* @param string $filename The full path and filename to the file to load
* the image data from.
*
* @throws Horde_Image_Exception
*/
public function loadFile($filename)
{
$this->reset();
if (!file_exists($filename)) {
throw new Horde_Image_Exception(
sprintf("The image file, %s, does not exist.", $filename)
);
}
$fp = fopen($filename, 'r');
$this->_data = new Horde_Stream_Temp();
$this->_data->add($fp, true);
if (!$this->_data->length()) {
throw new Horde_Image_Exception(
sprintf("Could not load the image file %s", $filename)
);
}
}
/**
* Saves image data to file.
*
* If $data is false-ish, saves current image data after performing pending
* operations on the data. If $data contains raw image data, saves that
* data to file without regard for the current image data.
*
* @param mixed String or stream resource of binary image data.
*
* @return string Path to temporary file.
* @throws Horde_Image_Exception
*/
public function toFile($data = null)
{
if (empty($data)) {
if ($data = $this->raw(false, array('stream' => true))) {
return $this->toFile($data);
}
throw new Horde_Image_Exception('Unable to copy to file.');
}
$tmp = Horde_Util::getTempFile('img', false, $this->_tmpdir);
$fp = fopen($tmp, 'wb');
if (is_resource($data)) {
rewind($data);
while (!feof($data)) {
fwrite($fp, fread($data, 8192));
}
} elseif ($data) {
fwrite($fp, $data);
}
fclose($fp);
return $tmp;
}
/**
* Displays the current image.
*/
public function display()
{
$this->headers();
$data = $this->raw(true, array('stream' => true));
$output = fopen('php://output', 'w');
while (!feof($data)) {
fwrite($output, fread($data, 8192));
}
}
/**
* Returns the raw data for this image.
*
* @param boolean $convert If true, the image data will be returned in the
* target format, independently from any image
* operations.
* @param array $options Array of options:
* - stream: If true, return as a stream resource.
* DEFAULT: false.
*
* @return string The raw image data.
*/
public function raw($convert = false, $options = array())
{
if (empty($options['stream'])) {
return $this->_data->__toString();
}
return $this->_data->stream;
}
/**
* Attempts to apply requested effect to this image.
*
* @param string $type The type of effect to apply.
* @param array $params Any parameters for the effect.
*/
public function addEffect($type, $params)
{
$class = str_replace('Horde_Image_', '', get_class($this));
$params['logger'] = $this->_logger;
$effect = Horde_Image_Effect::factory($type, $class, $params);
$effect->setImageObject($this);
$effect->apply();
}
/**
* Returns a list of available effects for this driver.
*/
public function getLoadedEffects()
{
if (!count($this->_loadedEffects)) {
$class = str_replace('Horde_Image_', '', get_class($this));
$this->_loadedEffects = array();
// First, load the driver-agnostic Effects.
$path = __DIR__ . '/Effect/';
if (is_dir($path)) {
if ($handle = opendir($path)) {
while (($file = readdir($handle)) !== false) {
if (substr($file, -4, 4) == '.php') {
$this->_loadedEffects[] = substr(
$file, 0, strlen($file) - 4
);
}
}
}
}
// Driver specific effects.
$path = $path . $class;
if (is_dir($path)) {
if ($handle = opendir($path)) {
while (($file = readdir($handle)) !== false) {
if (substr($file, -4, 4) == '.php') {
$this->_loadedEffects[] = substr(
$file, 0, strlen($file) - 4
);
}
}
}
}
}
return $this->_loadedEffects;
}
/**
* Applies any effects in the effect queue.
*/
public function applyEffects()
{
$this->raw();
}
/**
* Returns the current temporary directory.
*
* @return string The current temporary directory.
*/
public function getTmpDir()
{
return $this->_tmpdir;
}
/**
* Utility function to zero out cached geometry information.
*
* Shouldn't really be called from client code, but is needed since effects
* may need to clear these.
*/
public function clearGeometry()
{
$this->_height = 0;
$this->_width = 0;
}
/**
* Logs a message at debug level.
*
* @param string $message The log message.
*/
protected function _logDebug($message)
{
$this->_logger->debug($message);
}
/**
* Logs a message at error level.
*
* @param string $message The log message.
*/
protected function _logErr($message)
{
$this->_logger->err($message);
}
/**
* Returns a specific image from the pages of images.
*
* @param integer $index The index to return.
*
* @return Horde_Image_Base The requested image
*/
public function getImageAtIndex($index)
{
if ($index > 0) {
throw new Horde_Image_Exception('Image index out of bounds.');
}
$class = get_class($this);
return new $class(array('data' => $this->raw()), $this->_context);
}
/**
* Returns the number of image pages available in the image object.
*
* @return integer The number of images.
*/
public function getImagePageCount()
{
return 1;
}
}