/**
  * Resizes an image to a specified height and width.
  *
  * Options:
  * scalePercent => scale the image by specified percentage - height and width are ignored
  * height => the height of the resized file
  * width => the width of the resized file
  * constrainProportions => if true retains image aspect ration only valid if height AND width is specified
  * algorithm => may be one of 'point','box','triangle','hermite','gaussian','quadratic','cubic','catrom' or 'mitchell'
  * unsharp => unsharp mask to sharpen image after scaling in format 'RADIUSxSIGMA' useful examples are 0x1 or 0x2
  *
  * @param $sourceFile The source file to operate on
  * @param $options An array of otions as described above
  *
  * @see AbstractImageFilter::applyFilter()
  */
 public function applyFilter($sourceFile, $options = null)
 {
     if (!empty($options['scalePercent']) && !is_numeric($options['scalePercent'])) {
         throw new ImageFilterException("ScalePercent parameter must be a valid integer value");
     }
     $this->scalePercent = !empty($options['scalePercent']) ? intval($options['scalePercent']) : 0;
     $this->height = !empty($options['height']) && strcmp($options['height'], 'auto') != 0 ? intval($options['height']) : 0;
     if (!empty($this->height) && !is_numeric($this->height)) {
         throw new ImageFilterException("Height parameter must be a valid integer value");
     }
     $this->width = !empty($options['width']) && strcmp($options['width'], 'auto') != 0 ? intval($options['width']) : 0;
     if (!empty($this->width) && !is_numeric($this->width)) {
         throw new ImageFilterException("Width parameter must be a valid integer value");
     }
     // we must have one height or width parameter or scalePercent
     if ($this->height == 0 && $this->width == 0 && $this->scalePercent == 0) {
         throw new ImageFilterException("Either scaleHeight or either (or both) height and width must be specified");
     }
     $this->algorithm = !empty($options['algorithm']) ? $options['algorithm'] : null;
     if (!empty($this->algorithm)) {
         $this->algorithm = trim(strtolower($this->algorithm));
         $aa = array('point', 'box', 'triangle', 'hermite', 'gaussian', 'quadratic', 'cubic', 'catrom', 'mitchell');
         if (!in_array($this->algorithm, $aa)) {
             throw new ImageFilterException("Algorithm must be one of 'point','box','triangle','hermite','gaussian','quadratic','cubic','catrom' or 'mitchell'");
         }
     }
     $this->unsharp = !empty($options['unsharp']) ? $options['unsharp'] : null;
     return parent::applyFilter($sourceFile, $options);
 }
 /**
  * Crops a file to a specified height and width.
  *
  * Options:
  * height => the height of the cropped file
  * width => the width of the cropped file
  * anchor => may be one of 'northwest', 'north', 'northeast', 'west', 'center', 'east', 'southwest', 'south' or 'southeast'
  * offsetX => the X offset from relative to the anchor position (default is 0)
  * offsetY => the Y offset from relative to the anchor position (default is 0)
  *
  * @param $sourceFile The source file to operate on
  * @param $options An array of options as described above
  *
  * @see AbstractImageFilter::applyFilter()
  */
 public function applyFilter($sourceFile, $options = null)
 {
     if (!empty($options['width']) && !is_numeric($options['width'])) {
         throw new ImageFilterException("Width parameter must be a valid integer value");
     }
     $this->width = !empty($options['width']) ? intval($options['width']) : 0;
     if (!empty($options['height']) && !is_numeric($options['height'])) {
         throw new ImageFilterException("Height parameter must be a valid integer value");
     }
     $this->height = !empty($options['height']) ? intval($options['height']) : 0;
     $this->populateAnchorFields($options);
     return parent::applyFilter($sourceFile, $options);
 }
 /**
  * Helper method to add common parameters and code.
  *
  * @param AbstractImageFilter $filter The filter to execute method on.
  * @param string $op The filter-specific command line options
  * @param $ignoreSourceFile If true source file is not added to exec cmdParams
  * @return string The filter's target file
  */
 public static function executeFilter(AbstractImageFilter $filter, $op, $ignoreSourceFile = false)
 {
     if ($filter->outputAlpha != null) {
         if ($filter->outputAlpha == true) {
             $op .= " -alpha On";
         } else {
             $op .= " -alpha Off";
         }
     }
     if (null !== $filter->interlace) {
         $op .= " -interlace {$filter->interlace}";
     }
     $op .= " -quality {$filter->outputQuality}";
     $inFile = escapeshellarg($filter->sourceFile);
     $outFile = escapeshellarg($filter->targetFile);
     if ($ignoreSourceFile) {
         $cmdParams = "{$op} {$outFile}";
     } else {
         $cmdParams = "{$inFile} {$op} {$outFile}";
     }
     $filter->exec($cmdParams);
     return $filter->targetFile;
 }
 /**
  * Rotates an image by specified degrees.
  *
  * Options:
  * degrees => value between -180 and +180 (default is 90)
  * autoCrop => if true, image is auto-cropped to remove empty corners (only valid in 'rotate' mode - default scale = 1.3)
  * backgroundColor => color to fill empty corners with e.g. 'blue' or 'rgb(255,255,255)' or '#FFFFFF'(default)
  * mode => 'rotate'(default) or 'distort' - if 'distort' empty corners are blurred rather than filled with background color
  * scale => for distort mode scales image to reduce empty corners - useful values are between about 1.1 to 1.5
  *
  * @param $sourceFile The source file to operate on
  * @param $options An array of otions as described above
  *
  * @see AbstractImageFilter::applyFilter()
  */
 public function applyFilter($sourceFile, $options = null)
 {
     if (!empty($options['angle']) && !is_numeric($options['angle'])) {
         throw new ImageFilterException("Degrees parameter must be a valid integer value");
     }
     $this->degrees = sprintf("%+d", !empty($options['angle']) ? intval($options['angle']) : 0);
     if ($this->degrees < -180 || $this->degrees > 180) {
         throw new ImageFilterException("Degrees parameter must be between -180 and +180");
     }
     $this->autoCrop = StringUtils::strToBool($options['autoCrop']);
     $this->mode = !empty($options['mode']) ? $options['mode'] : 'rotate';
     $this->scale = !empty($options['scale']) ? $options['scale'] : '0';
     // default to white if no background color specified
     $this->backgroundColor = !empty($options['backgroundColor']) ? $options['backgroundColor'] : '#ffffff';
     return parent::applyFilter($sourceFile, $options);
 }
 /**
  * Applies an unsharp mask to sharpen an image.
  *
  * Options:
  * radius => the radius value of the unsharp mask - typically 0
  * sigma => the sigma value of the unsharp mask - useful values are 1 -10
  *
  * @param $sourceFile The source file to operate on
  * @param $options An array of otions as described above
  *
  * @see AbstractImageFilter::applyFilter()
  */
 public function applyFilter($sourceFile, $options = null, $inputFiles = null)
 {
     if (!isset($options['radius'])) {
         throw new ImageFilterException("Radius is a required value");
     }
     if (!is_int($options['radius'])) {
         throw new ImageFilterException("Radius parameter must be a valid integer value");
     }
     $this->radius = $options['radius'];
     if (!isset($options['sigma'])) {
         throw new ImageFilterException("Sigma is a required value");
     }
     if (!is_int($options['sigma'])) {
         throw new ImageFilterException("Sigma parameter must be a valid integer value");
     }
     $this->sigma = $options['sigma'];
     return parent::applyFilter($sourceFile, $options, $inputFiles);
 }
 /**
  * Creates an underlay of color or an image to the source image.
  *
  * image => a base64 encoded data string to use as background matte
  * repeat => if the image should be repeated
  * color => a color to use if an image is not set
  * width => the width of the canvas to place the source image on
  * height => the height of the canvas to place the source image on
  * anchor => may be one of 'northwest', 'north', 'northeast', 'west', 'center', 'east', 'southwest', 'south' or 'southeast'
  * offsetX => the X offset from relative to the anchor position (default is 0)
  * offsetY => the Y offset from relative to the anchor position (default is 0)
  *
  * @param $sourceFile The source file to operate on
  * @param $options An array of options as described above
  *
  * @see AbstractImageFilter::applyFilter()
  */
 public function applyFilter($sourceFile, $options = null)
 {
     if (!empty($options['image']) && $options['image'] != 'null') {
         $this->image = $this->prepareImageData($options['image']);
         $this->repeat = StringUtils::strToBool($options['repeat']);
     }
     $this->color = !empty($options['color']) ? '#' . preg_replace('/[^a-f0-9]/i', '', $options['color']) : "#ffffff";
     if (!empty($this->color) && !preg_match('/^#[a-f0-9]{6}$/i', $this->color)) {
         throw new ImageFilterException("Color must be a valid hex value " . $this->color);
     }
     if (!empty($options['width']) && !is_numeric($options['width'])) {
         throw new ImageFilterException("Canvas width must be a valid integer value");
     }
     $this->width = !empty($options['width']) ? intval($options['width']) : 0;
     if (!empty($options['height']) && !is_numeric($options['height'])) {
         throw new ImageFilterException("Canvas height must be a valid integer value");
     }
     $this->height = !empty($options['height']) ? intval($options['height']) : 0;
     $this->populateAnchorFields($options);
     return parent::applyFilter($sourceFile, $options);
 }
 /**
  * Adds text to an image either as an overlay or by adding a text box to the bottom.
  *
  * Options:
  * type => maybe either 'add' - add a text box to bottom of image - or 'overlay' - overlay text on image
  * fontFamily => the typeface of the font to use - default is system dependent
  * fontSize => integer value of the font point size - default is 12
  * fontColor => the color of the text - default is black
  * fontWeight => the font weight - maybe be one of 'normal', 'bolder', 'bold' or 'lighter'
  * fontStyle => may be one of 'normal', 'italic' or 'oblique'
  * text => the text to apply to the image
  *
  * Options for type = 'add':
  * padding => padding for the text box
  * backgroundColor => the color to use as the background of the text box
  *
  * Options for type = 'overlay':
  * anchor => may be one of 'northwest', 'north', 'northeast', 'southwest', 'south' or 'southeast'
  * offsetX => the X offset from relative to the anchor position
  * offsetY => the Y offset from relative to the anchor position
  *
  * @param $sourceFile The source file to operate on
  * @param $options An array of options as described above
  *
  * @see AbstractImageFilter::applyFilter()
  */
 public function applyFilter($sourceFile, $options = null)
 {
     $this->type = !empty($options['mode']) ? strtolower($options['mode']) : 'add';
     if (!($this->type == 'overlay' || $this->type == 'add')) {
         throw new ImageFilterException("Type option must be 'add' or 'overlay'");
     }
     $this->populateAnchorFields($options);
     $this->populateFontFields($options);
     $this->backgroundColor = !empty($options['backgroundColor']) ? '#' . preg_replace('/[^a-f0-9]/i', '', $options['backgroundColor']) : "#ffffff";
     if (!empty($this->backgroundColor) && !preg_match('/^#[a-f0-9]{6}$/i', $this->backgroundColor)) {
         throw new ImageFilterException("Background Color must be a valid hex value " . $this->backgroundColor);
     }
     if (!empty($options['padding']) && !is_numeric($options['padding'])) {
         throw new ImageFilterException("Text padding must be a valid integer value");
     }
     $this->padding = !empty($options['padding']) ? intval($options['padding']) : 0;
     if (empty($options['text'])) {
         throw new ImageFilterException("Text option cannot be empty");
     }
     $this->text = $options['text'];
     return parent::applyFilter($sourceFile, $options);
 }
 /**
  * Overlays either a text string or an image file to the source image.
  *
  * Options:
  * type => maybe either 'text' or 'image' - if 'image' a valid image input file must be specified
  * anchor => may be one of 'northwest', 'north', 'northeast', 'west', 'center', 'east', 'southwest', 'south' or 'southeast'
  * offsetX => the X offset from relative to the anchor position (default is 0)
  * offsetY => the Y offset from relative to the anchor position (default is 0)
  * opacity => the opacity of the watermark
  *
  * Options for type = 'text':
  * fontFamily => the typeface of the font to use - default is system dependant
  * fontSize => integer value of the font point size - default is 12
  * fontColor => the color of the text - default is black
  * fontWeight => the font weight - maybe be one of 'normal', 'bolder', 'bold' or 'lighter'
  * fontStyle => may be one of 'normal', 'italic' or 'oblique'
  * rotate => an integer value between -180 and +180 to rotate the text
  * text => the text to apply to the image
  *
  * Options for type = 'image':
  * image => the key to a corresponding image file path in the inputFiles array
  * scaleX => the width of the overlaid image
  * scaleY => the height of the overlaid image
  *
  * @param $sourceFile The source file to operate on
  * @param $options An array of options as described above
  *
  * @see AbstractImageFilter::applyFilter()
  */
 public function applyFilter($sourceFile, $options = null)
 {
     $this->type = !empty($options['mode']) ? strtolower($options['mode']) : 'text';
     if ($this->type == 'text') {
         if (empty($options['text'])) {
             throw new ImageFilterException("Text option cannot be empty");
         }
         $this->text = $options['text'];
         $this->width = intval($options['width']);
         $this->height = intval($options['height']);
         $this->populateFontFields($options);
         $this->populateRotateField($options);
     } else {
         if ($this->type == 'image') {
             if (!empty($options['image']) && $options['image'] != 'null') {
                 $this->image = $this->prepareImageData($options['image']);
             }
             $this->scaleX = isset($options['scaleX']) ? $options['scaleX'] : null;
             if ($this->scaleX != null && !is_int($this->scaleX)) {
                 throw new ImageFilterException("ScaleX option must be a valid integer value");
             }
             $this->scaleY = isset($options['scaleY']) ? $options['scaleY'] : null;
             if ($this->scaleY != null && !is_int($this->scaleY)) {
                 throw new ImageFilterException("ScaleY option must be a valid integer value");
             }
             // if only one scale option specified - set both to same value
             if ($this->scaleX != null && $this->scaleY == null) {
                 $this->scaleY = $this->scaleX;
             } else {
                 if ($this->scaleY != null && $this->scaleX == null) {
                     $this->scaleX = $this->scaleY;
                 }
             }
         } else {
             throw new ImageFilterException("Type option must be 'text' or 'image'");
         }
     }
     if (!empty($options['opacity']) && !is_numeric($options['opacity'])) {
         throw new ImageFilterException("Opacity must be a valid integer value");
     }
     $this->opacity = !empty($options['opacity']) ? intval($options['opacity']) : 0;
     if ($this->opacity < 0 || $this->opacity > 100) {
         throw new ImageFilterException("Opacity must be between 0 and 100");
     }
     $this->populateAnchorFields($options);
     return parent::applyFilter($sourceFile, $options);
 }