844 lines
27 KiB
PHP
844 lines
27 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 Mike Cochrane <mike@graftonhall.co.nz>
|
|
* @author Michael J. Rubinsky <mrubinsk@horde.org>
|
|
* @category Horde
|
|
* @license http://www.horde.org/licenses/lgpl21 LGPL-2.1
|
|
* @package Image
|
|
*/
|
|
|
|
/**
|
|
* ImageMagick driver for the Horde_Image API.
|
|
*
|
|
* @author Chuck Hagenbuch <chuck@horde.org>
|
|
* @author Mike Cochrane <mike@graftonhall.co.nz>
|
|
* @author Michael J. Rubinsky <mrubinsk@horde.org>
|
|
* @category Horde
|
|
* @copyright 2002-2017 Horde LLC
|
|
* @license http://www.horde.org/licenses/lgpl21 LGPL-2.1
|
|
* @package Image
|
|
*
|
|
* @property-read Imagick $imagick The underlaying Imagick object.
|
|
*/
|
|
class Horde_Image_Im extends Horde_Image_Base
|
|
{
|
|
/**
|
|
* Capabilites of this driver.
|
|
*
|
|
* @var string[]
|
|
*/
|
|
protected $_capabilities = array(
|
|
'arc',
|
|
'canvas',
|
|
'circle',
|
|
'crop',
|
|
'dashedLine',
|
|
'flip',
|
|
'grayscale',
|
|
'line',
|
|
'mirror',
|
|
'multipage',
|
|
'pdf',
|
|
'polygon',
|
|
'polyline',
|
|
'rectangle',
|
|
'resize',
|
|
'rotate',
|
|
'roundedRectangle',
|
|
'sepia',
|
|
'text',
|
|
);
|
|
|
|
/**
|
|
* Operations to be performed before the source filename is specified on
|
|
* the command line.
|
|
*
|
|
* @var array
|
|
*/
|
|
protected $_operations = array();
|
|
|
|
/**
|
|
* Operations to be added after the source filename is specified on the
|
|
* command line.
|
|
*
|
|
* @var array
|
|
*/
|
|
protected $_postSrcOperations = array();
|
|
|
|
/**
|
|
* An array of temporary filenames that need to be unlinked at the end of
|
|
* processing.
|
|
*
|
|
* Use addFileToClean() from client code (effects) to add files to this
|
|
* array.
|
|
*
|
|
* @var array
|
|
*/
|
|
protected $_toClean = array();
|
|
|
|
/**
|
|
* Path to the convert binary.
|
|
*
|
|
* @var string
|
|
*/
|
|
protected $_convert = '';
|
|
|
|
/**
|
|
* Path to the identify binary.
|
|
*
|
|
* @var string
|
|
*/
|
|
protected $_identify;
|
|
|
|
/**
|
|
* Cache for the number of image pages.
|
|
*
|
|
* @var integer
|
|
*/
|
|
private $_pages;
|
|
|
|
/**
|
|
* The current page for the iterator.
|
|
*
|
|
* @var integer
|
|
*/
|
|
private $_currentPage = 0;
|
|
|
|
/**
|
|
* Constructor.
|
|
*
|
|
* @see Horde_Image_Base::_construct
|
|
*/
|
|
public function __construct($params, $context = array())
|
|
{
|
|
parent::__construct($params, $context);
|
|
|
|
if (empty($context['convert'])) {
|
|
throw new InvalidArgumentException(
|
|
'A path to the convert binary is required.'
|
|
);
|
|
}
|
|
$this->_convert = $context['convert'];
|
|
if (!empty($context['identify'])) {
|
|
$this->_identify = $context['identify'];
|
|
}
|
|
|
|
if (!empty($params['filename'])) {
|
|
$this->loadFile($params['filename']);
|
|
} elseif (!empty($params['data'])) {
|
|
$this->loadString($params['data']);
|
|
} else {
|
|
$cmd = sprintf(
|
|
'-size %dx%d xc:%s -strip %s:__FILEOUT__',
|
|
$this->_width,
|
|
$this->_height,
|
|
escapeshellarg($this->_background),
|
|
escapeshellarg($this->_type)
|
|
);
|
|
$this->executeConvertCmd($cmd);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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 mixed The raw image data either as a string or stream resource.
|
|
*/
|
|
public function raw($convert = false, $options = array())
|
|
{
|
|
if (!empty($options['stream'])) {
|
|
return $this->_raw($convert)->stream;
|
|
}
|
|
|
|
return $this->_raw($convert)->__toString();
|
|
}
|
|
|
|
/**
|
|
* 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 An array of options:
|
|
* - index: (integer) An image index.
|
|
* - preserve_data (boolean) If true, return the converted image but
|
|
* preserve the internal image data.
|
|
*
|
|
* @return Horde_Stream The data, in a Horde_Stream object.
|
|
*/
|
|
private function _raw($convert = false, $options = array())
|
|
{
|
|
$options = array_merge(
|
|
array('index' => 0, 'preserve_data' => false),
|
|
$options
|
|
);
|
|
|
|
if (empty($this->_data) ||
|
|
// If there are no operations, and we already have data, don't
|
|
// bother writing out files, just return the current data.
|
|
(!$convert &&
|
|
!count($this->_operations) &&
|
|
!count($this->_postSrcOperations))) {
|
|
return $this->_data;
|
|
}
|
|
|
|
$tmpin = $this->toFile($this->_data);
|
|
$tmpout = Horde_Util::getTempFile('img', false, $this->_tmpdir);
|
|
$command = $this->_convert . ' ' . implode(' ', $this->_operations)
|
|
. ' "' . $tmpin . '"\'[' . (integer)$options['index'] . ']\' '
|
|
. implode(' ', $this->_postSrcOperations)
|
|
. ' -strip ' . escapeshellarg($this->_type) . ':"' . $tmpout . '" 2>&1';
|
|
$this->_logDebug(sprintf("convert command executed by Horde_Image_im::raw(): %s", $command));
|
|
exec($command, $output, $retval);
|
|
if ($retval) {
|
|
$error = sprintf("Error running command: %s", $command . "\n" . implode("\n", $output));
|
|
$this->_logErr($error);
|
|
throw new Horde_Image_Exception($error);
|
|
}
|
|
|
|
/* Empty the operations queue */
|
|
$this->_operations = array();
|
|
$this->_postSrcOperations = array();
|
|
|
|
/* Load the result */
|
|
$fp = fopen($tmpout, 'r');
|
|
$return = new Horde_Stream_Temp();
|
|
$return->add($fp, true);
|
|
if (empty($options['preserve_data'])) {
|
|
if ($this->_data) {
|
|
$this->_data->close();
|
|
}
|
|
$this->_data = $return;
|
|
}
|
|
|
|
@unlink($tmpin);
|
|
@unlink($tmpout);
|
|
|
|
return $return;
|
|
}
|
|
|
|
/**
|
|
* Resets the image data.
|
|
*/
|
|
public function reset()
|
|
{
|
|
parent::reset();
|
|
$this->_operations = array();
|
|
$this->_postSrcOperations = array();
|
|
$this->clearGeometry();
|
|
}
|
|
|
|
/**
|
|
* Resizes the current image.
|
|
*
|
|
* @param integer $width The new width.
|
|
* @param integer $height The new height.
|
|
* @param boolean $ratio Maintain original aspect ratio.
|
|
* @param boolean $keepProfile Keep the image meta data.
|
|
*/
|
|
public function resize($width, $height, $ratio = true, $keepProfile = false)
|
|
{
|
|
$resWidth = $width * 2;
|
|
$resHeight = $height * 2;
|
|
$this->_operations[] = "-size {$resWidth}x{$resHeight}";
|
|
if ($ratio) {
|
|
$this->_postSrcOperations[] =
|
|
($keepProfile ? '-resize' : '-thumbnail')
|
|
. sprintf(' %dx%d', $width, $height);
|
|
} else {
|
|
$this->_postSrcOperations[] =
|
|
($keepProfile ? '-resize' : '-thumbnail')
|
|
. sprintf(' %dx%d!', $width, $height);
|
|
}
|
|
|
|
// Refresh the data
|
|
$this->raw(false, array('stream' => true));
|
|
|
|
// Reset the width and height instance variables since after resize we
|
|
// don't know the *exact* dimensions yet (especially if we maintained
|
|
// aspect ratio.
|
|
$this->clearGeometry();
|
|
}
|
|
|
|
/**
|
|
* Crops the current image.
|
|
*
|
|
* @param integer $x1 x for the top left corner.
|
|
* @param integer $y1 y for the top left corner.
|
|
* @param integer $x2 x for the bottom right corner.
|
|
* @param integer $y2 y for the bottom right corner.
|
|
*/
|
|
public function crop($x1, $y1, $x2, $y2)
|
|
{
|
|
$line = ($x2 - $x1) . 'x' . ($y2 - $y1) . '+' . (integer)$x1 . '+' . (integer)$y1;
|
|
$this->_operations[] = '-crop ' . $line . ' +repage';
|
|
|
|
// Reset width/height since these might change
|
|
$this->raw(false, array('stream' => true));
|
|
$this->clearGeometry();
|
|
}
|
|
|
|
/**
|
|
* Rotates the current image.
|
|
*
|
|
* @param integer $angle The angle to rotate the image by,
|
|
* in the clockwise direction.
|
|
* @param integer $background The background color to fill any triangles.
|
|
*/
|
|
public function rotate($angle, $background = 'white')
|
|
{
|
|
$this->raw(false, array('stream' => true));
|
|
$this->_operations[] = sprintf(
|
|
'-background %s -rotate %d',
|
|
escapeshellarg($this->_background),
|
|
(integer)$angle
|
|
);
|
|
$this->raw(false, array('stream' => true));
|
|
|
|
// Reset width/height since these might have changed
|
|
$this->clearGeometry();
|
|
}
|
|
|
|
/**
|
|
* Flips the current image.
|
|
*/
|
|
public function flip()
|
|
{
|
|
$this->_operations[] = '-flip';
|
|
}
|
|
|
|
/**
|
|
* Mirrors the current image.
|
|
*/
|
|
public function mirror()
|
|
{
|
|
$this->_operations[] = '-flop';
|
|
}
|
|
|
|
/**
|
|
* Converts the current image to grayscale.
|
|
*/
|
|
public function grayscale()
|
|
{
|
|
$this->_postSrcOperations[] = '-colorspace GRAY';
|
|
}
|
|
|
|
/**
|
|
* Applies a sepia filter.
|
|
*
|
|
* @param integer $threshold Extent of sepia effect.
|
|
*/
|
|
public function sepia($threshold = 85)
|
|
{
|
|
$this->_operations[] = '-sepia-tone ' . (integer)$threshold . '%';
|
|
}
|
|
|
|
/**
|
|
* Draws a text string on the image in a specified location, with the
|
|
* specified style information.
|
|
*
|
|
* @TODO: Need to differentiate between the stroke (border) and the fill
|
|
* color, but this is a BC break, since we were just not providing a
|
|
* border.
|
|
*
|
|
* @param string $text The text to draw.
|
|
* @param integer $x The left x coordinate of the start of the
|
|
* text string.
|
|
* @param integer $y The top y coordinate of the start of the text
|
|
* string.
|
|
* @param string $font The font identifier you want to use for the
|
|
* text.
|
|
* @param string $color The color that you want the text displayed in.
|
|
* @param integer $direction An integer that specifies the orientation of
|
|
* the text.
|
|
* @param string $fontsize Size of the font (small, medium, large, giant)
|
|
*/
|
|
public function text(
|
|
$string, $x, $y, $font = '', $color = 'black', $direction = 0,
|
|
$fontsize = 'small'
|
|
)
|
|
{
|
|
$string = addslashes($string);
|
|
$fontsize = Horde_Image::getFontSize($fontsize);
|
|
$command = 'text ' . (integer)$x . ',' . (integer)$y . ' ' . $string;
|
|
$this->_postSrcOperations[] = '-fill ' . escapeshellarg($color)
|
|
. (!empty($font) ? ' -font ' . escapeshellarg($font) : '')
|
|
. sprintf(
|
|
' -pointsize %d -gravity northwest -draw "%s" -fill none',
|
|
$fontsize, $command
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Draws a circle.
|
|
*
|
|
* @param integer $x The x coordinate of the centre.
|
|
* @param integer $y The y coordinate of the centre.
|
|
* @param integer $r The radius of the circle.
|
|
* @param string $color The line color of the circle.
|
|
* @param string $fill The color to fill the circle.
|
|
*/
|
|
public function circle($x, $y, $r, $color, $fill = 'none')
|
|
{
|
|
$xMax = $x + $r;
|
|
$this->_postSrcOperations[] = sprintf(
|
|
'-stroke %s -fill %s -draw "circle %d,%d %d,%d" -stroke none -fill none',
|
|
escapeshellarg($color), escapeshellarg($fill), $x, $y, $xMax, $y
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Draws a polygon based on a set of vertices.
|
|
*
|
|
* @param array $vertices An array of x and y labeled arrays
|
|
* (eg. $vertices[0]['x'], $vertices[0]['y'], ...).
|
|
* @param string $color The color you want to draw the polygon with.
|
|
* @param string $fill The color to fill the polygon.
|
|
*/
|
|
public function polygon($verts, $color, $fill = 'none')
|
|
{
|
|
$command = '';
|
|
foreach ($verts as $vert) {
|
|
$command .= sprintf(' %d,%d', $vert['x'], $vert['y']);
|
|
}
|
|
$this->_postSrcOperations[] = sprintf(
|
|
'-stroke %s -fill %s -draw "polygon $command" -stroke none -fill none',
|
|
escapeshellarg($color), escapeshellarg($fill)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Draws a rectangle.
|
|
*
|
|
* @param integer $x The left x-coordinate of the rectangle.
|
|
* @param integer $y The top y-coordinate of the rectangle.
|
|
* @param integer $width The width of the rectangle.
|
|
* @param integer $height The height of the rectangle.
|
|
* @param string $color The line color of the rectangle.
|
|
* @param string $fill The color to fill the rectangle.
|
|
*/
|
|
public function rectangle($x, $y, $width, $height, $color, $fill = 'none')
|
|
{
|
|
$xMax = $x + $width;
|
|
$yMax = $y + $height;
|
|
$this->_postSrcOperations[] = sprintf(
|
|
'-stroke %s -fill %s -draw "rectangle %d,%d %d,%d" -stroke none -fill none',
|
|
escapeshellarg($color), escapeshellarg($fill), $x, $y, $xMax, $yMax
|
|
);
|
|
|
|
}
|
|
|
|
/**
|
|
* Draws a rounded rectangle.
|
|
*
|
|
* @param integer $x The left x-coordinate of the rectangle.
|
|
* @param integer $y The top y-coordinate of the rectangle.
|
|
* @param integer $width The width of the rectangle.
|
|
* @param integer $height The height of the rectangle.
|
|
* @param integer $round The width of the corner rounding.
|
|
* @param string $color The line color of the rectangle.
|
|
* @param string $fill The color to fill the rounded rectangle with.
|
|
*/
|
|
public function roundedRectangle(
|
|
$x, $y, $width, $height, $round, $color, $fill
|
|
)
|
|
{
|
|
$x1 = $x + $width;
|
|
$y1 = $y + $height;
|
|
$this->_postSrcOperations[] = sprintf(
|
|
'-stroke %s -fill %s -draw "roundRectangle %d,%d %d,%d %d,%d" -stroke none -fill none',
|
|
escapeshellarg($color), escapeshellarg($fill), $x, $y, $x1, $y1, $round, $round
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Draws a line.
|
|
*
|
|
* @param integer $x0 The x coordinate of the start.
|
|
* @param integer $y0 The y coordinate of the start.
|
|
* @param integer $x1 The x coordinate of the end.
|
|
* @param integer $y1 The y coordinate of the end.
|
|
* @param string $color The line color.
|
|
* @param string $width The width of the line.
|
|
*/
|
|
public function line($x0, $y0, $x1, $y1, $color = 'black', $width = 1)
|
|
{
|
|
$this->_operations[] = sprintf(
|
|
'-stroke %s -strokewidth %d -draw "line %d,%d %d,%d"',
|
|
escapeshellarg($color), $width, $x0, $y0, $x1, $y1
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Draws a dashed line.
|
|
*
|
|
* @param integer $x0 The x co-ordinate of the start.
|
|
* @param integer $y0 The y co-ordinate of the start.
|
|
* @param integer $x1 The x co-ordinate of the end.
|
|
* @param integer $y1 The y co-ordinate of the end.
|
|
* @param string $color The line color.
|
|
* @param string $width The width of the line.
|
|
* @param integer $dash_length The length of a dash on the dashed line
|
|
* @param integer $dash_space The length of a space in the dashed line
|
|
*/
|
|
public function dashedLine(
|
|
$x0, $y0, $x1, $y1, $color = 'black', $width = 1, $dash_length = 2,
|
|
$dash_space = 2
|
|
)
|
|
{
|
|
$this->_operations[] = sprintf(
|
|
'-stroke %s -strokewidth %d -draw "line %d,%d %d,%d"',
|
|
escapeshellarg($color), $width, $x0, $y0, $x1, $y1
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Draws a polyline (a non-closed, non-filled polygon) based on a set of
|
|
* vertices.
|
|
*
|
|
* @param array $vertices An array of x and y labeled arrays
|
|
* (eg. $vertices[0]['x'], $vertices[0]['y'], ...).
|
|
* @param string $color The color you want to draw the line with.
|
|
* @param string $width The width of the line.
|
|
*/
|
|
public function polyline($verts, $color, $width = 1)
|
|
{
|
|
$command = '';
|
|
foreach ($verts as $vert) {
|
|
$command .= sprintf(' %d,%d', $vert['x'], $vert['y']);
|
|
}
|
|
$this->_operations[] = sprintf(
|
|
'-stroke %s -strokewidth %d -fill none -draw "polyline $command" -strokewidth 1 -stroke none -fill none',
|
|
escapeshellarg($color), $width
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Draws an arc.
|
|
*
|
|
* @param integer $x The x coordinate of the centre.
|
|
* @param integer $y The y coordinate of the centre.
|
|
* @param integer $r The radius of the arc.
|
|
* @param integer $start The start angle of the arc.
|
|
* @param integer $end The end angle of the arc.
|
|
* @param string $color The line color of the arc.
|
|
* @param string $fill The fill color of the arc (defaults to none).
|
|
*/
|
|
public function arc(
|
|
$x, $y, $r, $start, $end, $color = 'black', $fill = 'none'
|
|
)
|
|
{
|
|
// Split up arcs greater than 180 degrees into two pieces.
|
|
$this->_postSrcOperations[] = sprintf(
|
|
'-stroke %s -fill %s',
|
|
escapeshellarg($color), escapeshellarg($fill)
|
|
);
|
|
$mid = round(($start + $end) / 2);
|
|
$x = round($x);
|
|
$y = round($y);
|
|
$r = round($r);
|
|
if ($mid > 90) {
|
|
$this->_postSrcOperations[] = sprintf(
|
|
'-draw "ellipse %d,%d %d,%d %d,%d"',
|
|
$x, $y, $r, $r, $start, $mid
|
|
);
|
|
$this->_postSrcOperations[] = sprintf(
|
|
'-draw "ellipse %d,%d %d,%d %d,%d"',
|
|
$x, $y, $r, $r, $mid, $end
|
|
);
|
|
} else {
|
|
$this->_postSrcOperations[] = sprintf(
|
|
'-draw "ellipse %d,%d %d,%d %d,%d"',
|
|
$x, $y, $r, $r, $start, $end
|
|
);
|
|
}
|
|
|
|
// If filled, draw the outline.
|
|
if (!empty($fill)) {
|
|
list($x1, $y1) = Horde_Image::circlePoint($start, $r * 2);
|
|
list($x2, $y2) = Horde_Image::circlePoint($mid, $r * 2);
|
|
list($x3, $y3) = Horde_Image::circlePoint($end, $r * 2);
|
|
|
|
$verts = array(
|
|
array('x' => $x + round($x3), 'y' => $y + round($y3)),
|
|
array('x' => $x, 'y' => $y),
|
|
array('x' => $x + round($x1), 'y' => $y + round($y1))
|
|
);
|
|
|
|
if ($mid > 90) {
|
|
$verts1 = array(
|
|
array('x' => $x + round($x2), 'y' => $y + round($y2)),
|
|
array('x' => $x, 'y' => $y),
|
|
array('x' => $x + round($x1), 'y' => $y + round($y1))
|
|
);
|
|
$verts2 = array(
|
|
array('x' => $x + round($x3), 'y' => $y + round($y3)),
|
|
array('x' => $x, 'y' => $y),
|
|
array('x' => $x + round($x2), 'y' => $y + round($y2))
|
|
);
|
|
|
|
$this->polygon($verts1, $fill, $fill);
|
|
$this->polygon($verts2, $fill, $fill);
|
|
} else {
|
|
$this->polygon($verts, $fill, $fill);
|
|
}
|
|
|
|
$this->polyline($verts, $color);
|
|
|
|
$this->_postSrcOperations[] = '-stroke none -fill none';
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Applies any effects in the effect queue.
|
|
*/
|
|
public function applyEffects()
|
|
{
|
|
$this->raw(false, array('stream' => true));
|
|
foreach ($this->_toClean as $tempfile) {
|
|
@unlink($tempfile);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Method to execute a raw command directly in convert.
|
|
*
|
|
* Useful for executing more involved operations that may require multiple
|
|
* convert commands piped into each other as could be needed by
|
|
* Im based Horde_Image_Effect objects.
|
|
*
|
|
* The input and output files are quoted and substituted for __FILEIN__ and
|
|
* __FILEOUT__ respectfully. In order to support piped convert commands,
|
|
* the path to the convert command is substitued for __CONVERT__ (but the
|
|
* initial convert command is added automatically).
|
|
*
|
|
* @param string $cmd The command string, with substitutable tokens
|
|
* @param array $values Any values that should be substituted for tokens.
|
|
*/
|
|
public function executeConvertCmd($cmd, $values = array())
|
|
{
|
|
// First, get a temporary file for the input
|
|
if (strpos($cmd, '__FILEIN__') !== false) {
|
|
$tmpin = $this->toFile($this->_data);
|
|
} else {
|
|
$tmpin = '';
|
|
}
|
|
|
|
// Now an output file
|
|
$tmpout = Horde_Util::getTempFile('img', false, $this->_tmpdir);
|
|
|
|
// Substitue them in the cmd string
|
|
$cmd = str_replace(
|
|
array('__FILEIN__', '__FILEOUT__', '__CONVERT__'),
|
|
array('"' . $tmpin . '"', '"' . $tmpout . '"', $this->_convert),
|
|
$cmd
|
|
);
|
|
|
|
//TODO: See what else needs to be replaced.
|
|
$cmd = $this->_convert . ' ' . $cmd . ' 2>&1';
|
|
|
|
// Log it
|
|
$this->_logDebug(sprintf("convert command executed by Horde_Image_im::executeConvertCmd(): %s", $cmd));
|
|
exec($cmd, $output, $retval);
|
|
if ($retval) {
|
|
$this->_logErr(sprintf("Error running command: %s", $cmd . "\n" . implode("\n", $output)));
|
|
}
|
|
$fp = fopen($tmpout, 'r');
|
|
if ($this->_data) {
|
|
$this->_data->close();
|
|
}
|
|
$this->_data = new Horde_Stream_Temp();
|
|
$this->_data->add($fp, true);
|
|
|
|
@unlink($tmpin);
|
|
@unlink($tmpout);
|
|
}
|
|
|
|
/**
|
|
* Returns the version of the convert command available.
|
|
*
|
|
* This needs to be publicly visable since it's used by various effects.
|
|
*
|
|
* @return string A version string suitable for using in version_compare().
|
|
*/
|
|
public function getIMVersion()
|
|
{
|
|
static $version = null;
|
|
if (!is_array($version)) {
|
|
$commandline = $this->_convert . ' --version';
|
|
exec($commandline, $output, $retval);
|
|
if (preg_match('/([0-9])\.([0-9])\.([0-9])/', $output[0], $matches)) {
|
|
$version = $matches;
|
|
return $matches;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return $version;
|
|
}
|
|
|
|
public function addPostSrcOperation($operation)
|
|
{
|
|
$this->_postSrcOperations[] = $operation;
|
|
}
|
|
|
|
public function addOperation($operation)
|
|
{
|
|
$this->_operations[] = $operation;
|
|
}
|
|
|
|
public function addFileToClean($filename)
|
|
{
|
|
$this->_toClean[] = $filename;
|
|
}
|
|
|
|
public function getConvertPath()
|
|
{
|
|
return $this->_convert;
|
|
}
|
|
|
|
/**
|
|
* Reset the imagick iterator to the first image in the set.
|
|
*
|
|
* @return void
|
|
*/
|
|
public function rewind()
|
|
{
|
|
$this->_logDebug('Horde_Image_Im#rewind');
|
|
$this->_currentPage = 0;
|
|
}
|
|
|
|
/**
|
|
* Return the current image from the internal iterator.
|
|
*
|
|
* @return Horde_Image_Imagick
|
|
*/
|
|
public function current()
|
|
{
|
|
$this->_logDebug('Horde_Image_Im#current');
|
|
return $this->getImageAtIndex($this->_currentPage);
|
|
}
|
|
|
|
/**
|
|
* Get the index of the internal iterator.
|
|
*
|
|
* @return integer
|
|
*/
|
|
public function key()
|
|
{
|
|
$this->_logDebug('Horde_Image_Im#key');
|
|
return $this->_currentPage;
|
|
}
|
|
|
|
/**
|
|
* Advance the iterator
|
|
*
|
|
* @return Horde_Image_Im
|
|
*/
|
|
public function next()
|
|
{
|
|
$this->_logDebug('Horde_Image_Im#next');
|
|
$this->_currentPage++;
|
|
if ($this->valid()) {
|
|
return $this->getImageAtIndex($this->_currentPage);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Deterimines if the current iterator item is valid.
|
|
*
|
|
* @return boolean
|
|
*/
|
|
public function valid()
|
|
{
|
|
return $this->_currentPage < $this->getImagePageCount();
|
|
}
|
|
|
|
/**
|
|
* Request a specific image from the collection of images.
|
|
*
|
|
* @param integer $index The index to return
|
|
* @param boolean $flatten If true, flatten the returned image using
|
|
* $bgColor as the background color. Useful
|
|
* for PDF files to remove transparency before
|
|
* rendering.
|
|
* @param string $bgColor The background color to use when flattening the
|
|
* image.
|
|
* @return Horde_Image_Base
|
|
*/
|
|
public function getImageAtIndex($index, $flatten = false, $bgColor = 'white')
|
|
{
|
|
$this->_logDebug('Horde_Image_Im#getImageAtIndex: ' . $index);
|
|
|
|
if ($index > 0 && $index >= $this->getImagePageCount()) {
|
|
throw new Horde_Image_Exception('Image index out of bounds.');
|
|
}
|
|
$rawImage = $this->_raw(true, array('index' => $index, 'preserve_data' => true));
|
|
$image = new Horde_Image_Im(array('data' => $rawImage), $this->_context);
|
|
|
|
if ($flatten) {
|
|
$image->flattenImage($bgColor);
|
|
}
|
|
return $image;
|
|
}
|
|
|
|
/**
|
|
* Flatten this image and remove transparency.
|
|
*
|
|
* @param string $bgColor The background color to use.
|
|
*/
|
|
public function flattenImage($bgColor = 'white')
|
|
{
|
|
$this->_postSrcOperations[] = sprintf('-background %s -flatten', $bgColor);
|
|
$this->raw(false, array('stream' => true));
|
|
}
|
|
|
|
/**
|
|
* Return the number of image pages available in the image object.
|
|
*
|
|
* @return integer
|
|
*/
|
|
public function getImagePageCount()
|
|
{
|
|
if (is_null($this->_pages)) {
|
|
$this->_pages = $this->_getImagePages();
|
|
}
|
|
$this->_logDebug('Horde_Image_Im#getImagePageCount: ' . $this->_pages);
|
|
|
|
return $this->_pages;
|
|
|
|
}
|
|
|
|
private function _getImagePages()
|
|
{
|
|
$this->_logDebug('Horde_Image_Im#_getImagePages');
|
|
$filename = $this->toFile();
|
|
$cmd = $this->_identify . ' -format "%n " ' . $filename;
|
|
$this->_logDebug($cmd);
|
|
exec($cmd, $output, $retval);
|
|
if ($retval) {
|
|
$this->_logErr(sprintf("Error running command: %s", $cmd . "\n" . implode("\n", $output)));
|
|
}
|
|
$output = array_pop($output);
|
|
$output = substr($output, 0, strpos($output, ' ') + 1);
|
|
$this->_logDebug(print_r($output, true));
|
|
unlink($filename);
|
|
|
|
return $output;
|
|
}
|
|
}
|