| <?php |
| /** |
| * This file contains the ezcImageImagemagickBaseHandler class. |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| * @package ImageConversion |
| * @version //autogentag// |
| * @copyright Copyright (C) 2005-2010 eZ Systems AS. All rights reserved. |
| * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0 |
| * @filesource |
| */ |
| |
| |
| /** |
| * ezcImageHandler implementation for ImageMagick. |
| * This class only implements the base funtionality of handling images with |
| * ImageMagick. If you want to manipulate images using ImageMagick in your |
| * application, you should use the {@link ezcImageImagemagickHandler}. |
| * |
| * You can use this base class to implement your own filter set on basis of |
| * ImageMagick, but you can also use {@link ezcImageImagemagickHandler} for |
| * this and profit from its already implemented filters. |
| * |
| * @see ezcImageConverter |
| * @see ezcImageHandler |
| * |
| * @package ImageConversion |
| * @version //autogentag// |
| */ |
| class ezcImageImagemagickBaseHandler extends ezcImageMethodcallHandler |
| { |
| /** |
| * Path to the convert binary. |
| * |
| * @var string |
| */ |
| private $binary; |
| |
| /** |
| * Map of MIME types to convert tags. |
| * |
| * @var array(string=>string) |
| */ |
| private $tagMap = array(); |
| |
| /** |
| * Filter options per reference. |
| * |
| * @var array(string=>array) |
| */ |
| private $filterOptions = array(); |
| |
| /** |
| * Composite image setting per reference. |
| * |
| * @var array(string=>bool) |
| */ |
| private $compositeImages = array(); |
| |
| /** |
| * Create a new image handler. |
| * Creates an image handler. This should never be done directly, |
| * but only through the manager for configuration reasons. One can |
| * get a direct reference through manager afterwards. |
| * |
| * This handler has an option 'binary' available, which allows you to |
| * explicitly set the path to your ImageMagicks "convert" binary (this |
| * may be necessary on Windows, since there may be an obscure "convert.exe" |
| * in the $PATH variable available, which has nothing to do with |
| * ImageMagick). |
| * |
| * @throws ezcImageHandlerNotAvailableException |
| * If the ImageMagick binary is not found. |
| * |
| * @param ezcImageHandlerSettings $settings Settings for the handler. |
| */ |
| public function __construct( ezcImageHandlerSettings $settings ) |
| { |
| // Check for ImageMagick |
| $this->checkImageMagick( $settings ); |
| $this->determineTypes(); |
| parent::__construct( $settings ); |
| } |
| |
| /** |
| * Load an image file. |
| * Loads an image file and returns a reference to it. |
| * |
| * @param string $file File to load. |
| * @param string $mime The MIME type of the file. |
| * |
| * @return string Reference to the file in this handler. |
| * |
| * @see ezcImageAnalyzer |
| * |
| * @throws ezcBaseFileNotFoundException |
| * If the desired file does not exist. |
| * @throws ezcImageMimeTypeUnsupportedException |
| * If the desired file has a not recognized type. |
| * @throws ezcImageFileNameInvalidException |
| * If an invalid character (", ', $) is found in the file name. |
| */ |
| public function load( $file, $mime = null ) |
| { |
| $this->checkFileName( $file ); |
| $ref = $this->loadCommon( $file, $mime ); |
| |
| // Atomic file operation |
| $fileTmp = tempnam( dirname( $file ) . DIRECTORY_SEPARATOR, '.' . basename( $file ) ); |
| copy( $file, $fileTmp ); |
| |
| $this->setReferenceData( $ref, $fileTmp, 'resource' ); |
| return $ref; |
| } |
| |
| /** |
| * Save an image file. |
| * Saves a given open file. Can optionally save to a new file name. |
| * |
| * @see ezcImageHandler::load() |
| * |
| * @param string $image File reference created through load(). |
| * @param string $newFile Filename to save the image to. |
| * @param string $mime New MIME type, if differs from initial one. |
| * @param ezcImageSaveOptions $options Save options. |
| * @return void |
| * |
| * @throws ezcBaseFilePermissionException |
| * If the desired file exists and is not writeable. |
| * @throws ezcImageMimeTypeUnsupportedException |
| * If the desired MIME type is not recognized. |
| * @throws ezcImageFileNameInvalidException |
| * If an invalid character (", ', $) is found in the file name. |
| */ |
| public function save( $image, $newFile = null, $mime = null, ezcImageSaveOptions $options = null ) |
| { |
| if ( $options === null ) |
| { |
| $options = new ezcImageSaveOptions(); |
| } |
| |
| if ( $newFile !== null ) |
| { |
| $this->checkFileName( $newFile ); |
| } |
| |
| // Check is transparency must be converted |
| if ( $this->needsTransparencyConversion( $this->getReferenceData( $image, 'mime' ), $mime ) && $options->transparencyReplacementColor !== null ) |
| { |
| $this->addFilterOption( $image, '-background', $this->colorArrayToString( $options->transparencyReplacementColor ) ); |
| $this->addFilterOption( $image, '-flatten' ); |
| } |
| |
| $this->saveCommon( $image, $newFile, $mime ); |
| |
| switch ( $this->getReferenceData( $image, 'mime' ) ) |
| { |
| case "image/jpeg": |
| if ( $options->quality !== null ) |
| { |
| $this->addFilterOption( $image, "-quality", $options->quality ); |
| } |
| break; |
| case "image/png": |
| if ( $options->compression !== null ) |
| { |
| // ImageMagick uses qualtiy options here and incorporates filter options |
| $this->addFilterOption( $image, "-quality", $options->compression * 10 ); |
| } |
| break; |
| } |
| |
| // Prepare ImageMagick command |
| // Here we need a work around, because older ImageMagick versions do not |
| // support this option order |
| |
| if ( isset( $this->compositeImages[$image] ) ) |
| { |
| $command = $this->binary . ' ' . |
| ( isset( $this->filterOptions[$image] ) ? implode( ' ', $this->filterOptions[$image] ) : '' ) . ' ' . |
| escapeshellarg( $this->getReferenceData( $image, 'resource' ) ) . ' ' . |
| implode( ' ', $this->compositeImages[$image] ) . ' ' . |
| escapeshellarg( $this->tagMap[$this->getReferenceData( $image, 'mime' )] . ':' . $this->getReferenceData( $image, 'resource' ) ); |
| } |
| else |
| { |
| $command = $this->binary . ' ' . |
| escapeshellarg( $this->getReferenceData( $image, 'resource' ) ) . ' ' . |
| ( isset( $this->filterOptions[$image] ) ? implode( ' ', $this->filterOptions[$image] ) : '' ) . ' ' . |
| escapeshellarg( $this->tagMap[$this->getReferenceData( $image, 'mime' )] . ':' . $this->getReferenceData( $image, 'resource' ) ); |
| } |
| |
| |
| // Prepare to run ImageMagick command |
| $descriptors = array( |
| array( 'pipe', 'r' ), |
| array( 'pipe', 'w' ), |
| array( 'pipe', 'w' ), |
| ); |
| |
| // Open ImageMagick process |
| $imageProcess = proc_open( $command, $descriptors, $pipes ); |
| // Close STDIN pipe |
| fclose( $pipes[0] ); |
| |
| $errorString = ''; |
| $outputString = ''; |
| // Read STDERR |
| do |
| { |
| $outputString .= rtrim( fgets( $pipes[1], 1024 ), "\n" ); |
| $errorString .= rtrim( fgets( $pipes[2], 1024 ), "\n" ); |
| } while ( !feof( $pipes[2] ) ); |
| |
| // Wait for process to terminate and store return value |
| $status = proc_get_status( $imageProcess ); |
| while ( $status['running'] !== false ) |
| { |
| // Sleep 1/100 second to wait for convert to exit |
| usleep( 10000 ); |
| $status = proc_get_status( $imageProcess ); |
| } |
| $return = proc_close( $imageProcess ); |
| |
| // Process potential errors |
| // Exit code may be messed up with -1, especially on Windoze |
| if ( ( $status['exitcode'] != 0 && $status['exitcode'] != -1 ) || strlen( $errorString ) > 0 ) |
| { |
| throw new ezcImageFileNotProcessableException( |
| $this->getReferenceData( $image, 'resource' ), |
| "The command '{$command}' resulted in an error ({$status['exitcode']}): '{$errorString}'. Output: '{$outputString}'" |
| ); |
| } |
| // Finish atomic file operation |
| copy( $this->getReferenceData( $image, 'resource' ), $this->getReferenceData( $image, 'file' ) ); |
| } |
| |
| /** |
| * Returns a string representation of the given color array. |
| * |
| * ImageConversion uses arrays to represent color values, in the format: |
| * <code> |
| * array( |
| * 255, |
| * 0, |
| * 0, |
| * ) |
| * </code> |
| * This array represents the color red. |
| * |
| * This method takes such a color array and converts it into a string |
| * representation usable by the convert binary. For the above examle it |
| * would be '#FF0000'. |
| * |
| * @param array $color |
| * @return void |
| * |
| * @throws ezcBaseValueException |
| * if one of the color values in the array is invalid (not integer, |
| * smaller than 0 or larger than 255). |
| */ |
| protected function colorArrayToString( array $color ) |
| { |
| $colorString = '#'; |
| $i = 0; |
| foreach ( $color as $id => $colorVal ) |
| { |
| if ( $i++ > 2 ) |
| { |
| break; |
| } |
| if ( !is_int( $colorVal ) || $colorVal < 0 || $colorVal > 255 ) |
| { |
| throw new ezcBaseValueException( "color[$id]", $color[$id], 'int > 0 and < 256' ); |
| } |
| $colorString .= sprintf( '%02x', $colorVal ); |
| } |
| return $colorString; |
| } |
| |
| /** |
| * Close the file referenced by $image. |
| * Frees the image reference. You should call close() before. |
| * |
| * @see ezcImageHandler::load() |
| * @see ezcImageHandler::save() |
| * @param string $image The image reference. |
| */ |
| public function close( $image ) |
| { |
| unlink( $this->getReferenceData( $image, 'resource' ) ); |
| $this->setReferenceData( $image, false, 'resource' ); |
| $this->closeCommon( $image ); |
| } |
| |
| /** |
| * Add a filter option to a given reference |
| * |
| * @param string $reference The reference to add a filter for. |
| * @param string $name The option name. |
| * @param string $parameter The option parameter. |
| * @return void |
| */ |
| protected function addFilterOption( $reference, $name, $parameter = null ) |
| { |
| $this->filterOptions[$reference][] = $name . ( $parameter !== null ? ' ' . escapeshellarg( $parameter ) : '' ); |
| } |
| |
| /** |
| * Add an image to composite with the given reference. |
| * |
| * @param string $reference The reference to add an image to |
| * @param string $file The file to composite with the image. |
| * @return void |
| */ |
| protected function addCompositeImage( $reference, $file ) |
| { |
| $this->compositeImages[$reference][] = $file; |
| } |
| |
| /** |
| * Determines the supported input/output types supported by handler. |
| * Set's various attributes to reflect the MIME types this handler is |
| * capable to process. |
| * |
| * @return void |
| * |
| * @apichange Faulty MIME type "image/svg" will be removed and replaced by |
| * correct MIME type image/svg+xml. |
| */ |
| private function determineTypes() |
| { |
| $tagMap = array( |
| 'application/pcl' => 'PCL', |
| 'application/pdf' => 'PDF', |
| 'application/postscript' => 'PS', |
| 'application/vnd.palm' => 'PDB', |
| 'application/x-icb' => 'ICB', |
| 'application/x-mif' => 'MIFF', |
| 'image/bmp' => 'BMP3', |
| 'image/dcx' => 'DCX', |
| 'image/g3fax' => 'G3', |
| 'image/gif' => 'GIF', |
| 'image/jng' => 'JNG', |
| 'image/jpeg' => 'JPG', |
| 'image/pbm' => 'PBM', |
| 'image/pcd' => 'PCD', |
| 'image/pict' => 'PCT', |
| 'image/pjpeg' => 'PJPEG', |
| 'image/png' => 'PNG', |
| 'image/ras' => 'RAS', |
| 'image/sgi' => 'SGI', |
| 'image/svg+xml' => 'SVG', |
| // Left over for BC reasons |
| 'image/svg' => 'SVG', |
| 'image/tga' => 'TGA', |
| 'image/tiff' => 'TIF', |
| 'image/vda' => 'VDA', |
| 'image/vnd.wap.wbmp' => 'WBMP', |
| 'image/vst' => 'VST', |
| 'image/x-fits' => 'FITS', |
| 'image/x-otb' => 'OTB', |
| 'image/x-palm' => 'PALM', |
| 'image/x-pcx' => 'PCX', |
| 'image/x-pgm' => 'PGM', |
| 'image/psd' => 'PSD', |
| 'image/x-ppm' => 'PPM', |
| 'image/x-ptiff' => 'PTIF', |
| 'image/x-viff' => 'VIFF', |
| 'image/x-xbitmap' => 'XPM', |
| 'image/x-xv' => 'P7', |
| 'image/xpm' => 'PICON', |
| 'image/xwd' => 'XWD', |
| 'text/plain' => 'TXT', |
| 'video/mng' => 'MNG', |
| 'video/mpeg' => 'MPEG', |
| 'video/mpeg2' => 'M2V', |
| ); |
| $types = array_keys( $tagMap ); |
| $this->inputTypes = $types; |
| $this->outputTypes = $types; |
| $this->tagMap = $tagMap; |
| } |
| |
| /** |
| * Checks for ImageMagick on the system. |
| * |
| * @param ezcImageHandlerSettings $settings The settings object of the current handler instance. |
| * @return void |
| * |
| * @throws ezcImageHandlerNotAvailableException |
| * If the ImageMagick binary is not found. |
| */ |
| private function checkImageMagick( ezcImageHandlerSettings $settings ) |
| { |
| if ( !isset( $settings->options['binary'] ) ) |
| { |
| $this->binary = ezcBaseFeatures::getImageConvertExecutable(); |
| } |
| else if ( file_exists( $settings->options['binary'] ) ) |
| { |
| $this->binary = $settings->options['binary']; |
| } |
| |
| if ( $this->binary === null ) |
| { |
| throw new ezcImageHandlerNotAvailableException( |
| 'ezcImageImagemagickHandler', |
| 'ImageMagick not installed or not available in PATH variable.' |
| ); |
| } |
| |
| // Prepare to run ImageMagick command |
| $descriptors = array( |
| array( 'pipe', 'r' ), |
| array( 'pipe', 'w' ), |
| array( 'pipe', 'w' ), |
| ); |
| |
| // Open ImageMagick process |
| $imageProcess = proc_open( $this->binary, $descriptors, $pipes ); |
| |
| // Close STDIN pipe |
| fclose( $pipes[0] ); |
| |
| $outputString = ''; |
| // Read STDOUT |
| do |
| { |
| $outputString .= rtrim( fgets( $pipes[1], 1024 ), "\n" ); |
| } while ( !feof( $pipes[1] ) ); |
| |
| $errorString = ''; |
| // Read STDERR |
| do |
| { |
| $errorString .= rtrim( fgets( $pipes[2], 1024 ), "\n" ); |
| } while ( !feof( $pipes[2] ) ); |
| |
| // Wait for process to terminate and store return value |
| $return = proc_close( $imageProcess ); |
| |
| // Process potential errors |
| if ( strlen( $errorString ) > 0 || strpos( $outputString, 'ImageMagick' ) === false ) |
| { |
| throw new ezcImageHandlerNotAvailableException( 'ezcImageImagemagickHandler', 'ImageMagick not installed or not available in PATH variable.' ); |
| } |
| } |
| |
| /** |
| * Creates default settings for the handler and returns it. |
| * The reference name will be set to 'ImageMagick'. |
| * |
| * @return ezcImageHandlerSettings |
| */ |
| static public function defaultSettings() |
| { |
| return new ezcImageHandlerSettings( 'ImageMagick', 'ezcImageImagemagickHandler' ); |
| } |
| } |
| |
| ?> |