* @package Image
*/
class Horde_Image_Effect_Gd_RoundCorners extends Horde_Image_Effect
{
/**
* Valid parameters:
*
* radius - Radius of rounded corners.
*
* @var array
*/
protected $_params = array('radius' => 10);
/**
* Apply the round_corners effect.
*
* @return boolean
*/
public function apply()
{
// Original comments from phpThumb projet:
// generate mask at twice desired resolution and downsample afterwards
// for easy antialiasing mask is generated as a white double-size
// elipse on a triple-size black background and copy-paste-resampled
// onto a correct-size mask image as 4 corners due to errors when the
// entire mask is resampled at once (gray edges)
$radius_x = $radius_y = $this->_params['radius'];
$gdimg = $this->_image->_im;
$imgX = round($this->_image->call('imageSX', array($gdimg)));
$imgY = round($this->_image->call('imageSY', array($gdimg)));
$gdimg_cornermask_triple = $this->_image->create(round($radius_x * 6), round($radius_y * 6));
$gdimg_cornermask = $this->_image->create($imgX, $imgY);
$color_transparent = $this->_image->call('imageColorAllocate',
array($gdimg_cornermask_triple,
255,
255,
255));
$this->_image->call('imageFilledEllipse',
array($gdimg_cornermask_triple,
$radius_x * 3,
$radius_y * 3,
$radius_x * 4,
$radius_y * 4,
$color_transparent));
$this->_image->call('imageFilledRectangle',
array($gdimg_cornermask,
0,
0,
$imgX,
$imgY,
$color_transparent));
$this->_image->call('imageCopyResampled',
array($gdimg_cornermask,
$gdimg_cornermask_triple,
0,
0,
$radius_x,
$radius_y,
$radius_x,
$radius_y,
$radius_x * 2,
$radius_y * 2));
$this->_image->call('imageCopyResampled',
array($gdimg_cornermask,
$gdimg_cornermask_triple,
0,
$imgY - $radius_y,
$radius_x,
$radius_y * 3,
$radius_x,
$radius_y,
$radius_x * 2,
$radius_y * 2));
$this->_image->call('imageCopyResampled',
array($gdimg_cornermask,
$gdimg_cornermask_triple,
$imgX - $radius_x,
$imgY - $radius_y,
$radius_x * 3,
$radius_y * 3,
$radius_x,
$radius_y,
$radius_x * 2,
$radius_y * 2));
$this->_image->call('imageCopyResampled',
array($gdimg_cornermask,
$gdimg_cornermask_triple,
$imgX - $radius_x,
0,
$radius_x * 3,
$radius_y,
$radius_x,
$radius_y,
$radius_x * 2,
$radius_y * 2));
$result = $this->_image->_applyMask($gdimg_cornermask);
$this->_image->call('imageDestroy', array($gdimg_cornermask));
$this->_image->call('imageDestroy', array($gdimg_cornermask_triple));
return true;
}
} Horde_Image-2.0.5/lib/Horde/Image/Effect/Gd/TextWatermark.php 0000664 0000766 0000024 00000007560 12170303167 020504 0 ustar
* @package Image
*/
class Horde_Image_Effect_Gd_TextWatermark extends Horde_Image_Effect
{
/**
* Valid parameters for watermark effects:
*
* text (required) - The text of the watermark.
* halign - The horizontal placement
* valign - The vertical placement
* font - The font name or family to use
* fontsize - The size of the font to use
* (small, medium, large, giant)
*
* @var array
*/
protected $_params = array('halign' => 'right',
'valign' => 'bottom',
'font' => 'courier',
'fontsize' => 'small');
/**
* Add the watermark
*/
public function apply()
{
$color = $this->_image->call('imageColorClosest', array($this->_image->_im, 255, 255, 255));
$shadow = $this->_image->call('imageColorClosest', array($this->_image->_im, 0, 0, 0));
// Shadow offset in pixels.
$drop = 1;
// Maximum text width.
$maxwidth = 200;
// Amount of space to leave between the text and the image border.
$padding = 10;
$f = $this->_image->getFont($this->_params['fontsize']);
$fontwidth = $this->_image->call('imageFontWidth', array($f));
$fontheight = $this->_image->call('imageFontHeight', array($f));
// So that shadow is not off the image with right align and bottom valign.
$margin = floor($padding + $drop) / 2;
if ($maxwidth) {
$maxcharsperline = floor(($maxwidth - ($margin * 2)) / $fontwidth);
$text = wordwrap($this->_params['text'], $maxcharsperline, "\n", 1);
}
// Split $text into individual lines.
$lines = explode("\n", $text);
switch ($this->_params['valign']) {
case 'center':
$y = ($this->_image->call('imageSY', array($this->_image->_im)) - ($fontheight * count($lines))) / 2;
break;
case 'bottom':
$y = $this->_image->call('imageSY', array($this->_image->_im)) - (($fontheight * count($lines)) + $margin);
break;
default:
$y = $margin;
break;
}
switch ($this->_params['halign']) {
case 'right':
foreach ($lines as $line) {
$this->_image->call('imageString', array($this->_image->_im, $f, ($this->_image->call('imageSX', array($this->_image->_im)) - $fontwidth * strlen($line)) - $margin + $drop, ($y + $drop), $line, $shadow));
$this->_image->call('imageString', array($this->_image->_im, $f, ($this->_image->call('imageSX', array($this->_image->_im)) - $fontwidth * strlen($line)) - $margin, $y, $line, $color));
$y += $fontheight;
}
break;
case 'center':
foreach ($lines as $line) {
$this->_image->call('imageString', array($this->_image->_im, $f, floor(($this->_image->call('imageSX', array($this->_image->_im)) - $fontwidth * strlen($line)) / 2) + $drop, ($y + $drop), $line, $shadow));
$this->_image->call('imageString', array($this->_image->_im, $f, floor(($this->_image->call('imageSX', array($this->_image->_im)) - $fontwidth * strlen($line)) / 2), $y, $line, $color));
$y += $fontheight;
}
break;
default:
foreach ($lines as $line) {
$this->_image->call('imageString', array($this->_image->_im, $f, $margin + $drop, ($y + $drop), $line, $shadow));
$this->_image->call('imageString', array($this->_image->_im, $f, $margin, $y, $line, $color));
$y += $fontheight;
}
break;
}
}
} Horde_Image-2.0.5/lib/Horde/Image/Effect/Gd/Unsharpmask.php 0000664 0000766 0000024 00000013122 12170303167 020165 0 ustar
* From: http://www.vikjavev.com/hovudsida/umtestside.php
*
* @package Image
*/
class Horde_Image_Effect_Gd_Unsharpmask extends Horde_Image_Effect
{
/**
* Valid parameters:
*
* @TODO
*
* @var array
*/
protected $_params = array('amount' => 0,
'radius' => 0,
'threshold' => 0);
/**
* Apply the unsharp_mask effect.
*
* @return mixed true
*/
public function apply()
{
$amount = $this->_params['amount'];
$radius = $this->_params['radius'];
$threshold = $this->_params['threshold'];
// Attempt to calibrate the parameters to Photoshop:
$amount = min($amount, 500);
$amount = $amount * 0.016;
if ($amount == 0) {
return true;
}
$radius = min($radius, 50);
$radius = $radius * 2;
$threshold = min($threshold, 255);
$radius = abs(round($radius)); // Only integers make sense.
if ($radius == 0) {
return true;
}
$img = $this->_image->_im;
$w = ImageSX($img);
$h = ImageSY($img);
$imgCanvas = ImageCreateTrueColor($w, $h);
$imgCanvas2 = ImageCreateTrueColor($w, $h);
$imgBlur = ImageCreateTrueColor($w, $h);
$imgBlur2 = ImageCreateTrueColor($w, $h);
ImageCopy($imgCanvas, $img, 0, 0, 0, 0, $w, $h);
ImageCopy($imgCanvas2, $img, 0, 0, 0, 0, $w, $h);
// Gaussian blur matrix:
//
// 1 2 1
// 2 4 2
// 1 2 1
//
//////////////////////////////////////////////////
// Move copies of the image around one pixel at the time and merge them with weight
// according to the matrix. The same matrix is simply repeated for higher radii.
for ($i = 0; $i < $radius; $i++) {
ImageCopy ($imgBlur, $imgCanvas, 0, 0, 1, 1, $w - 1, $h - 1); // up left
ImageCopyMerge($imgBlur, $imgCanvas, 1, 1, 0, 0, $w, $h, 50); // down right
ImageCopyMerge($imgBlur, $imgCanvas, 0, 1, 1, 0, $w - 1, $h, 33.33333); // down left
ImageCopyMerge($imgBlur, $imgCanvas, 1, 0, 0, 1, $w, $h - 1, 25); // up right
ImageCopyMerge($imgBlur, $imgCanvas, 0, 0, 1, 0, $w - 1, $h, 33.33333); // left
ImageCopyMerge($imgBlur, $imgCanvas, 1, 0, 0, 0, $w, $h, 25); // right
ImageCopyMerge($imgBlur, $imgCanvas, 0, 0, 0, 1, $w, $h - 1, 20 ); // up
ImageCopyMerge($imgBlur, $imgCanvas, 0, 1, 0, 0, $w, $h, 16.666667); // down
ImageCopyMerge($imgBlur, $imgCanvas, 0, 0, 0, 0, $w, $h, 50); // center
ImageCopy ($imgCanvas, $imgBlur, 0, 0, 0, 0, $w, $h);
// During the loop above the blurred copy darkens, possibly due to a roundoff
// error. Therefore the sharp picture has to go through the same loop to
// produce a similar image for comparison. This is not a good thing, as processing
// time increases heavily.
ImageCopy ($imgBlur2, $imgCanvas2, 0, 0, 0, 0, $w, $h);
ImageCopyMerge($imgBlur2, $imgCanvas2, 0, 0, 0, 0, $w, $h, 50);
ImageCopyMerge($imgBlur2, $imgCanvas2, 0, 0, 0, 0, $w, $h, 33.33333);
ImageCopyMerge($imgBlur2, $imgCanvas2, 0, 0, 0, 0, $w, $h, 25);
ImageCopyMerge($imgBlur2, $imgCanvas2, 0, 0, 0, 0, $w, $h, 33.33333);
ImageCopyMerge($imgBlur2, $imgCanvas2, 0, 0, 0, 0, $w, $h, 25);
ImageCopyMerge($imgBlur2, $imgCanvas2, 0, 0, 0, 0, $w, $h, 20 );
ImageCopyMerge($imgBlur2, $imgCanvas2, 0, 0, 0, 0, $w, $h, 16.666667);
ImageCopyMerge($imgBlur2, $imgCanvas2, 0, 0, 0, 0, $w, $h, 50);
ImageCopy ($imgCanvas2, $imgBlur2, 0, 0, 0, 0, $w, $h);
}
// Calculate the difference between the blurred pixels and the original
// and set the pixels
for ($x = 0; $x < $w; $x++) { // each row
for ($y = 0; $y < $h; $y++) { // each pixel
$rgbOrig = ImageColorAt($imgCanvas2, $x, $y);
$rOrig = (($rgbOrig >> 16) & 0xFF);
$gOrig = (($rgbOrig >> 8) & 0xFF);
$bOrig = ($rgbOrig & 0xFF);
$rgbBlur = ImageColorAt($imgCanvas, $x, $y);
$rBlur = (($rgbBlur >> 16) & 0xFF);
$gBlur = (($rgbBlur >> 8) & 0xFF);
$bBlur = ($rgbBlur & 0xFF);
// When the masked pixels differ less from the original
// than the threshold specifies, they are set to their original value.
$rNew = (abs($rOrig - $rBlur) >= $threshold) ? max(0, min(255, ($amount * ($rOrig - $rBlur)) + $rOrig)) : $rOrig;
$gNew = (abs($gOrig - $gBlur) >= $threshold) ? max(0, min(255, ($amount * ($gOrig - $gBlur)) + $gOrig)) : $gOrig;
$bNew = (abs($bOrig - $bBlur) >= $threshold) ? max(0, min(255, ($amount * ($bOrig - $bBlur)) + $bOrig)) : $bOrig;
if (($rOrig != $rNew) || ($gOrig != $gNew) || ($bOrig != $bNew)) {
$pixCol = ImageColorAllocate($img, $rNew, $gNew, $bNew);
ImageSetPixel($img, $x, $y, $pixCol);
}
}
}
ImageDestroy($imgCanvas);
ImageDestroy($imgCanvas2);
ImageDestroy($imgBlur);
ImageDestroy($imgBlur2);
return true;
}
}
Horde_Image-2.0.5/lib/Horde/Image/Effect/Im/Border.php 0000775 0000766 0000024 00000002264 12170303167 017131 0 ustar
* @package Image
*/
class Horde_Image_Effect_Im_Border extends Horde_Image_Effect
{
/**
* Valid parameters for border effects:
*
* bordercolor - Border color. Defaults to black.
* borderwidth - Border thickness, defaults to 1 pixel.
* preserve - Preserves the alpha transparency layer (if present)
*
* @var array
*/
protected $_params = array('bordercolor' => 'black',
'borderwidth' => 1,
'preserve' => true);
/**
* Draw the border.
*
* This draws the configured border to the provided image. Beware,
* that every pixel inside the border clipping will be overwritten
* with the background color.
*/
public function apply()
{
$this->_image->addPostSrcOperation(sprintf(
"-bordercolor \"%s\" %s -border %s",
$this->_params['bordercolor'],
(!empty($this->_params['preserve']) ? '-compose Copy' : ''),
$this->_params['borderwidth']));
return true;
}
} Horde_Image-2.0.5/lib/Horde/Image/Effect/Im/CenterCrop.php 0000664 0000766 0000024 00000002520 12170303167 017750 0 ustar
* @package Image
*/
class Horde_Image_Effect_Im_CenterCrop extends Horde_Image_Effect
{
/**
* Valid parameters:
*
* width - Target width
* height - Target height
*
*
* @var array
*/
protected $_params = array();
public function apply()
{
$this->_params = new Horde_Support_Array($this->_params);
$ver = $this->_image->getIMVersion();
if (is_array($ver) && version_compare($ver[0], '6.3.8') < 0) {
$initialCrop = $this->_params->width * 2;
$command = "-resize x{$initialCrop} -resize '{$initialCrop}x<' -resize 50% -gravity center -crop {$this->_params->width}x{$this->_params->height}+0+0 +repage";
} else {
$command = "-thumbnail {$this->_params->width}x{$this->_params->height}\^ -gravity center -extent {$this->_params->width}x{$this->_params->height}";
}
$this->_image->addPostSrcOperation($command);
$this->_image->clearGeometry();
return;
}
} Horde_Image-2.0.5/lib/Horde/Image/Effect/Im/Composite.php 0000775 0000766 0000024 00000004057 12170303167 017660 0 ustar
* @package Image
*/
class Horde_Image_Effect_Im_Composite extends Horde_Image_Effect
{
/**
* Valid parameters for border effects:
*
* 'images' - an array of Horde_Image objects to overlay.
*
* ...and ONE of the following. If both are provided, the behaviour is
* undefined.
*
* 'gravity' - the ImageMagick gravity constant describing placement
* (IM driver only so far, not imagick)
*
* 'x' and 'y' - coordinates for the overlay placement.
*
* @var array
*/
protected $_params = array();
/**
* Draw the border.
*
* This draws the configured border to the provided image. Beware,
* that every pixel inside the border clipping will be overwritten
* with the background color.
*/
public function apply()
{
$ops = $geometry = $gravity = '';
if (isset($this->_params['gravity'])) {
$gravity = ' -gravity ' . $this->_params['gravity'];
}
if (isset($this->_params['x']) && isset($this->_params['y'])) {
$geometry = ' -geometry +' . $this->_params['x'] . '+' . $this->_params['y'] . ' ';
}
if (isset($this->_params['compose'])) {
// The -matte ensures that the destination (background) image
// has an alpha channel - to avoid black holes in the image.
$compose = ' -compose ' . $this->_params['compose'] . ' -matte';
}
foreach($this->_params['images'] as $image) {
$temp = $image->toFile();
$this->_image->addFileToClean($temp);
$ops .= ' ' . $temp . $gravity . $compose . ' -composite';
}
$this->_image->addOperation($geometry);
$this->_image->addPostSrcOperation($ops);
return true;
}
} Horde_Image-2.0.5/lib/Horde/Image/Effect/Im/DropShadow.php 0000664 0000766 0000024 00000002510 12170303167 017755 0 ustar
* @package Image
*/
class Horde_Image_Effect_Im_DropShadow extends Horde_Image_Effect
{
/**
* Valid parameters: Most are currently ignored for the im version
* of this effect.
*
* @TODO
*
* @var array
*/
protected $_params = array('distance' => 5, // This is used as the x and y offset
'width' => 2,
'hexcolor' => '000000',
'angle' => 215,
'fade' => 3, // Sigma value
'padding' => 0,
'background' => 'none');
/**
* Apply the effect.
*
* @return mixed true
*/
public function apply()
{
$size = $this->_image->getDimensions();
$this->_image->addPostSrcOperation('\( +clone -background black -shadow 80x' . $this->_params['fade'] . '+' . $this->_params['distance'] . '+' . $this->_params['distance'] . ' \) +swap -background none -flatten +repage -bordercolor ' . $this->_params['background'] . ' -border ' . $this->_params['padding']);
$this->_image->clearGeometry();
return true;
}
} Horde_Image-2.0.5/lib/Horde/Image/Effect/Im/LiquidResize.php 0000664 0000766 0000024 00000002443 12170303167 020321 0 ustar
* @package Image
*/
class Horde_Image_Effect_Im_LiquidResize extends Horde_Image_Effect
{
/**
* Valid parameters:
*
* width - The target width
* height - the target height
* ratio - Keep aspect ratio
*
*
* @var array
*/
protected $_params = array();
public function apply()
{
$this->_params = new Horde_Support_Array($this->_params);
$resWidth = $this->_params->width * 2;
$resHeight = $this->_params->height * 2;
$this->_image->addOperation("-size {$resWidth}x{$resHeight}");
if ($this->_params->get('ratio', true)) {
$this->_image->addPostSrcOperation('-liquid-rescale' . " {$this->_params->width}x{$this->_params->height}");
} else {
$this->_image->addPostSrcOperation('-liquid-rescale' . " {$this->_params->width}x{$this->_params->height}!");
}
$this->_image->clearGeometry();
}
} Horde_Image-2.0.5/lib/Horde/Image/Effect/Im/PhotoStack.php 0000664 0000766 0000024 00000017024 12170303167 017770 0 ustar
* @package Image
*/
class Horde_Image_Effect_Im_PhotoStack extends Horde_Image_Effect
{
/**
* Valid parameters for the stack effect
*
* images - An array of Horde_Image objects to stack. Images
* are stacked in a FIFO manner, so that the top-most
* image is the last one in this array.
*
* type - Determines the style for the composition.
* 'plain' or 'polaroid' are supported.
*
* resize_height - The height that each individual thumbnail
* should be resized to before composing on the image.
*
* padding - How much padding should we ensure is left around
* the active image area?
*
* background - The background canvas color - this is used as the
* color to set any padding to.
*
* bordercolor - If using type 'plain' this sets the color of the
* border that each individual thumbnail gets.
*
* borderwidth - If using type 'plain' this sets the width of the
* border on each individual thumbnail.
*
* offset - If using type 'plain' this determines the amount of
* x and y offset to give each successive image when
* it is placed on the top of the stack.
*
* @var array
*/
protected $_params = array('type' => 'plain',
'resize_height' => '150',
'padding' => 0,
'background' => 'none',
'bordercolor' => '#333',
'borderwidth' => 1,
'borderrounding' => 10,
'offset' => 5);
/**
* Create the photo_stack
*
*/
public function apply()
{
$i = 1;
$cnt = count($this->_params['images']);
if ($cnt <=0) {
throw new Horde_Image_Exception('No Images provided.');
}
// Start out fresh.
$this->_image->raw();
switch ($this->_params['type']) {
case 'plain':
case 'rounded':
// Get top image dimensions, then force each bottom image to the
// same dimensions.
$this->_params['images'][$cnt - 1]->resize($this->_params['resize_height'],
$this->_params['resize_height'],
true);
$size = $this->_params['images'][$cnt - 1]->getDimensions();
$xo = $yo = (count($this->_params['images'])) * $this->_params['offset'];
$ops = '';
$haveBottom = false;
foreach ($this->_params['images'] as $image) {
$image->resize($size['height'], $size['width'], false);
$xo -= $this->_params['offset'];
$yo -= $this->_params['offset'];
if ($this->_params['type'] == 'rounded') {
$temp = $this->_roundBorder($image);
} else {
$temp = $image->toFile();
}
$this->_image->addFileToClean($temp);
$ops .= ' \( ' . $temp . ' -background none -thumbnail ' . $size['width'] . 'x' . $size['height'] . '! -repage +' . $xo . '+' . $yo . ($this->_params['type'] == 'plain' ? ' -bordercolor "#333" -border 1 ' : ' ' ) . ((!$haveBottom) ? '\( +clone -shadow 80x3+4+4 \) +swap -mosaic' : '') . ' \) ';
$haveBottom = true;
}
// The first -background none option below is only honored in
// convert versions before 6.4 it seems. Without it specified as
// none here, all stacks come out with a white background.
$this->_image->addPostSrcOperation($ops . ' -background ' . $this->_params['background'] . ' -mosaic -bordercolor ' . $this->_params['background'] . ' -border ' . $this->_params['padding']);
break;
case 'polaroid':
// Check for im version > 6.3.2
$ver = $this->_image->getIMVersion();
if (is_array($ver) && version_compare($ver[0], '6.3.2') >= 0) {
$ops = '';
foreach ($this->_params['images'] as $image) {
$temp = $image->toFile();
// Remember the temp files so we can nuke them later.
$this->_image->addFileToClean($temp);
// Don't rotate the top image.
if ($i++ == $cnt) {
$angle = 0;
} else {
$angle = mt_rand(1, 45);
if (mt_rand(1, 2) % 2 === 0) {
$angle = $angle * -1;
}
}
$ops .= ' \( ' . $temp . ' -geometry +' . mt_rand(1, $this->_params['resize_height']) . '+' . mt_rand(1, $this->_params['resize_height']) . ' -thumbnail \'' . $this->_params['resize_height'] . 'x' . $this->_params['resize_height'] . '>\' -bordercolor Snow -border 1 -polaroid ' . $angle . ' \) ';
}
$this->_image->addPostSrcOperation('-background none' . $ops . '-mosaic -bordercolor ' . $this->_params['background'] . ' -border ' . $this->_params['padding']);
} else {
// An attempt at a -polaroid command free version of this
// effect based on various examples and ideas at
// http://imagemagick.org
$ops = '';
foreach ($this->_params['images'] as $image) {
$temp = $image->toFile();
$this->_image->addFileToClean($temp);
if ($i++ == $cnt) {
$angle = 0;
} else {
$angle = mt_rand(1, 45);
if (mt_rand(1, 2) % 2 === 0) {
$angle = $angle * -1;
}
}
$ops .= '\( ' . $temp . ' -thumbnail \'' . $this->_params['resize_height'] . 'x' . $this->_params['resize_height']. '>\' -bordercolor "#eee" -border 4 -bordercolor grey90 -border 1 -bordercolor none -background none -rotate ' . $angle . ' -background none \( +clone -shadow 60x4+4+4 \) +swap -background none -flatten \) ';
}
$this->_image->addPostSrcOperation('-background none ' . $ops . '-mosaic -trim +repage -bordercolor ' . $this->_params['background'] . ' -border ' . $this->_params['padding']);
}
break;
}
return true;
}
private function _roundBorder($image)
{
$context = array('tmpdir' => $this->_image->getTmpDir(),
'convert' => $this->_image->getConvertPath());
$size = $image->getDimensions();
$new = new Horde_Image_Im(array('data' => $image->raw()), $context);
$new->addEffect('RoundCorners', array('border' => 2, 'bordercolor' => '#111', 'background' => 'none'));
$new->applyEffects();
return $new->toFile();
}
} Horde_Image-2.0.5/lib/Horde/Image/Effect/Im/PolaroidImage.php 0000775 0000766 0000024 00000004015 12170303167 020424 0 ustar
* @package Image
*/
class Horde_Image_Effect_Im_PolaroidImage extends Horde_Image_Effect
{
/**
* Valid parameters for the polaroid effect
*
* resize_height - The height that each individual thumbnail
* should be resized to before composing on the image.
*
* background - The color of the image background.
*
* angle - Angle to rotate the image.
*
* shadowcolor - The color of the image shadow.
*/
/**
* @var array
*/
protected $_params = array('background' => 'none',
'angle' => 0,
'shadowcolor' => 'black');
/**
* Create the effect
*
*/
public function apply()
{
// Check for im version > 6.3.2
$this->_image->_imagick = null;
$ver = $this->_image->getIMVersion();
if (is_array($ver) && version_compare($ver[0], '6.3.2') >= 0) {
$this->_image->addPostSrcOperation(sprintf("-bordercolor \"#eee\" -background none -polaroid %s \( +clone -fill %s -draw 'color 0,0 reset' \) +swap +flatten",
$this->_params['angle'], $this->_params['background']));
} else {
$size = $this->_image->getDimensions();
$this->_image->addPostSrcOperation(sprintf("-bordercolor \"#eee\" -border 8 -bordercolor grey90 -border 1 -bordercolor none -background none -rotate %s \( +clone -shadow 60x1.5+1+1 -rotate 90 -wave 1x%s -rotate 90 \) +swap -rotate 90 -wave 1x%s -rotate -90 -flatten \( +clone -fill %s -draw 'color 0,0 reset ' \) +swap -flatten",
$this->_params['angle'], $size['height'] * 2, $size['height'] * 2, $this->_params['background']));
}
return true;
}
} Horde_Image-2.0.5/lib/Horde/Image/Effect/Im/RoundCorners.php 0000664 0000766 0000024 00000003032 12170303167 020326 0 ustar
* @package Image
*/
class Horde_Image_Effect_Im_RoundCorners extends Horde_Image_Effect
{
/**
* Valid parameters:
*
* radius - Radius of rounded corners.
*
* @var array
*/
protected $_params = array('radius' => 10,
'background' => 'none',
'border' => 0,
'bordercolor' => 'none');
public function apply()
{
/* Use imagick extension if available */
$round = $this->_params['radius'];
// Get image dimensions
$dimensions = $this->_image->getDimensions();
$height = $dimensions['height'];
$width = $dimensions['width'];
$this->_image->addOperation("-size {$width}x{$height} xc:{$this->_params['background']} "
. "-fill {$this->_params['background']} -draw \"matte 0,0 reset\" -tile");
$this->_image->roundedRectangle(round($round / 2),
round($round / 2),
$width - round($round / 2) - 2,
$height - round($round / 2) - 2,
$round + 2,
'none',
'white');
// Reset width/height since these might have changed
$this->_image->clearGeometry();
return true;
}
} Horde_Image-2.0.5/lib/Horde/Image/Effect/Im/TextWatermark.php 0000664 0000766 0000024 00000004331 12170303167 020510 0 ustar
* @package Image
*/
class Horde_Image_Effect_Im_TextWatermark extends Horde_Image_Effect
{
/**
* Valid parameters for watermark effects:
*
* text (required) - The text of the watermark.
* halign - The horizontal placement
* valign - The vertical placement
* font - The font name or family to use
* fontsize - The size of the font to use
* (small, medium, large, giant)
*
* @var array
*/
protected $_params = array('halign' => 'right',
'valign' => 'bottom',
'font' => 'courier',
'fontsize' => 'small');
/**
* Add the watermark
*
*/
public function apply()
{
/* Determine placement on image */
switch ($this->_params['valign']) {
case 'bottom':
$v = 'south';
break;
case 'center':
$v = 'center';
break;
default:
$v = 'north';
}
switch ($this->_params['halign']) {
case 'right':
$h = 'east';
break;
case 'center':
$h = 'center';
break;
default:
$h = 'west';
}
if (($v == 'center' && $h != 'center') ||
($v == 'center' && $h == 'center')) {
$gravity = $h;
} elseif ($h == 'center' && $v != 'center') {
$gravity = $v;
} else {
$gravity = $v . $h;
}
/* Determine font point size */
$point = Horde_Image::getFontSize($this->_params['fontsize']);
$this->_image->raw();
$this->_image->addPostSrcOperation(' -font ' . $this->_params['font'] . ' -pointsize ' . $point . ' \( +clone -resize 1x1 -fx 1-intensity -threshold 50% -scale 32x32 -write mpr:color +delete \) -tile mpr:color -gravity ' . $gravity . ' -annotate +20+10 "' . $this->_params['text'] . '"');
$this->_image->raw();
}
}
Horde_Image-2.0.5/lib/Horde/Image/Effect/Im/Unsharpmask.php 0000664 0000766 0000024 00000004463 12170303167 020210 0 ustar
* @package Image
*/
class Horde_Image_Effect_Im_Unsharpmask extends Horde_Image_Effect
{
/**
*
* Valid parameters:
*
* (float)radius - Thickness of the sharpened edge. Should be greater then
* sigma (or 0, and imagick will attempt to auto choose).
* In general, radius should be roughly output dpi / 150.
* So for display purposes a radius of 0.5 is suggested.
*
* (float)amount - Amount of the difference between original and the
* blur image that gets added back to the original. Can be
* thought of as the "strength" of the effect. Too high
* may cause blocking of shadows and highlights. Given
* a decimal value indicating percentage, e.g. 1.2 = 120%
*
* (float)threshold - Determines how large the brightness delta between
* adjacent pixels needs to be to sharpen the edge.
* Larger values == less sharpening. Useful for
* preventing noisy images from being oversharpened.
*
* (integer)channel - Which channel to apply the sharpening to.
*
* @var array
*/
protected $_params = array('radius' => 0.5,
'amount' => 1,
'threshold' => 0.05);
public function apply()
{
/* Calculate appropriate sigma:
* Determines how the sharpening is graduated away from
* the center pixel of the sharpened edge. In general,
* if radius < 1, then sigma = radius else sigma = sqrt(radius)
*/
$this->_params['sigma'] = ($this->_params['radius'] < 1) ?
$this->_params['radius'] : sqrt($this->_params['radius']);
$this->_image->addPostSrcOperation("-unsharp {$this->_params['radius']}x{$this->_params['sigma']}+{$this->_params['amount']}+{$this->_params['threshold']}");
return true;
}
} Horde_Image-2.0.5/lib/Horde/Image/Effect/Imagick/Border.php 0000775 0000766 0000024 00000002762 12170303167 020133 0 ustar
* @package Image
*/
class Horde_Image_Effect_Imagick_Border extends Horde_Image_Effect
{
/**
* Valid parameters for border effects:
*
* bordercolor - Border color. Defaults to black.
* borderwidth - Border thickness, defaults to 1 pixel.
* preserve - Preserves the alpha transparency layer (if present)
*
* @var array
*/
protected $_params = array('bordercolor' => 'black',
'borderwidth' => 1,
'preserve' => true);
/**
* Draw the border.
*
* This draws the configured border to the provided image. Beware,
* that every pixel inside the border clipping will be overwritten
* with the background color.
*/
public function apply()
{
if ($this->_params['preserve']) {
Horde_Image_Imagick::frameImage($this->_image->imagick,
$this->_params['bordercolor'],
$this->_params['borderwidth'],
$this->_params['borderwidth']);
} else {
$this->_image->imagick->borderImage(
new ImagickPixel($this->_params['bordercolor']),
$this->_params['borderwidth'],
$this->_params['borderwidth']);
}
return true;
}
} Horde_Image-2.0.5/lib/Horde/Image/Effect/Imagick/CenterCrop.php 0000664 0000766 0000024 00000001600 12170303167 020745 0 ustar
* @package Image
*/
class Horde_Image_Effect_Imagick_CenterCrop extends Horde_Image_Effect
{
/**
* Valid parameters:
*
* width - Target width
* height - Target height
*
*
* @var array
*/
protected $_params = array();
public function apply()
{
$this->_params = new Horde_Support_Array($this->_params);
$this->_image->imagick->cropThumbnailImage($this->_params->width, $this->_params->height);
$this->_image->clearGeometry();
return;
}
} Horde_Image-2.0.5/lib/Horde/Image/Effect/Imagick/Composite.php 0000775 0000766 0000024 00000003435 12170303167 020656 0 ustar
* @package Image
*/
class Horde_Image_Effect_Imagick_Composite extends Horde_Image_Effect
{
/**
* Valid parameters for border effects:
*
* 'images' - an array of Horde_Image objects to overlay.
*
* ...and ONE of the following. If both are provided, the behaviour is
* undefined.
*
* 'gravity' - the ImageMagick gravity constant describing placement
* (IM driver only so far, not imagick)
*
* 'x' and 'y' - coordinates for the overlay placement.
*
* @var array
*/
protected $_params = array();
/**
* Draw the border.
*
* This draws the configured border to the provided image. Beware,
* that every pixel inside the border clipping will be overwritten
* with the background color.
*/
public function apply()
{
foreach ($this->_params['images'] as $image) {
$topimg = new Imagick();
$topimg->clear();
$topimg->readImageBlob($image->raw());
/* Calculate center for composite (gravity center)*/
$geometry = $this->_image->imagick->getImageGeometry();
$x = $geometry['width'] / 2;
$y = $geometry['height'] / 2;
if (isset($this->_params['x']) && isset($this->_params['y'])) {
$x = $this->_params['x'];
$y = $this->_params['y'];
}
$this->_image->_imagick->compositeImage($topimg, Imagick::COMPOSITE_OVER, $x, $y);
}
return true;
}
} Horde_Image-2.0.5/lib/Horde/Image/Effect/Imagick/DropShadow.php 0000664 0000766 0000024 00000004100 12170303167 020751 0 ustar
* @package Image
*/
class Horde_Image_Effect_Imagick_DropShadow extends Horde_Image_Effect
{
/**
* Valid parameters: Most are currently ignored for the im version
* of this effect.
*
* @TODO
*
* @var array
*/
protected $_params = array('distance' => 5, // This is used as the x and y offset
'width' => 2,
'hexcolor' => '000000',
'angle' => 215,
'fade' => 3, // Sigma value
'padding' => 0,
'background' => 'none');
/**
* Apply the effect.
*
* @return boolean
*/
public function apply()
{
// There is what *I* call a bug in the magickwand interface of Im that
// Imagick is compiled against. The X and Y parameters are ignored, and
// the distance of the shadow is determined *solely* by the sigma value
// which makes it pretty much impossible to have Imagick shadows look
// identical to Im shadows...
$shadow = $this->_image->imagick->clone();
$shadow->setImageBackgroundColor(new ImagickPixel('black'));
$shadow->shadowImage(80, $this->_params['fade'],
$this->_params['distance'],
$this->_params['distance']);
$shadow->compositeImage($this->_image->imagick, Imagick::COMPOSITE_OVER, 0, 0);
if ($this->_params['padding']) {
$shadow->borderImage($this->_params['background'],
$this->_params['padding'],
$this->_params['padding']);
}
$this->_image->imagick->clear();
$this->_image->imagick->addImage($shadow);
$shadow->destroy();
$this->_image->clearGeometry();
return true;
}
} Horde_Image-2.0.5/lib/Horde/Image/Effect/Imagick/LiquidResize.php 0000664 0000766 0000024 00000003011 12170303167 021310 0 ustar
* @package Image
*/
class Horde_Image_Effect_Imagick_LiquidResize extends Horde_Image_Effect
{
/**
*
* Valid parameters:
*
* width - The target width
* height - the target height
* delta_x - How much the seam may move on x axis (A value of 0
* causes the seam to be straight).
* rigidity - Introduces a bias for non-straight seams. Typically zero
*
*
* @var array
*/
protected $_params = array();
public function apply()
{
$this->_params = new Horde_Support_Array($this->_params);
try {
// Only supported if ImageMagick is compiled against lqr library.
if (method_exists($this->_image->imagick, 'liquidRescaleImage')) {
$this->_image->imagick->liquidRescaleImage(
$this->_params->width, $this->_params->height, $this->_params->delta_x, $this->_params->rigidity);
} else {
throw new Horde_Image_Exception('Missing support for lqr in ImageMagick.');
}
} catch (ImagickException $e) {
throw new Horde_Image_Exception($e);
}
}
} Horde_Image-2.0.5/lib/Horde/Image/Effect/Imagick/PhotoStack.php 0000664 0000766 0000024 00000021452 12170303167 020767 0 ustar
* @package Image
*/
class Horde_Image_Effect_Imagick_PhotoStack extends Horde_Image_Effect
{
/**
* Valid parameters for the stack effect
*
* images - An array of Horde_Image objects to stack. Images
* are stacked in a FIFO manner, so that the top-most
* image is the last one in this array.
*
* type - Determines the style for the composition.
* 'plain' or 'polaroid' are supported.
*
* resize_height - The height that each individual thumbnail
* should be resized to before composing on the image.
*
* padding - How much padding should we ensure is left around
* the active image area?
*
* background - The background canvas color - this is used as the
* color to set any padding to.
*
* bordercolor - If using type 'plain' this sets the color of the
* border that each individual thumbnail gets.
*
* borderwidth - If using type 'plain' this sets the width of the
* border on each individual thumbnail.
*
* offset - If using type 'plain' this determines the amount of
* x and y offset to give each successive image when
* it is placed on the top of the stack.
*
* @var array
*/
protected $_params = array('type' => 'plain',
'resize_height' => '150',
'padding' => 0,
'background' => 'none',
'bordercolor' => '#333',
'borderwidth' => 1,
'borderrounding' => 10,
'offset' => 5);
/**
* Create the photo_stack
*
*/
public function apply()
{
$i = 1;
$cnt = count($this->_params['images']);
if ($cnt <=0) {
throw new Horde_Image_Exception('No Images provided.');
}
if (!method_exists($this->_image->imagick, 'polaroidImage') ||
!method_exists($this->_image->imagick, 'trimImage')) {
throw new Horde_Image_Exception('Your version of Imagick is not compiled against a recent enough ImageMagick library to use the PhotoStack effect.');
}
$imgs = array();
$length = 0;
switch ($this->_params['type']) {
case 'plain':
case 'rounded':
$haveBottom = false;
// First, we need to resize the top image to get the dimensions
// for the rest of the stack.
$topimg = new Imagick();
$topimg->clear();
$topimg->readImageBlob($this->_params['images'][$cnt - 1]->raw());
$topimg->thumbnailImage(
$this->_params['resize_height'],
$this->_params['resize_height'],
true);
if ($this->_params['type'] == 'rounded') {
$topimg = $this->_roundBorder($topimg);
}
$size = $topimg->getImageGeometry();
foreach ($this->_params['images'] as $image) {
$imgk= new Imagick();
$imgk->clear();
$imgk->readImageBlob($image->raw());
// Either resize the thumbnail to match the top image or we *are*
// the top image already.
if ($i++ <= $cnt) {
$imgk->thumbnailImage($size['width'], $size['height'], false);
} else {
$imgk->destroy();
$imgk = $topimg->clone();
}
if ($this->_params['type'] == 'rounded') {
$imgk = $this->_roundBorder($imgk);
} else {
$imgk->borderImage($this->_params['bordercolor'],
$this->_params['borderwidth'],
$this->_params['borderwidth']);
}
// Only shadow the bottom image for 'plain' stacks
if (!$haveBottom) {
$shad = $imgk->clone();
$shad->setImageBackgroundColor(new ImagickPixel('black'));
$shad->shadowImage(80, 4, 0, 0);
$shad->compositeImage($imgk, Imagick::COMPOSITE_OVER, 0, 0);
$imgk->clear();
$imgk->addImage($shad);
$shad->destroy();
$haveBottom = true;
}
// Get the geometry of the image and remember the largest.
$geo = $imgk->getImageGeometry();
$length = max(
$length,
sqrt(pow($geo['height'], 2) + pow($geo['width'], 2)));
$imgs[] = $imgk;
}
break;
case 'polaroid':
foreach ($this->_params['images'] as $image) {
//@TODO: instead of doing $image->raw(), we might be able to clone
// the imagick object if we can do it cleanly might
// be faster, less memory intensive?
$imgk= new Imagick();
$imgk->clear();
$imgk->readImageBlob($image->raw());
$imgk->thumbnailImage($this->_params['resize_height'],
$this->_params['resize_height'],
true);
$imgk->setImageBackgroundColor('black');
if ($i++ == $cnt) {
$angle = 0;
} else {
$angle = mt_rand(1, 45);
if (mt_rand(1, 2) % 2 === 0) {
$angle = $angle * -1;
}
}
$result = $imgk->polaroidImage(new ImagickDraw(), $angle);
// Get the geometry of the image and remember the largest.
$geo = $imgk->getImageGeometry();
$length = max(
$length,
sqrt(pow($geo['height'], 2) + pow($geo['width'], 2)));
$imgs[] = $imgk;
}
break;
}
// Make sure the background canvas is large enough to hold it all.
$this->_image->imagick->thumbnailImage($length * 1.5 + 20,
$length * 1.5 + 20);
// x and y offsets.
$xo = $yo = (count($imgs) + 1) * $this->_params['offset'];
foreach ($imgs as $image) {
if ($this->_params['type'] == 'polaroid') {
$xo = mt_rand(1, $this->_params['resize_height'] / 2);
$yo = mt_rand(1, $this->_params['resize_height'] / 2);
} elseif ($this->_params['type'] == 'plain' ||
$this->_params['type'] == 'rounded') {
$xo -= $this->_params['offset'];
$yo -= $this->_params['offset'];
}
$this->_image->imagick->compositeImage($image, Imagick::COMPOSITE_OVER, $xo, $yo);
$image->removeImage();
$image->destroy();
}
// Trim the canvas before resizing to keep the thumbnails as large
// as possible.
$this->_image->imagick->trimImage(0);
if ($this->_params['padding'] || $this->_params['background'] != 'none') {
$this->_image->imagick->borderImage(
new ImagickPixel($this->_params['background']),
$this->_params['padding'],
$this->_params['padding']);
}
return true;
}
private function _roundBorder($image)
{
$context = array('tmpdir' => $this->_image->getTmpDir());
$size = $image->getImageGeometry();
$new = new Horde_Image_Imagick(array(), $context);
$new->loadString($image->getImageBlob());
$image->destroy();
$new->addEffect('RoundCorners', array('border' => 2, 'bordercolor' => '#111'));
$new->applyEffects();
$return = new Imagick();
$return->newImage($size['width'] + $this->_params['borderwidth'],
$size['height'] + $this->_params['borderwidth'],
$this->_params['bordercolor']);
$return->setImageFormat($this->_image->getType());
$return->clear();
$return->readImageBlob($new->raw());
return $return;
}
} Horde_Image-2.0.5/lib/Horde/Image/Effect/Imagick/PolaroidImage.php 0000775 0000766 0000024 00000004150 12170303167 021423 0 ustar
* @package Image
*/
class Horde_Image_Effect_Imagick_PolaroidImage extends Horde_Image_Effect
{
/**
* Valid parameters for the polaroid effect
*
* resize_height - The height that each individual thumbnail
* should be resized to before composing on the image.
*
* background - The color of the image background.
*
* angle - Angle to rotate the image.
*
* shadowcolor - The color of the image shadow.
*/
/**
* @var array
*/
protected $_params = array('background' => 'none',
'angle' => 0,
'shadowcolor' => 'black');
/**
* Create the effect
*
*/
public function apply()
{
if (!method_exists($this->_image->imagick, 'polaroidImage') ||
!method_exists($this->_image->imagick, 'trimImage')) {
throw new Horde_Image_Exception('Your version of Imagick is not compiled against a recent enough ImageMagick library to use the PolaroidImage effect.');
}
// This determines the color of the underlying shadow.
$this->_image->imagick->setImageBackgroundColor(new ImagickPixel($this->_params['shadowcolor']));
$this->_image->imagick->polaroidImage(new ImagickDraw(), $this->_params['angle']);
// We need to create a new image to composite the polaroid over.
// (yes, even if it's a transparent background evidently)
$size = $this->_image->getDimensions();
$imk = new Imagick();
$imk->newImage($size['width'], $size['height'], $this->_params['background']);
$imk->setImageFormat($this->_image->getType());
$result = $imk->compositeImage($this->_image->imagick, Imagick::COMPOSITE_OVER, 0, 0);
$this->_image->imagick->clear();
$this->_image->imagick->addImage($imk);
$imk->destroy();
return true;
}
} Horde_Image-2.0.5/lib/Horde/Image/Effect/Imagick/RoundCorners.php 0000664 0000766 0000024 00000005250 12170303167 021331 0 ustar
* @package Image
*/
class Horde_Image_Effect_Imagick_RoundCorners extends Horde_Image_Effect
{
/**
* Valid parameters:
*
* radius - Radius of rounded corners.
*
* @var array
*/
protected $_params = array('radius' => 10,
'background' => 'none',
'border' => 0,
'bordercolor' => 'none');
public function apply()
{
if (!method_exists($this->_image->imagick, 'roundCorners')) {
throw new Horde_Image_Exception('Your version of Imagick is not compiled against a recent enough ImageMagick library (> 6.2.8) to use the RoundCorners effect.');
}
$round = $this->_params['radius'];
$result = $this->_image->imagick->roundCorners($round, $round);
// Using a border?
if ($this->_params['bordercolor'] != 'none' &&
$this->_params['border'] > 0) {
$size = $this->_image->getDimensions();
$new = new Imagick();
$new->newImage($size['width'] + $this->_params['border'],
$size['height'] + $this->_params['border'],
$this->_params['bordercolor']);
$new->setImageFormat($this->_image->getType());
$new->roundCorners($round, $round);
$new->compositeImage($this->_image->imagick, Imagick::COMPOSITE_OVER, 1, 1);
$this->_image->imagick->clear();
$this->_image->imagick->addImage($new);
$new->destroy();
}
// If we have a background other than 'none' we need to
// compose two images together to make sure we *have* a background. We
// can't use border because we don't want to extend the image area, just
// fill in the parts removed by the rounding.
if ($this->_params['background'] != 'none') {
$size = $this->_image->getDimensions();
$new = new Imagick();
$new->newImage($size['width'],
$size['height'],
$this->_params['background']);
$new->setImageFormat($this->_image->getType());
$new->compositeImage($this->_image->imagick, Imagick::COMPOSITE_OVER, 0, 0);
$this->_image->imagick->clear();
$this->_image->imagick->addImage($new);
$new->destroy();
}
// Reset width/height since these might have changed
$this->_image->clearGeometry();
return true;
}
} Horde_Image-2.0.5/lib/Horde/Image/Effect/Imagick/SmartCrop.php 0000664 0000766 0000024 00000012057 12170303167 020623 0 ustar
* http://jueseph.com/2010/06/opticrop-usage-and-implementation/
*
* See the enclosed file COPYING for license information (LGPL). If you
* did not receive this file, see http://www.horde.org/licenses/lgpl21.
*
* @author Michael J. Rubinsky
* @package Image
*/
class Horde_Image_Effect_Imagick_SmartCrop extends Horde_Image_Effect
{
/**
* Valid parameters:
*
* width - Target width
* height - Target height
*
*
* @var array
*/
protected $_params = array();
public function apply()
{
$this->_params = new Horde_Support_Array($this->_params);
// Existing geometry
$geometry = $this->_image->getDimensions();
$w0 = $geometry['width'];
$h0 = $geometry['height'];
$w = $this->_params->width;
$h = $this->_params->height;
// @TODO: Parameterize these
$r = 1; // radius of edge filter
$nk = 9; // scale count: number of crop sizes to try
$gamma = 0.2; // edge normalization parameter -- see documentation
// Target AR
$ar = $this->_params->width / $this->_params->height;
// Existing AR
$ar0 = $w0 / $h0;
$this->_logger->debug(sprintf("SmartCrop: %d x %d => %d x %d ", $w0, $h0, $w, $h));
$this->_logger->debug('OAR: ' . $ar0);
$this->_logger->debug('TAR: ' . $ar);
// Compute COE
$img = $this->_image->imagick->clone();
$img->edgeImage($r);
$img->modulateImage(100,0,100);
$img->blackThresholdImage("#0f0f0f");
$xcenter = $ycenter = $sum = 0;
$n = 100000;
for ($k = 0; $k < $n; $k++) {
$i = mt_rand(0, $w0 - 1);
$j = mt_rand(0, $h0 - 1);
$pixel = $img->getImagePixelColor($i, $j);
$val = $pixel->getColor();
$val = $val['b'];
$sum += $val;
$xcenter = $xcenter + ($i + 1) * $val;
$ycenter = $ycenter + ($j + 1) * $val;
}
$xcenter /= $sum;
$ycenter /= $sum;
$this->_logger->debug('COE: ' . $xcenter . 'x' . $ycenter);
// crop source img to target AR
if ($w0 / $h0 > $ar) {
// source AR wider than target
// crop width to target AR
$wcrop0 = round($ar * $h0);
$hcrop0 = $h0;
} else {
// crop height to target AR
$wcrop0 = $w0;
$hcrop0 = round($w0 / $ar);
}
// crop parameters for all scales and translations
$params = array();
// crop at different scales
$hgap = $hcrop0 - $h;
$hinc = ($nk == 1) ? 0 : $hgap / ($nk - 1);
$wgap = $wcrop0 - $w;
$winc = ($nk == 1) ? 0 : $wgap / ($nk - 1);
// find window with highest normalized edginess
$n = 10000;
$maxbetanorm = 0;
$maxfile = '';
$maxparam = array('w' => 0,
'h' => 0,
'x' => 0,
'y' => 0);
for ($k = 0; $k < $nk; $k++) {
$hcrop = round($hcrop0 - $k * $hinc);
$wcrop = round($wcrop0 - $k * $winc);
$xcrop = $xcenter - $wcrop / 2;
$ycrop = $ycenter - $hcrop / 2;
if ($xcrop < 0) {
$xcrop = 0;
}
if ($xcrop + $wcrop > $w0) {
$xcrop = $w0 - $wcrop;
}
if ($ycrop < 0) {
$ycrop = 0;
}
if ($ycrop+$hcrop > $h0) {
$ycrop = $h0 - $hcrop;
}
$this->_logger->debug("crop: $wcrop, $hcrop, $xcrop, $ycrop");
$beta = 0;
for ($c = 0; $c < $n; $c++) {
$i = mt_rand(0, $wcrop - 1);
$j = mt_rand(0, $hcrop - 1);
$pixel = $img->getImagePixelColor($xcrop + $i, $ycrop + $j);
$val = $pixel->getColor();
$beta += $val['b'];// & 0xFF;
}
$area = $wcrop * $hcrop;
$betanorm = $beta / ($n * pow($area, $gamma - 1));
// best image found, save the params
if ($betanorm > $maxbetanorm) {
$this->_logger->debug('Found best');
$maxbetanorm = $betanorm;
$maxparam['w'] = $wcrop;
$maxparam['h'] = $hcrop;
$maxparam['x'] = $xcrop;
$maxparam['y'] = $ycrop;
}
}
$this->_logger->debug('Cropping');
// Crop to best
$this->_image->imagick->cropImage($maxparam['w'],
$maxparam['h'],
$maxparam['x'],
$maxparam['y']);
$this->_image->imagick->scaleImage($w, $h);
$img->destroy();
}
} Horde_Image-2.0.5/lib/Horde/Image/Effect/Imagick/TextWatermark.php 0000664 0000766 0000024 00000003713 12170303167 021512 0 ustar
* @package Image
*/
class Horde_Image_Effect_Imagick_TextWatermark extends Horde_Image_Effect
{
/**
* Valid parameters for watermark effects:
*
* text (required) - The text of the watermark.
* halign - The horizontal placement
* valign - The vertical placement
* font - The font name or family to use
* fontsize - The size of the font to use
* (small, medium, large, giant)
*
* @var array
*/
protected $_params = array('halign' => 'right',
'valign' => 'bottom',
'font' => 'courier',
'fontsize' => 'small');
/**
* Add the watermark
*
*/
public function apply()
{
/* Determine placement on image */
switch ($this->_params['valign']) {
case 'bottom':
$v = 'south';
break;
case 'center':
$v = 'center';
break;
default:
$v = 'north';
}
switch ($this->_params['halign']) {
case 'right':
$h = 'east';
break;
case 'center':
$h = 'center';
break;
default:
$h = 'west';
}
if (($v == 'center' && $h != 'center') ||
($v == 'center' && $h == 'center')) {
$gravity = $h;
} elseif ($h == 'center' && $v != 'center') {
$gravity = $v;
} else {
$gravity = $v . $h;
}
/* Determine font point size */
$point = Horde_Image::getFontSize($this->_params['fontsize']);
//@TODO:
throw new Horde_Image_Exception('Not Yet Implemented.');
}
} Horde_Image-2.0.5/lib/Horde/Image/Effect/Imagick/Unsharpmask.php 0000664 0000766 0000024 00000005146 12170303167 021206 0 ustar
* @package Image
*/
class Horde_Image_Effect_Imagick_Unsharpmask extends Horde_Image_Effect
{
/**
*
* Valid parameters:
*
* (float)radius - Thickness of the sharpened edge. Should be greater then
* sigma (or 0, and imagick will attempt to auto choose).
* In general, radius should be roughly output dpi / 150.
* So for display purposes a radius of 0.5 is suggested.
*
* (float)amount - Amount of the difference between original and the
* blur image that gets added back to the original. Can be
* thought of as the "strength" of the effect. Too high
* may cause blocking of shadows and highlights. Given
* a decimal value indicating percentage, e.g. 1.2 = 120%
*
* (float)threshold - Determines how large the brightness delta between
* adjacent pixels needs to be to sharpen the edge.
* Larger values == less sharpening. Useful for
* preventing noisy images from being oversharpened.
*
* (integer)channel - Which channel to apply the sharpening to.
*
* @var array
*/
protected $_params = array('radius' => 0.5,
'amount' => 1,
'threshold' => 0.05,
'channel' => Imagick::CHANNEL_ALL);
public function apply()
{
/* Calculate appropriate sigma:
* Determines how the sharpening is graduated away from
* the center pixel of the sharpened edge. In general,
* if radius < 1, then sigma = radius else sigma = sqrt(radius)
*/
$this->_params['sigma'] = ($this->_params['radius'] < 1) ?
$this->_params['radius'] : sqrt($this->_params['radius']);
try {
$this->_image->imagick->unsharpMaskImage(
$this->_params['radius'],
$this->_params['sigma'],
$this->_params['amount'],
$this->_params['threshold'],
$this->_params['channel']);
} catch (Imagick_Exception $e) {
throw new Horde_Image_Exception($e);
}
return true;
}
} Horde_Image-2.0.5/lib/Horde/Image/Effect/Border.php 0000664 0000766 0000024 00000003202 12170303167 016552 0 ustar
* @package Image
*/
class Horde_Image_Effect_Border extends Horde_Image_Effect {
/**
* Valid parameters for border decorators:
*
* padding - Pixels from the image edge that the border will start.
* borderColor - Border color. Defaults to black.
* fillColor - Color to fill the border with. Defaults to white.
* lineWidth - Border thickness, defaults to 1 pixel.
* roundWidth - Width of the corner rounding. Defaults to none.
*
* @var array
*/
var $_params = array('padding' => 0,
'borderColor' => 'black',
'fillColor' => 'white',
'lineWidth' => 1,
'roundWidth' => 0);
/**
* Draw the border.
*
* This draws the configured border to the provided image. Beware,
* that every pixel inside the border clipping will be overwritten
* with the background color.
*/
function apply()
{
$o = $this->_params;
$d = $this->_image->getDimensions();
$x = $o['padding'];
$y = $o['padding'];
$width = $d['width'] - (2 * $o['padding']);
$height = $d['height'] - (2 * $o['padding']);
if ($o['roundWidth'] > 0) {
$this->_image->roundedRectangle($x, $y, $width, $height, $o['roundWidth'], $o['borderColor'], $o['fillColor']);
} else {
$this->_image->rectangle($x, $y, $width, $height, $o['borderColor'], $o['fillColor']);
}
}
}
Horde_Image-2.0.5/lib/Horde/Image/Exif/Parser/Base.php 0000664 0000766 0000024 00000002143 12170303167 017145 0 ustar
* @author Jan Schneider
* @category Horde
* @package Image
*/
/**
* Exifer
* Extracts EXIF information from digital photos.
*
* Copyright © 2003 Jake Olefsky
* http://www.offsky.com/software/exif/index.php
* jake@olefsky.com
*
* ------------
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details. http://www.horde.org/licenses/gpl
*/
class Horde_Image_Exif_Parser_Canon extends Horde_Image_Exif_Parser_Base
{
/**
* Looks up the name of the tag for the MakerNote (Depends on Manufacturer)
*/
protected function _lookupTag($tag)
{
switch($tag) {
case '0001': $tag = 'Settings 1'; break;
case '0004': $tag = 'Settings 4'; break;
case '0006': $tag = 'ImageType'; break;
case '0007': $tag = 'FirmwareVersion'; break;
case '0008': $tag = 'ImageNumber'; break;
case '0009': $tag = 'OwnerName'; break;
case '000c': $tag = 'CameraSerialNumber'; break;
case '000f': $tag = 'CustomFunctions'; break;
default: $tag = sprintf(Horde_Image_Translation::t("Unknown: (%s)"), $tag); break;
}
return $tag;
}
/**
* Formats Data for the data type
*/
protected function _formatData($type, $tag, $intel, $data, $exif, &$result)
{
$place = 0;
switch ($type) {
case 'ASCII':
$result = $data = str_replace('\0', '', $data);
break;
case 'URATIONAL':
case 'SRATIONAL':
$data = bin2hex($data);
if ($intel == 1) {
$data = Horde_Image_Exif::intel2Moto($data);
}
$top = hexdec(substr($data, 8, 8));
$bottom = hexdec(substr($data, 0, 8));
if ($bottom != 0) {
$data = $top / $bottom;
} elseif ($top == 0) {
$data = 0;
} else {
$data = $top . '/' . $bottom;
}
if ($tag == '0204') {
//DigitalZoom
$data = $data . 'x';
}
case 'USHORT':
case 'SSHORT':
case 'ULONG':
case 'SLONG':
case 'FLOAT':
case 'DOUBLE':
$data = bin2hex($data);
$result['RAWDATA'] = $data;
// TODO: split this code up
switch ($tag) {
case '0001':
//first chunk
$result['Bytes'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
$place += 4;
if ($result['Bytes'] != strlen($data) / 2) {
//Bad chunk
return $result;
}
$result['Macro'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
$place += 4;//1
switch($result['Macro']) {
case 1: $result['Macro'] = Horde_Image_Translation::t("Macro"); break;
case 2: $result['Macro'] = Horde_Image_Translation::t("Normal"); break;
default: $result['Macro'] = Horde_Image_Translation::t("Unknown");
}
$result['SelfTimer'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
$place += 4;//2
switch($result['SelfTimer']) {
case 0: $result['SelfTimer'] = Horde_Image_Translation::t("Off"); break;
default: $result['SelfTimer'] .= Horde_Image_Translation::t("/10s");
}
$result['Quality'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
$place += 4;//3
switch($result['Quality']) {
case 2: $result['Quality'] = Horde_Image_Translation::t("Normal"); break;
case 3: $result['Quality'] = Horde_Image_Translation::t("Fine"); break;
case 5: $result['Quality'] = Horde_Image_Translation::t("Superfine"); break;
default: $result['Quality'] = Horde_Image_Translation::t("Unknown");
}
$result['Flash'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
$place += 4;//4
switch($result['Flash']) {
case 0: $result['Flash'] = Horde_Image_Translation::t("Off"); break;
case 1: $result['Flash'] = Horde_Image_Translation::t("Auto"); break;
case 2: $result['Flash'] = Horde_Image_Translation::t("On"); break;
case 3: $result['Flash'] = Horde_Image_Translation::t("Red Eye Reduction"); break;
case 4: $result['Flash'] = Horde_Image_Translation::t("Slow Synchro"); break;
case 5: $result['Flash'] = Horde_Image_Translation::t("Auto + Red Eye Reduction"); break;
case 6: $result['Flash'] = Horde_Image_Translation::t("On + Red Eye Reduction"); break;
case 16: $result['Flash'] = Horde_Image_Translation::t("External Flash"); break;
default: $result['Flash'] = Horde_Image_Translation::t("Unknown");
}
$result['DriveMode'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
$place += 4;//5
switch($result['DriveMode']) {
case 0: $result['DriveMode'] = Horde_Image_Translation::t("Single/Timer"); break;
case 1: $result['DriveMode'] = Horde_Image_Translation::t("Continuous"); break;
default: $result['DriveMode'] = Horde_Image_Translation::t("Unknown");
}
$result['Unknown'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
$place += 4;//6
$result['FocusMode'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
$place += 4;//7
switch($result['FocusMode']) {
case 0: $result['FocusMode'] = Horde_Image_Translation::t("One-Shot"); break;
case 1: $result['FocusMode'] = Horde_Image_Translation::t("AI Servo"); break;
case 2: $result['FocusMode'] = Horde_Image_Translation::t("AI Focus"); break;
case 3: $result['FocusMode'] = Horde_Image_Translation::t("Manual Focus"); break;
case 4: $result['FocusMode'] = Horde_Image_Translation::t("Single"); break;
case 5: $result['FocusMode'] = Horde_Image_Translation::t("Continuous"); break;
case 6: $result['FocusMode'] = Horde_Image_Translation::t("Manual Focus"); break;
default: $result['FocusMode'] = Horde_Image_Translation::t("Unknown");
}
$result['Unknown'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
$place += 4;//8
$result['Unknown'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place,4 )));
$place+=4;//9
$result['ImageSize'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
$place += 4;//10
switch($result['ImageSize']) {
case 0: $result['ImageSize'] = Horde_Image_Translation::t("Large"); break;
case 1: $result['ImageSize'] = Horde_Image_Translation::t("Medium"); break;
case 2: $result['ImageSize'] = Horde_Image_Translation::t("Small"); break;
default: $result['ImageSize'] = Horde_Image_Translation::t("Unknown");
}
$result['EasyShooting'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
$place += 4;//11
switch($result['EasyShooting']) {
case 0: $result['EasyShooting'] = Horde_Image_Translation::t("Full Auto"); break;
case 1: $result['EasyShooting'] = Horde_Image_Translation::t("Manual"); break;
case 2: $result['EasyShooting'] = Horde_Image_Translation::t("Landscape"); break;
case 3: $result['EasyShooting'] = Horde_Image_Translation::t("Fast Shutter"); break;
case 4: $result['EasyShooting'] = Horde_Image_Translation::t("Slow Shutter"); break;
case 5: $result['EasyShooting'] = Horde_Image_Translation::t("Night"); break;
case 6: $result['EasyShooting'] = Horde_Image_Translation::t("Black & White"); break;
case 7: $result['EasyShooting'] = Horde_Image_Translation::t("Sepia"); break;
case 8: $result['EasyShooting'] = Horde_Image_Translation::t("Portrait"); break;
case 9: $result['EasyShooting'] = Horde_Image_Translation::t("Sport"); break;
case 10: $result['EasyShooting'] = Horde_Image_Translation::t("Macro/Close-Up"); break;
case 11: $result['EasyShooting'] = Horde_Image_Translation::t("Pan Focus"); break;
default: $result['EasyShooting'] = Horde_Image_Translation::t("Unknown");
}
$result['DigitalZoom'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
$place += 4;//12
switch($result['DigitalZoom']) {
case 0:
case 65535: $result['DigitalZoom'] = Horde_Image_Translation::t("None"); break;
case 1: $result['DigitalZoom'] = Horde_Image_Translation::t("2x"); break;
case 2: $result['DigitalZoom'] = Horde_Image_Translation::t("4x"); break;
default: $result['DigitalZoom'] = Horde_Image_Translation::t("Unknown");
}
$result['Contrast'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
$place += 4;//13
switch($result['Contrast']) {
case 0: $result['Contrast'] = Horde_Image_Translation::t("Normal"); break;
case 1: $result['Contrast'] = Horde_Image_Translation::t("High"); break;
case 65535: $result['Contrast'] = Horde_Image_Translation::t("Low"); break;
default: $result['Contrast'] = Horde_Image_Translation::t("Unknown");
}
$result['Saturation'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
$place += 4;//14
switch($result['Saturation']) {
case 0: $result['Saturation'] = Horde_Image_Translation::t("Normal"); break;
case 1: $result['Saturation'] = Horde_Image_Translation::t("High"); break;
case 65535: $result['Saturation'] = Horde_Image_Translation::t("Low"); break;
default: $result['Saturation'] = Horde_Image_Translation::t("Unknown");
}
$result['Sharpness'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
$place += 4;//15
switch($result['Sharpness']) {
case 0: $result['Sharpness'] = Horde_Image_Translation::t("Normal"); break;
case 1: $result['Sharpness'] = Horde_Image_Translation::t("High"); break;
case 65535: $result['Sharpness'] = Horde_Image_Translation::t("Low"); break;
default: $result['Sharpness'] = Horde_Image_Translation::t("Unknown");
}
$result['ISO'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
$place += 4;//16
switch($result['ISO']) {
case 32767:
case 0:
$result['ISO'] = isset($exif['SubIFD']['ISOSpeedRatings']) ?
$exif['SubIFD']['ISOSpeedRatings'] :
'Unknown';
break;
case 15:
$result['ISO'] = Horde_Image_Translation::t("Auto");
break;
case 16:
$result['ISO'] = 50;
break;
case 17:
$result['ISO'] = 100;
break;
case 18:
$result['ISO'] = 200;
break;
case 19:
$result['ISO'] = 400;
break;
default:
$result['ISO'] = Horde_Image_Translation::t("Unknown");
}
$result['MeteringMode'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
$place += 4;//17
switch($result['MeteringMode']) {
case 3: $result['MeteringMode'] = Horde_Image_Translation::t("Evaluative"); break;
case 4: $result['MeteringMode'] = Horde_Image_Translation::t("Partial"); break;
case 5: $result['MeteringMode'] = Horde_Image_Translation::t("Center-weighted"); break;
default: $result['MeteringMode'] = Horde_Image_Translation::t("Unknown");
}
$result['FocusType'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
$place += 4;//18
switch($result['FocusType']) {
case 0: $result['FocusType'] = Horde_Image_Translation::t("Manual"); break;
case 1: $result['FocusType'] = Horde_Image_Translation::t("Auto"); break;
case 3: $result['FocusType'] = Horde_Image_Translation::t("Close-up (Macro)"); break;
case 8: $result['FocusType'] = Horde_Image_Translation::t("Locked (Pan Mode)"); break;
default: $result['FocusType'] = Horde_Image_Translation::t("Unknown");
}
$result['AFPointSelected'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
$place += 4;//19
switch($result['AFPointSelected']) {
case 12288: $result['AFPointSelected'] = Horde_Image_Translation::t("Manual Focus"); break;
case 12289: $result['AFPointSelected'] = Horde_Image_Translation::t("Auto Selected"); break;
case 12290: $result['AFPointSelected'] = Horde_Image_Translation::t("Right"); break;
case 12291: $result['AFPointSelected'] = Horde_Image_Translation::t("Center"); break;
case 12292: $result['AFPointSelected'] = Horde_Image_Translation::t("Left"); break;
default: $result['AFPointSelected'] = Horde_Image_Translation::t("Unknown");
}
$result['ExposureMode'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
$place += 4;//20
switch($result['ExposureMode']) {
case 0: $result['ExposureMode'] = Horde_Image_Translation::t("EasyShoot"); break;
case 1: $result['ExposureMode'] = Horde_Image_Translation::t("Program"); break;
case 2: $result['ExposureMode'] = Horde_Image_Translation::t("Tv"); break;
case 3: $result['ExposureMode'] = Horde_Image_Translation::t("Av"); break;
case 4: $result['ExposureMode'] = Horde_Image_Translation::t("Manual"); break;
case 5: $result['ExposureMode'] = Horde_Image_Translation::t("Auto-DEP"); break;
default: $result['ExposureMode'] = Horde_Image_Translation::t("Unknown");
}
$result['Unknown'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
$place += 4;//21
$result['Unknown'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
$place += 4;//22
$result['LongFocalLength'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
$place += 4;//23
$result['LongFocalLength'] .= 'focal units';
$result['ShortFocalLength'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
$place += 4;//24
$result['ShortFocalLength'] .= ' focal units';
$result['FocalUnits'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
$place += 4;//25
$result['FocalUnits'] .= ' per mm';
$result['Unknown'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
$place += 4;//26
$result['Unknown'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
$place += 4;//27
$result['FlashActivity'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
$place += 4;//28
switch($result['FlashActivity']) {
case 0: $result['FlashActivity'] = Horde_Image_Translation::t("Flash Did Not Fire"); break;
case 1: $result['FlashActivity'] = Horde_Image_Translation::t("Flash Fired"); break;
default: $result['FlashActivity'] = Horde_Image_Translation::t("Unknown");
}
$result['FlashDetails'] = str_pad(base_convert(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)), 16, 2), 16, '0', STR_PAD_LEFT);
$place += 4;//29
$flashDetails = array();
if (substr($result['FlashDetails'], 1, 1) == 1) {
$flashDetails[] = Horde_Image_Translation::t("External E-TTL");
}
if (substr($result['FlashDetails'], 2, 1) == 1) {
$flashDetails[] = Horde_Image_Translation::t("Internal Flash");
}
if (substr($result['FlashDetails'], 4, 1) == 1) {
$flashDetails[] = Horde_Image_Translation::t("FP sync used");
}
if (substr($result['FlashDetails'], 8, 1) == 1) {
$flashDetails[] = Horde_Image_Translation::t("2nd(rear)-curtain sync used");
}
if (substr($result['FlashDetails'], 12, 1) == 1) {
$flashDetails[] = Horde_Image_Translation::t("1st curtain sync");
}
$result['FlashDetails'] = implode(',', $flashDetails);
$result['Unknown'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
$place += 4;//30
$result['Unknown'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
$place += 4;//31
$anotherFocusMode = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
$place += 4;//32
if (strpos(strtoupper($exif['IFD0']['Model']), 'G1') !== false) {
switch($anotherFocusMode) {
case 0: $result['FocusMode'] = Horde_Image_Translation::t("Single"); break;
case 1: $result['FocusMode'] = Horde_Image_Translation::t("Continuous"); break;
default: $result['FocusMode'] = Horde_Image_Translation::t("Unknown");
}
}
break;
case '0004':
//second chunk
$result['Bytes']=hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
$place += 4;//0
if ($result['Bytes'] != strlen($data) / 2) {
return $result; //Bad chunk
}
$result['Unknown'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
$place += 4;//1
$result['Unknown'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
$place += 4;//2
$result['Unknown'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
$place += 4;//3
$result['Unknown'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
$place += 4;//4
$result['Unknown'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
$place += 4;//5
$result['Unknown'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
$place += 4;//6
$result['WhiteBalance'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
$place += 4;//7
switch($result['WhiteBalance']) {
case 0: $result['WhiteBalance'] = Horde_Image_Translation::t("Auto"); break;
case 1: $result['WhiteBalance'] = Horde_Image_Translation::t("Sunny"); break;
case 2: $result['WhiteBalance'] = Horde_Image_Translation::t("Cloudy"); break;
case 3: $result['WhiteBalance'] = Horde_Image_Translation::t("Tungsten"); break;
case 4: $result['WhiteBalance'] = Horde_Image_Translation::t("Fluorescent"); break;
case 5: $result['WhiteBalance'] = Horde_Image_Translation::t("Flash"); break;
case 6: $result['WhiteBalance'] = Horde_Image_Translation::t("Custom"); break;
default: $result['WhiteBalance'] = Horde_Image_Translation::t("Unknown");
}
$result['Unknown'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
$place += 4;//8
$result['SequenceNumber'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
$place += 4;//9
$result['Unknown'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
$place += 4;//10
$result['Unknown'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
$place += 4;//11
$result['Unknown'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data ,$place, 4)));
$place += 4;//12
$result['Unknown'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
$place += 4;//13
$result['AFPointUsed']=hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
$place += 4;//14
$afPointUsed = array();
if ($result['AFPointUsed'] & 0x0001) {
$afPointUsed[] = Horde_Image_Translation::t("Right"); //bit 0
}
if ($result['AFPointUsed'] & 0x0002) {
$afPointUsed[] = Horde_Image_Translation::t("Center"); //bit 1
}
if ($result['AFPointUsed'] & 0x0004) {
$afPointUsed[] = Horde_Image_Translation::t("Left"); //bit 2
}
if ($result['AFPointUsed'] & 0x0800) {
$afPointUsed[] = 12; //bit 12
}
if ($result['AFPointUsed'] & 0x1000) {
$afPointUsed[] = 13; //bit 13
}
if ($result['AFPointUsed'] & 0x2000) {
$afPointUsed[] = 14; //bit 14
}
if ($result['AFPointUsed'] & 0x4000) {
$afPointUsed[] = 15; //bit 15
}
$result['AFPointUsed'] = implode(',', $afPointUsed);
$result['FlashBias'] = Horde_Image_Exif::intel2Moto(substr($data, $place, 4));
$place += 4;//15
switch($result['FlashBias']) {
case 'ffc0': $result['FlashBias'] = '-2 EV'; break;
case 'ffcc': $result['FlashBias'] = '-1.67 EV'; break;
case 'ffd0': $result['FlashBias'] = '-1.5 EV'; break;
case 'ffd4': $result['FlashBias'] = '-1.33 EV'; break;
case 'ffe0': $result['FlashBias'] = '-1 EV'; break;
case 'ffec': $result['FlashBias'] = '-0.67 EV'; break;
case 'fff0': $result['FlashBias'] = '-0.5 EV'; break;
case 'fff4': $result['FlashBias'] = '-0.33 EV'; break;
case '0000': $result['FlashBias'] = '0 EV'; break;
case '000c': $result['FlashBias'] = '0.33 EV'; break;
case '0010': $result['FlashBias'] = '0.5 EV'; break;
case '0014': $result['FlashBias'] = '0.67 EV'; break;
case '0020': $result['FlashBias'] = '1 EV'; break;
case '002c': $result['FlashBias'] = '1.33 EV'; break;
case '0030': $result['FlashBias'] = '1.5 EV'; break;
case '0034': $result['FlashBias'] = '1.67 EV'; break;
case '0040': $result['FlashBias'] = '2 EV'; break;
default: $result['FlashBias'] = Horde_Image_Translation::t("Unknown");
}
$result['Unknown'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
$place += 4;//16
$result['Unknown'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
$place += 4;//17
$result['Unknown'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
$place += 4;//18
$result['SubjectDistance'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
$place += 4;//19
$result['SubjectDistance'] .= '/100 m';
break;
case '0008':
//image number
if ($intel == 1) {
$data = Horde_Image_Exif::intel2Moto($data);
}
$data = hexdec($data);
$result = round($data / 10000) . '-' . $data % 10000;
break;
case '000c':
//camera serial number
if ($intel == 1) {
$data = Horde_Image_Exif::intel2Moto($data);
}
$data = hexdec($data);
$result = '#' . bin2hex(substr($data, 0, 16)) . substr($data, 16, 16);
break;
}
break;
default:
if ($type != 'UNDEFINED') {
$data = bin2hex($data);
if ($intel == 1) {
$data = Horde_Image_Exif::intel2Moto($data);
}
}
break;
}
return $data;
}
/**
* Canon Special data section.
*
* @see http://www.burren.cx/david/canon.html
* @see http://www.burren.cx/david/canon.html
* @see http://www.ozhiker.com/electronics/pjmt/jpeg_info/canon_mn.html
*/
public function parse($block, &$result, $seek, $globalOffset)
{
$place = 0; //current place
if ($result['Endien'] == 'Intel') {
$intel = 1;
} else {
$intel = 0;
}
$model = $result['IFD0']['Model'];
//Get number of tags (2 bytes)
$num = bin2hex(substr($block, $place, 2));
$place += 2;
if ($intel == 1) {
$num = Horde_Image_Exif::intel2Moto($num);
}
$result['SubIFD']['MakerNote']['MakerNoteNumTags'] = hexdec($num);
//loop thru all tags Each field is 12 bytes
for ($i = 0; $i < hexdec($num); $i++) {
//2 byte tag
$tag = bin2hex(substr($block, $place, 2));
$place += 2;
if ($intel == 1) {
$tag = Horde_Image_Exif::intel2Moto($tag);
}
$tag_name = $this->_lookupTag($tag);
//2 byte type
$type = bin2hex(substr($block, $place, 2));
$place += 2;
if ($intel == 1) {
$type = Horde_Image_Exif::intel2Moto($type);
}
$this->_lookupType($type, $size);
//4 byte count of number of data units
$count = bin2hex(substr($block, $place, 4));
$place += 4;
if ($intel == 1) {
$count = Horde_Image_Exif::intel2Moto($count);
}
$bytesofdata = $size * hexdec($count);
if ($bytesofdata <= 0) {
return; //if this value is 0 or less then we have read all the tags we can
}
//4 byte value of data or pointer to data
$value = substr($block, $place, 4);
$place += 4;
if ($bytesofdata <= 4) {
$data = $value;
} else {
$value = bin2hex($value);
if ($intel == 1) {
$value = Horde_Image_Exif::intel2Moto($value);
}
//offsets are from TIFF header which is 12 bytes from the start
//of the file
$v = fseek($seek, $globalOffset + hexdec($value));
$exiferFileSize = 0;
if ($v == 0 && $bytesofdata < $exiferFileSize) {
$data = fread($seek, $bytesofdata);
} elseif ($v == -1) {
$result['Errors'] = $result['Errors']++;
$data = '';
} else {
$data = '';
}
}
// Ensure the index exists.
$result['SubIFD']['MakerNote'][$tag_name] = '';
$formated_data = $this->_formatData($type, $tag, $intel, $data, $result, $result['SubIFD']['MakerNote'][$tag_name]);
$result['SubIFD']['MakerNote'][$tag_name] = $formated_data;
}
}
}
Horde_Image-2.0.5/lib/Horde/Image/Exif/Parser/Fujifilm.php 0000664 0000766 0000024 00000030167 12170303167 020047 0 ustar
* @author Jan Schneider
* @category Horde
* @package Image
*/
/**
* Exifer
* Extracts EXIF information from digital photos.
*
* Copyright © 2003 Jake Olefsky
* http://www.offsky.com/software/exif/index.php
* jake@olefsky.com
*
* ------------
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details. http://www.horde.org/licenses/gpl
*/
class Horde_Image_Exif_Parser_Fujifilm extends Horde_Image_Exif_Parser_Base
{
/**
* Looks up the name of the tag for the MakerNote (Depends on Manufacturer)
*/
protected function _lookupTag($tag)
{
switch ($tag) {
case '0000': return 'Version';
case '1000': return 'Quality';
case '1001': return 'Sharpness';
case '1002': return 'WhiteBalance';
case '1003': return 'Color';
case '1004': return 'Tone';
case '1010': return 'FlashMode';
case '1011': return 'FlashStrength';
case '1020': return 'Macro';
case '1021': return 'FocusMode';
case '1030': return 'SlowSync';
case '1031': return 'PictureMode';
case '1032': return 'Unknown';
case '1100': return 'ContinuousTakingBracket';
case '1200': return 'Unknown';
case '1300': return 'BlurWarning';
case '1301': return 'FocusWarning';
case '1302': return 'AEWarning';
default: return 'unknown:' . $tag;
}
}
/**
*
* @param $type
* @param $tag
* @param $intel
* @param $data
* @return unknown_type
*/
protected function _formatData($type, $tag, $intel, $data)
{
switch ($type) {
case 'ASCII':
case 'UNDEFINED':
break;
case 'URATIONAL':
case 'SRATIONAL':
$data = bin2hex($data);
if ($intel) {
$data = Horde_Image_Exif::intel2Moto($data);
}
$top = hexdec(substr($data, 8, 8));
$bottom = hexdec(substr($data, 0, 8));
if ($bottom) {
$data = $top / $bottom;
} elseif (!$top) {
$data = 0;
} else {
$data = $top . '/' . $bottom;
}
if ($tag == '1011') {
//FlashStrength
$data = $data . ' EV';
}
break;
case 'USHORT':
case 'SSHORT':
case 'ULONG':
case 'SLONG':
case 'FLOAT':
case 'DOUBLE':
$data = bin2hex($data);
if ($intel) {
$data = Horde_Image_Exif::intel2Moto($data);
}
$data = hexdec($data);
switch ($tag) {
case '1001':
//Sharpness
switch ($data) {
case 1: $data = Horde_Image_Translation::t("Soft"); break;
case 2: $data = Horde_Image_Translation::t("Soft"); break;
case 3: $data = Horde_Image_Translation::t("Normal"); break;
case 4: $data = Horde_Image_Translation::t("Hard"); break;
case 5: $data = Horde_Image_Translation::t("Hard"); break;
default: $data = Horde_Image_Translation::t("Unknown") . ': ' . $data; break;
}
break;
case '1002':
//WhiteBalance
switch ($data) {
case 0: $data = Horde_Image_Translation::t("Auto"); break;
case 256: $data = Horde_Image_Translation::t("Daylight"); break;
case 512: $data = Horde_Image_Translation::t("Cloudy"); break;
case 768: $data = Horde_Image_Translation::t("DaylightColor-fluorescence"); break;
case 769: $data = Horde_Image_Translation::t("DaywhiteColor-fluorescence"); break;
case 770: $data = Horde_Image_Translation::t("White-fluorescence"); break;
case 1024: $data = Horde_Image_Translation::t("Incandescense"); break;
case 3840: $data = Horde_Image_Translation::t("Custom"); break;
default: $data = Horde_Image_Translation::t("Unknown") . ': ' . $data; break;
}
break;
case '1003':
//Color
switch ($data) {
case 0: $data = Horde_Image_Translation::t("Chroma Saturation Normal(STD)"); break;
case 256: $data = Horde_Image_Translation::t("Chroma Saturation High"); break;
case 512: $data = Horde_Image_Translation::t("Chroma Saturation Low(ORG)"); break;
default: $data = Horde_Image_Translation::t("Unknown: ") . $data; break;
}
break;
case '1004':
//Tone
switch ($data) {
case 0: $data = Horde_Image_Translation::t("Contrast Normal(STD)"); break;
case 256: $data = Horde_Image_Translation::t("Contrast High(HARD)"); break;
case 512: $data = Horde_Image_Translation::t("Contrast Low(ORG)"); break;
default: $data = Horde_Image_Translation::t("Unknown: ") . $data; break;
}
break;
case '1010':
//FlashMode
switch ($data) {
case 0: $data = Horde_Image_Translation::t("Auto"); break;
case 1: $data = Horde_Image_Translation::t("On"); break;
case 2: $data = Horde_Image_Translation::t("Off"); break;
case 3: $data = Horde_Image_Translation::t("Red-Eye Reduction"); break;
default: $data = Horde_Image_Translation::t("Unknown: ") . $data; break;
}
break;
case '1020':
//Macro
switch ($data) {
case 0: $data = Horde_Image_Translation::t("Off"); break;
case 1: $data = Horde_Image_Translation::t("On"); break;
default: $data = Horde_Image_Translation::t("Unknown: ") . $data; break;
}
break;
case '1021':
//FocusMode
switch ($data) {
case 0: $data = Horde_Image_Translation::t("Auto"); break;
case 1: $data = Horde_Image_Translation::t("Manual"); break;
default: $data = Horde_Image_Translation::t("Unknown: ") . $data; break;
}
break;
case '1030':
//SlowSync
switch ($data) {
case 0: $data = Horde_Image_Translation::t("Off"); break;
case 1: $data = Horde_Image_Translation::t("On"); break;
default: $data = Horde_Image_Translation::t("Unknown: ") . $data; break;
}
break;
case '1031':
//PictureMode
switch ($data) {
case 0: $data = Horde_Image_Translation::t("Auto"); break;
case 1: $data = Horde_Image_Translation::t("Portrait"); break;
case 2: $data = Horde_Image_Translation::t("Landscape"); break;
case 4: $data = Horde_Image_Translation::t("Sports"); break;
case 5: $data = Horde_Image_Translation::t("Night"); break;
case 6: $data = Horde_Image_Translation::t("Program AE"); break;
case 256: $data = Horde_Image_Translation::t("Aperture Prority AE"); break;
case 512: $data = Horde_Image_Translation::t("Shutter Priority"); break;
case 768: $data = Horde_Image_Translation::t("Manual Exposure"); break;
default: $data = Horde_Image_Translation::t("Unknown: ") . $data; break;
}
break;
case '1100':
//ContinuousTakingBracket
switch ($data) {
case 0: $data = Horde_Image_Translation::t("Off"); break;
case 1: $data = Horde_Image_Translation::t("On"); break;
default: $data = Horde_Image_Translation::t("Unknown: ") . $data; break;
}
break;
case '1300':
//BlurWarning
switch ($data) {
case 0: $data = Horde_Image_Translation::t("No Warning"); break;
case 1: $data = Horde_Image_Translation::t("Warning"); break;
default: $data = Horde_Image_Translation::t("Unknown: ") . $data; break;
}
break;
case '1301':
//FocusWarning
switch ($data) {
case 0: $data = Horde_Image_Translation::t("Auto Focus Good"); break;
case 1: $data = Horde_Image_Translation::t("Out of Focus"); break;
default: $data = Horde_Image_Translation::t("Unknown: ") . $data; break;
}
break;
case '1302':
//AEWarning
switch ($data) {
case 0: $data = Horde_Image_Translation::t("AE Good"); break;
case 1: $data = Horde_Image_Translation::t("Over Exposure"); break;
default: $data = Horde_Image_Translation::t("Unknown: ") . $data; break;
}
break;
}
break;
default:
$data = bin2hex($data);
if ($intel) {
$data = Horde_Image_Exif::intel2Moto($data);
}
break;
}
return $data;
}
/**
*
* @param $block
* @param $result
* @return unknown_type
*/
public function parse($block, &$result)
{
$intel = true;
$model = $result['IFD0']['Model'];
//current place
$place = 8;
$offset = 8;
$num = bin2hex(substr($block, $place, 4));
$place += 4;
if ($intel) {
$num = Horde_Image_Exif::intel2Moto($num);
}
$result['SubIFD']['MakerNote']['Offset'] = hexdec($num);
//Get number of tags (2 bytes)
$num = bin2hex(substr($block, $place, 2));
$place += 2;
if ($intel) {
$num = Horde_Image_Exif::intel2Moto($num);
}
$result['SubIFD']['MakerNote']['MakerNoteNumTags'] = hexdec($num);
//loop thru all tags Each field is 12 bytes
for ($i = 0; $i < hexdec($num); $i++) {
//2 byte tag
$tag = bin2hex(substr($block, $place, 2));
$place += 2;
if ($intel) {
$tag = Horde_Image_Exif::intel2Moto($tag);
}
$tag_name = $this->_lookupTag($tag);
//2 byte type
$type = bin2hex(substr($block, $place, 2));
$place += 2;
if ($intel) {
$type = Horde_Image_Exif::intel2Moto($type);
}
$this->_lookupType($type, $size);
//4 byte count of number of data units
$count = bin2hex(substr($block, $place, 4));
$place += 4;
if ($intel) {
$count = Horde_Image_Exif::intel2Moto($count);
}
$bytesofdata = $size * hexdec($count);
//4 byte value of data or pointer to data
$value = substr($block, $place, 4);
$place += 4;
if ($bytesofdata <= 4) {
$data = $value;
} else {
$value = bin2hex($value);
if ($intel) {
$value = Horde_Image_Exif::intel2Moto($value);
}
$data = substr($block, hexdec($value) - $offset, $bytesofdata * 2);
}
$formated_data = $this->_formatData($type, $tag, $intel, $data);
$result['SubIFD']['MakerNote'][$tag_name] = $formated_data;
}
}
}
Horde_Image-2.0.5/lib/Horde/Image/Exif/Parser/Gps.php 0000664 0000766 0000024 00000025273 12170303167 017035 0 ustar
* @author Jan Schneider
* @category Horde
* @package Image
*/
/**
* Exifer
* Extracts EXIF information from digital photos.
*
* Copyright 2003 Jake Olefsky
* http://www.offsky.com/software/exif/index.php
* jake@olefsky.com
*
* ------------
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details. http://www.horde.org/licenses/gpl
*/
class Horde_Image_Exif_Parser_Gps extends Horde_Image_Exif_Parser_Base
{
/**
* Looks up the name of the tag
*
* @param unknown_type $tag
* @return string
*/
protected function _lookupTag($tag)
{
switch($tag) {
case '0000': return 'Version';
//north or south
case '0001': return 'LatitudeRef';
//dd mm.mm or dd mm ss
case '0002': return 'Latitude';
//east or west
case '0003': return 'LongitudeRef';
//dd mm.mm or dd mm ss
case '0004': return 'Longitude';
//sea level or below sea level
case '0005': return 'AltitudeRef';
//positive rational number
case '0006': return 'Altitude';
//three positive rational numbers
case '0007': return 'Time';
//text string up to 999 bytes long
case '0008': return 'Satellite';
//in progress or interop
case '0009': return 'ReceiveStatus';
//2D or 3D
case '000a': return 'MeasurementMode';
//positive rational number
case '000b': return 'MeasurementPrecision';
//KPH, MPH, knots
case '000c': return 'SpeedUnit';
//positive rational number
case '000d': return 'ReceiverSpeed';
//true or magnetic north
case '000e': return 'MovementDirectionRef';
//positive rational number
case '000f': return 'MovementDirection';
//true or magnetic north
case '0010': return 'ImageDirectionRef';
//positive rational number
case '0011': return 'ImageDirection';
//text string up to 999 bytes long
case '0012': return 'GeodeticSurveyData';
//north or south
case '0013': return 'DestLatitudeRef';
//three positive rational numbers
case '0014': return 'DestinationLatitude';
//east or west
case '0015': return 'DestLongitudeRef';
//three positive rational numbers
case '0016': return 'DestinationLongitude';
//true or magnetic north
case '0017': return 'DestBearingRef';
//positive rational number
case '0018': return 'DestinationBearing';
//km, miles, knots
case '0019': return 'DestDistanceRef';
//positive rational number
case '001a': return 'DestinationDistance';
case '001b': return 'ProcessingMethod';
case '001c': return 'AreaInformation';
//text string 10 bytes long
case '001d': return 'Datestamp';
//integer in range 0-65535
case '001e': return 'DifferentialCorrection';
default: return 'unknown: ' . $tag;
}
}
/**
* Formats a rational number
*/
protected function _rational($data, $intel)
{
if ($intel == 1) {
//intel stores them bottom-top
$top = hexdec(substr($data, 8, 8));
} else {
//motorola stores them top-bottom
$top = hexdec(substr($data, 0, 8));
}
if ($intel == 1) {
$bottom = hexdec(substr($data, 0, 8));
} else {
$bottom = hexdec(substr($data, 8, 8));
}
if ($bottom != 0) {
$data = $top / $bottom;
} elseif ($top == 0) {
$data = 0;
} else {
$data = $top . '/' . $bottom;
}
return $data;
}
/**
* Formats Data for the data type
*/
protected function _formatData($type, $tag, $intel, $data)
{
switch ($type) {
case 'ASCII':
// Latitude Reference, Longitude Reference
if ($tag == '0001' || $tag == '0003') {
$data = ($data{1} == $data{2} && $data{1} == $data{3}) ? $data{0} : $data;
}
break;
case 'URATIONAL':
case 'SRATIONAL':
$data = bin2hex($data);
if ($intel == 1) {
$data = Horde_Image_Exif::intel2Moto($data);
}
if ($intel == 1) {
//intel stores them bottom-top
$top = hexdec(substr($data, 8, 8));
} else {
//motorola stores them top-bottom
$top = hexdec(substr($data, 0, 8));
}
if ($intel == 1) {
$bottom = hexdec(substr($data, 0, 8));
} else {
$bottom = hexdec(substr($data, 8, 8));
}
if ($type == 'SRATIONAL' && $top > 2147483647) {
// make the number signed instead of unsigned
$top = $top - 4294967296;
}
switch ($tag) {
case '0002':
case '0004':
//Latitude, Longitude
if ($intel == 1) {
$seconds = $this->_rational(substr($data, 0, 16), $intel);
$hour = $this->_rational(substr($data, 32, 16), $intel);
} else {
$hour = $this->_rational(substr($data, 0, 16), $intel);
$seconds = $this->_rational(substr($data, 32, 16), $intel);
}
$minutes = $this->_rational(substr($data, 16, 16), $intel);
$data = array($hour, $minutes, $seconds);
break;
case '0007':
//Time
$seconds = $this->_rational(substr($data, 0, 16), $intel);
$minutes = $this->_rational(substr($data, 16, 16), $intel);
$hour = $this->_rational(substr($data, 32, 16), $intel);
$data = $hour . ':' . $minutes . ':' . $seconds;
break;
default:
if ($bottom != 0) {
$data = $top / $bottom;
} elseif ($top == 0) {
$data = 0;
} else {
$data = $top . '/' . $bottom;
}
if ($tag == '0006') {
$data .= 'm';
}
break;
}
break;
case 'USHORT':
case 'SSHORT':
case 'ULONG':
case 'SLONG':
case 'FLOAT':
case 'DOUBLE':
$data = bin2hex($data);
if ($intel == 1) {
$data = Horde_Image_Exif::intel2Moto($data);
}
$data = hexdec($data);
break;
case 'UNDEFINED':
break;
case 'UBYTE':
$data = bin2hex($data);
if ($intel == 1) {
$num = Horde_Image_Exif::intel2Moto($data);
}
switch ($tag) {
case '0000':
// VersionID
$data = hexdec(substr($data, 0, 2))
. '.' . hexdec(substr($data, 2, 2))
. '.' . hexdec(substr($data, 4, 2))
. '.'. hexdec(substr($data, 6, 2));
break;
case '0005':
// Altitude Reference
if ($data == '00000000') {
$data = 'Above Sea Level';
} elseif ($data == '01000000') {
$data = 'Below Sea Level';
}
break;
}
break;
default:
$data = bin2hex($data);
if ($intel == 1) {
$data = Horde_Image_Exif::intel2Moto($data);
}
break;
}
return $data;
}
/**
* GPS Special data section
*
* @see http://drewnoakes.com/code/exif/sampleOutput.html
* @see http://www.geosnapper.com
*/
public function parse($block, &$result, $offset, $seek, $globalOffset)
{
if ($result['Endien'] == 'Intel') {
$intel = 1;
} else {
$intel = 0;
}
//offsets are from TIFF header which is 12 bytes from the start of the
//file
$v = fseek($seek, $globalOffset + $offset);
if ($v == -1) {
$result['Errors'] = $result['Errors']++;
}
$num = bin2hex(fread($seek, 2));
if ($intel == 1) {
$num = Horde_Image_Exif::intel2Moto($num);
}
$num = hexdec($num);
$result['GPS']['NumTags'] = $num;
$block = fread($seek, $num * 12);
$place = 0;
//loop thru all tags Each field is 12 bytes
for ($i = 0; $i < $num; $i++) {
//2 byte tag
$tag = bin2hex(substr($block, $place, 2));
$place += 2;
if ($intel == 1) {
$tag = Horde_Image_Exif::intel2Moto($tag);
}
$tag_name = $this->_lookupTag($tag);
//2 byte datatype
$type = bin2hex(substr($block, $place, 2));
$place += 2;
if ($intel == 1) {
$type = Horde_Image_Exif::intel2Moto($type);
}
$this->_lookupType($type, $size);
//4 byte number of elements
$count = bin2hex(substr($block, $place, 4));
$place += 4;
if ($intel==1) {
$count = Horde_Image_Exif::intel2Moto($count);
}
$bytesofdata = $size * hexdec($count);
//4 byte value or pointer to value if larger than 4 bytes
$value = substr($block, $place, 4);
$place += 4;
if ($bytesofdata <= 4) {
$data = $value;
} else {
$value = bin2hex($value);
if ($intel == 1) {
$value = Horde_Image_Exif::intel2Moto($value);
}
//offsets are from TIFF header which is 12 bytes from the start
//of the file
$v = fseek($seek, $globalOffset + hexdec($value));
if ($v == 0) {
$data = fread($seek, $bytesofdata);
} elseif ($v == -1) {
$result['Errors'] = $result['Errors']++;
}
}
$result['GPS' . $tag_name] = $this->_formatData($type, $tag, $intel, $data);
}
}
}
Horde_Image-2.0.5/lib/Horde/Image/Exif/Parser/Nikon.php 0000664 0000766 0000024 00000037247 12170303167 017366 0 ustar
* @author Jan Schneider
* @category Horde
* @package Image
*/
/**
* Exifer
* Extracts EXIF information from digital photos.
*
* Copyright © 2003 Jake Olefsky
* http://www.offsky.com/software/exif/index.php
* jake@olefsky.com
*
* ------------
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details. http://www.horde.org/licenses/gpl
*/
class Horde_Image_Exif_Parser_Nikon extends Horde_Image_Exif_Parser_Base
{
/**
*
* @param $tag
* @param $model
* @return unknown_type
*/
protected function _lookupTag($tag, $model)
{
switch ($model) {
case 0:
switch($tag) {
case '0003': $tag = 'Quality'; break;
case '0004': $tag = 'ColorMode'; break;
case '0005': $tag = 'ImageAdjustment'; break;
case '0006': $tag = 'CCDSensitivity'; break;
case '0007': $tag = 'WhiteBalance'; break;
case '0008': $tag = 'Focus'; break;
case '0009': $tag = 'Unknown2'; break;
case '000a': $tag = 'DigitalZoom'; break;
case '000b': $tag = 'Converter'; break;
default: $tag = 'unknown: ' . $tag; break;
}
break;
case 1:
switch($tag) {
case '0002': $tag = 'ISOSetting'; break;
case '0003': $tag = 'ColorMode'; break;
case '0004': $tag = 'Quality'; break;
case '0005': $tag = 'Whitebalance'; break;
case '0006': $tag = 'ImageSharpening'; break;
case '0007': $tag = 'FocusMode'; break;
case '0008': $tag = 'FlashSetting'; break;
case '0009': $tag = 'FlashMode'; break;
case '000b': $tag = 'WhiteBalanceFine'; break;
case '000f': $tag = 'ISOSelection'; break;
case '0013': $tag = 'ISOSelection2'; break;
case '0080': $tag = 'ImageAdjustment'; break;
case '0081': $tag = 'ToneCompensation'; break;
case '0082': $tag = 'Adapter'; break;
case '0083': $tag = 'LensType'; break;
case '0084': $tag = 'LensInfo'; break;
case '0085': $tag = 'ManualFocusDistance'; break;
case '0086': $tag = 'DigitalZoom'; break;
case '0087': $tag = 'FlashUsed'; break;
case '0088': $tag = 'AFFocusPosition'; break;
case '008d': $tag = 'ColorMode'; break;
case '0090': $tag = 'LightType'; break;
case '0094': $tag = 'Saturation'; break;
case '0095': $tag = 'NoiseReduction'; break;
case '0010': $tag = 'DataDump'; break;
default: $tag = 'unknown: ' . $tag; break;
}
break;
}
return $tag;
}
/**
*
* @param $type
* @param $tag
* @param $intel
* @param $model
* @param $data
* @return unknown_type
*/
protected function _formatData($type, $tag, $intel, $model, $data)
{
switch ($type) {
case 'URATIONAL':
case 'SRATIONAL':
$data = bin2hex($data);
if ($intel) {
$data = Horde_Image_Exif::intel2Moto($data);
}
$top = hexdec(substr($data, 8, 8));
$bottom = hexdec(substr($data, 0, 8));
if ($bottom != 0) {
$data = $top / $bottom;
} elseif ($top == 0) {
$data = 0;
} else {
$data = $top . '/' . $bottom;
}
if ($tag == '0085' && $model == 1) {
//ManualFocusDistance
$data = $data . ' m';
}
if ($tag == '0086' && $model == 1) {
//DigitalZoom
$data = $data . 'x';
}
if ($tag == '000a' && $model == 0) {
//DigitalZoom
$data = $data . 'x';
}
break;
case 'USHORT':
case 'SSHORT':
case 'ULONG':
case 'SLONG':
case 'FLOAT':
case 'DOUBLE':
$data = bin2hex($data);
if ($intel) {
$data = Horde_Image_Exif::intel2Moto($data);
}
$data = hexdec($data);
if ($model != 0) {
break;
}
switch ($tag) {
case '0003':
//Quality
switch ($data) {
case 1: $data = Horde_Image_Translation::t("VGA Basic"); break;
case 2: $data = Horde_Image_Translation::t("VGA Normal"); break;
case 3: $data = Horde_Image_Translation::t("VGA Fine"); break;
case 4: $data = Horde_Image_Translation::t("SXGA Basic"); break;
case 5: $data = Horde_Image_Translation::t("SXGA Normal"); break;
case 6: $data = Horde_Image_Translation::t("SXGA Fine"); break;
default: $data = Horde_Image_Translation::t("Unknown") . ': ' . $data; break;
}
break;
case '0004':
//Color
switch ($data) {
case 1: $data = Horde_Image_Translation::t("Color"); break;
case 2: $data = Horde_Image_Translation::t("Monochrome"); break;
default: $data = Horde_Image_Translation::t("Unknown") . ': ' . $data; break;
}
break;
case '0005':
//Image Adjustment
switch ($data) {
case 0: $data = Horde_Image_Translation::t("Normal"); break;
case 1: $data = Horde_Image_Translation::t("Bright+"); break;
case 2: $data = Horde_Image_Translation::t("Bright-"); break;
case 3: $data = Horde_Image_Translation::t("Contrast+"); break;
case 4: $data = Horde_Image_Translation::t("Contrast-"); break;
default: $data = Horde_Image_Translation::t("Unknown") . ': ' . $data; break;
}
break;
case '0006':
//CCD Sensitivity
switch ($data) {
case 0: $data = 'ISO-80'; break;
case 2: $data = 'ISO-160'; break;
case 4: $data = 'ISO-320'; break;
case 5: $data = 'ISO-100'; break;
default: $data = Horde_Image_Translation::t("Unknown") . ': ' . $data; break;
}
break;
case '0007':
//White Balance
switch ($data) {
case 0: $data = Horde_Image_Translation::t("Auto"); break;
case 1: $data = Horde_Image_Translation::t("Preset"); break;
case 2: $data = Horde_Image_Translation::t("Daylight"); break;
case 3: $data = Horde_Image_Translation::t("Incandescense"); break;
case 4: $data = Horde_Image_Translation::t("Flourescence"); break;
case 5: $data = Horde_Image_Translation::t("Cloudy"); break;
case 6: $data = Horde_Image_Translation::t("SpeedLight"); break;
default: $data = Horde_Image_Translation::t("Unknown") . ': ' . $data; break;
}
break;
case '000b':
//Converter
switch ($data) {
case 0: $data = Horde_Image_Translation::t("None"); break;
case 1: $data = Horde_Image_Translation::t("Fisheye"); break;
default: $data = Horde_Image_Translation::t("Unknown") . ': ' . $data; break;
}
break;
}
case 'UNDEFINED':
if ($model != 1) {
break;
}
switch ($tag) {
case '0001':
$data = $data/100;
break;
case '0088':
//AF Focus Position
$temp = Horde_Image_Translation::t("Center");
$data = bin2hex($data);
$data = str_replace('01', 'Top', $data);
$data = str_replace('02', 'Bottom', $data);
$data = str_replace('03', 'Left', $data);
$data = str_replace('04', 'Right', $data);
$data = str_replace('00', '', $data);
if (!strlen($data)) {
$data = $temp;
}
break;
}
break;
default:
$data = bin2hex($data);
if ($intel) {
$data = Horde_Image_Exif::intel2Moto($data);
}
if ($model != 1) {
break;
}
switch ($tag) {
case '0083':
//Lens Type
$data = hexdec(substr($data, 0, 2));
switch ($data) {
case 0: $data = Horde_Image_Translation::t("AF non D"); break;
case 1: $data = Horde_Image_Translation::t("Manual"); break;
case 2: $data = 'AF-D or AF-S'; break;
case 6: $data = 'AF-D G'; break;
case 10: $data = 'AF-D VR'; break;
default: $data = Horde_Image_Translation::t("Unknown") . ': ' . $data; break;
}
break;
case '0087':
//Flash type
$data = hexdec(substr($data,0,2));
switch ($data) {
case 0: $data = Horde_Image_Translation::t("Did Not Fire"); break;
case 4: $data = Horde_Image_Translation::t("Unknown"); break;
case 7: $data = Horde_Image_Translation::t("External"); break;
case 9: $data = Horde_Image_Translation::t("On Camera"); break;
default: $data = Horde_Image_Translation::t("Unknown") . ': ' . $data; break;
}
break;
}
break;
}
return $data;
}
/**
*
* @param $block
* @param $result
* @return unknown_type
*/
public function parse($block, &$result)
{
$intel = $result['Endien'] == 'Intel';
$model = $result['IFD0']['Model'];
//these 6 models start with "Nikon". Other models dont.
if ($model == "E700\0" ||
$model == "E800\0" ||
$model == "E900\0" ||
$model == "E900S\0" ||
$model == "E910\0" ||
$model == "E950\0") {
//current place
$place = 8;
$model = 0;
//Get number of tags (2 bytes)
$num = bin2hex(substr($block, $place, 2));
$place += 2;
if ($intel) {
$num = Horde_Image_Exif::intel2Moto($num);
}
$result['SubIFD']['MakerNote']['MakerNoteNumTags'] = hexdec($num);
//loop thru all tags Each field is 12 bytes
for ($i = 0; $i < hexdec($num); $i++) {
//2 byte tag
$tag = bin2hex(substr($block, $place, 2));
$place += 2;
if ($intel) {
$tag = Horde_Image_Exif::intel2Moto($tag);
}
$tag_name = $this->_lookupTag($tag, $model);
//2 byte type
$type = bin2hex(substr($block, $place, 2));
$place += 2;
if ($intel) {
$type = Horde_Image_Exif::intel2Moto($type);
}
$this->_lookupType($type, $size);
//4 byte count of number of data units
$count = bin2hex(substr($block, $place, 4));
$place += 4;
if ($intel) {
$count = Horde_Image_Exif::intel2Moto($count);
}
$bytesofdata = $size * hexdec($count);
//4 byte value of data or pointer to data
$value = substr($block, $place, 4);
$place += 4;
//if tag is 0002 then its the ASCII value which we know is at 140 so calc offset
//THIS HACK ONLY WORKS WITH EARLY NIKON MODELS
if ($tag == '0002') {
$offset = hexdec($value) - 140;
}
if ($bytesofdata <= 4) {
$data = $value;
} else {
$value = bin2hex($value);
if ($intel) {
$value = Horde_Image_Exif::intel2Moto($value);
}
$data = substr($block, hexdec($value) - $offset, $bytesofdata * 2);
}
$formated_data = $this->_formatData($type, $tag, $intel, $model, $data);
$result['SubIFD']['MakerNote'][$tag_name] = $formated_data;
}
} else {
//current place
$place = 0;
$model = 1;
$nikon = substr($block, $place, 8);
$place += 8;
$endien = substr($block, $place, 4);
$place += 4;
//2 bytes of 0x002a
$tag = bin2hex(substr($block, $place, 2));
$place += 2;
//Then 4 bytes of offset to IFD0 (usually 8 which includes all 8
//bytes of TIFF header)
$offset = bin2hex(substr($block, $place, 4));
$place += 4;
if ($intel) {
$offset = Horde_Image_Exif::intel2Moto($offset);
}
if (hexdec($offset) > 8) {
$place += $offset - 8;
}
//Get number of tags (2 bytes)
$num = bin2hex(substr($block, $place, 2));
$place += 2;
if ($intel) {
$num = Horde_Image_Exif::intel2Moto($num);
}
//loop thru all tags Each field is 12 bytes
for ($i = 0; $i < hexdec($num); $i++) {
//2 byte tag
$tag = bin2hex(substr($block, $place, 2));
$place += 2;
if ($intel) {
$tag = Horde_Image_Exif::intel2Moto($tag);
}
$tag_name = $this->_lookupTag($tag, $model);
//2 byte type
$type = bin2hex(substr($block, $place, 2));
$place += 2;
if ($intel) {
$type = Horde_Image_Exif::intel2Moto($type);
}
$this->_lookupType($type, $size);
//4 byte count of number of data units
$count = bin2hex(substr($block, $place, 4));
$place += 4;
if ($intel) {
$count = Horde_Image_Exif::intel2Moto($count);
}
$bytesofdata = $size * hexdec($count);
//4 byte value of data or pointer to data
$value = substr($block, $place, 4);
$place += 4;
if ($bytesofdata <= 4) {
$data = $value;
} else {
$value = bin2hex($value);
if ($intel) {
$value = Horde_Image_Exif::intel2Moto($value);
}
$data = substr($block, hexdec($value) + hexdec($offset) + 2, $bytesofdata);
}
$formated_data = $this->_formatData($type, $tag, $intel, $model, $data);
$result['SubIFD']['MakerNote'][$tag_name] = $formated_data;
}
}
}
} Horde_Image-2.0.5/lib/Horde/Image/Exif/Parser/Olympus.php 0000664 0000766 0000024 00000016124 12170303167 017747 0 ustar
* @author Jan Schneider
* @category Horde
* @package Image
*/
/**
* Exifer
* Extracts EXIF information from digital photos.
*
* Copyright © 2003 Jake Olefsky
* http://www.offsky.com/software/exif/index.php
* jake@olefsky.com
*
* ------------
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details. http://www.horde.org/licenses/gpl
*/
class Horde_Image_Exif_Parser_Olympus extends Horde_Image_Exif_Parser_Base
{
/**
*
* @param $tag
* @return unknown_type
*/
protected function _lookupTag($tag)
{
switch($tag) {
case '0200': $tag = 'SpecialMode'; break;
case '0201': $tag = 'JpegQual'; break;
case '0202': $tag = 'Macro'; break;
case '0203': $tag = 'Unknown1'; break;
case '0204': $tag = 'DigiZoom'; break;
case '0205': $tag = 'Unknown2'; break;
case '0206': $tag = 'Unknown3'; break;
case '0207': $tag = 'SoftwareRelease'; break;
case '0208': $tag = 'PictInfo'; break;
case '0209': $tag = 'CameraID'; break;
case '0f00': $tag = 'DataDump'; break;
default: $tag = 'unknown: ' . $tag; break;
}
return $tag;
}
/**
*
* @param $type
* @param $tag
* @param $intel
* @param $data
* @return unknown_type
*/
protected function _formatData($type, $tag, $intel, $data)
{
switch ($type) {
case 'ASCII':
case 'UNDEFINED':
break;
case 'URATIONAL':
case 'SRATIONAL':
$data = bin2hex($data);
if ($intel) {
$data = Horde_Image_Exif::intel2Moto($data);
}
$top = hexdec(substr($data, 8, 8));
$bottom = hexdec(substr($data, 0, 8));
if ($bottom) {
$data = $top / $bottom;
} elseif (!$top) {
$data = 0;
} else {
$data = $top . '/' . $bottom;
}
switch ($tag) {
case '0204':
//DigitalZoom
$data .= 'x';
break;
case '0205':
//Unknown2
$data = $top . '/' . $bottom;
break;
}
break;
case 'USHORT':
case 'SSHORT':
case 'ULONG':
case 'SLONG':
case 'FLOAT':
case 'DOUBLE':
$data = bin2hex($data);
if ($intel) {
$data = Horde_Image_Exif::intel2Moto($data);
}
$data = hexdec($data);
switch ($tag) {
case '0201':
//JPEGQuality
switch ($data) {
case 1: $data = 'SQ'; break;
case 2: $data = 'HQ'; break;
case 3: $data = 'SHQ'; break;
default: $data = Horde_Image_Translation::t("Unknown") . ': ' . $data; break;
}
break;
case '0202':
//Macro
switch ($data) {
case 0: $data = 'Normal'; break;
case 1: $data = 'Macro'; break;
default: $data = Horde_Image_Translation::t("Unknown") . ': ' . $data; break;
}
break;
}
break;
default:
$data = bin2hex($data);
if ($intel) {
$data = Horde_Image_Exif::intel2Moto($data);
}
break;
}
return $data;
}
/**
*
* @param $block
* @param $result
* @param $seek
* @param $globalOffset
* @return unknown_type
*/
public function parse($block, &$result, $seek, $globalOffset)
{
$intel = $result['Endien']=='Intel';
$model = $result['IFD0']['Model'];
// New header for new DSLRs - Check for it because the number of bytes
// that count the IFD fields differ in each case. Fixed by Zenphoto
// 2/24/08
$new = false;
if (substr($block, 0, 8) == "OLYMPUS\x00") {
$new = true;
} elseif (substr($block, 0, 7) == "OLYMP\x00\x01" ||
substr($block, 0, 7) == "OLYMP\x00\x02") {
$new = false;
} else {
// Header does not match known Olympus headers.
// This is not a valid OLYMPUS Makernote.
return false;
}
// Offset of IFD entry after Olympus header.
$place = 8;
$offset = 8;
// Get number of tags (1 or 2 bytes, depending on New or Old makernote)
$countfieldbits = $new ? 1 : 2;
// New makernote repeats 1-byte value twice, so increment $place by 2
// in either case.
$num = bin2hex(substr($block, $place, $countfieldbits));
$place += 2;
if ($intel) {
$num = Horde_Image_Exif::intel2Moto($num);
}
$ntags = hexdec($num);
$result['SubIFD']['MakerNote']['MakerNoteNumTags'] = $ntags;
//loop thru all tags Each field is 12 bytes
for ($i = 0; $i < $ntags; $i++) {
//2 byte tag
$tag = bin2hex(substr($block, $place, 2));
$place += 2;
if ($intel) {
$tag = Horde_Image_Exif::intel2Moto($tag);
}
$tag_name = $this->_lookupTag($tag);
//2 byte type
$type = bin2hex(substr($block, $place, 2));
$place += 2;
if ($intel) {
$type = Horde_Image_Exif::intel2Moto($type);
}
$this->_lookupType($type, $size);
//4 byte count of number of data units
$count = bin2hex(substr($block, $place, 4));
$place += 4;
if ($intel) {
$count = Horde_Image_Exif::intel2Moto($count);
}
$bytesofdata = $size * hexdec($count);
//4 byte value of data or pointer to data
$value = substr($block, $place, 4);
$place += 4;
if ($bytesofdata <= 4) {
$data = $value;
} else {
$value = bin2hex($value);
if ($intel) {
$value = Horde_Image_Exif::intel2Moto($value);
}
//offsets are from TIFF header which is 12 bytes from the start
//of the file
$v = fseek($seek, $globalOffset + hexdec($value));
$result['Errors'] = $result['Errors']++;
$data = '';
}
$formated_data = $this->_formatData($type, $tag, $intel, $data);
$result['SubIFD']['MakerNote'][$tag_name] = $formated_data;
}
}
} Horde_Image-2.0.5/lib/Horde/Image/Exif/Parser/Panasonic.php 0000664 0000766 0000024 00000045234 12170303167 020216 0 ustar
* @author Jan Schneider
* @category Horde
* @package Image
*/
/**
* Exifer
* Extracts EXIF information from digital photos.
*
* Copyright © 2003 Jake Olefsky
* http://www.offsky.com/software/exif/index.php
* jake@olefsky.com
*
* ------------
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details. http://www.horde.org/licenses/gpl
*/
class Horde_Image_Exif_Parser_Panasonic extends Horde_Image_Exif_Parser_Base
{
/**
* Looks up the name of the tag for the MakerNote (Depends on Manufacturer)
*/
protected function _lookupTag($tag)
{
switch ($tag) {
case '0001': $tag = 'Quality'; break;
case '0002': $tag = 'FirmwareVersion'; break;
case '0003': $tag = 'WhiteBalance'; break;
case '0007': $tag = 'FocusMode'; break;
case '000f': $tag = 'AFMode'; break;
case '001a': $tag = 'ImageStabilizer'; break;
case '001c': $tag = 'MacroMode'; break;
case '001f': $tag = 'ShootingMode'; break;
case '0020': $tag = 'Audio'; break;
case '0021': $tag = 'DataDump'; break;
case '0023': $tag = 'WhiteBalanceBias'; break;
case '0024': $tag = 'FlashBias'; break;
case '0025': $tag = 'SerialNumber'; break;
case '0028': $tag = 'ColourEffect'; break;
case '002a': $tag = 'BurstMode'; break;
case '002b': $tag = 'SequenceNumber'; break;
case '002c': $tag = 'Contrast'; break;
case '002d': $tag = 'NoiseReduction'; break;
case '002e': $tag = 'SelfTimer'; break;
case '0030': $tag = 'Rotation'; break;
case '0032': $tag = 'ColorMode'; break;
case '0036': $tag = 'TravelDay'; break;
default: $tag = 'unknown:' . $tag; break;
}
return $tag;
}
/**
* Formats Data for the data type
*/
protected function _formatData($type, $tag, $intel, $data)
{
switch ($type) {
case 'UBYTE':
case 'SBYTE':
$data = bin2hex($data);
if ($intel) {
$data = Horde_Image_Exif::intel2Moto($data);
}
$data = hexdec($data);
if ($tag == '000f') {
//AFMode
switch ($data) {
case 256:
$data = Horde_Image_Translation::t("9-area-focusing");
break;
case 16:
$data = Horde_Image_Translation::t("1-area-focusing");
break;
case 4096:
$data = Horde_Image_Translation::t("3-area-focusing (High speed)");
break;
case 4112:
$data = Horde_Image_Translation::t("1-area-focusing (High speed)");
break;
case 16:
$data = Horde_Image_Translation::t("1-area-focusing");
break;
case 1:
$data = Horde_Image_Translation::t("Spot-focusing");
break;
default:
$data = sprintf(Horde_Image_Translation::t("Unknown (%s)"), $data);
break;
}
}
break;
case 'URATIONAL':
case 'SRATIONAL':
$data = bin2hex($data);
if ($intel) {
$data = Horde_Image_Exif::intel2Moto($data);
}
$top = hexdec(substr($data, 8, 8));
$bottom = hexdec(substr($data, 0, 8));
if ($bottom != 0) {
$data = $top / $bottom;
} elseif ($top == 0) {
$data = 0;
} else {
$data = $top . '/' . $bottom;
}
break;
case 'USHORT':
case 'SSHORT':
case 'ULONG':
case 'SLONG':
case 'FLOAT':
case 'DOUBLE':
$data = bin2hex($data);
if ($intel) {
$data = Horde_Image_Exif::intel2Moto($data);
}
$data = hexdec($data);
switch ($tag) {
case '0001':
//Image Quality
switch ($data) {
case 2:
$data = Horde_Image_Translation::t("High");
break;
case 3:
$data = Horde_Image_Translation::t("Standard");
break;
case 6:
$data = Horde_Image_Translation::t("Very High");
break;
case 7:
$data = Horde_Image_Translation::t("RAW");
break;
default:
$data = sprintf(Horde_Image_Translation::t("Unknown (%s)"), $data);
break;
}
break;
case '0003':
//White Balance
switch ($data) {
case 1:
$data = Horde_Image_Translation::t("Auto");
break;
case 2:
$data = Horde_Image_Translation::t("Daylight");
break;
case 3:
$data = Horde_Image_Translation::t("Cloudy");
break;
case 4:
$data = Horde_Image_Translation::t("Halogen");
break;
case 5:
$data = Horde_Image_Translation::t("Manual");
break;
case 8:
$data = Horde_Image_Translation::t("Flash");
break;
case 10:
$data = Horde_Image_Translation::t("Black and White");
break;
case 11:
$data = Horde_Image_Translation::t("Manual");
break;
default:
$data = sprintf(Horde_Image_Translation::t("Unknown(%s)"), $data);
break;
}
break;
case '0007':
//Focus Mode
switch ($data) {
case 1:
$data = Horde_Image_Translation::t("Auto");
break;
case 2:
$data = Horde_Image_Translation::t("Manual");
break;
case 4:
$data = Horde_Image_Translation::t("Auto, Focus button");
break;
case 5:
$data = Horde_Image_Translation::t("Auto, Continuous");
break;
default:
$data = sprintf(Horde_Image_Translation::t("Unknown(%s)"), $data);
break;
}
break;
case '001a':
//Image Stabilizer
switch ($data) {
case 2:
$data = Horde_Image_Translation::t("Mode 1");
break;
case 3:
$data = Horde_Image_Translation::t("Off");
break;
case 4:
$data = Horde_Image_Translation::t("Mode 2");
break;
default:
$data = sprintf(Horde_Image_Translation::t("Unknown(%s)"), $data);
break;
}
break;
case '001c':
//Macro mode
switch ($data) {
case 1:
$data = Horde_Image_Translation::t("On");
break;
case 2:
$data = Horde_Image_Translation::t("Off");
break;
default:
$data = sprintf(Horde_Image_Translation::t("Unknown(%s)"), $data);
break;
}
break;
case '001f':
//Shooting Mode
switch ($data) {
case 1:
$data = Horde_Image_Translation::t("Normal");
break;
case 2:
$data = Horde_Image_Translation::t("Portrait");
break;
case 3:
$data = Horde_Image_Translation::t("Scenery");
break;
case 4:
$data = Horde_Image_Translation::t("Sports");
break;
case 5:
$data = Horde_Image_Translation::t("Night Portrait");
break;
case 6:
$data = Horde_Image_Translation::t("Program");
break;
case 7:
$data = Horde_Image_Translation::t("Aperture Priority");
break;
case 8:
$data = Horde_Image_Translation::t("Shutter Priority");
break;
case 9:
$data = Horde_Image_Translation::t("Macro");
break;
case 11:
$data = Horde_Image_Translation::t("Manual");
break;
case 13:
$data = Horde_Image_Translation::t("Panning");
break;
case 14:
$data = Horde_Image_Translation::t("Simple");
break;
case 18:
$data = Horde_Image_Translation::t("Fireworks");
break;
case 19:
$data = Horde_Image_Translation::t("Party");
break;
case 20:
$data = Horde_Image_Translation::t("Snow");
break;
case 21:
$data = Horde_Image_Translation::t("Night Scenery");
break;
case 22:
$data = Horde_Image_Translation::t("Food");
break;
case 23:
$data = Horde_Image_Translation::t("Baby");
break;
case 27:
$data = Horde_Image_Translation::t("High Sensitivity");
break;
case 29:
$data = Horde_Image_Translation::t("Underwater");
break;
case 33:
$data = Horde_Image_Translation::t("Pet");
break;
default:
$data = sprintf(Horde_Image_Translation::t("Unknown(%s)"), $data);
break;
}
break;
case '0020':
//Audio
switch ($data) {
case 1:
$data = Horde_Image_Translation::t("Yes");
break;
case 2:
$data = Horde_Image_Translation::t("No");
break;
default:
$data = sprintf(Horde_Image_Translation::t("Unknown (%s)"), $data);
break;
}
break;
case '0023':
//White Balance Bias
$data = $data . ' EV';
break;
case '0024':
//Flash Bias
$data = $data;
break;
case '0028':
//Colour Effect
switch ($data) {
case 1:
$data = Horde_Image_Translation::t("Off");
break;
case 2:
$data = Horde_Image_Translation::t("Warm");
break;
case 3:
$data = Horde_Image_Translation::t("Cool");
break;
case 4:
$data = Horde_Image_Translation::t("Black and White");
break;
case 5:
$data = Horde_Image_Translation::t("Sepia");
break;
default:
$data = sprintf(Horde_Image_Translation::t("Unknown (%s)"), $data);
break;
}
break;
case '002a':
//Burst Mode
switch ($data) {
case 0:
$data = Horde_Image_Translation::t("Off");
break;
case 1:
$data = Horde_Image_Translation::t("Low/High Quality");
break;
case 2:
$data = Horde_Image_Translation::t("Infinite");
break;
default:
$data = sprintf(Horde_Image_Translation::t("Unknown (%s)"), $data);
break;
}
break;
case '002c':
//Contrast
switch ($data) {
case 0:
$data = Horde_Image_Translation::t("Standard");
break;
case 1:
$data = Horde_Image_Translation::t("Low");
break;
case 2:
$data = Horde_Image_Translation::t("High");
break;
default:
$data = sprintf(Horde_Image_Translation::t("Unknown (%s)"), $data);
break;
}
break;
case '002d':
//Noise Reduction
switch ($data) {
case 0:
$data = Horde_Image_Translation::t("Standard");
break;
case 1:
$data = Horde_Image_Translation::t("Low");
break;
case 2:
$data = Horde_Image_Translation::t("High");
break;
default:
$data = sprintf(Horde_Image_Translation::t("Unknown (%s)"), $data);
break;
}
break;
case '002e':
//Self Timer
switch ($data) {
case 1:
$data = Horde_Image_Translation::t("Off");
break;
case 2:
$data = Horde_Image_Translation::t("10s");
break;
case 3:
$data = Horde_Image_Translation::t("2s");
break;
default:
$data = sprintf(Horde_Image_Translation::t("Unknown (%s)"), $data);
break;
}
break;
case '0030':
//Rotation
switch ($data) {
case 1:
$data = Horde_Image_Translation::t("Horizontal (normal)");
break;
case 6:
$data = Horde_Image_Translation::t("Rotate 90 CW");
break;
case 8:
$data = Horde_Image_Translation::t("Rotate 270 CW");
break;
default:
$data = sprintf(Horde_Image_Translation::t("Unknown (%s)"), $data);
break;
}
break;
case '0032':
//Color Mode
switch ($data) {
case 0:
$data = Horde_Image_Translation::t("Normal");
break;
case 1:
$data = Horde_Image_Translation::t("Natural");
break;
default:
$data = sprintf(Horde_Image_Translation::t("Unknown (%s)"), $data);
break;
}
break;
case '0036':
//Travel Day
$data = $data;
break;
}
break;
case 'UNDEFINED':
break;
default:
$data = bin2hex($data);
if ($intel) {
$data = Horde_Image_Exif::intel2Moto($data);
}
break;
}
return $data;
}
/**
* Panasonic Special data section
*/
public function parse($block, &$result)
{
$intel = true;
$model = $result['IFD0']['Model'];
//current place
$place = 8;
$offset = 8;
$num = bin2hex(substr($block, $place, 4));
$place += 4;
if ($intel) {
$num = Horde_Image_Exif::intel2Moto($num);
}
$result['SubIFD']['MakerNote']['Offset'] = hexdec($num);
//Get number of tags (2 bytes)
$num = bin2hex(substr($block, $place, 2));
$place += 2;
if ($intel) {
$num = Horde_Image_Exif::intel2Moto($num);
}
$result['SubIFD']['MakerNote']['MakerNoteNumTags'] = hexdec($num);
//loop thru all tags Each field is 12 bytes
for ($i = 0; $i < hexdec($num); $i++) {
//2 byte tag
$tag = bin2hex(substr($block, $place, 2));
$place += 2;
if ($intel) {
$tag = Horde_Image_Exif::intel2Moto($tag);
}
$tag_name = $this->_lookupTag($tag);
//2 byte type
$type = bin2hex(substr($block, $place, 2));
$place += 2;
if ($intel) {
$type = Horde_Image_Exif::intel2Moto($type);
}
$this->_lookupType($type, $size);
//4 byte count of number of data units
$count = bin2hex(substr($block, $place, 4));
$place += 4;
if ($intel) {
$count = Horde_Image_Exif::intel2Moto($count);
}
$bytesofdata = $size * hexdec($count);
//4 byte value of data or pointer to data
$value = substr($block, $place, 4);
$place += 4;
if ($bytesofdata <= 4) {
$data = $value;
} else {
$value = bin2hex($value);
if ($intel) {
$value = Horde_Image_Exif::intel2Moto($value);
}
$data = substr($block, hexdec($value) - $offset, $bytesofdata * 2);
}
$formated_data = $this->_formatData($type, $tag, $intel, $data);
$result['SubIFD']['MakerNote'][$tag_name] = $formated_data;
}
}
} Horde_Image-2.0.5/lib/Horde/Image/Exif/Parser/Sanyo.php 0000664 0000766 0000024 00000013450 12170303167 017367 0 ustar
* @author Jan Schneider
* @category Horde
* @package Image
*/
/**
* Exifer
* Extracts EXIF information from digital photos.
*
* Copyright © 2003 Jake Olefsky
* http://www.offsky.com/software/exif/index.php
* jake@olefsky.com
*
* ------------
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details. http://www.horde.org/licenses/gpl
*/
class Horde_Image_Exif_Parser_Sanyo extends Horde_Image_Exif_Parser_Base
{
/**
*
* @param $tag
* @return unknown_type
*/
protected function _lookupTag($tag)
{
switch($tag) {
case '0200': $tag = 'SpecialMode'; break;
case '0201': $tag = 'Quality'; break;
case '0202': $tag = 'Macro'; break;
case '0203': $tag = 'Unknown'; break;
case '0204': $tag = 'DigiZoom'; break;
case '0f00': $tag = 'DataDump'; break;
default: $tag = 'unknown:' . $tag; break;
}
return $tag;
}
/**
*
* @param $type
* @param $tag
* @param $intel
* @param $data
* @return unknown_type
*/
protected function _formatData($type, $tag, $intel, $data)
{
switch ($type) {
case 'ASCII':
case 'UNDEFINED':
break;
case 'URATIONAL':
case 'SRATIONAL':
$data = bin2hex($data);
if ($intel) {
$data = Horde_Image_Exif::intel2Moto($data);
}
$top = hexdec(substr($data, 8, 8));
$bottom = hexdec(substr($data, 0, 8));
if ($bottom) {
$data = $top / $bottom;
} elseif (!$top) {
$data = 0;
} else {
$data = $top . '/' . $bottom;
}
break;
case 'USHORT':
case 'SSHORT':
case 'ULONG':
case 'SLONG':
case 'FLOAT':
case 'DOUBLE':
$data = bin2hex($data);
if ($intel) {
$data = Horde_Image_Exif::intel2Moto($data);
}
$data = hexdec($data);
switch ($tag) {
case '0200':
//SpecialMode
$data = $data == 0 ? Horde_Image_Translation::t("Normal") : Horde_Image_Translation::t("Unknown") . ': ' . $data;
break;
case '0201':
//Quality
$data = $data == 2 ? Horde_Image_Translation::t("High") : Horde_Image_Translation::t("Unknown") . ': ' . $data;
break;
case '0202':
//Macro
$data = $data == 0 ? Horde_Image_Translation::t("Normal") : Horde_Image_Translation::t("Unknown") . ': ' . $data;
break;
}
break;
default:
$data = bin2hex($data);
if ($intel) {
$data = Horde_Image_Exif::intel2Moto($data);
}
}
return $data;
}
/**
*
* @param $block
* @param $result
* @param $seek
* @param $globalOffset
* @return unknown_type
*/
public function parse($block, &$result, $seek, $globalOffset)
{
$intel = $result['Endien']=='Intel';
$model = $result['IFD0']['Model'];
//current place
$place = 8;
$offset = 8;
//Get number of tags (2 bytes)
$num = bin2hex(substr($block, $place, 2));
$place += 2;
if ($intel) {
$num = Horde_Image_Exif::intel2Moto($num);
}
$result['SubIFD']['MakerNote']['MakerNoteNumTags'] = hexdec($num);
//loop thru all tags Each field is 12 bytes
for ($i = 0; $i < hexdec($num); $i++) {
//2 byte tag
$tag = bin2hex(substr($block, $place, 2));
$place += 2;
if ($intel) {
$tag = Horde_Image_Exif::intel2Moto($tag);
}
$tag_name = $this->_lookupTag($tag);
//2 byte type
$type = bin2hex(substr($block, $place, 2));
$place += 2;
if ($intel) {
$type = Horde_Image_Exif::intel2Moto($type);
}
$this->_lookupType($type, $size);
//4 byte count of number of data units
$count = bin2hex(substr($block, $place, 4));
$place += 4;
if ($intel) {
$count = Horde_Image_Exif::intel2Moto($count);
}
$bytesofdata = $size * hexdec($count);
//4 byte value of data or pointer to data
$value = substr($block, $place, 4);
$place += 4;
if ($bytesofdata <= 4) {
$data = $value;
} else {
$value = bin2hex($value);
if ($intel) {
$value = Horde_Image_Exif::intel2Moto($value);
}
//offsets are from TIFF header which is 12 bytes from the start
//of the file
$v = fseek($seek, $globalOffset + hexdec($value));
if ($v == 0) {
$data = fread($seek, $bytesofdata);
} elseif ($v == -1) {
$result['Errors'] = $result['Errors']++;
}
}
$formated_data = $this->_formatData($type, $tag, $intel, $data);
$result['SubIFD']['MakerNote'][$tag_name] = $formated_data;
}
}
}
Horde_Image-2.0.5/lib/Horde/Image/Exif/Base.php 0000664 0000766 0000024 00000010761 12170303167 015716 0 ustar
* @category Horde
* @package Image
*/
abstract class Horde_Image_Exif_Base
{
/**
* Instance parameters.
*
* @var array
*/
protected $_params;
/**
* Optional Logger
*/
protected $_logger;
/**
*
* @param $params
*/
public function __construct($params = array())
{
if (!empty($params['logger'])) {
$this->_logger = $params['logger'];
unset($params['logger']);
}
$this->_params = $params;
}
/**
*
* @param $exif
* @return unknown_type
*/
protected function _processData($exif)
{
if (!$exif) {
return array();
}
$results = array();
$fields = Horde_Image_Exif::getFields($this);
foreach ($fields as $field => $data) {
$value = isset($exif[$field]) ? $exif[$field] : '';
// Don't store empty fields.
if ($value === '') {
continue;
}
/* Special handling of GPS data */
if ($data['type'] == 'gps') {
$value = $this->_parseGPSData($exif[$field]);
if (!empty($exif[$field . 'Ref']) &&
in_array($exif[$field . 'Ref'], array('S', 'South', 'W', 'West'))) {
$value = - abs($value);
}
}
/* Date fields are converted to a timestamp.*/
if ($data['type'] == 'date') {
@list($ymd, $hms) = explode(' ', $value, 2);
@list($year, $month, $day) = explode(':', $ymd, 3);
if (!empty($hms) && !empty($year) && !empty($month) && !empty($day)) {
$time = "$month/$day/$year $hms";
$value = strtotime($time);
}
}
if ($data['type'] == 'array' || is_array($value)) {
if (is_array($value)) {
$value = implode(',', $value);
}
}
$results[$field] = $value;
}
return $results;
}
/**
* Parse the Longitude and Latitude values into a standardized format
* regardless of the source format.
*
* @param mixed $data An array containing degrees, minutes, seconds
* in index 0, 1, 2 respectifully.
*
* @return double The location data in a decimal format.
*/
protected function _parseGPSData($data)
{
// According to EXIF standard, GPS data can be in the form of
// dd/1 mm/1 ss/1 or as a decimal reprentation.
if (!is_array($data)) {
// Assume a scalar is a decimal representation. Cast it to a float
// which will get rid of any stray ordinal indicators. (N, S,
// etc...)
return (double)$data;
}
if ($data[0] == 0) {
return 0;
}
if (strpos($data[1], '/') !== false) {
$min = explode('/', $data[1]);
if (count($min) > 1) {
$min = $min[0] / $min[1];
} else {
$min = $min[0];
}
} else {
$min = $data[1];
}
if (strpos($data[2], '/') !== false) {
$sec = explode('/', $data[2]);
if (count($sec) > 1) {
$sec = $sec[0] / $sec[1];
} else {
$sec = $sec[0];
}
} else {
$sec = $data[2];
}
return self::_degToDecimal($data[0], $min, $sec);
}
/**
*
* @param $degrees
* @param $minutes
* @param $seconds
* @return unknown_type
*/
protected function _degToDecimal($degrees, $minutes, $seconds)
{
$degs = (double)($degrees + ($minutes / 60) + ($seconds / 3600));
return round($degs, 6);
}
protected function _logDebug($message)
{
if (!empty($this->_logger)) {
$this->_logger->debug($message);
}
}
protected function _logErr($message)
{
if (!empty($this->_logger)) {
$this->_logger->err($message);
}
}
abstract public function getData($image);
abstract public function supportedCategories();
} Horde_Image-2.0.5/lib/Horde/Image/Exif/Bundled.php 0000664 0000766 0000024 00000077045 12170303167 016431 0 ustar
* @package Image
*/
class Horde_Image_Exif_Bundled extends Horde_Image_Exif_Base
{
public function getData($image)
{
$raw = $this->_readData($image);
$exif = array();
foreach ($raw as $key => $value) {
if ($key == 'IFD0' || $key == 'SubIFD') {
foreach ($value as $subkey => $subvalue) {
$exif[$subkey] = $subvalue;
}
} else {
$exif[$key] = $value;
}
}
// Not really an EXIF property, but an attribute nonetheless...
// PHP's exif functions return it, so add it here to be consistent.
$exif['FileSize'] = @filesize($imageFile);
return $this->_processData($exif);
}
/**
*
* @param $path
*
* @return array
*/
protected function _readData($path)
{
// There may be an elegant way to do this with one file handle.
$in = @fopen($path, 'rb');
$seek = @fopen($path, 'rb');
$globalOffset = 0;
$result = array('Errors' => 0);
// if the path was invalid, this error will catch it
if (!$in || !$seek) {
$result['Errors'] = 1;
$result['Error'][$result['Errors']] = Horde_Image_Translation::t("The file could not be opened.");
return $result;
}
// First 2 bytes of JPEG are 0xFFD8
$data = bin2hex(fread($in, 2));
if ($data == 'ffd8') {
$result['ValidJpeg'] = 1;
} else {
$result['ValidJpeg'] = 0;
fclose($in);
fclose($seek);
return $result;
}
$result['ValidIPTCData'] = 0;
$result['ValidJFIFData'] = 0;
$result['ValidEXIFData'] = 0;
$result['ValidAPP2Data'] = 0;
$result['ValidCOMData'] = 0;
// Next 2 bytes are marker tag (0xFFE#)
$data = bin2hex(fread($in, 2));
$size = bin2hex(fread($in, 2));
// Loop through markers till you get to FFE1 (Exif marker)
while(!feof($in) && $data != 'ffe1' && $data != 'ffc0' && $data != 'ffd9') {
switch ($data) {
case 'ffe0':
// JFIF Marker
$result['ValidJFIFData'] = 1;
$result['JFIF']['Size'] = hexdec($size);
if (hexdec($size) - 2 > 0) {
$data = fread($in, hexdec($size) - 2);
$result['JFIF']['Data'] = $data;
}
$result['JFIF']['Identifier'] = substr($data, 0, 5);
$result['JFIF']['ExtensionCode'] = bin2hex(substr($data, 6, 1));
$globalOffset += hexdec($size) + 2;
break;
case 'ffed':
// IPTC Marker
$result['ValidIPTCData'] = 1;
$result['IPTC']['Size'] = hexdec($size);
if (hexdec($size) - 2 > 0) {
$data = fread($in, hexdec($size) - 2);
$result['IPTC']['Data'] = $data ;
}
$globalOffset += hexdec($size) + 2;
break;
case 'ffe2':
// EXIF extension Marker
$result['ValidAPP2Data'] = 1;
$result['APP2']['Size'] = hexdec($size);
if (hexdec($size) - 2 > 0) {
$data = fread($in, hexdec($size) - 2);
$result['APP2']['Data'] = $data ;
}
$globalOffset += hexdec($size) + 2;
break;
case 'fffe':
// COM extension Marker
$result['ValidCOMData'] = 1;
$result['COM']['Size'] = hexdec($size);
if (hexdec($size) - 2 > 0) {
$data = fread($in, hexdec($size) - 2);
$result['COM']['Data'] = $data ;
}
$globalOffset += hexdec($size) + 2;
break;
case 'ffe1':
$result['ValidEXIFData'] = 1;
break;
}
$data = bin2hex(fread($in, 2));
$size = bin2hex(fread($in, 2));
}
if ($data != 'ffe1') {
fclose($in);
fclose($seek);
return $result;
}
$result['ValidEXIFData'] = 1;
// Size of APP1
$result['APP1Size'] = hexdec($size);
// Start of APP1 block starts with 'Exif' header (6 bytes)
$header = fread($in, 6);
// Then theres a TIFF header with 2 bytes of endieness (II or MM)
$header = fread($in, 2);
switch ($header) {
case 'II':
$intel = 1;
$result['Endien'] = 'Intel';
break;
case 'MM':
$intel = 0;
$result['Endien'] = 'Motorola';
break;
default:
// not sure what the default should be, but this seems reasonable
$intel = 1;
$result['Endien'] = 'Unknown';
break;
}
// 2 bytes of 0x002a
$tag = bin2hex(fread( $in, 2 ));
// Then 4 bytes of offset to IFD0 (usually 8 which includes all 8 bytes
// of TIFF header)
$offset = bin2hex(fread($in, 4));
if ($intel == 1) {
$offset = Horde_Image_Exif::intel2Moto($offset);
}
// Check for extremely large values here
if (hexdec($offset) > 100000) {
$result['ValidEXIFData'] = 0;
fclose($in);
fclose($seek);
return $result;
}
if (hexdec($offset) > 8) {
$unknown = fread($in, hexdec($offset) - 8);
}
// add 12 to the offset to account for TIFF header
$globalOffset += 12;
//===========================================================
// Start of IFD0
$num = bin2hex(fread($in, 2));
if ($intel == 1) {
$num = Horde_Image_Exif::intel2Moto($num);
}
$num = hexdec($num);
$result['IFD0NumTags'] = $num;
// 1000 entries is too much and is probably an error.
if ($num < 1000) {
for ($i = 0; $i < $num; $i++) {
$this->_readEntry($result, $in, $seek, $intel, 'IFD0', $globalOffset);
}
} else {
$result['Errors'] = $result['Errors'] + 1;
$result['Error'][$result['Errors']] = 'Illegal size for IFD0';
}
// store offset to IFD1
$offset = bin2hex(fread($in, 4));
if ($intel == 1) {
$offset = Horde_Image_Exif::intel2Moto($offset);
}
$result['IFD1Offset'] = hexdec($offset);
// Check for SubIFD
if (!isset($result['IFD0']['ExifOffset']) ||
$result['IFD0']['ExifOffset'] == 0) {
fclose($in);
fclose($seek);
return $result;
}
// seek to SubIFD (Value of ExifOffset tag) above.
$ExitOffset = $result['IFD0']['ExifOffset'];
$v = fseek($in, $globalOffset + $ExitOffset);
if ($v == -1) {
$result['Errors'] = $result['Errors'] + 1;
$result['Error'][$result['Errors']] = Horde_Image_Translation::t("Couldnt Find SubIFD");
}
//===========================================================
// Start of SubIFD
$num = bin2hex(fread($in, 2));
if ($intel == 1) {
$num = Horde_Image_Exif::intel2Moto($num);
}
$num = hexdec($num);
$result['SubIFDNumTags'] = $num;
// 1000 entries is too much and is probably an error.
if ($num < 1000) {
for ($i = 0; $i < $num; $i++) {
$this->_readEntry($result, $in, $seek, $intel, 'SubIFD', $globalOffset);
}
} else {
$result['Errors'] = $result['Errors'] + 1;
$result['Error'][$result['Errors']] = Horde_Image_Translation::t("Illegal size for SubIFD");
}
// Add the 35mm equivalent focal length:
// Now properly get this using the FocalLength35mmFilm tag
//$result['SubIFD']['FocalLength35mmEquiv'] = get35mmEquivFocalLength($result);
// Check for IFD1
if (!isset($result['IFD1Offset']) || $result['IFD1Offset'] == 0) {
fclose($in);
fclose($seek);
return $result;
}
// seek to IFD1
$v = fseek($in, $globalOffset + $result['IFD1Offset']);
if ($v == -1) {
$result['Errors'] = $result['Errors'] + 1;
$result['Error'][$result['Errors']] = Horde_Image_Translation::t("Couldnt Find IFD1");
}
//===========================================================
// Start of IFD1
$num = bin2hex(fread($in, 2));
if ($intel == 1) {
$num = Horde_Image_Exif::intel2Moto($num);
}
$num = hexdec($num);
$result['IFD1NumTags'] = $num;
// 1000 entries is too much and is probably an error.
if ($num < 1000) {
for ($i = 0; $i < $num; $i++) {
$this->_readEntry($result, $in, $seek, $intel, 'IFD1', $globalOffset);
}
} else {
$result['Errors'] = $result['Errors'] + 1;
$result['Error'][$result['Errors']] = Horde_Image_Translation::t("Illegal size for IFD1");
}
// include the thumbnail raw data...
if ($result['IFD1']['JpegIFOffset'] > 0 &&
$result['IFD1']['JpegIFByteCount'] > 0) {
$v = fseek($seek, $globalOffset + $result['IFD1']['JpegIFOffset']);
if ($v == 0) {
$data = fread($seek, $result['IFD1']['JpegIFByteCount']);
} else if ($v == -1) {
$result['Errors'] = $result['Errors'] + 1;
}
$result['IFD1']['ThumbnailData'] = $data;
}
// Check for Interoperability IFD
if (!isset($result['SubIFD']['ExifInteroperabilityOffset']) ||
$result['SubIFD']['ExifInteroperabilityOffset'] == 0) {
fclose($in);
fclose($seek);
return $result;
}
// Seek to InteroperabilityIFD
$v = fseek($in, $globalOffset + $result['SubIFD']['ExifInteroperabilityOffset']);
if ($v == -1) {
$result['Errors'] = $result['Errors'] + 1;
$result['Error'][$result['Errors']] = Horde_Image_Translation::t("Couldnt Find InteroperabilityIFD");
}
//===========================================================
// Start of InteroperabilityIFD
$num = bin2hex(fread($in, 2));
if ($intel == 1) {
$num = Horde_Image_Exif::intel2Moto($num);
}
$num = hexdec($num);
$result['InteroperabilityIFDNumTags'] = $num;
// 1000 entries is too much and is probably an error.
if ($num < 1000) {
for ($i = 0; $i < $num; $i++) {
$this->_readEntry($result, $in, $seek, $intel, 'InteroperabilityIFD', $globalOffset);
}
} else {
$result['Errors'] = $result['Errors'] + 1;
$result['Error'][$result['Errors']] = Horde_Image_Translation::t("Illegal size for InteroperabilityIFD");
}
fclose($in);
fclose($seek);
return $result;
}
/**
*
* @param $result
* @param $in
* @param $seek
* @param $intel
* @param $ifd_name
* @param $globalOffset
* @return unknown_type
*/
protected function _readEntry(&$result, $in, $seek, $intel, $ifd_name,
$globalOffset)
{
// Still ok to read?
if (feof($in)) {
$result['Errors'] = $result['Errors'] + 1;
return;
}
// 2 byte tag
$tag = bin2hex(fread($in, 2));
if ($intel == 1) {
$tag = Horde_Image_Exif::intel2Moto($tag);
}
$tag_name = $this->_lookupTag($tag);
// 2 byte datatype
$type = bin2hex(fread($in, 2));
if ($intel == 1) {
$type = Horde_Image_Exif::intel2Moto($type);
}
$this->_lookupType($type, $size);
// 4 byte number of elements
$count = bin2hex(fread($in, 4));
if ($intel == 1) {
$count = Horde_Image_Exif::intel2Moto($count);
}
$bytesofdata = $size * hexdec($count);
// 4 byte value or pointer to value if larger than 4 bytes
$value = fread($in, 4);
// if datatype is 4 bytes or less, its the value
if ($bytesofdata <= 4) {
$data = $value;
} elseif ($bytesofdata < 100000) {
// otherwise its a pointer to the value, so lets go get it
$value = bin2hex($value);
if ($intel == 1) {
$value = Horde_Image_Exif::intel2Moto($value);
}
// offsets are from TIFF header which is 12 bytes from the start of file
$v = fseek($seek, $globalOffset+hexdec($value));
if ($v == 0) {
$data = fread($seek, $bytesofdata);
} elseif ($v == -1) {
$result['Errors'] = $result['Errors'] + 1;
}
} else {
// bytesofdata was too big, so the exif had an error
$result['Errors'] = $result['Errors'] + 1;
return;
}
// if its a maker tag, we need to parse this specially
switch ($tag_name) {
case 'MakerNote':
$make = Horde_String::lower($result['IFD0']['Make']);
$parser = null;
if (strpos($make, 'nikon') !== false) {
$parser = new Horde_Image_Exif_Parser_Nikon();
$result[$ifd_name]['KnownMaker'] = 1;
} elseif (strpos($make, 'olympus') !== false) {
$parser = new Horde_Image_Exif_Parser_Olympus();
$result[$ifd_name]['KnownMaker'] = 1;
} elseif (strpos($make, 'canon') !== false) {
$parser = new Horde_Image_Exif_Parser_Canon();
$result[$ifd_name]['KnownMaker'] = 1;
} elseif (strpos($make, 'fujifilm') !== false) {
$parser = new Horde_Image_Exif_Parser_Fujifilm();
$result[$ifd_name]['KnownMaker'] = 1;
} elseif (strpos($make, 'sanyo') !== false) {
$parser = new Horde_Image_Exif_Parser_Sanyo();
$result[$ifd_name]['KnownMaker'] = 1;
} elseif (strpos($make, 'panasonic') !== false) {
$parser = new Horde_Image_Exif_Parser_Panasonic();
$result[$ifd_name]['KnownMaker'] = 1;
} else {
$result[$ifd_name]['KnownMaker'] = 0;
}
if ($parser) {
$parser->parse($data, $result, $seek, $globalOffset);
}
break;
case 'GPSInfoOffset':
$formated_data = $this->_formatData($type, $tag, $intel, $data);
$result[$ifd_name]['GPSInfo'] = $formated_data;
$parser = new Horde_Image_Exif_Parser_Gps();
$parser->parse($data, $result, $formated_data, $seek, $globalOffset);
break;
default:
// Format the data depending on the type and tag
$formated_data = $this->_formatData($type, $tag, $intel, $data);
$result[$ifd_name][$tag_name] = $formated_data;
}
}
/**
*
* @param $tag
* @return unknown_type
*/
protected function _lookupTag($tag)
{
switch($tag)
{
// used by IFD0 'Camera Tags'
// text string up to 999 bytes long
case '000b': $tag = 'ACDComment'; break;
// integer -2147483648 to 2147483647
case '00fe': $tag = 'ImageType'; break;
// ?? Please send sample image with this tag
case '0106': $tag = 'PhotometricInterpret'; break;
// text string up to 999 bytes long
case '010e': $tag = 'ImageDescription'; break;
// text string up to 999 bytes long
case '010f': $tag = 'Make'; break;
// text string up to 999 bytes long
case '0110': $tag = 'Model'; break;
// integer values 1-9
case '0112': $tag = 'Orientation'; break;
// integer 0-65535
case '0115': $tag = 'SamplePerPixel'; break;
// positive rational number
case '011a': $tag = 'xResolution'; break;
// positive rational number
case '011b': $tag = 'yResolution'; break;
// integer values 1-2
case '011c': $tag = 'PlanarConfig'; break;
// integer values 1-3
case '0128': $tag = 'ResolutionUnit'; break;
// text string up to 999 bytes long
case '0131': $tag = 'Software'; break;
// YYYY:MM:DD HH:MM:SS
case '0132': $tag = 'DateTime'; break;
// text string up to 999 bytes long
case '013b': $tag = 'Artist'; break;
// text string
case '013c': $tag = 'HostComputer'; break;
// two positive rational numbers
case '013e': $tag = 'WhitePoint'; break;
// six positive rational numbers
case '013f': $tag = 'PrimaryChromaticities'; break;
// three positive rational numbers
case '0211': $tag = 'YCbCrCoefficients'; break;
// integer values 1-2
case '0213': $tag = 'YCbCrPositioning'; break;
// six positive rational numbers
case '0214': $tag = 'ReferenceBlackWhite'; break;
// text string up to 999 bytes long
case '8298': $tag = 'Copyright'; break;
// ??
case '8649': $tag = 'PhotoshopSettings'; break;
case '8825': $tag = 'GPSInfoOffset'; break;
// positive integer
case '8769': $tag = 'ExifOffset'; break;
// used by Exif SubIFD 'Image Tags'
// seconds or fraction of seconds 1/x
case '829a': $tag = 'ExposureTime'; break;
// positive rational number
case '829d': $tag = 'FNumber'; break;
// integer value 1-9
case '8822': $tag = 'ExposureProgram'; break;
// ??
case '8824': $tag = 'SpectralSensitivity'; break;
// integer 0-65535
case '8827': $tag = 'ISOSpeedRatings'; break;
// ??
case '9000': $tag = 'ExifVersion'; break;
// YYYY:MM:DD HH:MM:SS
case '9003': $tag = 'DateTimeOriginal'; break;
// YYYY:MM:DD HH:MM:SS
case '9004': $tag = 'DateTimedigitized'; break;
// ??
case '9101': $tag = 'ComponentsConfiguration'; break;
// positive rational number
case '9102': $tag = 'CompressedBitsPerPixel'; break;
// seconds or fraction of seconds 1/x
case '9201': $tag = 'ShutterSpeedValue'; break;
// positive rational number
case '9202': $tag = 'ApertureValue'; break;
// positive rational number
case '9203': $tag = 'BrightnessValue'; break;
// positive rational number (EV)
case '9204': $tag = 'ExposureBiasValue'; break;
// positive rational number
case '9205': $tag = 'MaxApertureValue'; break;
// positive rational number (meters)
case '9206': $tag = 'SubjectDistance'; break;
// integer 1-6 and 255
case '9207': $tag = 'MeteringMode'; break;
// integer 1-255
case '9208': $tag = 'LightSource'; break;
// integer 1-255
case '9209': $tag = 'Flash'; break;
// positive rational number (mm)
case '920a': $tag = 'FocalLength'; break;
// text string up to 999 bytes long
case '9213': $tag = 'ImageHistory'; break;
// a bunch of data
case '927c': $tag = 'MakerNote'; break;
// text string
case '9286': $tag = 'UserComment'; break;
// text string up to 999 bytes long
case '9290': $tag = 'SubsecTime'; break;
// text string up to 999 bytes long
case '9291': $tag = 'SubsecTimeOriginal'; break;
// text string up to 999 bytes long
case '9292': $tag = 'SubsecTimeDigitized'; break;
// ??
case 'a000': $tag = 'FlashPixVersion'; break;
// values 1 or 65535
case 'a001': $tag = 'ColorSpace'; break;
// ingeter 1-65535
case 'a002': $tag = 'ExifImageWidth'; break;
// ingeter 1-65535
case 'a003': $tag = 'ExifImageHeight'; break;
// text string 12 bytes long
case 'a004': $tag = 'RelatedSoundFile'; break;
// positive integer
case 'a005': $tag = 'ExifInteroperabilityOffset'; break;
// ??
case 'a20c': $tag = 'SpacialFreqResponse'; break;
// positive rational number
case 'a20b': $tag = 'FlashEnergy'; break;
// positive rational number
case 'a20e': $tag = 'FocalPlaneXResolution'; break;
// positive rational number
case 'a20f': $tag = 'FocalPlaneYResolution'; break;
// values 1-3
case 'a210': $tag = 'FocalPlaneResolutionUnit'; break;
// two integers 0-65535
case 'a214': $tag = 'SubjectLocation'; break;
// positive rational number
case 'a215': $tag = 'ExposureIndex'; break;
// values 1-8
case 'a217': $tag = 'SensingMethod'; break;
// integer
case 'a300': $tag = 'FileSource'; break;
// integer
case 'a301': $tag = 'SceneType'; break;
// undefined data type
case 'a302': $tag = 'CFAPattern'; break;
// values 0 or 1
case 'a401': $tag = 'CustomerRender'; break;
// values 0-2
case 'a402': $tag = 'ExposureMode'; break;
// values 0 or 1
case 'a403': $tag = 'WhiteBalance'; break;
// positive rational number
case 'a404': $tag = 'DigitalZoomRatio'; break;
case 'a405': $tag = 'FocalLengthIn35mmFilm';break;
// values 0-3
case 'a406': $tag = 'SceneCaptureMode'; break;
// values 0-4
case 'a407': $tag = 'GainControl'; break;
// values 0-2
case 'a408': $tag = 'Contrast'; break;
// values 0-2
case 'a409': $tag = 'Saturation'; break;
// values 0-2
case 'a40a': $tag = 'Sharpness'; break;
// used by Interoperability IFD
// text string 3 bytes long
case '0001': $tag = 'InteroperabilityIndex'; break;
// datatype undefined
case '0002': $tag = 'InteroperabilityVersion'; break;
// text string up to 999 bytes long
case '1000': $tag = 'RelatedImageFileFormat'; break;
// integer in range 0-65535
case '1001': $tag = 'RelatedImageWidth'; break;
// integer in range 0-65535
case '1002': $tag = 'RelatedImageLength'; break;
// used by IFD1 'Thumbnail'
// integer in range 0-65535
case '0100': $tag = 'ImageWidth'; break;
// integer in range 0-65535
case '0101': $tag = 'ImageLength'; break;
// integers in range 0-65535
case '0102': $tag = 'BitsPerSample'; break;
// values 1 or 6
case '0103': $tag = 'Compression'; break;
// values 0-4
case '0106': $tag = 'PhotometricInterpretation'; break;
// text string up to 999 bytes long
case '010e': $tag = 'ThumbnailDescription'; break;
// text string up to 999 bytes long
case '010f': $tag = 'ThumbnailMake'; break;
// text string up to 999 bytes long
case '0110': $tag = 'ThumbnailModel'; break;
// ??
case '0111': $tag = 'StripOffsets'; break;
// integer 1-9
case '0112': $tag = 'ThumbnailOrientation'; break;
// ??
case '0115': $tag = 'SamplesPerPixel'; break;
// ??
case '0116': $tag = 'RowsPerStrip'; break;
// ??
case '0117': $tag = 'StripByteCounts'; break;
// positive rational number
case '011a': $tag = 'ThumbnailXResolution'; break;
// positive rational number
case '011b': $tag = 'ThumbnailYResolution'; break;
// values 1 or 2
case '011c': $tag = 'PlanarConfiguration'; break;
// values 1-3
case '0128': $tag = 'ThumbnailResolutionUnit'; break;
case '0201': $tag = 'JpegIFOffset'; break;
case '0202': $tag = 'JpegIFByteCount'; break;
case '0212': $tag = 'YCbCrSubSampling'; break;
// misc
case '00ff': $tag = 'SubfileType'; break;
case '012d': $tag = 'TransferFunction'; break;
case '013d': $tag = 'Predictor'; break;
case '0142': $tag = 'TileWidth'; break;
case '0143': $tag = 'TileLength'; break;
case '0144': $tag = 'TileOffsets'; break;
case '0145': $tag = 'TileByteCounts'; break;
case '014a': $tag = 'SubIFDs'; break;
case '015b': $tag = 'JPEGTables'; break;
case '828d': $tag = 'CFARepeatPatternDim'; break;
case '828e': $tag = 'CFAPattern'; break;
case '828f': $tag = 'BatteryLevel'; break;
case '83bb': $tag = 'IPTC/NAA'; break;
case '8773': $tag = 'InterColorProfile'; break;
case '8828': $tag = 'OECF'; break;
case '8829': $tag = 'Interlace'; break;
case '882a': $tag = 'TimeZoneOffset'; break;
case '882b': $tag = 'SelfTimerMode'; break;
case '920b': $tag = 'FlashEnergy'; break;
case '920c': $tag = 'SpatialFrequencyResponse'; break;
case '920d': $tag = 'Noise'; break;
case '9211': $tag = 'ImageNumber'; break;
case '9212': $tag = 'SecurityClassification'; break;
case '9214': $tag = 'SubjectLocation'; break;
case '9215': $tag = 'ExposureIndex'; break;
case '9216': $tag = 'TIFF/EPStandardID'; break;
case 'a20b': $tag = 'FlashEnergy'; break;
default: $tag = 'unknown:'.$tag; break;
}
return $tag;
}
/**
*
* @param $type
* @param $tag
* @param $intel
* @param $data
* @return unknown_type
*/
protected function _formatData($type, $tag, $intel, $data)
{
switch ($type) {
case 'ASCII':
// Search for a null byte and stop there.
if (($pos = strpos($data, chr(0))) !== false) {
$data = substr($data, 0, $pos);
}
// Format certain kinds of strings nicely (Camera make etc.)
if ($tag == '010f') {
$data = ucwords(strtolower(trim($data)));
}
break;
case 'URATIONAL':
case 'SRATIONAL':
$data = bin2hex($data);
if ($intel == 1) {
$data = Horde_Image_Exif::intel2Moto($data);
}
if ($intel == 1) {
// intel stores them bottom-top
$top = hexdec(substr($data, 8, 8));
} else {
// motorola stores them top-bottom
$top = hexdec(substr($data, 0, 8));
}
if ($intel == 1) {
// intel stores them bottom-top
$bottom = hexdec(substr($data, 0, 8));
} else {
// motorola stores them top-bottom
$bottom = hexdec(substr($data, 8, 8));
}
if ($type == 'SRATIONAL' && $top > 2147483647) {
// this makes the number signed instead of unsigned
$top = $top - 4294967296;
}
if ($bottom != 0) {
$data = $top / $bottom;
} elseif ($top == 0) {
$data = 0;
} else {
$data = $top . '/' . $bottom;
}
// Exposure Time
if ($tag == '829a') {
if ($bottom != 0) {
$data = $top . '/' . $bottom;
} else {
$data = 0;
}
}
break;
case 'USHORT':
case 'SSHORT':
case 'ULONG':
case 'SLONG':
case 'FLOAT':
case 'DOUBLE':
$data = bin2hex($data);
if ($intel == 1) {
$data = Horde_Image_Exif::intel2Moto($data);
}
if ($intel == 0 && ($type == 'USHORT' || $type == 'SSHORT')) {
$data = substr($data, 0, 4);
}
$data = hexdec($data);
if ($type == 'SSHORT' && $data > 32767) {
// this makes the number signed instead of unsigned
$data = $data - 65536;
}
if ($type == 'SLONG' && $data > 2147483647) {
// this makes the number signed instead of unsigned
$data = $data - 4294967296;
}
break;
case 'UNDEFINED':
// ExifVersion,FlashPixVersion,InteroperabilityVersion
if ($tag == '9000' || $tag == 'a000' || $tag == '0002') {
$data = sprintf(Horde_Image_Translation::t("version %d"), $data / 100);
}
break;
default:
$data = bin2hex($data);
if ($intel == 1) {
$data = Horde_Image_Exif::intel2Moto($data);
}
break;
}
return $data;
}
/**
*
* @param $type
* @param $size
* @return unknown_type
*/
protected function _lookupType(&$type, &$size)
{
switch ($type) {
case '0001': $type = 'UBYTE'; $size = 1; break;
case '0002': $type = 'ASCII'; $size = 1; break;
case '0003': $type = 'USHORT'; $size = 2; break;
case '0004': $type = 'ULONG'; $size = 4; break;
case '0005': $type = 'URATIONAL'; $size = 8; break;
case '0006': $type = 'SBYTE'; $size = 1; break;
case '0007': $type = 'UNDEFINED'; $size = 1; break;
case '0008': $type = 'SSHORT'; $size = 2; break;
case '0009': $type = 'SLONG'; $size = 4; break;
case '000a': $type = 'SRATIONAL'; $size = 8; break;
case '000b': $type = 'FLOAT'; $size = 4; break;
case '000c': $type = 'DOUBLE'; $size = 8; break;
default: $type = 'error:'.$type; $size = 0; break;
}
return $type;
}
public function supportedCategories()
{
return array('EXIF');
}
} Horde_Image-2.0.5/lib/Horde/Image/Exif/Exiftool.php 0000664 0000766 0000024 00000005242 12170303167 016633 0 ustar
* @package Image
*/
class Horde_Image_Exif_Exiftool extends Horde_Image_Exif_Base
{
/**
* Path to exiftool binary
*
* @var string
*/
protected $_exiftool;
public function __construct($params)
{
parent::__construct($params);
if (empty($this->_params['exiftool'])) {
throw new InvalidArgumentException('Missing required exiftool path');
}
$this->_exiftool = $this->_params['exiftool'];
}
/**
* Get the image's EXIF data.
*
* @param string $image The path to an image.
*
* @return array The exif data.
*/
public function getData($image)
{
// Request the full stream of meta data in JSON format.
// -j option outputs in JSON, appending '#' to the -TAG prevents
// screen formatting.
$categories = Horde_Image_Exif::getCategories();
$tags = '';
foreach (array('EXIF', 'IPTC', 'XMP') as $category) {
foreach ($categories[$category] as $field => $value) {
$tags .= ' -' . $field . '#';
}
}
foreach ($categories['COMPOSITE'] as $field => $value) {
$tags .= ' -' . $field;
}
$command = '-j' . $tags . ' ' . $image;
$this->_logDebug('Command executed by Exiftool: ' . $command);
$results = json_decode($this->_execute($command));
$this->_logDebug('Results of Exiftool command: ' . $results);
if (is_array($results)) {
return $this->_processData((array)array_pop($results));
}
throw new Horde_Image_Exception('Unknown error running exiftool command');
}
public function supportedCategories()
{
return array('EXIF', 'IPTC', 'XMP', 'COMPOSITE');
}
/**
* Executes a exiftool command.
*
* @param string $command The command to run
*
* @return mixed The result of the command.
*/
protected function _execute($command)
{
$output = array();
$retval = null;
exec($this->_exiftool . ' ' . escapeshellcmd($command), $output, $retval);
if ($retval) {
$this->_logErr(sprintf("Error running command: %s", $command . "\n" . implode("\n", $output)));
}
if (is_array($output)) {
$output = implode('', $output);
}
return $output;
}
} Horde_Image-2.0.5/lib/Horde/Image/Exif/Php.php 0000664 0000766 0000024 00000001172 12170303167 015567 0 ustar
* @package Image
*/
class Horde_Image_Exif_Php extends Horde_Image_Exif_Base
{
public function getData($image)
{
return $this->_processData(@exif_read_data($image, 0, false));
}
public function supportedCategories()
{
return array('EXIF');
}
}
Horde_Image-2.0.5/lib/Horde/Image/Base.php 0000664 0000766 0000024 00000030353 12170303167 015022 0 ustar
* @author Michael J. Rubinsky
* @package Image
*/
abstract class Horde_Image_Base extends EmptyIterator
{
/**
* Background color.
*
* @var string
*/
protected $_background = 'white';
/**
* Observers.
*
* @var array
*/
protected $_observers = array();
/**
* The current image data.
*
* @var string
*/
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:
*
* (optional)width - The desired image width
* (optional)height - The desired image height
* (optional)type - The image type (png, jpeg etc...) If not provided,
* or set by the setType method, any image output will
* be converted to the default image type of png.
* (optional)data - The image binary data.
*
* @param array $context The object context - configuration, injected objects
*
* (required)tmpdir - Temporary directory
* (optional)logger - The logger
*
* @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'];
if (isset($params['width'])) {
$this->_width = $params['width'];
}
if (isset($params['height'])) {
$this->_height = $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($context['logger'])) {
$this->_logger = $context['logger'];
}
$this->_background = isset($params['background']) ? $params['background'] : 'white';
}
/**
* Getter for the capabilities array
*
* @return array
*/
public function getCapabilities()
{
return $this->_capabilities;
}
/**
* Check the existence of a particular capability.
*
* @param string $capability The capability to check for.
*
* @return boolean
*/
public function hasCapability($capability)
{
return in_array($capability, $this->_capabilities);
}
/**
* Generate image headers.
*/
public function headers()
{
header('Content-type: ' . $this->getContentType());
}
/**
* Return the content type for this image.
*
* @return string The content type for this image.
*/
public function getContentType()
{
return 'image/' . $this->_type;
}
/**
* Getter for the simplified image type.
*
* @return string The type of image (png, jpg, etc...)
*/
public function getType()
{
return $this->_type;
}
/**
* Setter for the image type.
*
* @param string $type The simple type for the imag (png, jpg, etc...)
*
* @return void
*/
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;
}
/**
* Draw 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;
}
}
/**
* Reset the image data to defaults.
*/
public function reset()
{
$this->_data = '';
$this->_width = null;
$this->_height = null;
$this->_background = 'white';
}
/**
* Get 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);
}
/**
* Load the image data from a string.
*
* @param string $id An arbitrary id for the image.
* @param string $image_data The data to use for the image.
*/
public function loadString($image_data)
{
$this->reset();
$this->_data = $image_data;
}
/**
* Load the image data from a file.
*
* @param string $filename The full path and filename to the file to load
* the image data from. The filename will also be
* used for the image id.
*
* @return mixed True if successful or already loaded, PEAR Error if file
* does not exist or could not be loaded.
* @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));
}
if (!$this->_data = file_get_contents($filename)) {
throw new Horde_Image_Exception(sprintf("Could not load the image file %s", $filename));
}
return true;
}
/**
* Ouputs image data to file. If $data is false, outputs current
* image data after performing any pending operations on the data.
* If $data contains raw image data, outputs that data to file without
* regard for $this->_data
*
* @param mixed String of binary image data | false
*
* @return string Path to temporary file.
*/
public function toFile($data = false)
{
$tmp = Horde_Util::getTempFile('img', false, $this->_tmpdir);
$fp = @fopen($tmp, 'wb');
fwrite($fp, $data ? $data : $this->raw());
fclose($fp);
return $tmp;
}
/**
* Display the current image.
*/
public function display()
{
$this->headers();
echo $this->raw(true);
}
/**
* 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.
*
* @return string The raw image data.
*/
public function raw($convert = false)
{
return $this->_data;
}
/**
* 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.
*
* @return boolean
*/
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);
return $effect->apply();
}
/**
* Load 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;
}
/**
* Apply any effects in the effect queue.
*/
public function applyEffects()
{
$this->raw();
}
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;
}
protected function _logDebug($message)
{
if (!empty($this->_logger)) {
$this->_logger->debug($message);
}
}
protected function _logErr($message)
{
if (!empty($this->_logger)) {
$this->_logger->err($message);
}
}
/**
* Request a specific image from the collection of images.
*
* @param integer $index The index to return
*
* @return Horde_Image_Base
*/
abstract function getImageAtIndex($index);
/**
* Return the number of image pages available in the image object.
*
* @return integer
*/
abstract function getImagePageCount();
}
Horde_Image-2.0.5/lib/Horde/Image/Effect.php 0000664 0000766 0000024 00000005616 12170303167 015350 0 ustar
* @author Michael J. Rubinsky
* @package Image
*/
class Horde_Image_Effect
{
/**
* Effect parameters.
*
* @var array
*/
protected $_params = array();
/**
* The bound Horde_Image object
*
* @var Horde_Image
*/
protected $_image = null;
protected $_logger;
/**
* Effect constructor.
*
* @param array $params Any parameters for the effect. Parameters are
* documented in each subclass.
*/
public function __construct($params = array())
{
foreach ($params as $key => $val) {
$this->_params[$key] = $val;
}
}
/**
* Bind this effect to a Horde_Image object.
*
* @param Horde_Image $image The Horde_Image object
*
* @TODO: Can we get rid of the reference here? (Looks OK for GD, but need
* to test im/imagick also).
*
* @return void
*/
public function setImageObject(&$image)
{
$this->_image = &$image;
}
public function setLogger($logger)
{
$this->_logger = $logger;
}
static public function factory($type, $driver, $params)
{
if (is_array($type)) {
list($app, $type) = $type;
}
// First check for a driver specific effect, if we can't find one,
// assume there is a vanilla effect object around.
$class = 'Horde_Image_Effect_' . $driver . '_' . $type;
$vclass = 'Horde_Image_Effect_' . $type;
if (!class_exists($class) && !class_exists($vclass)) {
if (!empty($app)) {
$path = $GLOBALS['registry']->get('fileroot', $app) . '/lib/Image/Effect/' . $driver . '/' . $type . '.php';
} else {
$path = 'Horde/Image/Effect/' . $driver . '/' . $type . '.php';
}
@include_once $path;
if (!class_exists($class)) {
if (!empty($app)) {
$path = $GLOBALS['registry']->get('fileroot', $app) . '/lib/Image/Effect/' . $type . '.php';
} else {
$path = 'Horde/Image/Effect/' . $type . '.php';
}
$class = $vclass;
@include_once $path;
}
}
if (class_exists($class)) {
$effect = new $class($params);
} else {
$params['logger']->err(sprintf("Horde_Image_Effect %s for %s driver not found.", $type, $driver));
throw new Horde_Image_Exception(sprintf("Horde_Image_Effect %s for %s driver not found.", $type, $driver));
}
if (!empty($params['logger'])) {
$effect->setLogger($params['logger']);
}
return $effect;
}
} Horde_Image-2.0.5/lib/Horde/Image/Exception.php 0000664 0000766 0000024 00000000315 12170303167 016101 0 ustar
* @category Horde
* @package Image
*/
class Horde_Image_Exception extends Horde_Exception_Wrapped {
}
Horde_Image-2.0.5/lib/Horde/Image/Exif.php 0000664 0000766 0000024 00000055277 12170303167 015057 0 ustar
* @author Chuck Hagenbuch
* @category Horde
* @package Image
*/
class Horde_Image_Exif
{
/**
* Factory method for instantiating a Horde_Image_Exif object.
*
* @param string $driver
* @param array $params
*
* @return Horde_Image_Exif
*/
static public function factory($driver = null, $params = array())
{
if (empty($driver) && function_exists('exif_read_data')) {
$driver = 'Php';
} elseif (empty($driver)) {
$driver = 'Bundled';
} else {
$driver = basename($driver);
}
$class = 'Horde_Image_Exif_' . $driver;
return new $class($params);
}
/**
* Converts from Intel to Motorola endien. Just reverses the bytes
* (assumes hex is passed in)
*
* @param $intel
*
* @return
*/
static public function intel2Moto($intel)
{
$len = strlen($intel);
$moto = '';
for($i = 0; $i <= $len; $i += 2) {
$moto .= substr($intel, $len-$i, 2);
}
return $moto;
}
/**
* Obtain an array of supported meta data fields.
*
* @TODO: This should probably be extended by the subclass?
*
* @return array
*/
static public function getCategories()
{
return array(
'IPTC' => array(
'Keywords' => array('description' => Horde_Image_Translation::t("Image keywords"), 'type' => 'array'),
'ObjectName' => array('description' => Horde_Image_Translation::t("Image Title"), 'type' => 'text'),
'By-line' => array('description' => Horde_Image_Translation::t("By"), 'type' => 'text'),
'CopyrightNotice' => array('description' => Horde_Image_Translation::t("Copyright"), 'type' => 'text'),
'Caption-Abstract' => array('description' => Horde_Image_Translation::t("Caption"), 'type' => 'text'),
),
'XMP' => array(
'Creator' => array('description' => Horde_Image_Translation::t("Image Creator"), 'type' => 'text'),
'Rights' => array('description' => Horde_Image_Translation::t("Rights"), 'type' => 'text'),
'UsageTerms' => array('description' => Horde_Image_Translation::t("Usage Terms"), 'type' => 'type'),
),
'EXIF' => array(
'DateTime' => array('description' => Horde_Image_Translation::t("Date Photo Modified"), 'type' => 'date'),
'DateTimeOriginal' => array('description' => Horde_Image_Translation::t("Date Photo Taken"), 'type' => 'date'),
'DateTimeDigitized' => array('description' => Horde_Image_Translation::t("Date Photo Digitized"), 'type' => 'date'),
'GPSLatitude' => array('description' => Horde_Image_Translation::t("Latitude"), 'type' => 'gps'),
'GPSLongitude' => array('description' => Horde_Image_Translation::t("Longitude"), 'type' => 'gps'),
'Make' => array('description' => Horde_Image_Translation::t("Camera Make"), 'type' => 'text'),
'Model' => array('description' => Horde_Image_Translation::t("Camera Model"), 'type' => 'text'),
'Software' => array('description' => Horde_Image_Translation::t("Software Version"), 'type' => 'text'),
'ImageType' => array('description' => Horde_Image_Translation::t("Photo Type"), 'type' => 'text'),
'ImageDescription' => array('description' => Horde_Image_Translation::t("Photo Description"), 'type' => 'text'),
'FileSize' => array('description' => Horde_Image_Translation::t("File Size"), 'type' => 'number'),
'ExifImageWidth' => array('description' => Horde_Image_Translation::t("Width"), 'type' => 'number'),
'ExifImageLength' => array('description' => Horde_Image_Translation::t("Height"), 'type' => 'number'),
'XResolution' => array('description' => Horde_Image_Translation::t("X Resolution"), 'type' => 'number'),
'YResolution' => array('description' => Horde_Image_Translation::t("Y Resolution"), 'type' => 'number'),
'ResolutionUnit' => array('description' => Horde_Image_Translation::t("Resolution Unit"), 'type' => 'text'),
'ShutterSpeedValue' => array('description' => Horde_Image_Translation::t("Shutter Speed"), 'type' => 'number'),
'ExposureTime' => array('description' => Horde_Image_Translation::t("Exposure"), 'type' => 'number'),
'FocalLength' => array('description' => Horde_Image_Translation::t("Focal Length"), 'type' => 'number'),
'FocalLengthIn35mmFilm' => array('description' => Horde_Image_Translation::t("Focal Length (35mm equiv)"), 'type' => 'number'),
'ApertureValue' => array('description' => Horde_Image_Translation::t("Aperture"), 'type' => 'number'),
'FNumber' => array('description' => Horde_Image_Translation::t("F-Number"), 'type' => 'number'),
'ISOSpeedRatings' => array('description' => Horde_Image_Translation::t("ISO Setting"), 'type' => 'number'),
'ExposureBiasValue' => array('description' => Horde_Image_Translation::t("Exposure Bias"), 'type' => 'number'),
'ExposureMode' => array('description' => Horde_Image_Translation::t("Exposure Mode"), 'type' => 'number'),
'ExposureProgram' => array('description' => Horde_Image_Translation::t("Exposure Program"), 'type' => 'number'),
'MeteringMode' => array('description' => Horde_Image_Translation::t("Metering Mode"), 'type' => 'number'),
'Flash' => array('description' => Horde_Image_Translation::t("Flash Setting"), 'type' => 'number'),
'UserComment' => array('description' => Horde_Image_Translation::t("User Comment"), 'type' => 'text'),
'ColorSpace' => array('description' => Horde_Image_Translation::t("Color Space"), 'type' => 'number'),
'SensingMethod' => array('description' => Horde_Image_Translation::t("Sensing Method"), 'type' => 'number'),
'WhiteBalance' => array('description' => Horde_Image_Translation::t("White Balance"), 'type' => 'number'),
'Orientation' => array('description' => Horde_Image_Translation::t("Camera Orientation"), 'type' => 'number'),
'Copyright' => array('description' => Horde_Image_Translation::t("Copyright"), 'type' => 'text'),
'Artist' => array('description' => Horde_Image_Translation::t("Artist"), 'type' => 'text'),
'LightSource' => array('description' => Horde_Image_Translation::t("Light source"), 'type' => 'number'),
'ImageStabalization' => array('description' => Horde_Image_Translation::t("Image Stabilization"), 'type' => 'text'),
'SceneCaptureType' => array('description' => Horde_Image_Translation::t("Scene Type"), 'type' => 'number'),
),
'COMPOSITE' => array(
'LensID' => array('description' => Horde_Image_Translation::t("Lens Id"), 'type' => 'text'),
'Lens' => array('description' => 'Lens', 'type' => 'text'),
'Aperture' => array('description' => Horde_Image_Translation::t("Aperture"), 'type' => 'text'),
'DOF' => array('description' => Horde_Image_Translation::t("Depth of Field"), 'type' => 'text'),
'FOV' => array('description' => Horde_Image_Translation::t("Field of View"), 'type' => 'text')
)
);
}
/**
* Return a flattened array of supported metadata fields.
*
* @param $driver
* @return unknown_type
*/
static public function getFields($driver = null, $description_only = false)
{
if (!is_null($driver) && is_array($driver)) {
$driver = self::factory($driver[0], $driver[1]);
}
if ($driver instanceof Horde_Image_Exif_Base) {
$supported = $driver->supportedCategories();
} else {
$supported = array('XMP', 'IPTC', 'EXIF' );
}
$categories = self::getCategories();
$flattened = array();
foreach ($supported as $category) {
$flattened = array_merge($flattened, $categories[$category]);
}
if ($description_only) {
foreach ($flattened as $key => $data) {
$return[$key] = $data['description'];
}
return $return;
}
return $flattened;
}
/**
* More human friendly exposure formatting.
*/
static protected function _formatExposure($data)
{
if ($data > 0) {
if ($data > 1) {
return sprintf(Horde_Image_Translation::t("%d sec"), round($data, 2));
} else {
$n = $d = 0;
self::_convertToFraction($data, $n, $d);
if ($n <> 1) {
return sprintf(Horde_Image_Translation::t("%4f sec"), $n / $d);
}
return sprintf(Horde_Image_Translation::t("%s / %s sec"), $n, $d);
}
} else {
return Horde_Image_Translation::t("Bulb");
}
}
/**
* Converts a floating point number into a fraction.
* Many thanks to Matthieu Froment for this code.
*
* (Ported from the Exifer library).
*/
static protected function _convertToFraction($v, &$n, &$d)
{
$MaxTerms = 15; // Limit to prevent infinite loop
$MinDivisor = 0.000001; // Limit to prevent divide by zero
$MaxError = 0.00000001; // How close is enough
// Initialize fraction being converted
$f = $v;
// Initialize fractions with 1/0, 0/1
$n_un = 1;
$d_un = 0;
$n_deux = 0;
$d_deux = 1;
for ($i = 0; $i < $MaxTerms; $i++) {
$a = floor($f); // Get next term
$f = $f - $a; // Get new divisor
$n = $n_un * $a + $n_deux; // Calculate new fraction
$d = $d_un * $a + $d_deux;
$n_deux = $n_un; // Save last two fractions
$d_deux = $d_un;
$n_un = $n;
$d_un = $d;
// Quit if dividing by zero
if ($f < $MinDivisor) {
break;
}
if (abs($v - $n / $d) < $MaxError) {
break;
}
// reciprocal
$f = 1 / $f;
}
}
/**
* Convert an exif field into human-readable form.
* Some of these cases are ported from the Exifer library, others were
* changed from their implementation where the EXIF standard dictated
* different behaviour.
*
* @param string $field The name of the field to translate.
* @param string $data The data value to translate.
*
* @return string The converted data.
*/
static public function getHumanReadable($field, $data)
{
switch ($field) {
case 'ExposureMode':
switch ($data) {
case 0: return Horde_Image_Translation::t("Auto exposure");
case 1: return Horde_Image_Translation::t("Manual exposure");
case 2: return Horde_Image_Translation::t("Auto bracket");
default: return Horde_Image_Translation::t("Unknown");
}
case 'ExposureProgram':
switch ($data) {
case 1: return Horde_Image_Translation::t("Manual");
case 2: return Horde_Image_Translation::t("Normal Program");
case 3: return Horde_Image_Translation::t("Aperture Priority");
case 4: return Horde_Image_Translation::t("Shutter Priority");
case 5: return Horde_Image_Translation::t("Creative");
case 6: return Horde_Image_Translation::t("Action");
case 7: return Horde_Image_Translation::t("Portrait");
case 8: return Horde_Image_Translation::t("Landscape");
default: return Horde_Image_Translation::t("Unknown");
}
case 'XResolution':
case 'YResolution':
if (strpos($data, '/') !== false) {
list($n, $d) = explode('/', $data, 2);
return sprintf(Horde_Image_Translation::t("%d dots per unit"), $n);
}
return sprintf(Horde_Image_Translation::t("%d per unit"), $data);
case 'ResolutionUnit':
switch ($data) {
case 1: return Horde_Image_Translation::t("Pixels");
case 2: return Horde_Image_Translation::t("Inch");
case 3: return Horde_Image_Translation::t("Centimeter");
default: return Horde_Image_Translation::t("Unknown");
}
case 'ExifImageWidth':
case 'ExifImageLength':
return sprintf(Horde_Image_Translation::t("%d pixels"), $data);
case 'Orientation':
switch ($data) {
case 1:
return sprintf(Horde_Image_Translation::t("Normal (O deg)"));
case 2:
return sprintf(Horde_Image_Translation::t("Mirrored"));
case 3:
return sprintf(Horde_Image_Translation::t("Upsidedown"));
case 4:
return sprintf(Horde_Image_Translation::t("Upsidedown Mirrored"));
case 5:
return sprintf(Horde_Image_Translation::t("90 deg CW Mirrored"));
case 6:
return sprintf(Horde_Image_Translation::t("90 deg CCW"));
case 7:
return sprintf(Horde_Image_Translation::t("90 deg CCW Mirrored"));
case 8:
return sprintf(Horde_Image_Translation::t("90 deg CW"));
}
break;
case 'ExposureTime':
if (strpos($data, '/') !== false) {
list($n, $d) = explode('/', $data, 2);
if ($d == 0) {
return;
}
$data = $n / $d;
}
return self::_formatExposure($data);
case 'ShutterSpeedValue':
if (strpos($data, '/') !== false) {
list($n, $d) = explode('/', $data, 2);
if ($d == 0) {
return;
}
$data = $n / $d;
}
$data = exp($data * log(2));
if ($data > 0) {
$data = 1 / $data;
}
return self::_formatExposure($data);
case 'ApertureValue':
case 'MaxApertureValue':
if (strpos($data, '/') !== false) {
list($n, $d) = explode('/', $data, 2);
if ($d == 0) {
return;
}
$data = $n / $d;
$data = exp(($data * log(2)) / 2);
// Precision is 1 digit.
$data = round($data, 1);
}
return 'f/' . $data;
case 'FocalLength':
if (strpos($data, '/') !== false) {
list($n, $d) = explode('/', $data, 2);
if ($d == 0) {
return;
}
return sprintf(Horde_Image_Translation::t("%d mm"), round($n / $d));
}
return sprintf(Horde_Image_Translation::t("%d mm"), $data);
case 'FNumber':
if (strpos($data, '/') !== false) {
list($n, $d) = explode('/', $data, 2);
if ($d != 0) {
return 'f/' . round($n / $d, 1);
}
}
return 'f/' . $data;
case 'ExposureBiasValue':
if (strpos($data, '/') !== false) {
list($n, $d) = explode('/', $data, 2);
if ($n == 0) {
return '0 EV';
}
}
return $data . ' EV';
case 'MeteringMode':
switch ($data) {
case 0: return Horde_Image_Translation::t("Unknown");
case 1: return Horde_Image_Translation::t("Average");
case 2: return Horde_Image_Translation::t("Center Weighted Average");
case 3: return Horde_Image_Translation::t("Spot");
case 4: return Horde_Image_Translation::t("Multi-Spot");
case 5: return Horde_Image_Translation::t("Multi-Segment");
case 6: return Horde_Image_Translation::t("Partial");
case 255: return Horde_Image_Translation::t("Other");
default: return sprintf(Horde_Image_Translation::t("Unknown: %s"), $data);
}
break;
case 'LightSource':
switch ($data) {;
case 1: return Horde_Image_Translation::t("Daylight");
case 2: return Horde_Image_Translation::t("Fluorescent");
case 3: return Horde_Image_Translation::t("Tungsten");
case 4: return Horde_Image_Translation::t("Flash");
case 9: return Horde_Image_Translation::t("Fine weather");
case 10: return Horde_Image_Translation::t("Cloudy weather");
case 11: return Horde_Image_Translation::t("Shade");
case 12: return Horde_Image_Translation::t("Daylight fluorescent");
case 13: return Horde_Image_Translation::t("Day white fluorescent");
case 14: return Horde_Image_Translation::t("Cool white fluorescent");
case 15: return Horde_Image_Translation::t("White fluorescent");
case 17: return Horde_Image_Translation::t("Standard light A");
case 18: return Horde_Image_Translation::t("Standard light B");
case 19: return Horde_Image_Translation::t("Standard light C");
case 20: return 'D55';
case 21: return 'D65';
case 22: return 'D75';
case 23: return 'D50';
case 24: return Horde_Image_Translation::t("ISO studio tungsten");
case 255: return Horde_Image_Translation::t("other light source");
default: return Horde_Image_Translation::t("Unknown");
}
case 'WhiteBalance':
switch ($data) {
case 0: return Horde_Image_Translation::t("Auto");
case 1: return Horde_Image_Translation::t("Manual");
default: Horde_Image_Translation::t("Unknown");
}
break;
case 'FocalLengthIn35mmFilm':
return $data . ' mm';
case 'Flash':
switch ($data) {
case 0: return Horde_Image_Translation::t("No Flash");
case 1: return Horde_Image_Translation::t("Flash");
case 5: return Horde_Image_Translation::t("Flash, strobe return light not detected");
case 7: return Horde_Image_Translation::t("Flash, strobe return light detected");
case 9: return Horde_Image_Translation::t("Compulsory Flash");
case 13: return Horde_Image_Translation::t("Compulsory Flash, Return light not detected");
case 15: return Horde_Image_Translation::t("Compulsory Flash, Return light detected");
case 16: return Horde_Image_Translation::t("No Flash");
case 24: return Horde_Image_Translation::t("No Flash");
case 25: return Horde_Image_Translation::t("Flash, Auto-Mode");
case 29: return Horde_Image_Translation::t("Flash, Auto-Mode, Return light not detected");
case 31: return Horde_Image_Translation::t("Flash, Auto-Mode, Return light detected");
case 32: return Horde_Image_Translation::t("No Flash");
case 65: return Horde_Image_Translation::t("Red Eye");
case 69: return Horde_Image_Translation::t("Red Eye, Return light not detected");
case 71: return Horde_Image_Translation::t("Red Eye, Return light detected");
case 73: return Horde_Image_Translation::t("Red Eye, Compulsory Flash");
case 77: return Horde_Image_Translation::t("Red Eye, Compulsory Flash, Return light not detected");
case 79: return Horde_Image_Translation::t("Red Eye, Compulsory Flash, Return light detected");
case 89: return Horde_Image_Translation::t("Red Eye, Auto-Mode");
case 93: return Horde_Image_Translation::t("Red Eye, Auto-Mode, Return light not detected");
case 95: return Horde_Image_Translation::t("Red Eye, Auto-Mode, Return light detected");
}
break;
case 'FileSize':
if ($data <= 0) {
return '0 Bytes';
}
$s = array('B', 'kB', 'MB', 'GB');
$e = floor(log($data, 1024));
return round($data/pow(1024, $e), 2) . ' ' . $s[$e];
case 'SensingMethod':
switch ($data) {
case 1: return Horde_Image_Translation::t("Not defined");
case 2: return Horde_Image_Translation::t("One Chip Color Area Sensor");
case 3: return Horde_Image_Translation::t("Two Chip Color Area Sensor");
case 4: return Horde_Image_Translation::t("Three Chip Color Area Sensor");
case 5: return Horde_Image_Translation::t("Color Sequential Area Sensor");
case 7: return Horde_Image_Translation::t("Trilinear Sensor");
case 8: return Horde_Image_Translation::t("Color Sequential Linear Sensor");
default: return Horde_Image_Translation::t("Unknown");
}
case 'ColorSpace':
switch ($data) {
case 1: return Horde_Image_Translation::t("sRGB");
default: return Horde_Image_Translation::t("Uncalibrated");
}
case 'SceneCaptureType':
switch ($data) {
case 0: return Horde_Image_Translation::t("Standard");
case 1: return Horde_Image_Translation::t("Landscape");
case 2: return Horde_Image_Translation::t("Portrait");
case 3: return Horde_Image_Translation::t("Night Scene");
default: return Horde_Image_Translation::t("Unknown");
}
case 'DateTime':
case 'DateTimeOriginal':
case 'DateTimeDigitized':
return date('m/d/Y H:i:s O', $data);
case 'UserComment':
//@TODO: the first 8 bytes of this field contain the charset used
// to encode the comment. Either ASCII, JIS, UNICODE, or
// UNDEFINED. Should probably either convert to a known charset
// here and let the calling code deal with it, or allow this
// method to take an optional charset to convert to (would
// introduce a dependency on Horde_String to do the conversion).
$data = trim(substr($data, 7)) ;
default:
return !empty($data) ? $data : '---';
}
}
}
Horde_Image-2.0.5/lib/Horde/Image/Gd.php 0000664 0000766 0000024 00000077222 12170303167 014510 0 ustar
* @author Michael J. Rubinsky
* @package Image
*/
class Horde_Image_Gd extends Horde_Image_Base
{
/**
* Capabilites of this driver.
*
* @var array
*/
protected $_capabilities = array('resize',
'crop',
'rotate',
'flip',
'mirror',
'grayscale',
'sepia',
'yellowize',
'canvas');
/**
* GD Image resource for the current image data.
*
* @var resource
*/
protected $_im;
/**
* Const'r
*
* @param $params
*
* @return Horde_Image_gd
*/
public function __construct($params, $context = array())
{
parent::__construct($params, $context);
if (!empty($params['filename'])) {
$this->loadFile($params['filename']);
} elseif (!empty($params['data'])) {
$this->loadString($params['data']);
} elseif (!empty($params['width'])) {
$this->_im = $this->create($this->_width, $this->_height);
$this->call('imageFill', array($this->_im, 0, 0, $this->_allocateColor($this->_background)));
}
}
public function __get($property)
{
switch ($property) {
case '_im':
return $this->_im;
}
}
/**
* Display the current image.
*/
public function display()
{
$this->headers();
return $this->call('image' . $this->_type, array($this->_im));
}
/**
* Returns the raw data for this image.
*
* @param boolean $convert (ignored)
*
* @return string The raw image data.
*/
public function raw($convert = false)
{
if (!is_resource($this->_im)) {
return '';
}
ob_start();
call_user_func('image' . $this->_type, $this->_im);
return ob_get_clean();
}
/**
* Reset the image data.
*/
public function reset()
{
parent::reset();
if (is_resource($this->_im)) {
return $this->call('imageDestroy', array($this->_im));
}
return true;
}
/**
* Get the height and width of the current image.
*
* @return array An hash with 'width' containing the width,
* 'height' containing the height of the image.
*/
public function getDimensions()
{
if (is_resource($this->_im) && $this->_width == 0 && $this->_height ==0) {
$this->_width = $this->call('imageSX', array($this->_im));
$this->_height = $this->call('imageSY', array($this->_im));
return array('width' => $this->_width,
'height' => $this->_height);
} else {
return array('width' => $this->_width,
'height' => $this->_height);
}
}
/**
* Creates a color that can be accessed in this object. When a
* color is set, the integer resource of it is returned.
*
* @param string $name The name of the color.
* @param int $alpha Alpha transparency (0 - 127)
*
* @return integer The resource of the color that can be passed to GD.
*/
private function _allocateColor($name, $alpha = 0)
{
static $colors = array();
if (empty($colors[$name])) {
list($r, $g, $b) = self::getRGB($name);
$colors[$name] = $this->call('imageColorAllocateAlpha', array($this->_im, $r, $g, $b, $alpha));
}
return $colors[$name];
}
/**
* Get numeric font size from textual description
*
* @param string $font The textual size description
*
* @return integer
*/
private function _getFont($font)
{
switch ($font) {
case 'tiny':
return 1;
case 'medium':
return 3;
case 'large':
return 4;
case 'giant':
return 5;
case 'small':
default:
return 2;
}
}
/**
* Load the image data from a string.
*
* @param string $image_data The data to use for the image.
*
* @return void
*/
public function loadString($image_data)
{
$this->_im = $this->call('imageCreateFromString', array($image_data));
}
/**
* Load the image data from a file.
*
* @param string $filename The full path and filename to the file to load
* the image data from. The filename will also be
* used for the image id.
*
* @return boolean
* @throws Horde_Image_Exception
*/
public function loadFile($filename)
{
$info = $this->call('getimagesize', array($filename));
if (is_array($info)) {
switch ($info[2]) {
case 1:
if (function_exists('imagecreatefromgif')) {
$this->_im = $this->call('imagecreatefromgif', array($filename));
}
break;
case 2:
$this->_im = $this->call('imagecreatefromjpeg', array($filename));
break;
case 3:
$this->_im = $this->call('imagecreatefrompng', array($filename));
break;
case 15:
if (function_exists('imagecreatefromgwbmp')) {
$this->_im = $this->call('imagecreatefromgwbmp', array($filename));
}
break;
case 16:
$this->_im = $this->call('imagecreatefromxbm', array($filename));
break;
}
}
if (is_resource($this->_im)) {
return true;
}
$result = parent::loadFile($filename);
$this->_im = $this->call('imageCreateFromString', array($this->_data));
}
/**
* Resize the current image.
*
* @param integer $width The new width.
* @param integer $height The new height.
* @param boolean $ratio Maintain original aspect ratio.
*
* @return boolean
*/
public function resize($width, $height, $ratio = true)
{
/* Abort if we're asked to divide by zero, truncate the image
* completely in either direction, or there is no image data.
*/
if (!$width || !$height || !is_resource($this->_im)) {
throw new Horde_Image_Exception('Unable to resize image.');
}
if ($ratio) {
if ($width / $height > $this->call('imageSX', array($this->_im)) / $this->call('imageSY', array($this->_im))) {
$width = $height * $this->call('imageSX', array($this->_im)) / $this->call('imageSY', array($this->_im));
} else {
$height = $width * $this->call('imageSY', array($this->_im)) / $this->call('imageSX', array($this->_im));
}
}
$im = $this->_im;
$this->_im = $this->create($width, $height);
/* Reset geometry since it will change */
$this->_width = 0;
$this->_height = 0;
$this->call('imageFill', array($this->_im, 0, 0, $this->call('imageColorAllocate', array($this->_im, 255, 255, 255))));
try {
$this->call('imageCopyResampled', array($this->_im, $im, 0, 0, 0, 0, $width, $height, $this->call('imageSX', array($im)), $this->call('imageSY', array($im))));
} catch (Horde_Image_Exception $e) {
$this->call('imageCopyResized', array($this->_im, $im, 0, 0, 0, 0, $width, $height, $this->call('imageSX', array($im)), $this->call('imageSY', array($im))));
}
}
/**
* Crop the current image.
*
* @param integer $x1 The top left corner of the cropped image.
* @param integer $y1 The top right corner of the cropped image.
* @param integer $x2 The bottom left corner of the cropped image.
* @param integer $y2 The bottom right corner of the cropped image.
*/
public function crop($x1, $y1, $x2, $y2)
{
$im = $this->_im;
$this->_im = $this->create($x2 - $x1, $y2 - $y1);
$this->_width = 0;
$this->_height = 0;
$this->call('imageCopy', array($this->_im, $im, 0, 0, $x1, $y1, $x2 - $x1, $y2 - $y1));
}
/**
* Rotate 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')
{
$background = $this->_allocateColor($background);
$this->_width = 0;
$this->_height = 0;
switch ($angle) {
case '90':
$x = $this->call('imageSX', array($this->_im));
$y = $this->call('imageSY', array($this->_im));
$xymax = max($x, $y);
$im = $this->create($xymax, $xymax);
$im = $this->call('imageRotate', array($im, 270, $background));
$this->_im = $im;
$im = $this->create($y, $x);
if ($x < $y) {
$this->call('imageCopy', array($im, $this->_im, 0, 0, 0, 0, $xymax, $xymax));
} elseif ($x > $y) {
$this->call('imageCopy', array($im, $this->_im, 0, 0, $xymax - $y, $xymax - $x, $xymax, $xymax));
}
$this->_im = $im;
break;
default:
$this->_im = $this->call('imageRotate', array($this->_im, 360 - $angle, $background));
}
}
/**
* Flip the current image.
*/
public function flip()
{
$x = $this->call('imageSX', array($this->_im));
$y = $this->call('imageSY', array($this->_im));
$im = $this->create($x, $y);
for ($curY = 0; $curY < $y; $curY++) {
$this->call('imageCopy', array($im, $this->_im, 0, $y - ($curY + 1), 0, $curY, $x, 1));
}
$this->_im = $im;
}
/**
* Mirror the current image.
*/
public function mirror()
{
$x = $this->call('imageSX', array($this->_im));
$y = $this->call('imageSY', array($this->_im));
$im = $this->create($x, $y);
for ($curX = 0; $curX < $x; $curX++) {
$this->call('imageCopy', array($im, $this->_im, $x - ($curX + 1), 0, $curX, 0, 1, $y));
}
$this->_im = $im;
}
/**
* Convert the current image to grayscale.
*/
public function grayscale()
{
$rateR = .229;
$rateG = .587;
$rateB = .114;
$whiteness = 3;
if ($this->call('imageIsTrueColor', array($this->_im)) === true) {
$this->call('imageTrueColorToPalette', array($this->_im, true, 256));
}
$colors = min(256, $this->call('imageColorsTotal', array($this->_im)));
for ($x = 0; $x < $colors; $x++) {
$src = $this->call('imageColorsForIndex', array($this->_im, $x));
$new = min(255, abs($src['red'] * $rateR + $src['green'] * $rateG + $src['blue'] * $rateB) + $whiteness);
$this->call('imageColorSet', array($this->_im, $x, $new, $new, $new));
}
}
/**
* Sepia filter.
*
* Basically turns the image to grayscale and then adds some
* defined tint on it (R += 30, G += 43, B += -23) so it will
* appear to be a very old picture.
*
* @param integer $threshold (Ignored in GD driver for now)
*/
public function sepia($threshold = 85)
{
$tintR = 80;
$tintG = 43;
$tintB = -23;
$rateR = .229;
$rateG = .587;
$rateB = .114;
$whiteness = 3;
if ($this->call('imageIsTrueColor', array($this->_im)) === true) {
$this->call('imageTrueColorToPalette', array($this->_im, true, 256));
}
$colors = max(256, $this->call('imageColorsTotal', array($this->_im)));
for ($x = 0; $x < $colors; $x++) {
$src = $this->call('imageColorsForIndex', array($this->_im, $x));
$new = min(255, abs($src['red'] * $rateR + $src['green'] * $rateG + $src['blue'] * $rateB) + $whiteness);
$r = min(255, $new + $tintR);
$g = min(255, $new + $tintG);
$b = min(255, $new + $tintB);
$this->call('imageColorSet', array($this->_im, $x, $r, $g, $b));
}
}
/**
* Yellowize filter.
*
* Adds a layer of yellow that can be transparent or solid. If
* $intensityA is 255 the image will be 0% transparent (solid).
*
* @param integer $intensityY How strong should the yellow (red and green) be? (0-255)
* @param integer $intensityB How weak should the blue be? (>= 2, in the positive limit it will be make BLUE 0)
*/
public function yellowize($intensityY = 50, $intensityB = 3)
{
if ($this->call('imageIsTrueColor', array($this->_im)) === true) {
$this->call('imageTrueColorToPalette', array($this->_im, true, 256));
}
$colors = max(256, $this->call('imageColorsTotal', array($this->_im)));
for ($x = 0; $x < $colors; $x++) {
$src = $this->call('imageColorsForIndex', array($this->_im, $x));
$r = min($src['red'] + $intensityY, 255);
$g = min($src['green'] + $intensityY, 255);
$b = max(($r + $g) / max($intensityB, 2), 0);
$this->call('imageColorSet', array($this->_im, $x, $r, $g, $b));
}
}
/**
* Draws a text string on the image in a specified location, with
* the specified style information.
*
* @param string $string 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 (ignored for GD - font determined by
* $fontsize).
* @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 The font (size) to use.
*
* @return boolean
*/
public function text($string, $x, $y, $font = 'monospace', $color = 'black', $direction = 0, $fontsize = 'small')
{
$c = $this->_allocateColor($color);
$f = $this->_getFont($fontsize);
switch ($direction) {
case -90:
case 270:
$result = $this->call('imageStringUp', array($this->_im, $f, $x, $y, $string, $c));
break;
case 0:
default:
$result = $this->call('imageString', array($this->_im, $f, $x, $y, $string, $c));
}
return $result;
}
/**
* Draw a circle.
*
* @param integer $x The x co-ordinate of the centre.
* @param integer $y The y co-ordinate 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 = null)
{
$c = $this->_allocateColor($color);
if (is_null($fill)) {
$result = $this->call('imageEllipse', array($this->_im, $x, $y, $r * 2, $r * 2, $c));
} else {
if ($fill !== $color) {
$fillColor = $this->_allocateColor($fill);
$this->call('imageFilledEllipse', array($this->_im, $x, $y, $r * 2, $r * 2, $fillColor));
$this->call('imageEllipse', array($this->_im, $x, $y, $r * 2, $r * 2, $c));
} else {
$this->call('imageFilledEllipse', array($this->_im, $x, $y, $r * 2, $r * 2, $c));
}
}
}
/**
* Draw 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')
{
$vertices = array();
foreach ($verts as $vert) {
$vertices[] = $vert['x'];
$vertices[] = $vert['y'];
}
if ($fill != 'none') {
$f = $this->_allocateColor($fill);
$this->call('imageFilledPolygon', array($this->_im, $vertices, count($verts), $f));
}
if ($fill == 'none' || $fill != $color) {
$c = $this->_allocateColor($color);
$this->call('imagePolygon', array($this->_im, $vertices, count($verts), $c));
}
}
/**
* Draw 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 with.
*/
public function rectangle($x, $y, $width, $height, $color = 'black', $fill = 'none')
{
if ($fill != 'none') {
$f = $this->_allocateColor($fill);
$this->call('imageFilledRectangle', array($this->_im, $x, $y, $x + $width, $y + $height, $f));
}
if ($fill == 'none' || $fill != $color) {
$c = $this->_allocateColor($color);
$this->call('imageRectangle', array($this->_im, $x, $y, $x + $width, $y + $height, $c));
}
}
/**
* Draw 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 = 'black', $fill = 'none')
{
if ($round <= 0) {
// Optimize out any calls with no corner rounding.
return $this->rectangle($x, $y, $width, $height, $color, $fill);
}
$c = $this->_allocateColor($color);
// Set corner points to avoid lots of redundant math.
$x1 = $x + $round;
$y1 = $y + $round;
$x2 = $x + $width - $round;
$y2 = $y + $round;
$x3 = $x + $width - $round;
$y3 = $y + $height - $round;
$x4 = $x + $round;
$y4 = $y + $height - $round;
$r = $round * 2;
// Calculate the upper left arc.
$p1 = Horde_Image::arcPoints($round, 180, 225);
$p2 = Horde_Image::arcPoints($round, 225, 270);
// Calculate the upper right arc.
$p3 = Horde_Image::arcPoints($round, 270, 315);
$p4 = Horde_Image::arcPoints($round, 315, 360);
// Calculate the lower right arc.
$p5 = Horde_Image::arcPoints($round, 0, 45);
$p6 = Horde_Image::arcPoints($round, 45, 90);
// Calculate the lower left arc.
$p7 = Horde_Image::arcPoints($round, 90, 135);
$p8 = Horde_Image::arcPoints($round, 135, 180);
// Draw the corners - upper left, upper right, lower right,
// lower left.
$this->call('imageArc', array($this->_im, $x1, $y1, $r, $r, 180, 270, $c));
$this->call('imageArc', array($this->_im, $x2, $y2, $r, $r, 270, 360, $c));
$this->call('imageArc', array($this->_im, $x3, $y3, $r, $r, 0, 90, $c));
$this->call('imageArc', array($this->_im, $x4, $y4, $r, $r, 90, 180, $c));
// Draw the connecting sides - top, right, bottom, left.
$this->call('imageLine', array($this->_im, $x1 + $p2['x2'], $y1 + $p2['y2'], $x2 + $p3['x1'], $y2 + $p3['y1'], $c));
$this->call('imageLine', array($this->_im, $x2 + $p4['x2'], $y2 + $p4['y2'], $x3 + $p5['x1'], $y3 + $p5['y1'], $c));
$this->call('imageLine', array($this->_im, $x3 + $p6['x2'], $y3 + $p6['y2'], $x4 + $p7['x1'], $y4 + $p7['y1'], $c));
$this->call('imageLine', array($this->_im, $x4 + $p8['x2'], $y4 + $p8['y2'], $x1 + $p1['x1'], $y1 + $p1['y1'], $c));
if ($fill != 'none') {
$f = $this->_allocateColor($fill);
$this->call('imageFillToBorder', array($this->_im, $x + ($width / 2), $y + ($height / 2), $c, $f));
}
}
/**
* Draw a 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.
*/
public function line($x1, $y1, $x2, $y2, $color = 'black', $width = 1)
{
$c = $this->_allocateColor($color);
// Don't need to do anything special for single-width lines.
if ($width == 1) {
$this->call('imageLine', array($this->_im, $x1, $y1, $x2, $y2, $c));
} elseif ($x1 == $x2) {
// For vertical lines, we can just draw a vertical
// rectangle.
$left = $x1 - floor(($width - 1) / 2);
$right = $x1 + floor($width / 2);
$this->call('imageFilledRectangle', array($this->_im, $left, $y1, $right, $y2, $c));
} elseif ($y1 == $y2) {
// For horizontal lines, we can just draw a horizontal
// filled rectangle.
$top = $y1 - floor($width / 2);
$bottom = $y1 + floor(($width - 1) / 2);
$this->call('imageFilledRectangle', array($this->_im, $x1, $top, $x2, $bottom, $c));
} else {
// Angled lines.
// Make sure that the end points of the line are
// perpendicular to the line itself.
$a = atan2($y1 - $y2, $x2 - $x1);
$dx = (sin($a) * $width / 2);
$dy = (cos($a) * $width / 2);
$verts = array($x2 + $dx, $y2 + $dy, $x2 - $dx, $y2 - $dy, $x1 - $dx, $y1 - $dy, $x1 + $dx, $y1 + $dy);
$this->call('imageFilledPolygon', array($this->_im, $verts, count($verts) / 2, $c));
}
}
/**
* Draw 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)
{
$c = $this->_allocateColor($color);
$w = $this->_allocateColor('white');
// Set up the style array according to the $dash_* parameters.
$style = array();
for ($i = 0; $i < $dash_length; $i++) {
$style[] = $c;
}
for ($i = 0; $i < $dash_space; $i++) {
$style[] = $w;
}
$this->call('imageSetStyle', array($this->_im, $style));
$this->call('imageSetThickness', array($this->_im, $width));
$this->call('imageLine', array($this->_im, $x0, $y0, $x1, $y1, IMG_COLOR_STYLED));
}
/**
* Draw 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)
{
$first = true;
foreach ($verts as $vert) {
if (!$first) {
$this->line($lastX, $lastY, $vert['x'], $vert['y'], $color, $width);
} else {
$first = false;
}
$lastX = $vert['x'];
$lastY = $vert['y'];
}
}
/**
* Draw an arc.
*
* @param integer $x The x co-ordinate of the centre.
* @param integer $y The y co-ordinate 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 = null)
{
$c = $this->_allocateColor($color);
if (is_null($fill)) {
$this->call('imageArc', array($this->_im, $x, $y, $r * 2, $r * 2, $start, $end, $c));
} else {
if ($fill !== $color) {
$f = $this->_allocateColor($fill);
$this->call('imageFilledArc', array($this->_im, $x, $y, $r * 2, $r * 2, $start, $end, $f, IMG_ARC_PIE));
$this->call('imageFilledArc', array($this->_im, $x, $y, $r * 2, $r * 2, $start, $end, $c, IMG_ARC_EDGED | IMG_ARC_NOFILL));
} else {
$this->call('imageFilledArc', array($this->_im, $x, $y, $r * 2, $r * 2, $start, $end, $c, IMG_ARC_PIE));
}
}
}
/**
* Creates an image of the given size.
* If possible the function returns a true color image.
*
* @param integer $width The image width.
* @param integer $height The image height.
*
* @return resource The image handler.
* @throws Horde_Image_Exception
*/
public function create($width, $height)
{
$result = $this->call('imageCreateTrueColor', array($width, $height));
if (!is_resource($result)) {
throw new Horde_Image_Exception('Could not create image.');
}
return $result;
}
/**
* Wraps a call to a function of the gd extension.
*
* @param string $function The name of the function to wrap.
* @param array $params An array with all parameters for that function.
*
* @return mixed The result of the function call
* @throws Horde_Image_Exception
*/
public function call($function, $params = null)
{
unset($php_errormsg);
$track = ini_set('track_errors', 1);
$result = @call_user_func_array($function, $params);
if ($track !== false) {
ini_set('track_errors', $track);
}
if (!empty($php_errormsg)) {
$error_msg = $php_errormsg;
throw new Horde_Image_Exception($error_msg);
}
return $result;
}
/**
* Applies the specified mask to the image.
*
* @param resource $gdimg_mask The gd image resource representing the mask
*
* @return boolean
*/
public function applyMask($gdimg_mask)
{
$imgX = round($this->call('imageSX', array($this->_im)));
$imgY = round($this->call('imageSY', array($this->_im)));
$gdimg_mask_resized = $this->create($imgX, $imgY);
$result = $this->call('imageCopyResampled',
array($gdimg_mask_resized,
$gdimg_mask,
0, 0, 0, 0,
$imgX,
$imgY,
$this->call('imageSX', array($gdimg_mask)),
$this->call('imageSY', array($gdimg_mask))));
$gdimg_mask_blendtemp = $this->create($imgX, $imgY);
$mbtX = $this->call('imageSX', array($gdimg_mask_blendtemp));
$mbtY = $this->call('imageSY', array($gdimg_mask_blendtemp));
$color_background = $this->call('imageColorAllocate',
array($gdimg_mask_blendtemp, 0, 0, 0));
$this->call('imageFilledRectangle', array($gdimg_mask_blendtemp,
0,
0,
$mbtX,
$mbtY,
$color_background));
$this->call('imageAlphaBlending',
array($gdimg_mask_blendtemp, false));
$this->call('imageSaveAlpha',
array($gdimg_mask_blendtemp, true));
for ($x = 0; $x < $imgX; $x++) {
for ($y = 0; $y < $imgY; $y++) {
$colorat = $this->call('imageColorAt', array($this->_im, $x, $y));
$realPixel = $this->call('imageColorsForIndex', array($this->_im, $colorat));
$colorat = $this->call('imageColorAt', array($gdimg_mask_resized, $x, $y));
$maskPixel = Horde_Image::grayscalePixel($this->call('imageColorsForIndex', array($gdimg_mask_resized, $colorat)));
$maskAlpha = 127 - (floor($maskPixel['red'] / 2) * (1 - ($realPixel['alpha'] / 127)));
$newcolor = $this->_allocateColorAlpha($gdimg_mask_blendtemp,
$realPixel['red'],
$realPixel['green'],
$realPixel['blue'],
intval($maskAlpha));
$this->call('imageSetPixel', array($gdimg_mask_blendtemp, $x, $y, $newcolor));
}
}
$this->call('imageAlphaBlending', array($this->_im, false));
$this->call('imageSaveAlpha', array($this->_im, true));
$this->call('imageCopy', array($this->_im,
$gdimg_mask_blendtemp,
0, 0, 0, 0,
$mbtX,
$mbtY));
$this->call('imageDestroy', array($gdimg_mask_blendtemp));
$this->call('imageDestroy', array($gdimg_mask_resized));
return true;
}
protected function _allocateColorAlpha($gdimg_hexcolorallocate, $r, $g, $b , $alpha = false)
{
$result = $this->call('imageColorAllocateAlpha', array($gdimg_hexcolorallocate, $r, $g, $b, intval($alpha)));
$result = $this->call('imageColorAllocate', array($gdimg_hexcolorallocate, $r, $g, $b));
return $result;
}
/**
* Request a specific image from the collection of images.
*
* @param integer $index The index to return
*
* @return Horde_Image_Base
*/
public function getImageAtIndex($index)
{
if ($index > 0) {
throw new Horde_Image_Exception('Image index out of bounds.');
}
}
/**
* Return the number of image pages available in the image object.
*
* @return integer
*/
public function getImagePageCount()
{
return 1;
}
}
Horde_Image-2.0.5/lib/Horde/Image/Im.php 0000664 0000766 0000024 00000054550 12170303167 014522 0 ustar
* @author Mike Cochrane
* @author Michael J. Rubinsky
* @package Image
*/
class Horde_Image_Im extends Horde_Image_Base
{
/**
* Capabilites of this driver.
*
* @var array
*/
protected $_capabilities = array(
'resize',
'crop',
'rotate',
'grayscale',
'flip',
'mirror',
'sepia',
'canvas',
'multipage',
'pdf');
/**
* 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
*
* @string
*/
protected $_convert = '';
/**
* Path to the identify binary
*
* @string
*/
protected $_identify;
/**
* Cache the number of image pages
*
* @var integer
*/
private $_pages;
/**
* Track 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 = "-size {$this->_width}x{$this->_height} xc:{$this->_background} +profile \"*\" {$this->_type}:__FILEOUT__";
$this->executeConvertCmd($cmd);
}
}
/**
* Publically visible raw method.
*
* @see self::_raw
*/
public function raw($convert = false)
{
return $this->_raw($convert);
}
/**
* Returns the raw data for this image.
*
* @param boolean $convert If true, the image data will be returned in the
* target format, even if no other image operations
* are present, otherwise, if no operations are
* present, the current raw data is returned
* unmodified.
*
* @return string The raw image data.
*/
private function _raw($convert = false, $index = 0, $preserve_data = false)
{
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);
// Perform convert command if needed
if (count($this->_operations) || count($this->_postSrcOperations) || $convert) {
$tmpout = Horde_Util::getTempFile('img', false, $this->_tmpdir);
$command = $this->_convert . ' ' . implode(' ', $this->_operations)
. ' "' . $tmpin . '"\'[' . $index . ']\' '
. implode(' ', $this->_postSrcOperations)
. ' +profile "*" ' . $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 */
$return = file_get_contents($tmpout);
if (!$preserve_data) {
$this->_data = $return;
}
}
@unlink($tmpin);
@unlink($tmpout);
return $return;
}
/**
* Reset the image data.
*/
public function reset()
{
parent::reset();
$this->_operations = array();
$this->_postSrcOperations = array();
$this->clearGeometry();
}
/**
* Resize the current image. This operation takes place immediately.
*
* @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") . " {$width}x{$height}";
} else {
$this->_postSrcOperations[] = (($keepProfile) ? "-resize" : "-thumbnail") . " {$width}x{$height}!";
}
// Reset the width and height instance variables since after resize
// we don't know the *exact* dimensions yet (especially if we maintained
// aspect ratio.
// Refresh the data
$this->raw();
$this->clearGeometry();
}
/**
* Crop 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 of the cropped image.
* @param integer $y2 y for the bottom right corner of the cropped image.
*/
public function crop($x1, $y1, $x2, $y2)
{
$line = ($x2 - $x1) . 'x' . ($y2 - $y1) . '+' . $x1 . '+' . $y1;
$this->_operations[] = '-crop ' . $line . ' +repage';
// Reset width/height since these might change
$this->raw();
$this->clearGeometry();
}
/**
* Rotate the current image. This is an atomic operation.
*
* @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();
$this->_operations[] = "-background $background -rotate {$angle}";
$this->raw();
// Reset width/height since these might have changed
$this->clearGeometry();
}
/**
* Flip the current image.
*/
public function flip()
{
$this->_operations[] = '-flip';
}
/**
* Mirror the current image.
*/
public function mirror()
{
$this->_operations[] = '-flop';
}
/**
* Convert the current image to grayscale.
*/
public function grayscale()
{
$this->_postSrcOperations[] = '-colorspace GRAY';
}
/**
* Sepia filter.
*
* @param integer $threshold Extent of sepia effect.
*/
public function sepia($threshold = 85)
{
$this->_operations[] = '-sepia-tone ' . $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);
$this->_postSrcOperations[] = "-fill $color " . (!empty($font) ? "-font $font" : '') . " -pointsize $fontsize -gravity northwest -draw \"text $x,$y $string\" -fill none";
}
/**
* Draw 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[] = "-stroke $color -fill $fill -draw \"circle $x,$y $xMax,$y\" -stroke none -fill none";
}
/**
* Draw 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[] = "-stroke $color -fill $fill -draw \"polygon $command\" -stroke none -fill none";
}
/**
* Draw 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[] = "-stroke $color -fill $fill -draw \"rectangle $x,$y $xMax,$yMax\" -stroke none -fill none";
}
/**
* Draw 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[] = "-stroke $color -fill $fill -draw \"roundRectangle $x,$y $x1,$y1 $round,$round\" -stroke none -fill none";
}
/**
* Draw 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[] = "-stroke $color -strokewidth $width -draw \"line $x0,$y0 $x1,$y1\"";
}
/**
* Draw 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[] = "-stroke $color -strokewidth $width -draw \"line $x0,$y0 $x1,$y1\"";
}
/**
* Draw 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[] = "-stroke $color -strokewidth $width -fill none -draw \"polyline $command\" -strokewidth 1 -stroke none -fill none";
}
/**
* Draw 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[] = "-stroke $color -fill $fill";
$mid = round(($start + $end) / 2);
$x = round($x);
$y = round($y);
$r = round($r);
if ($mid > 90) {
$this->_postSrcOperations[] = "-draw \"ellipse $x,$y $r,$r $start,$mid\"";
$this->_postSrcOperations[] = "-draw \"ellipse $x,$y $r,$r $mid,$end\"";
} else {
$this->_postSrcOperations[] = "-draw \"ellipse $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);
// This seems to result in slightly better placement of
// pie slices.
$x++;
$y++;
$verts = array(array('x' => $x + $x3, 'y' => $y + $y3),
array('x' => $x, 'y' => $y),
array('x' => $x + $x1, 'y' => $y + $y1));
if ($mid > 90) {
$verts1 = array(array('x' => $x + $x2, 'y' => $y + $y2),
array('x' => $x, 'y' => $y),
array('x' => $x + $x1, 'y' => $y + $y1));
$verts2 = array(array('x' => $x + $x3, 'y' => $y + $y3),
array('x' => $x, 'y' => $y),
array('x' => $x + $x2, 'y' => $y + $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';
}
}
public function applyEffects()
{
$this->raw();
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 for example. Really designed for use 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.
*
* @return
*/
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)));
}
$this->_data = file_get_contents($tmpout);
@unlink($tmpin);
@unlink($tmpout);
}
/**
* Get the version of the convert command available. This needs to be
* publicly visable since it's used by various Effects.
*
* @return 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
*
* @return Horde_Image_Base
*/
public function getImageAtIndex($index)
{
$this->_logDebug('Horde_Image_Im#getImageAtIndex: ' . $index);
if ($index >= $this->getImagePageCount()) {
throw new Horde_Image_Exception('Image index out of bounds.');
}
$rawImage = $this->_raw(true, $index, true);
$image = new Horde_Image_Im(array('data' => $rawImage), $this->_context);
return $image;
}
/**
* Return the number of image pages available in the image object.
*
* @return integer
*/
public function getImagePageCount()
{
if (is_null($this->_pages)) {
$pages = $this->_getImagePages();
$this->_pages = array_pop($pages);
}
$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;
exec($cmd, $output, $retval);
if ($retval) {
$this->_logErr(sprintf("Error running command: %s", $cmd . "\n" . implode("\n", $output)));
}
unlink($filename);
return $output;
}
}
Horde_Image-2.0.5/lib/Horde/Image/Imagick.php 0000664 0000766 0000024 00000050133 12170303167 015512 0 ustar
* @package Image
*/
class Horde_Image_Imagick extends Horde_Image_Base
{
/**
* The underlaying Imagick object
*
* @var Imagick
*/
protected $_imagick;
/**
* Flag for iterator, since calling nextImage on Imagick would result in a
* fatal error if there are no more images.
*
* @var boolean
*/
private $_noMoreImages = false;
/**
* Capabilites of this driver.
*
* @var array
*/
protected $_capabilities = array('resize',
'crop',
'rotate',
'grayscale',
'flip',
'mirror',
'sepia',
'canvas',
'multipage',
'pdf');
/**
* Const'r
*
* @see Horde_Image_Base::_construct
*/
public function __construct($params, $context = array())
{
if (!Horde_Util::loadExtension('imagick')) {
throw new Horde_Image_Exception('Required PECL Imagick extension not found.');
}
parent::__construct($params, $context);
ini_set('imagick.locale_fix', 1);
$this->_imagick = new Imagick();
if (!empty($params['filename'])) {
$this->loadFile($params['filename']);
} elseif(!empty($params['data'])) {
$this->loadString($params['data']);
} else {
$this->_width = max(array($this->_width, 1));
$this->_height = max(array($this->_height, 1));
try {
$this->_imagick->newImage($this->_width, $this->_height, $this->_background);
} catch (ImagickException $e) {
throw new Horde_Image_Exception($e);
}
}
try {
$this->_imagick->setImageFormat($this->_type);
} catch (ImagickException $e) {
throw new Horde_Image_Exception($e);
}
}
/**
* Load image data from a string.
*
* @param string $id
* @param string $image_data
*
* @return void
*/
public function loadString($image_data)
{
parent::loadString($image_data);
$this->_imagick->clear();
try {
$this->_imagick->readImageBlob($this->_data);
$this->_imagick->setImageFormat($this->_type);
$this->_imagick->setIteratorIndex(0);
} catch (ImagickException $e) {
throw new Horde_Image_Exception($e);
}
unset($this->_data);
}
/**
* Load the image data from a file.
*
* @param string $filename The full path and filename to the file to load
* the image data from. The filename will also be
* used for the image id.
*
* @return mixed
*/
public function loadFile($filename)
{
// parent function loads image data into $this->_data
parent::loadFile($filename);
$this->_imagick->clear();
try {
$this->_imagick->readImageBlob($this->_data);
$this->_imagick->setImageFormat($this->_type);
$this->_imagick->setIteratorIndex(0);
} catch (ImagickException $e) {
throw new Horde_Image_Exception($e);
}
unset($this->_data);
}
/**
* Set the image type
*
* @see Horde_Image_Base::setType()
*/
public function setType($type)
{
$old = parent::setType($type);
try {
$this->_imagick->setImageFormat($this->_type);
} catch (ImagickException $e) {
// Don't care about an empty wand here.
}
return $old;
}
/*
* Return the raw image data.
*
* @param boolean $convert Ignored for imagick driver.
*
* @return string The raw image data.
*/
public function raw($convert = false)
{
try {
return $this->_imagick->getImageBlob();
} catch (ImagickException $e) {
throw Horde_Image_Exception($e);
}
}
public function reset()
{
parent::reset();
$this->_imagick->clear();
$this->_noMoreImages = false;
}
/**
* Resize current image.
*
* @see Horde_Image_im::resize()
*
* @return void
*/
public function resize($width, $height, $ratio = true, $keepProfile = false)
{
try {
if ($keepProfile) {
$this->_imagick->resizeImage($width, $height, $ratio);
} else {
$this->_imagick->thumbnailImage($width, $height, $ratio);
}
} catch (ImagickException $e) {
throw new Horde_Image_Exception($e);
}
$this->clearGeometry();
}
/**
* *ALWAYS* use getDimensions() to get image geometry...instance
* variables only cache geometry until it changes, then they go
* to zero.
*
* @return array of geometry information.
*/
public function getDimensions()
{
if ($this->_height == 0 && $this->_width == 0) {
try {
$size = $this->_imagick->getImageGeometry();
} catch (ImagickException $e) {
return array('width' => 0, 'height' => 0);
//throw new Horde_Image_Exception($e);
}
$this->_height = $size['height'];
$this->_width = $size['width'];
}
return array('width' => $this->_width,
'height' => $this->_height);
}
/**
* Crop 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 of the cropped image.
* @param integer $y2 y for the bottom right corner of the cropped image.
*/
public function crop($x1, $y1, $x2, $y2)
{
try {
$result = $this->_imagick->cropImage($x2 - $x1, $y2 - $y1, $x1, $y1);
$this->_imagick->setImagePage(0, 0, 0, 0);
} catch (ImagickException $e) {
throw new Horde_Image_Exception($e);
}
$this->clearGeometry();
}
/**
* Rotate 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')
{
try {
$this->_imagick->rotateImage($background, $angle);
} catch (ImagickException $e) {
throw new Horde_Image_Exception($e);
}
$this->clearGeometry();
}
/**
* Flip the current image.
*/
public function flip()
{
try {
$this->_imagick->flipImage();
} catch (ImagickException $e) {
throw new Horde_Image_Exception($e);
}
}
/**
* Mirror the current image.
*/
public function mirror()
{
try {
$this->_imagick->flopImage();
} catch (ImagickException $e) {
throw new Horde_Image_Exception($e);
}
}
/**
* Convert the current image to grayscale.
*/
public function grayscale()
{
try {
$this->_imagick->setImageColorSpace(Imagick::COLORSPACE_GRAY);
} catch (ImageException $e) {
throw new Horde_Image_Exception($e);
}
}
/**
* Sepia filter.
*
* @param integer $threshold Extent of sepia effect.
*/
public function sepia($threshold = 85)
{
try {
$this->_imagick->sepiaToneImage($threshold);
} catch (ImagickException $e) {
throw new Horde_Image_Exception($e);
}
}
/**
* 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')
{
$fontsize = Horde_Image::getFontSize($fontsize);
$pixel = new ImagickPixel($color);
$draw = new ImagickDraw();
$draw->setFillColor($pixel);
if (!empty($font)) {
$draw->setFont($font);
}
$draw->setFontSize($fontsize);
$draw->setGravity(Imagick::GRAVITY_NORTHWEST);
try {
$res = $this->_imagick->annotateImage($draw, $x, $y, $direction, $string);
} catch (ImagickException $e) {
throw new Horde_Image_Exception($e);
}
$draw->destroy();
}
/**
* Draw 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')
{
$draw = new ImagickDraw();
$draw->setFillColor(new ImagickPixel($fill));
$draw->setStrokeColor(new ImagickPixel($color));
$draw->circle($x, $y, $r + $x, $y);
try {
$res = $this->_imagick->drawImage($draw);
} catch (ImagickException $e) {
throw new Horde_Image_Exception($e);
}
$draw->destroy();
}
/**
* Draw 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')
{
$draw = new ImagickDraw();
$draw->setFillColor(new ImagickPixel($fill));
$draw->setStrokeColor(new ImagickPixel($color));
$draw->polygon($verts);
try {
$res = $this->_imagick->drawImage($draw);
} catch (ImagickException $e) {
throw new Horde_Image_Exception($e);
}
$draw->destroy();
}
/**
* Draw 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')
{
$draw = new ImagickDraw();
$draw->setStrokeColor(new ImagickPixel($color));
$draw->setFillColor(new ImagickPixel($fill));
$draw->rectangle($x, $y, $x + $width, $y + $height);
try {
$res = $this->_imagick->drawImage($draw);
} catch (ImagickException $e) {
throw new Horde_Image_Exception($e);
}
$draw->destroy();
}
/**
* Draw 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)
{
$draw = new ImagickDraw();
$draw->setStrokeColor(new ImagickPixel($color));
$draw->setFillColor(new ImagickPixel($fill));
$draw->roundRectangle($x, $y, $x + $width, $y + $height, $round, $round);
try {
$res = $this->_imagick->drawImage($draw);
} catch (ImagickException $e) {
throw new Horde_Image_Exception($e);
}
$draw->destroy();
}
/**
* Draw 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)
{
$draw = new ImagickDraw();
$draw->setStrokeColor(new ImagickPixel($color));
$draw->setStrokeWidth($width);
$draw->line($x0, $y0, $x1, $y1);
try {
$res = $this->_imagick->drawImage($draw);
} catch (ImagickException $e) {
throw Horde_Image_Exception($e);
}
$draw->destroy();
}
/**
* Draw 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)
{
$draw = new ImagickDraw();
$draw->setStrokeColor(new ImagickPixel($color));
$draw->setStrokeWidth($width);
$draw->setStrokeDashArray(array($dash_length, $dash_space));
$draw->line($x0, $y0, $x1, $y1);
try {
$res = $this->_imagick->drawImage($draw);
} catch (ImageException $e) {
throw new Horde_Image_Exception($e);
}
$draw->destroy();
}
/**
* Draw 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)
{
$draw = new ImagickDraw();
$draw->setStrokeColor(new ImagickPixel($color));
$draw->setStrokeWidth($width);
$draw->setFillColor(new ImagickPixel('none'));
$draw->polyline($verts);
try {
$res = $this->_imagick->drawImage($draw);
} catch (ImagickException $e) {
throw new Horde_Image_Exception($e);
}
$draw->destroy();
}
/**
* Draw an arc.
*
* @TODO
*
* @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')
{
throw new Horde_Image_Exception('Not Yet Implemented.');
}
public function applyEffects()
{
// noop for this driver.
}
public function __get($property)
{
switch ($property) {
case "imagick":
return $this->_imagick;
}
}
/**
* Utility function to wrap Imagick::borderImage. Use when you don't want
* to replace all pixels in the clipping area with the border color i.e.
* you want to "frame" the existing image. Preserves transparency etc...
*
* @param Imagick &$image The Imagick object to border.
* @param integer $width
* @param integer $height
*
* @return void
*/
static public function frameImage(&$image, $color, $width, $height)
{
// Need to jump through these hoops in order to preserve any
// transparency.
try {
$border = $image->clone();
$border->borderImage(new ImagickPixel($color), $width, $height);
$border->compositeImage($image, Imagick::COMPOSITE_COPY, $width, $height);
$image->clear();
$image->addImage($border);
} catch (ImagickException $e) {
throw new Horde_Image_Exception($e);
}
$border->destroy();
}
/**
* Reset the imagick iterator to the first image in the set.
*
* @return void
*/
public function rewind()
{
$this->_logDebug('Horde_Image_Imagick#rewind');
$this->_imagick->setFirstIterator();
$this->_noMoreImages = false;
}
/**
* Return the current image from the internal iterator.
*
* @return Horde_Image_Imagick
*/
public function current()
{
$this->_logDebug('Horde_Image_Imagick#current');
$params = array('data' => $this->raw());
$image = new Horde_Image_Imagick($params, $this->_context);
return $image;
}
/**
* Get the index of the internal iterator.
*
* @return integer
*/
public function key()
{
$this->_logDebug('Horde_Image_Imagick#key: ' . $this->_imagick->getIteratorIndex());
return $this->_imagick->getIteratorIndex();
}
/**
* Advance the iterator
*
* @return Horde_Image_Imagick
*/
public function next()
{
if ($this->_imagick->hasNextImage()) {
$this->_imagick->nextImage();
return $this->current();
} else {
$this->_noMoreImages = true;
return false;
}
}
/**
* Deterimines if the current iterator item is valid.
*
* @return boolean
*/
public function valid()
{
$this->_logDebug('Horde_Image_Imagick#valid:' . print_r(!$this->_noMoreImages, true));
return !$this->_noMoreImages;
}
/**
* Request a specific image from the collection of images.
*
* @param integer $index The index to return
*
* @return Horde_Image_Base
*/
public function getImageAtIndex($index)
{
if ($index >= $this->_imagick->getNumberImages()) {
throw Horde_Image_Exception('Image index out of bounds.');
}
$currentIndex = $this->_imagick->getIteratorIndex();
$this->_imagick->setIteratorIndex($index);
$image = $this->current();
$this->_imagick->setIteratorIndex($currentIndex);
return $image;
}
/**
* Return the number of image pages available in the image object.
*
* @return integer
*/
public function getImagePageCount()
{
return $this->_imagick->getNumberImages();
}
}
Horde_Image-2.0.5/lib/Horde/Image/Png.php 0000664 0000766 0000024 00000016463 12170303167 014702 0 ustar
* @package Image
*/
class Horde_Image_Png extends Horde_Image_Base {
/**
* The array of pixel data.
*
* @var array
*/
var $_img = array();
/**
* Color depth (only 8 and 16 implemented).
*
* @var integer
*/
var $_colorDepth = 8;
/**
* Color type (only 2 (true color) implemented).
*
* @var integer
*/
var $_colorType = 2;
/**
* Compression method (0 is the only current valid value).
*
* @var integer
*/
var $_compressionMethod = 0;
/**
* Filter method (0 is the only current valid value).
*
* @var integer
*/
var $_filterMethod = 0;
/**
* Interlace method (only 0 (no interlace) implemented).
*
* @var integer
*/
var $_interlaceMethod = 0;
/**
* PNG image constructor.
*/
public function __construct($params, $context = array())
{
parent::__construct($params, $context);
if (!empty($params['width'])) {
$this->rectangle(0, 0, $params['width'], $params['height'], $this->_background, $this->_background);
}
}
function getContentType()
{
return 'image/png';
}
/**
* Return the raw data for this image.
*
* @return string The raw image data.
*/
function raw()
{
return
$this->_header() .
$this->_IHDR() .
/* Say what created the image file. */
$this->_tEXt('Software', 'Horde Framework Image_png Class') .
/* Set the last modified date/time. */
$this->_tIME() .
$this->_IDAT() .
$this->_IEND();
}
/**
* Reset the image data.
*/
function reset()
{
parent::reset();
$this->_img = array();
}
/**
* Draw 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 with.
*/
function rectangle($x, $y, $width, $height, $color = 'black', $fill = 'none')
{
list($r, $g, $b) = Horde_Image::getRGB($color);
if ($fill != 'none') {
list($fR, $fG, $fB) = Horde_Image::getRGB($fill);
}
$x2 = $x + $width;
$y2 = $y + $height;
for ($h = $y; $h <= $y2; $h++) {
for ($w = $x; $w <= $x2; $w++) {
// See if we're on an edge.
if ($w == $x || $h == $y || $w == $x2 || $h == $y2) {
$this->_img[$h][$w] = array('r' => $r, 'g' => $g, 'b' => $b);
} elseif ($fill != 'none') {
$this->_img[$h][$w] = array('r' => $fR, 'g' => $fG, 'b' => $fB);
}
}
}
}
/**
* Create the PNG file header.
*/
function _header()
{
return pack('CCCCCCCC', 137, 80, 78, 71, 13, 10, 26, 10);
}
/**
* Create Image Header block.
*/
function _IHDR()
{
$data = pack('a4NNCCCCC', 'IHDR', $this->_width, $this->_height, $this->_colorDepth, $this->_colorType, $this->_compressionMethod, $this->_filterMethod, $this->_interlaceMethod);
return pack('Na' . strlen($data) . 'N', strlen($data) - 4, $data, crc32($data));
}
/**
* Create IEND block.
*/
function _IEND()
{
$data = 'IEND';
return pack('Na' . strlen($data) . 'N', strlen($data) - 4, $data, crc32($data));
}
/**
* Create Image Data block.
*/
function _IDAT()
{
$data = '';
$prevscanline = null;
$filter = 0;
for ($i = 0; $i < $this->_height; $i++) {
$scanline = array();
$data .= chr($filter);
for ($j = 0; $j < $this->_width; $j++) {
if ($this->_colorDepth == 8) {
$scanline[$j] = pack('CCC', $this->_img[$i][$j]['r'], $this->_img[$i][$j]['g'], $this->_img[$i][$j]['b']);
} elseif ($this->_colorDepth == 16) {
$scanline[$j] = pack('nnn', $this->_img[$i][$j]['r'] << 8, $this->_img[$i][$j]['g'] << 8, $this->_img[$i][$j]['b'] << 8);
}
if ($filter == 0) {
/* No Filter. */
$data .= $scanline[$j];
} elseif ($filter == 2) {
/* Up Filter. */
$pixel = $scanline[$j] - $prevscanline[$j];
if ($this->_colorDepth == 8) {
$data .= pack('CCC', $pixel >> 16, ($pixel >> 8) & 0xFF, $pixel & 0xFF);
} elseif ($this->_colorDepth == 16) {
$data .= pack('nnn', ($pixel >> 32), ($pixel >> 16) & 0xFFFF, $pixel & 0xFFFF);
}
}
}
$prevscanline = $scanline;
}
$compressed = gzdeflate($data, 9);
$data = 'IDAT' . pack('CCa' . strlen($compressed) . 'a4', 0x78, 0x01, $compressed, $this->_Adler32($data));
return pack('Na' . strlen($data) . 'N', strlen($data) - 4, $data, crc32($data));
}
/**
* Create tEXt block.
*/
function _tEXt($keyword, $text)
{
$data = 'tEXt' . $keyword . "\0" . $text;
return pack('Na' . strlen($data) . 'N', strlen($data) - 4, $data, crc32($data));
}
/**
* Create last modified time block.
*/
function _tIME($date = null)
{
if (is_null($date)) {
$date = time();
}
$data = 'tIME' . pack('nCCCCC', intval(date('Y', $date)), intval(date('m', $date)), intval(date('j', $date)), intval(date('G', $date)), intval(date('i', $date)), intval(date('s', $date)));
return pack('Na' . strlen($data) . 'N', strlen($data) - 4, $data, crc32($data));
}
/**
* Calculate an Adler32 checksum for a string.
*/
function _Adler32($input)
{
$s1 = 1;
$s2 = 0;
$iMax = strlen($input);
for ($i = 0; $i < $iMax; $i++) {
$s1 = ($s1 + ord($input[$i])) % 0xFFF1;
$s2 = ($s2 + $s1) % 0xFFF1;
}
return pack('N', (($s2 << 16) | $s1));
}
/**
* Request a specific image from the collection of images.
*
* @param integer $index The index to return
*
* @return Horde_Image_Base
* @throws Horde_Image_Exception
*/
public function getImageAtIndex($index)
{
if ($index > 0) {
throw new Horde_Image_Exception('Image index out of bounds.');
}
return clone($this);
}
/**
* Return the number of image pages available in the image object.
*
* @return integer
*/
public function getImagePageCount()
{
return 1;
}
}
Horde_Image-2.0.5/lib/Horde/Image/rgb.php 0000664 0000766 0000024 00000042530 12170303167 014722 0 ustar array(0, 255, 255),
'lime' => array(0, 255, 0),
'teal' => array(0, 128, 128),
'whitesmoke' => array(245, 245, 245),
'gainsboro' => array(220, 220, 220),
'oldlace' => array(253, 245, 230),
'linen' => array(250, 240, 230),
'antiquewhite' => array(250, 235, 215),
'papayawhip' => array(255, 239, 213),
'blanchedalmond' => array(255, 235, 205),
'bisque' => array(255, 228, 196),
'peachpuff' => array(255, 218, 185),
'navajowhite' => array(255, 222, 173),
'moccasin' => array(255, 228, 181),
'cornsilk' => array(255, 248, 220),
'ivory' => array(255, 255, 240),
'lemonchiffon' => array(255, 250, 205),
'seashell' => array(255, 245, 238),
'mintcream' => array(245, 255, 250),
'azure' => array(240, 255, 255),
'aliceblue' => array(240, 248, 255),
'lavender' => array(230, 230, 250),
'lavenderblush' => array(255, 240, 245),
'mistyrose' => array(255, 228, 225),
'white' => array(255, 255, 255),
'black' => array(0, 0, 0),
'darkslategray' => array(47, 79, 79),
'dimgray' => array(105, 105, 105),
'slategray' => array(112, 128, 144),
'lightslategray' => array(119, 136, 153),
'gray' => array(190, 190, 190),
'lightgray' => array(211, 211, 211),
'midnightblue' => array(25, 25, 112),
'navy' => array(0, 0, 128),
'cornflowerblue' => array(100, 149, 237),
'darkslateblue' => array(72, 61, 139),
'slateblue' => array(106, 90, 205),
'mediumslateblue' => array(123, 104, 238),
'lightslateblue' => array(132, 112, 255),
'mediumblue' => array(0, 0, 205),
'royalblue' => array(65, 105, 225),
'blue' => array(0, 0, 255),
'dodgerblue' => array(30, 144, 255),
'deepskyblue' => array(0, 191, 255),
'skyblue' => array(135, 206, 235),
'lightskyblue' => array(135, 206, 250),
'steelblue' => array(70, 130, 180),
'lightred' => array(211, 167, 168),
'lightsteelblue' => array(176, 196, 222),
'lightblue' => array(173, 216, 230),
'powderblue' => array(176, 224, 230),
'paleturquoise' => array(175, 238, 238),
'darkturquoise' => array(0, 206, 209),
'mediumturquoise' => array(72, 209, 204),
'turquoise' => array(64, 224, 208),
'cyan' => array(0, 255, 255),
'lightcyan' => array(224, 255, 255),
'cadetblue' => array(95, 158, 160),
'mediumaquamarine' => array(102, 205, 170),
'aquamarine' => array(127, 255, 212),
'darkgreen' => array(0, 100, 0),
'darkolivegreen' => array(85, 107, 47),
'darkseagreen' => array(143, 188, 143),
'seagreen' => array(46, 139, 87),
'mediumseagreen' => array(60, 179, 113),
'lightseagreen' => array(32, 178, 170),
'palegreen' => array(152, 251, 152),
'springgreen' => array(0, 255, 127),
'lawngreen' => array(124, 252, 0),
'green' => array(0, 255, 0),
'chartreuse' => array(127, 255, 0),
'mediumspringgreen' => array(0, 250, 154),
'greenyellow' => array(173, 255, 47),
'limegreen' => array(50, 205, 50),
'yellowgreen' => array(154, 205, 50),
'forestgreen' => array(34, 139, 34),
'olivedrab' => array(107, 142, 35),
'darkkhaki' => array(189, 183, 107),
'khaki' => array(240, 230, 140),
'palegoldenrod' => array(238, 232, 170),
'lightgoldenrodyellow' => array(250, 250, 210),
'lightyellow' => array(255, 255, 200),
'yellow' => array(255, 255, 0),
'gold' => array(255, 215, 0),
'lightgoldenrod' => array(238, 221, 130),
'goldenrod' => array(218, 165, 32),
'darkgoldenrod' => array(184, 134, 11),
'rosybrown' => array(188, 143, 143),
'indianred' => array(205, 92, 92),
'saddlebrown' => array(139, 69, 19),
'sienna' => array(160, 82, 45),
'peru' => array(205, 133, 63),
'burlywood' => array(222, 184, 135),
'beige' => array(245, 245, 220),
'wheat' => array(245, 222, 179),
'sandybrown' => array(244, 164, 96),
'tan' => array(210, 180, 140),
'chocolate' => array(210, 105, 30),
'firebrick' => array(178, 34, 34),
'brown' => array(165, 42, 42),
'darksalmon' => array(233, 150, 122),
'salmon' => array(250, 128, 114),
'lightsalmon' => array(255, 160, 122),
'orange' => array(255, 165, 0),
'darkorange' => array(255, 140, 0),
'coral' => array(255, 127, 80),
'lightcoral' => array(240, 128, 128),
'tomato' => array(255, 99, 71),
'orangered' => array(255, 69, 0),
'red' => array(255, 0, 0),
'hotpink' => array(255, 105, 180),
'deeppink' => array(255, 20, 147),
'pink' => array(255, 192, 203),
'lightpink' => array(255, 182, 193),
'palevioletred' => array(219, 112, 147),
'maroon' => array(176, 48, 96),
'mediumvioletred' => array(199, 21, 133),
'violetred' => array(208, 32, 144),
'magenta' => array(255, 0, 255),
'violet' => array(238, 130, 238),
'plum' => array(221, 160, 221),
'orchid' => array(218, 112, 214),
'mediumorchid' => array(186, 85, 211),
'darkorchid' => array(153, 50, 204),
'darkviolet' => array(148, 0, 211),
'blueviolet' => array(138, 43, 226),
'purple' => array(160, 32, 240),
'mediumpurple' => array(147, 112, 219),
'thistle' => array(216, 191, 216),
'snow1' => array(255, 250, 250),
'snow2' => array(238, 233, 233),
'snow3' => array(205, 201, 201),
'snow4' => array(139, 137, 137),
'seashell1' => array(255, 245, 238),
'seashell2' => array(238, 229, 222),
'seashell3' => array(205, 197, 191),
'seashell4' => array(139, 134, 130),
'AntiqueWhite1' => array(255, 239, 219),
'AntiqueWhite2' => array(238, 223, 204),
'AntiqueWhite3' => array(205, 192, 176),
'AntiqueWhite4' => array(139, 131, 120),
'bisque1' => array(255, 228, 196),
'bisque2' => array(238, 213, 183),
'bisque3' => array(205, 183, 158),
'bisque4' => array(139, 125, 107),
'peachPuff1' => array(255, 218, 185),
'peachpuff2' => array(238, 203, 173),
'peachpuff3' => array(205, 175, 149),
'peachpuff4' => array(139, 119, 101),
'navajowhite1' => array(255, 222, 173),
'navajowhite2' => array(238, 207, 161),
'navajowhite3' => array(205, 179, 139),
'navajowhite4' => array(139, 121, 94),
'lemonchiffon1' => array(255, 250, 205),
'lemonchiffon2' => array(238, 233, 191),
'lemonchiffon3' => array(205, 201, 165),
'lemonchiffon4' => array(139, 137, 112),
'ivory1' => array(255, 255, 240),
'ivory2' => array(238, 238, 224),
'ivory3' => array(205, 205, 193),
'ivory4' => array(139, 139, 131),
'honeydew' => array(193, 205, 193),
'lavenderblush1' => array(255, 240, 245),
'lavenderblush2' => array(238, 224, 229),
'lavenderblush3' => array(205, 193, 197),
'lavenderblush4' => array(139, 131, 134),
'mistyrose1' => array(255, 228, 225),
'mistyrose2' => array(238, 213, 210),
'mistyrose3' => array(205, 183, 181),
'mistyrose4' => array(139, 125, 123),
'azure1' => array(240, 255, 255),
'azure2' => array(224, 238, 238),
'azure3' => array(193, 205, 205),
'azure4' => array(131, 139, 139),
'slateblue1' => array(131, 111, 255),
'slateblue2' => array(122, 103, 238),
'slateblue3' => array(105, 89, 205),
'slateblue4' => array(71, 60, 139),
'royalblue1' => array(72, 118, 255),
'royalblue2' => array(67, 110, 238),
'royalblue3' => array(58, 95, 205),
'royalblue4' => array(39, 64, 139),
'dodgerblue1' => array(30, 144, 255),
'dodgerblue2' => array(28, 134, 238),
'dodgerblue3' => array(24, 116, 205),
'dodgerblue4' => array(16, 78, 139),
'steelblue1' => array(99, 184, 255),
'steelblue2' => array(92, 172, 238),
'steelblue3' => array(79, 148, 205),
'steelblue4' => array(54, 100, 139),
'deepskyblue1' => array(0, 191, 255),
'deepskyblue2' => array(0, 178, 238),
'deepskyblue3' => array(0, 154, 205),
'deepskyblue4' => array(0, 104, 139),
'skyblue1' => array(135, 206, 255),
'skyblue2' => array(126, 192, 238),
'skyblue3' => array(108, 166, 205),
'skyblue4' => array(74, 112, 139),
'lightskyblue1' => array(176, 226, 255),
'lightskyblue2' => array(164, 211, 238),
'lightskyblue3' => array(141, 182, 205),
'lightskyblue4' => array(96, 123, 139),
'slategray1' => array(198, 226, 255),
'slategray2' => array(185, 211, 238),
'slategray3' => array(159, 182, 205),
'slategray4' => array(108, 123, 139),
'lightsteelblue1' => array(202, 225, 255),
'lightsteelblue2' => array(188, 210, 238),
'lightsteelblue3' => array(162, 181, 205),
'lightsteelblue4' => array(110, 123, 139),
'lightblue1' => array(191, 239, 255),
'lightblue2' => array(178, 223, 238),
'lightblue3' => array(154, 192, 205),
'lightblue4' => array(104, 131, 139),
'lightcyan1' => array(224, 255, 255),
'lightcyan2' => array(209, 238, 238),
'lightcyan3' => array(180, 205, 205),
'lightcyan4' => array(122, 139, 139),
'paleturquoise1' => array(187, 255, 255),
'paleturquoise2' => array(174, 238, 238),
'paleturquoise3' => array(150, 205, 205),
'paleturquoise4' => array(102, 139, 139),
'cadetblue1' => array(152, 245, 255),
'cadetblue2' => array(142, 229, 238),
'cadetblue3' => array(122, 197, 205),
'cadetblue4' => array(83, 134, 139),
'turquoise1' => array(0, 245, 255),
'turquoise2' => array(0, 229, 238),
'turquoise3' => array(0, 197, 205),
'turquoise4' => array(0, 134, 139),
'cyan1' => array(0, 255, 255),
'cyan2' => array(0, 238, 238),
'cyan3' => array(0, 205, 205),
'cyan4' => array(0, 139, 139),
'darkslategray1' => array(151, 255, 255),
'darkslategray2' => array(141, 238, 238),
'darkslategray3' => array(121, 205, 205),
'darkslategray4' => array(82, 139, 139),
'aquamarine1' => array(127, 255, 212),
'aquamarine2' => array(118, 238, 198),
'aquamarine3' => array(102, 205, 170),
'aquamarine4' => array(69, 139, 116),
'darkseagreen1' => array(193, 255, 193),
'darkseagreen2' => array(180, 238, 180),
'darkseagreen3' => array(155, 205, 155),
'darkseagreen4' => array(105, 139, 105),
'seagreen1' => array(84, 255, 159),
'seagreen2' => array(78, 238, 148),
'seagreen3' => array(67, 205, 128),
'seagreen4' => array(46, 139, 87),
'palegreen1' => array(154, 255, 154),
'palegreen2' => array(144, 238, 144),
'palegreen3' => array(124, 205, 124),
'palegreen4' => array(84, 139, 84),
'springgreen1' => array(0, 255, 127),
'springgreen2' => array(0, 238, 118),
'springgreen3' => array(0, 205, 102),
'springgreen4' => array(0, 139, 69),
'chartreuse1' => array(127, 255, 0),
'chartreuse2' => array(118, 238, 0),
'chartreuse3' => array(102, 205, 0),
'chartreuse4' => array(69, 139, 0),
'olivedrab1' => array(192, 255, 62),
'olivedrab2' => array(179, 238, 58),
'olivedrab3' => array(154, 205, 50),
'olivedrab4' => array(105, 139, 34),
'darkolivegreen1' => array(202, 255, 112),
'darkolivegreen2' => array(188, 238, 104),
'darkolivegreen3' => array(162, 205, 90),
'darkolivegreen4' => array(110, 139, 61),
'khaki1' => array(255, 246, 143),
'khaki2' => array(238, 230, 133),
'khaki3' => array(205, 198, 115),
'khaki4' => array(139, 134, 78),
'lightgoldenrod1' => array(255, 236, 139),
'lightgoldenrod2' => array(238, 220, 130),
'lightgoldenrod3' => array(205, 190, 112),
'lightgoldenrod4' => array(139, 129, 76),
'yellow1' => array(255, 255, 0),
'yellow2' => array(238, 238, 0),
'yellow3' => array(205, 205, 0),
'yellow4' => array(139, 139, 0),
'gold1' => array(255, 215, 0),
'gold2' => array(238, 201, 0),
'gold3' => array(205, 173, 0),
'gold4' => array(139, 117, 0),
'goldenrod1' => array(255, 193, 37),
'goldenrod2' => array(238, 180, 34),
'goldenrod3' => array(205, 155, 29),
'goldenrod4' => array(139, 105, 20),
'darkgoldenrod1' => array(255, 185, 15),
'darkgoldenrod2' => array(238, 173, 14),
'darkgoldenrod3' => array(205, 149, 12),
'darkgoldenrod4' => array(139, 101, 8),
'rosybrown1' => array(255, 193, 193),
'rosybrown2' => array(238, 180, 180),
'rosybrown3' => array(205, 155, 155),
'rosybrown4' => array(139, 105, 105),
'indianred1' => array(255, 106, 106),
'indianred2' => array(238, 99, 99),
'indianred3' => array(205, 85, 85),
'indianred4' => array(139, 58, 58),
'sienna1' => array(255, 130, 71),
'sienna2' => array(238, 121, 66),
'sienna3' => array(205, 104, 57),
'sienna4' => array(139, 71, 38),
'burlywood1' => array(255, 211, 155),
'burlywood2' => array(238, 197, 145),
'burlywood3' => array(205, 170, 125),
'burlywood4' => array(139, 115, 85),
'wheat1' => array(255, 231, 186),
'wheat2' => array(238, 216, 174),
'wheat3' => array(205, 186, 150),
'wheat4' => array(139, 126, 102),
'tan1' => array(255, 165, 79),
'tan2' => array(238, 154, 73),
'tan3' => array(205, 133, 63),
'tan4' => array(139, 90, 43),
'chocolate1' => array(255, 127, 36),
'chocolate2' => array(238, 118, 33),
'chocolate3' => array(205, 102, 29),
'chocolate4' => array(139, 69, 19),
'firebrick1' => array(255, 48, 48),
'firebrick2' => array(238, 44, 44),
'firebrick3' => array(205, 38, 38),
'firebrick4' => array(139, 26, 26),
'brown1' => array(255, 64, 64),
'brown2' => array(238, 59, 59),
'brown3' => array(205, 51, 51),
'brown4' => array(139, 35, 35),
'salmon1' => array(255, 140, 105),
'salmon2' => array(238, 130, 98),
'salmon3' => array(205, 112, 84),
'salmon4' => array(139, 76, 57),
'lightsalmon1' => array(255, 160, 122),
'lightsalmon2' => array(238, 149, 114),
'lightsalmon3' => array(205, 129, 98),
'lightsalmon4' => array(139, 87, 66),
'orange1' => array(255, 165, 0),
'orange2' => array(238, 154, 0),
'orange3' => array(205, 133, 0),
'orange4' => array(139, 90, 0),
'darkorange1' => array(255, 127, 0),
'darkorange2' => array(238, 118, 0),
'darkorange3' => array(205, 102, 0),
'darkorange4' => array(139, 69, 0),
'coral1' => array(255, 114, 86),
'coral2' => array(238, 106, 80),
'coral3' => array(205, 91, 69),
'coral4' => array(139, 62, 47),
'tomato1' => array(255, 99, 71),
'tomato2' => array(238, 92, 66),
'tomato3' => array(205, 79, 57),
'tomato4' => array(139, 54, 38),
'orangered1' => array(255, 69, 0),
'orangered2' => array(238, 64, 0),
'orangered3' => array(205, 55, 0),
'orangered4' => array(139, 37, 0),
'deeppink1' => array(255, 20, 147),
'deeppink2' => array(238, 18, 137),
'deeppink3' => array(205, 16, 118),
'deeppink4' => array(139, 10, 80),
'hotpink1' => array(255, 110, 180),
'hotpink2' => array(238, 106, 167),
'hotpink3' => array(205, 96, 144),
'hotpink4' => array(139, 58, 98),
'pink1' => array(255, 181, 197),
'pink2' => array(238, 169, 184),
'pink3' => array(205, 145, 158),
'pink4' => array(139, 99, 108),
'lightpink1' => array(255, 174, 185),
'lightpink2' => array(238, 162, 173),
'lightpink3' => array(205, 140, 149),
'lightpink4' => array(139, 95, 101),
'palevioletred1' => array(255, 130, 171),
'palevioletred2' => array(238, 121, 159),
'palevioletred3' => array(205, 104, 137),
'palevioletred4' => array(139, 71, 93),
'maroon1' => array(255, 52, 179),
'maroon2' => array(238, 48, 167),
'maroon3' => array(205, 41, 144),
'maroon4' => array(139, 28, 98),
'violetred1' => array(255, 62, 150),
'violetred2' => array(238, 58, 140),
'violetred3' => array(205, 50, 120),
'violetred4' => array(139, 34, 82),
'magenta1' => array(255, 0, 255),
'magenta2' => array(238, 0, 238),
'magenta3' => array(205, 0, 205),
'magenta4' => array(139, 0, 139),
'mediumred' => array(140, 34, 34),
'orchid1' => array(255, 131, 250),
'orchid2' => array(238, 122, 233),
'orchid3' => array(205, 105, 201),
'orchid4' => array(139, 71, 137),
'plum1' => array(255, 187, 255),
'plum2' => array(238, 174, 238),
'plum3' => array(205, 150, 205),
'plum4' => array(139, 102, 139),
'mediumorchid1' => array(224, 102, 255),
'mediumorchid2' => array(209, 95, 238),
'mediumorchid3' => array(180, 82, 205),
'mediumorchid4' => array(122, 55, 139),
'darkorchid1' => array(191, 62, 255),
'darkorchid2' => array(178, 58, 238),
'darkorchid3' => array(154, 50, 205),
'darkorchid4' => array(104, 34, 139),
'purple1' => array(155, 48, 255),
'purple2' => array(145, 44, 238),
'purple3' => array(125, 38, 205),
'purple4' => array(85, 26, 139),
'mediumpurple1' => array(171, 130, 255),
'mediumpurple2' => array(159, 121, 238),
'mediumpurple3' => array(137, 104, 205),
'mediumpurple4' => array(93, 71, 139),
'thistle1' => array(255, 225, 255),
'thistle2' => array(238, 210, 238),
'thistle3' => array(205, 181, 205),
'thistle4' => array(139, 123, 139),
'gray1' => array(10, 10, 10),
'gray2' => array(40, 40, 30),
'gray3' => array(70, 70, 70),
'gray4' => array(100, 100, 100),
'gray5' => array(130, 130, 130),
'gray6' => array(160, 160, 160),
'gray7' => array(190, 190, 190),
'gray8' => array(210, 210, 210),
'gray9' => array(240, 240, 240),
'darkgray' => array(100, 100, 100),
'darkblue' => array(0, 0, 139),
'darkcyan' => array(0, 139, 139),
'darkmagenta' => array(139, 0, 139),
'darkred' => array(139, 0, 0),
'silver' => array(192, 192, 192),
'eggplant' => array(144, 176, 168),
'lightgreen' => array(144, 238, 144)
);
Horde_Image-2.0.5/lib/Horde/Image/Svg.php 0000664 0000766 0000024 00000031311 12170303167 014702 0 ustar
* @package Image
*/
class Horde_Image_Svg extends Horde_Image_Base
{
protected $_svg;
/**
* Capabilites of this driver.
*
* @var array
*/
protected $_capabilities = array('canvas');
public function __construct($params)
{
parent::__construct($params);
$this->_svg = new XML_SVG_Document(array('width' => $this->_width,
'height' => $this->_height));
}
public function getContentType()
{
return 'image/svg+xml';
}
public function display()
{
$this->_svg->printElement();
}
/**
* Return the raw data for this image.
*
* @return string The raw image data.
*/
public function raw()
{
return $this->_svg->bufferObject();
}
private function _createSymbol($s, $id)
{
$s->setParam('id', $id);
$defs = new XML_SVG_Defs();
$defs->addChild($s);
$this->_svg->addChild($defs);
}
private function _createDropShadow($id = 'dropShadow')
{
$defs = new XML_SVG_Defs();
$filter = new XML_SVG_Filter(array('id' => $id));
$filter->addPrimitive('GaussianBlur', array('in' => 'SourceAlpha',
'stdDeviation' => 2,
'result' => 'blur'));
$filter->addPrimitive('Offset', array('in' => 'blur',
'dx' => 4,
'dy' => 4,
'result' => 'offsetBlur'));
$merge = new XML_SVG_FilterPrimitive('Merge');
$merge->addMergeNode('offsetBlur');
$merge->addMergeNode('SourceGraphic');
$filter->addChild($merge);
$defs->addChild($filter);
$this->_svg->addChild($defs);
}
/**
* Draws a text string on the image in a specified location, with
* the specified style information.
*
* @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.
*/
public function text($string, $x, $y, $font = 'monospace', $color = 'black', $direction = 0)
{
$height = 12;
$style = 'font-family:' . $font . ';font-height:' . $height . 'px;fill:' . $this->getHexColor($color) . ';text-anchor:start;';
$transform = 'rotate(' . $direction . ',' . $x . ',' . $y . ')';
$this->_svg->addChild(new XML_SVG_Text(array('text' => $string,
'x' => (int)$x,
'y' => (int)$y + $height,
'transform' => $transform,
'style' => $style)));
}
/**
* Draw 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 = null)
{
if (!empty($fill)) {
$style = 'fill:' . $this->getHexColor($fill) . '; ';
} else {
$style = 'fill:none;';
}
$style .= 'stroke:' . $this->getHexColor($color) . '; stroke-width:1';
$this->_svg->addChild(new XML_SVG_Circle(array('cx' => $x,
'cy' => $y,
'r' => $r,
'style' => $style)));
}
/**
* Draw 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 = null)
{
if (!empty($fill)) {
$style = 'fill:' . $this->getHexColor($fill) . '; ';
} else {
$style = 'fill:none;';
}
$style .= 'stroke:' . $this->getHexColor($color) . '; stroke-width:1';
$points = '';
foreach ($verts as $v) {
$points .= $v['x'] . ',' . $v['y'] . ' ';
}
$points = trim($points);
$this->_svg->addChild(new XML_SVG_Polygon(array('points' => $points,
'style' => $style)));
}
/**
* Draw 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 = null)
{
if (!empty($fill)) {
$style = 'fill:' . $this->getHexColor($fill) . '; ';
} else {
$style = 'fill:none;';
}
$style .= 'stroke:' . $this->getHexColor($color) . '; stroke-width:1';
$this->_svg->addChild(new XML_SVG_Rect(array('x' => $x,
'y' => $y,
'width' => $width,
'height' => $height,
'style' => $style)));
}
/**
* Draw 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 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 rectangle.
*/
public function roundedRectangle($x, $y, $width, $height, $round, $color, $fill)
{
if (!empty($fill)) {
$style = 'fill:' . $this->getHexColor($fill) . '; ';
} else {
$style = 'fill:none;';
}
$style .= 'stroke:' . $this->getHexColor($color) . '; stroke-width:1';
$this->_svg->addChild(new XML_SVG_Rect(array('x' => $x,
'y' => $y,
'rx' => $round,
'ry' => $round,
'width' => $width,
'height' => $height,
'style' => $style)));
}
/**
* Draw 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($x1, $y1, $x2, $y2, $color = 'black', $width = 1)
{
$style = 'stroke:' . $this->getHexColor($color) . '; stroke-width:' . (int)$width;
$this->_svg->addChild(new XML_SVG_Line(array('x1' => $x1,
'y1' => $y1,
'x2' => $x2,
'y2' => $y2,
'style' => $style)));
}
/**
* Draw a dashed 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.
* @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($x1, $y1, $x2, $y2, $color = 'black', $width = 1, $dash_length = 2, $dash_space = 2)
{
$style = 'stroke:' . $this->getHexColor($color) . '; stroke-width:' . (int)$width . '; stroke-dasharray:' . $dash_length . ',' . $dash_space . ';';
$this->_svg->addChild(new XML_SVG_Line(array('x1' => $x1,
'y1' => $y1,
'x2' => $x2,
'y2' => $y2,
'style' => $style)));
}
/**
* Draw 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)
{
$style = 'stroke:' . $this->getHexColor($color) . '; stroke-width:' . $width . ';fill:none;';
// Calculate the path entry.
$path = '';
$first = true;
foreach ($verts as $vert) {
if ($first) {
$first = false;
$path .= 'M ' . $vert['x'] . ',' . $vert['y'];
} else {
$path .= ' L ' . $vert['x'] . ',' . $vert['y'];
}
}
$this->_svg->addChild(new XML_SVG_Path(array('d' => $path,
'style' => $style)));
}
/**
* Draw 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 = null)
{
if (!empty($fill)) {
$style = 'fill:' . $this->getHexColor($fill) . '; ';
} else {
$style = 'fill:none;';
}
$style .= 'stroke:' . $this->getHexColor($color) . '; stroke-width:1';
$mid = round(($start + $end) / 2);
// Calculate the path entry.
$path = '';
// If filled, draw the outline.
if (!empty($fill)) {
// Start at the center of the ellipse the arc is on.
$path .= "M $x,$y ";
// Draw out to ellipse edge.
list($arcX, $arcY) = Horde_Image::circlePoint($start, $r * 2);
$path .= 'L ' . round($x + $arcX) . ',' .
round($y + $arcY) . ' ';
}
// Draw arcs.
list($arcX, $arcY) = Horde_Image::circlePoint($mid, $r * 2);
$path .= "A $r,$r 0 0 1 " .
round($x + $arcX) . ',' .
round($y + $arcY) . ' ';
list($arcX, $arcY) = Horde_Image::circlePoint($end, $r * 2);
$path .= "A $r,$r 0 0 1 " .
round($x + $arcX) . ',' .
round($y + $arcY) . ' ';
// If filled, close the outline.
if (!empty($fill)) {
$path .= 'Z';
}
$path = trim($path);
$this->_svg->addChild(new XML_SVG_Path(array('d' => $path,
'style' => $style)));
}
} Horde_Image-2.0.5/lib/Horde/Image/Swf.php 0000664 0000766 0000024 00000044512 12170303167 014711 0 ustar
* @package Image
*/
class Horde_Image_Swf extends Horde_Image_Base {
/**
* Capabilites of this driver.
*
* @var array
*/
var $_capabilities = array('canvas');
/**
* SWF root movie.
*
* @var resource
*/
var $_movie;
function Horde_Image_swf($params)
{
parent::Horde_Image($params);
$this->_movie = new SWFMovie();
$this->_movie->setDimension($this->_width, $this->_height);
// FIXME: honor the 'background' parameter here.
$this->_movie->setBackground(0xff, 0xff, 0xff);
$this->_movie->setRate(30);
}
function getContentType()
{
return 'application/x-shockwave-flash';
}
/**
* Display the movie.
*/
function display()
{
$this->headers();
$this->_movie->output();
}
/**
* Return the raw data for this image.
*
* @return string The raw image data.
*/
function raw()
{
ob_start();
$this->_movie->output();
$data = ob_get_contents();
ob_end_clean();
return $data;
}
/**
* Creates a color that can be accessed in this object. When a
* color is set, the rgba values are returned in an array.
*
* @param string $name The name of the color.
*
* @return array The red, green, blue, alpha values of the color.
*/
function allocateColor($name)
{
list($r, $g, $b) = $this->getRGB($name);
return array('red' => $r,
'green' => $g,
'blue' => $b,
'alpha' => 255);
}
function getFont($font)
{
switch ($font) {
case 'sans-serif':
return '_sans';
case 'serif':
return '_serif';
case 'monospace':
return '_typewriter';
default:
return $font;
}
}
/**
* Draws a text string on the image in a specified location, with
* the specified style information.
*
* @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.
*/
function text($string, $x, $y, $font = 'monospace', $color = 'black', $direction = 0)
{
$color = $this->allocateColor($color);
if (!strncasecmp(PHP_OS, 'WIN', 3)) {
$text = new SWFTextField(SWFTEXTFIELD_NOEDIT);
} else {
$text = new SWFText();
}
$text->setColor($color['red'], $color['green'], $color['blue'], $color['alpha']);
$text->addString($string);
$text->setFont(new SWFFont($this->getFont($font)));
$t = $this->_movie->add($text);
$t->moveTo($x, $y);
$t->rotate($direction);
return $t;
}
/**
* Draw a circle.
*
* @param integer $x The x co-ordinate of the centre.
* @param integer $y The y co-ordinate 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.
*/
function circle($x, $y, $r, $color, $fill = 'none')
{
$s = new SWFShape();
$color = $this->allocateColor($color);
$s->setLine(1, $color['red'], $color['green'], $color['blue'], $color['alpha']);
if ($fill != 'none') {
$fillColor = $this->allocateColor($fill);
$f = $s->addFill($fillColor['red'], $fillColor['green'], $fillColor['blue'], $fillColor['alpha']);
$s->setRightFill($f);
}
$a = $r * 0.414213562; // = tan(22.5 deg)
$b = $r * 0.707106781; // = sqrt(2)/2 = sin(45 deg)
$s->movePenTo($x + $r, $y);
$s->drawCurveTo($x + $r, $y - $a, $x + $b, $y - $b);
$s->drawCurveTo($x + $a, $y - $r, $x, $y - $r);
$s->drawCurveTo($x - $a, $y - $r, $x - $b, $y - $b);
$s->drawCurveTo($x - $r, $y - $a, $x - $r, $y);
$s->drawCurveTo($x - $r, $y + $a, $x - $b, $y + $b);
$s->drawCurveTo($x - $a, $y + $r, $x, $y + $r);
$s->drawCurveTo($x + $a, $y + $r, $x + $b, $y + $b);
$s->drawCurveTo($x + $r, $y + $a, $x + $r, $y);
return $this->_movie->add($s);
}
/**
* Draw 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.
*/
function polygon($verts, $color, $fill = 'none')
{
$color = $this->allocateColor($color);
if (is_array($color) && is_array($verts) && (sizeof($verts) > 2)) {
$shape = new SWFShape();
$shape->setLine(1, $color['red'], $color['green'], $color['blue'], $color['alpha']);
if ($fill != 'none') {
$fillColor = $this->allocateColor($fill);
$f = $shape->addFill($fillColor['red'], $fillColor['green'], $fillColor['blue'], $fillColor['alpha']);
$shape->setRightFill($f);
}
$first_done = false;
foreach ($verts as $value) {
if (!$first_done) {
$shape->movePenTo($value['x'], $value['y']);
$first_done = true;
$first_x = $value['x'];
$first_y = $value['y'];
}
$shape->drawLineTo($value['x'], $value['y']);
}
$shape->drawLineTo($first_x, $first_y);
return $this->_movie->add($shape);
} else {
// If the color is an array and the vertices is a an array
// of more than 2 points.
return false;
}
}
/**
* Draw 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.
*/
function rectangle($x, $y, $width, $height, $color, $fill = 'none')
{
$verts[0] = array('x' => $x, 'y' => $y);
$verts[1] = array('x' => $x + $width, 'y' => $y);
$verts[2] = array('x' => $x + $width, 'y' => $y + $height);
$verts[3] = array('x' => $x, 'y' => $y + $height);
return $this->polygon($verts, $color, $fill);
}
/**
* Draw 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 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 rectangle.
*/
function roundedRectangle($x, $y, $width, $height, $round, $color = 'black', $fill = 'none')
{
if ($round <= 0) {
// Optimize out any calls with no corner rounding.
return $this->rectangle($x, $y, $width, $height, $color, $fill);
}
$s = new SWFShape();
$color = $this->allocateColor($color);
$s->setLine(1, $color['red'], $color['green'], $color['blue'], $color['alpha']);
if ($fill != 'none') {
$fillColor = $this->allocateColor($fill);
$f = $s->addFill($fillColor['red'], $fillColor['green'], $fillColor['blue'], $fillColor['alpha']);
$s->setRightFill($f);
}
// Set corner points to avoid lots of redundant math.
$x1 = $x + $round;
$y1 = $y + $round;
$x2 = $x + $width - $round;
$y2 = $y + $round;
$x3 = $x + $width - $round;
$y3 = $y + $height - $round;
$x4 = $x + $round;
$y4 = $y + $height - $round;
// Start in the upper left.
$p1 = Horde_Image::arcPoints($round, 180, 225);
$p2 = Horde_Image::arcPoints($round, 225, 270);
// Start at the lower left corner of the top left curve.
$s->movePenTo($x1 + $p1['x1'], $y1 + $p1['y1']);
// Draw the upper left corner.
$s->drawCurveTo($x1 + $p1['x3'], $y1 + $p1['y3'], $x1 + $p1['x2'], $y1 + $p1['y2']);
$s->drawCurveTo($x1 + $p2['x3'], $y1 + $p2['y3'], $x1 + $p2['x2'], $y1 + $p2['y2']);
// Calculate the upper right points.
$p3 = Horde_Image::arcPoints($round, 270, 315);
$p4 = Horde_Image::arcPoints($round, 315, 360);
// Connect the top left and right curves.
$s->drawLineTo($x2 + $p3['x1'], $y2 + $p3['y1']);
// Draw the upper right corner.
$s->drawCurveTo($x2 + $p3['x3'], $y2 + $p3['y3'], $x2 + $p3['x2'], $y2 + $p3['y2']);
$s->drawCurveTo($x2 + $p4['x3'], $y2 + $p4['y3'], $x2 + $p4['x2'], $y2 + $p4['y2']);
// Calculate the lower right points.
$p5 = Horde_Image::arcPoints($round, 0, 45);
$p6 = Horde_Image::arcPoints($round, 45, 90);
// Connect the top right and lower right curves.
$s->drawLineTo($x3 + $p5['x1'], $y3 + $p5['y1']);
// Draw the lower right corner.
$s->drawCurveTo($x3 + $p5['x3'], $y3 + $p5['y3'], $x3 + $p5['x2'], $y3 + $p5['y2']);
$s->drawCurveTo($x3 + $p6['x3'], $y3 + $p6['y3'], $x3 + $p6['x2'], $y3 + $p6['y2']);
// Calculate the lower left points.
$p7 = Horde_Image::arcPoints($round, 90, 135);
$p8 = Horde_Image::arcPoints($round, 135, 180);
// Connect the bottom right and bottom left curves.
$s->drawLineTo($x4 + $p7['x1'], $y4 + $p7['y1']);
// Draw the lower left corner.
$s->drawCurveTo($x4 + $p7['x3'], $y4 + $p7['y3'], $x4 + $p7['x2'], $y4 + $p7['y2']);
$s->drawCurveTo($x4 + $p8['x3'], $y4 + $p8['y3'], $x4 + $p8['x2'], $y4 + $p8['y2']);
// Close the shape.
$s->drawLineTo($x1 + $p1['x1'], $y1 + $p1['y1']);
return $this->_movie->add($s);
}
/**
* Draw a 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.
*/
function line($x1, $y1, $x2, $y2, $color = 'black', $width = 1)
{
$color = $this->allocateColor($color);
if (is_array($color)) {
$shape = new SWFShape();
$shape->setLine($width, $color['red'], $color['green'], $color['blue'], $color['alpha']);
$shape->movePenTo($x1, $y1);
$shape->drawLineTo($x2, $y2);
return $this->_movie->add($shape);
} else {
return false;
}
}
/**
* Draw 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
*/
function dashedLine($x0, $y0, $x1, $y1, $color = 'black', $width = 1, $dash_length = 2, $dash_space = 2)
{
// Get the length of the line in pixels.
$line_length = max(ceil(sqrt(pow(($x1 - $x0), 2) + pow(($y1 - $y0), 2))), 2);
$cosTheta = ($x1 - $x0) / $line_length;
$sinTheta = ($y1 - $y0) / $line_length;
$lastx = $x0;
$lasty = $y0;
// Draw the dashed line.
for ($i = 0; $i < $line_length; $i += ($dash_length + $dash_space)) {
$x = ($dash_length * $cosTheta) + $lastx;
$y = ($dash_length * $sinTheta) + $lasty;
$this->line($lastx, $lasty, $x, $y, $color);
$lastx = $x + ($dash_space * $cosTheta);
$lasty = $y + ($dash_space * $sinTheta);
}
}
/**
* Draw 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.
*/
function polyline($verts, $color, $width = 1)
{
$color = $this->allocateColor($color);
$shape = new SWFShape();
$shape->setLine($width, $color['red'], $color['green'], $color['blue'], $color['alpha']);
$first_done = false;
foreach ($verts as $value) {
if (!$first_done) {
$shape->movePenTo($value['x'], $value['y']);
$first_done = true;
}
$shape->drawLineTo($value['x'], $value['y']);
}
return $this->_movie->add($shape);
}
/**
* Draw an arc.
*
* @param integer $x The x co-ordinate of the centre.
* @param integer $y The y co-ordinate 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.
*/
function arc($x, $y, $r, $start, $end, $color = 'black', $fill = 'none')
{
$s = new SWFShape();
$color = $this->allocateColor($color);
$s->setLine(1, $color['red'], $color['green'], $color['blue'], $color['alpha']);
if ($fill != 'none') {
$fillColor = $this->allocateColor($fill);
$f = $s->addFill($fillColor['red'], $fillColor['green'], $fillColor['blue'], $fillColor['alpha']);
$s->setRightFill($f);
}
if ($end - $start <= 45) {
$pts = Horde_Image::arcPoints($r, $start, $end);
$s->movePenTo($x, $y);
$s->drawLineTo($pts['x1'] + $x, $pts['y1'] + $y);
$s->drawCurveTo($pts['x3'] + $x, $pts['y3'] + $y, $pts['x2'] + $x, $pts['y2'] + $y);
$s->drawLineTo($x, $y);
} else {
$sections = ceil(($end - $start) / 45);
for ($i = 0; $i < $sections; $i++) {
$pts = Horde_Image::arcPoints($r, $start + ($i * 45), ($start + (($i + 1) * 45) > $end)
? $end
: ($start + (($i + 1) * 45)));
// If we are on the first section, move the pen to the
// centre and draw out to the edge.
if ($i == 0 && $fill != 'none') {
$s->movePenTo($x, $y);
$s->drawLineTo($pts['x1'] + $x, $pts['y1'] + $y);
} else {
$s->movePenTo($pts['x1'] + $x, $pts['y1'] + $y);
}
// Draw the arc.
$s->drawCurveTo($pts['x3'] + $x, $pts['y3'] + $y, $pts['x2'] + $x, $pts['y2'] + $y);
}
if ($fill != 'none') {
// Draw a line from the edge back to the centre to close
// off the segment.
$s->drawLineTo($x, $y);
}
}
return $this->_movie->add($s);
}
/**
* Draw a rectangle filled with a gradient from $color1 to
* $color2.
*
* @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 outline color of the rectangle.
* @param string $fill1 The name of the start color for the gradient.
* @param string $fill2 The name of the end color for the gradient.
*/
function gradientRectangle($x, $y, $width, $height, $color = 'black', $fill1 = 'black', $fill2 = 'white')
{
$s = new SWFShape();
if ($color != 'none') {
$color = $this->allocateColor($color);
$s->setLine(1, $color['red'], $color['green'], $color['blue'], $color['alpha']);
}
$fill1 = $this->allocateColor($fill1);
$fill2 = $this->allocateColor($fill2);
$gradient = new SWFGradient();
$gradient->addEntry(0.0, $fill1['red'], $fill1['green'], $fill1['blue'], $fill1['alpha']);
$gradient->addEntry(1.0, $fill2['red'], $fill2['green'], $fill2['blue'], $fill2['alpha']);
$f = $s->addFill($gradient, SWFFILL_LINEAR_GRADIENT);
$f->scaleTo($width / $this->_width);
$f->moveTo($x, $y);
$s->setRightFill($f);
$verts[0] = array('x' => $x, 'y' => $y);
$verts[1] = array('x' => $x + $width, 'y' => $y);
$verts[2] = array('x' => $x + $width, 'y' => $y + $height);
$verts[3] = array('x' => $x, 'y' => $y + $height);
$first_done = false;
foreach ($verts as $vert) {
if (!$first_done) {
$s->movePenTo($vert['x'], $vert['y']);
$first_done = true;
$first_x = $vert['x'];
$first_y = $vert['y'];
}
$s->drawLineTo($vert['x'], $vert['y']);
}
$s->drawLineTo($first_x, $first_y);
return $this->_movie->add($s);
}
}
Horde_Image-2.0.5/lib/Horde/Image/Translation.php 0000664 0000766 0000024 00000003212 12170303167 016440 0 ustar
* @package Image
*/
class Horde_Image_Translation extends Horde_Translation
{
/**
* Returns the translation of a message.
*
* @var string $message The string to translate.
*
* @return string The string translation, or the original string if no
* translation exists.
*/
static public function t($message)
{
self::$_domain = 'Horde_Image';
self::$_directory = '@data_dir@' == '@'.'data_dir'.'@' ? __DIR__ . '/../../../locale' : '@data_dir@/Horde_Image/locale';
return parent::t($message);
}
/**
* Returns the plural translation of a message.
*
* @param string $singular The singular version to translate.
* @param string $plural The plural version to translate.
* @param integer $number The number that determines singular vs. plural.
*
* @return string The string translation, or the original string if no
* translation exists.
*/
static public function ngettext($singular, $plural, $number)
{
self::$_domain = 'Horde_Image';
self::$_directory = '@data_dir@' == '@'.'data_dir'.'@' ? __DIR__ . '/../../../locale' : '@data_dir@/Horde_Image/locale';
return parent::ngettext($singular, $plural, $number);
}
}
Horde_Image-2.0.5/lib/Horde/Image.php 0000664 0000766 0000024 00000015375 12170303167 014157 0 ustar
* @author Michael J. Rubinsky
* @package Image
*/
class Horde_Image
{
/**
* Calculate a lighter (or darker) version of a color.
*
* @param string $color An HTML color, e.g.: #ffffcc.
* @param string $factor TODO
*
* @return string A modified HTML color.
*/
static public function modifyColor($color, $factor = 0x11)
{
list($r, $g, $b) = self::getColor($color);
$r = min(max($r + $factor, 0), 255);
$g = min(max($g + $factor, 0), 255);
$b = min(max($b + $factor, 0), 255);
return '#' . str_pad(dechex($r), 2, '0', STR_PAD_LEFT) . str_pad(dechex($g), 2, '0', STR_PAD_LEFT) . str_pad(dechex($b), 2, '0', STR_PAD_LEFT);
}
/**
* Calculate a more intense version of a color.
*
* @param string $color An HTML color, e.g.: #ffffcc.
* @param string $factor TODO
*
* @return string A more intense HTML color.
*/
static public function moreIntenseColor($color, $factor = 0x11)
{
list($r, $g, $b) = self::getColor($color);
if ($r >= $g && $r >= $b) {
$g = $g / $r;
$b = $b / $r;
$r += $factor;
$g = floor($g * $r);
$b = floor($b * $r);
} elseif ($g >= $r && $g >= $b) {
$r = $r / $g;
$b = $b / $g;
$g += $factor;
$r = floor($r * $g);
$b = floor($b * $g);
} else {
$r = $r / $b;
$g = $g / $b;
$b += $factor;
$r = floor($r * $b);
$g = floor($g * $b);
}
$r = min(max($r, 0), 255);
$g = min(max($g, 0), 255);
$b = min(max($b, 0), 255);
return '#' . str_pad(dechex($r), 2, '0', STR_PAD_LEFT) . str_pad(dechex($g), 2, '0', STR_PAD_LEFT) . str_pad(dechex($b), 2, '0', STR_PAD_LEFT);
}
/**
* Returns the brightness of a color.
*
* @param string $color An HTML color, e.g.: #ffffcc.
*
* @return integer The brightness on a scale of 0 to 255.
*/
static public function brightness($color)
{
list($r, $g, $b) = self::getColor($color);
return round((($r * 299) + ($g * 587) + ($b * 114)) / 1000);
}
/**
* @TODO
*/
static public function grayscaleValue($r, $g, $b)
{
return round(($r * 0.30) + ($g * 0.59) + ($b * 0.11));
}
/**
* @TODO
*/
static public function grayscalePixel($originalPixel)
{
$gray = Horde_Image::grayscaleValue($originalPixel['red'], $originalPixel['green'], $originalPixel['blue']);
return array('red'=>$gray, 'green'=>$gray, 'blue'=>$gray);
}
/**
* Normalizes an HTML color.
*
* @param string $color An HTML color, e.g.: #ffffcc or #ffc.
*
* @return array Array with three elements: red, green, and blue.
*/
static public function getColor($color)
{
if ($color[0] == '#') {
$color = substr($color, 1);
}
if (strlen($color) == 3) {
$color = str_repeat($color[0], 2) .
str_repeat($color[1], 2) .
str_repeat($color[2], 2);
}
return array(
hexdec(substr($color, 0, 2)),
hexdec(substr($color, 2, 2)),
hexdec(substr($color, 4, 2))
);
}
/**
* Get the RGB value for a given colorname.
*
* @param string $colorname The colorname
*
* @return array An array of RGB values.
*/
static public function getRGB($colorname)
{
require_once __DIR__ . '/Image/rgb.php';
return isset($GLOBALS['horde_image_rgb_colors'][$colorname]) ?
$GLOBALS['horde_image_rgb_colors'][$colorname] :
array(0, 0, 0);
}
/**
* Get the hex representation of the given colorname.
*
* @param string $colorname The colorname
*
* @return string The hex representation of the color.
*/
static public function getHexColor($colorname)
{
require_once __DIR__ . '/Image/rgb.php';
if (isset($GLOBALS['horde_image_rgb_colors'][$colorname])) {
list($r, $g, $b) = $GLOBALS['horde_image_rgb_colors'][$colorname];
return '#' . str_pad(dechex(min($r, 255)), 2, '0', STR_PAD_LEFT) . str_pad(dechex(min($g, 255)), 2, '0', STR_PAD_LEFT) . str_pad(dechex(min($b, 255)), 2, '0', STR_PAD_LEFT);
} else {
return 'black';
}
}
/**
* Get an x,y pair on circle, assuming center is 0,0.
*
* @access private
*
* @param double $degrees The degrees of arc to get the point for.
* @param integer $diameter The diameter of the circle.
*
* @return array (x coordinate, y coordinate) of the point.
*/
static public function circlePoint($degrees, $diameter)
{
// Avoid problems with doubles.
$degrees += 0.0001;
return array(cos(deg2rad($degrees)) * ($diameter / 2),
sin(deg2rad($degrees)) * ($diameter / 2));
}
/**
* Get point coordinates at the limits of an arc. Only valid for
* angles ($end - $start) <= 45 degrees.
*
* @access private
*
* @param integer $r The radius of the arc.
* @param integer $start The starting angle.
* @param integer $end The ending angle.
*
* @return array The start point, end point, and anchor point.
*/
static public function arcPoints($r, $start, $end)
{
// Start point.
$pts['x1'] = $r * cos(deg2rad($start));
$pts['y1'] = $r * sin(deg2rad($start));
// End point.
$pts['x2'] = $r * cos(deg2rad($end));
$pts['y2'] = $r * sin(deg2rad($end));
// Anchor point.
$a3 = ($start + $end) / 2;
$r3 = $r / cos(deg2rad(($end - $start) / 2));
$pts['x3'] = $r3 * cos(deg2rad($a3));
$pts['y3'] = $r3 * sin(deg2rad($a3));
return $pts;
}
/**
* Return point size for font
*/
static public function getFontSize($fontsize)
{
switch ($fontsize) {
case 'medium':
$point = 18;
break;
case 'large':
$point = 24;
break;
case 'giant':
$point = 30;
break;
default:
$point = 12;
}
return $point;
}
}
Horde_Image-2.0.5/locale/ar/LC_MESSAGES/Horde_Image.mo 0000664 0000766 0000024 00000000732 12170303167 016732 0 ustar <