blob: 0a271c3d15a3394dc4e85ffbccde95eb524e7d5c [file] [log] [blame]
<?php
/**
* File containing the ezcImageConverter 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//
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
* @filesource
*/
/**
* Class to manage conversion and filtering of image files.
* This class is highly recommended to be used with an external
* singleton pattern to have just 1 converter in place over the whole
* application.
*
* As back-ends 2 handler classes are available, of which at least 1 has to be
* configured during the instantiation of the ezcImageConverter. Both handlers
* utilize different image manipulation tools and are capable of different
* sets of filters:
*
* <ul>
* <li>ezcImageGdHandler
* <ul>
* <li>Uses PHP's GD extension for image manipulation.</li>
* <li>Implements the following filter interfaces
* <ul>
* <li>{@link ezcImageGeometryFilters}</li>
* <li>{@link ezcImageColorspaceFilters}</li>
* </ul>
* </li>
* </ul>
* </li>
* <li>ezcImageImagemagickHandler
* <ul>
* <li>Uses the external "convert" program, contained in ImageMagick</li>
* <li>Implements the following interfaces:
* <ul>
* <li>{@link ezcImageGeometryFilters}</li>
* <li>{@link ezcImageColorspaceFilters}</li>
* <li>{@link ezcImageEffectFilters}</li>
* </ul>
* </li>
* </ul>
* </li>
* </ul>
*
* A general example, how to use ezcImageConversion to convert images:
* <code>
* // Prepare settings for ezcImageConverter
* // Defines the handlers to utilize and auto conversions.
* $settings = new ezcImageConverterSettings(
* array(
* new ezcImageHandlerSettings( 'GD', 'ezcImageGdHandler' ),
* new ezcImageHandlerSettings( 'ImageMagick', 'ezcImageImagemagickHandler' ),
* ),
* array(
* 'image/gif' => 'image/png',
* 'image/bmp' => 'image/jpeg',
* )
* );
*
* // Create the converter itself.
* $converter = new ezcImageConverter( $settings );
*
* // Define a transformation
* $filters = array(
* new ezcImageFilter(
* 'scaleWidth',
* array(
* 'width' => 100,
* 'direction' => ezcImageGeometryFilters::SCALE_BOTH,
* )
* ),
* new ezcImageFilter(
* 'colorspace',
* array(
* 'space' => ezcImageColorspaceFilters::COLORSPACE_GREY,
* )
* ),
* );
*
* // Which MIME types the conversion may output
* $mimeTypes = array( 'image/jpeg', 'image/png' );
*
* // Create the transformation inside the manager
* $converter->createTransformation( 'thumbnail', $filters, $mimeTypes );
*
* // Transform an image.
* $converter->transform( 'thumbnail', dirname(__FILE__).'/jpeg.jpg', dirname(__FILE__).'/jpeg_thumb.jpg' );
* </code>
*
* It's recommended to create only a single ezcImageConverter instance in your
* application to avoid creating multiple instances of it's internal objects.
* You can implement a singleton pattern for this, which might look similar to
* the following example:
* <code>
* function getImageConverterInstance()
* {
* if ( !isset( $GLOBALS['_ezcImageConverterInstance'] ) )
* {
* // Prepare settings for ezcImageConverter
* // Defines the handlers to utilize and auto conversions.
* $settings = new ezcImageConverterSettings(
* array(
* new ezcImageHandlerSettings( 'GD', 'ezcImageGdHandler' ),
* new ezcImageHandlerSettings( 'ImageMagick', 'ezcImageImagemagickHandler' ),
* ),
* array(
* 'image/gif' => 'image/png',
* 'image/bmp' => 'image/jpeg',
* )
* );
*
*
* // Create the converter itself.
* $converter = new ezcImageConverter( $settings );
*
* // Define a transformation
* $filters = array(
* new ezcImageFilter(
* 'scale',
* array(
* 'width' => 100,
* 'height' => 300,
* 'direction' => ezcImageGeometryFilters::SCALE_BOTH,
* )
* ),
* new ezcImageFilter(
* 'colorspace',
* array(
* 'space' => ezcImageColorspaceFilters::COLORSPACE_SEPIA,
* )
* ),
* new ezcImageFilter(
* 'border',
* array(
* 'width' => 5,
* 'color' => array(255, 0, 0),
* )
* ),
* );
*
* // Which MIME types the conversion may output
* $mimeTypes = array( 'image/jpeg', 'image/png' );
*
* // Create the transformation inside the manager
* $converter->createTransformation( 'funny', $filters, $mimeTypes );
*
* // Assign singleton instance
* $GLOBALS['_ezcImageConverterInstance'] = $converter;
* }
*
* // Return singleton instance
* return $GLOBALS['_ezcImageConverterInstance'];
* }
*
* // ...
* // Somewhere else in the code...
* // Transform an image.
* getImageConverterInstance()->transform( 'funny', dirname(__FILE__).'/jpeg.jpg', dirname(__FILE__).'/jpeg_singleton.jpg' );
*
* </code>
*
* @see ezcImageHandler
* @see ezcImageTransformation
*
* @package ImageConversion
* @version //autogentag//
* @mainclass
*/
class ezcImageConverter
{
/**
* Manager settings
* Settings basis for all image manipulations.
*
* @var ezcImageConverterSettings
*/
protected $settings;
/**
* Keeps the handlers used by the converter.
*
* @var array(ezcImageHandler)
*/
protected $handlers = array();
/**
* Stores transformation registered with this converter.
*
* @var array
*/
protected $transformations = array();
/**
* Initialize converter with settings object.
* The ezcImageConverter can be directly instantiated, but it's
* highly recommended to use a manual singleton implementation
* to have just 1 instance of a ezcImageConverter per Request.
*
* ATTENTION: The ezcImageConverter does not support animated
* GIFs. Animated GIFs will simply be ignored by all filters and
* conversions.
*
* @param ezcImageConverterSettings $settings Settings for the converter.
*
* @throws ezcImageHandlerSettingsInvalidException
* If handler settings are invalid.
* @throws ezcImageMimeTypeUnsupportedException
* If a given MIME type is not supported.
*/
public function __construct( ezcImageConverterSettings $settings )
{
// Initialize handlers
foreach ( $settings->handlers as $i => $handlerSettings )
{
if ( !$handlerSettings instanceof ezcImageHandlerSettings )
{
throw new ezcImageHandlerSettingsInvalidException();
}
$handlerClass = $handlerSettings->className;
if ( !ezcBaseFeatures::classExists( $handlerClass ) )
{
throw new ezcImageHandlerNotAvailableException( $handlerClass );
}
$handler = new $handlerClass( $handlerSettings );
$this->handlers[$handlerSettings->referenceName] = $handler;
}
// Check implicit conversions
foreach ( $settings->conversions as $mimeIn => $mimeOut )
{
if ( !$this->allowsInput( $mimeIn ) )
{
throw new ezcImageMimeTypeUnsupportedException( $mimeIn, 'input' );
}
if ( !$this->allowsOutput( $mimeOut ) )
{
throw new ezcImageMimeTypeUnsupportedException( $mimeOut, 'output' );
}
}
$this->settings = $settings;
}
/**
* Create a transformation in the manager.
*
* Creates a transformation and stores it in the manager. A reference to the
* transformation is returned by this method for further manipulation and
* to set options on it. The $name can later be used to remove a
* transfromation using {@link removeTransformation()} or to execute it
* using {@link transform()}. The $filters and $mimeOut parameters specify
* the transformation actions as described with {@link
* ezcImageTransformation::__construct()}. The $saveOptions are used when
* the finally created image is saved and can configure compression and
* quality options.
*
* @param string $name Name for the transformation.
* @param array(ezcImageFilter) $filters Filters.
* @param array(string) $mimeOut Output MIME types.
* @param ezcImageSaveOptions $saveOptions Save options.
*
* @return ezcImageTransformation
*
* @throws ezcImageFiltersException
* If a given filter does not exist.
* @throws ezcImageTransformationAlreadyExists
* If a transformation with the given name does already exist.
*/
public function createTransformation( $name, array $filters, array $mimeOut, ezcImageSaveOptions $saveOptions = null )
{
if ( isset( $this->transformations[$name] ) )
{
throw new ezcImageTransformationAlreadyExistsException( $name );
}
$this->transformations[$name] = new ezcImageTransformation( $this, $name, $filters, $mimeOut, $saveOptions );
return $this->transformations[$name];
}
/**
* Removes a transformation from the manager.
*
* @param string $name Name of the transformation to remove
*
* @return ezcImageTransformation The removed transformation
*
* @throws ezcImageTransformationNotAvailableExeption
* If the requested transformation is unknown.
*/
public function removeTransformation( $name )
{
if ( !isset( $this->transformations[$name] ) )
{
throw new ezcImageTransformationNotAvailableException( $name );
}
unset( $this->transformations[$name] );
}
/**
* Apply transformation on a file.
* This applies the given transformation to the given file.
*
* @param string $name Name of the transformation to perform
* @param string $inFile The file to transform
* @param string $outFile The file to save transformed version to
*
* @throws ezcImageTransformationNotAvailableExeption
* If the requested transformation is unknown.
* @throws ezcImageTransformationException If an error occurs during the
* transformation. The returned exception contains the exception
* the problem resulted from in it's public $parent attribute.
* @throws ezcBaseFileNotFoundException If the file you are trying to
* transform does not exists.
* @throws ezcBaseFilePermissionException If the file you are trying to
* transform is not readable.
*/
public function transform( $name, $inFile, $outFile )
{
if ( !isset( $this->transformations[$name] ) )
{
throw new ezcImageTransformationNotAvailableException( $name );
}
$this->transformations[$name]->transform( $inFile, $outFile );
}
/**
* Returns if a handler is found, supporting the given MIME type for output.
*
* @param string $mime The MIME type to check for.
* @return bool Whether the MIME type is supported.
*/
public function allowsInput( $mime )
{
foreach ( $this->handlers as $handler )
{
if ( $handler->allowsInput( $mime ) )
{
return true;
}
}
return false;
}
/**
* Returns if a handler is found, supporting the given MIME type for output.
*
* @param string $mime The MIME type to check for.
* @return bool Whether the MIME type is supported.
*/
public function allowsOutput( $mime )
{
foreach ( $this->handlers as $handler )
{
if ( $handler->allowsOutput( $mime ) )
{
return true;
}
}
return false;
}
/**
* Returns the MIME type that will be outputted for a given input type.
* Checks whether the given input type can be processed. If not, an
* exception is thrown. Checks then, if an implicit conversion for that
* MIME type is defined. If so, outputs the given output MIME type. In
* every other case, just outputs the MIME type given, because no
* conversion is implicitly required.
*
* @param string $mimeIn Input MIME type.
* @return string Output MIME type.
*
* @throws ezcImageMimeTypeUnsupportedException
* If the input MIME type is not supported.
*/
public function getMimeOut( $mimeIn )
{
if ( $this->allowsInput( $mimeIn ) === false )
{
throw new ezcImageMimeTypeUnsupportedException( $mimeIn, 'input' );
}
if ( isset( $this->settings->conversions[$mimeIn] ) )
{
return $this->settings->conversions[$mimeIn];
}
return $mimeIn;
}
/**
* Returns if a given filter is available.
* Returns either an array of handler names this filter
* is available in or false if the filter is not enabled.
*
* @param string $name Name of the filter to query existance for
*
* @return mixed Array of handlers on success, otherwise false.
*/
public function hasFilter( $name )
{
foreach ( $this->handlers as $handler )
{
if ( $handler->hasFilter( $name ) )
{
return true;
}
}
return false;
}
/**
* Returns a list of enabled filters.
* Gives you an overview on filters enabled in the manager.
* Format is:
* <code>
* array(
* '<filterName>',
* );
* </code>
*
* @return array(string)
*/
public function getFilterNames()
{
$filters = array();
foreach ( $this->handlers as $handler )
{
$filters = array_merge( $filters, $handler->getFilterNames() );
}
return array_unique( $filters );
}
/**
* Apply a single filter to an image.
* Applies just a single filter to an image. Optionally you can select
* a handler yourself, which is not recommended, but possible. If the
* specific handler does not have that filter, ImageConverter will try
* to fall back on another handler.
*
* @param ezcImageFilter $filter Filter object to apply.
* @param string $inFile Name of the input file.
* @param string $outFile Name of the output file.
* @param string $handlerName
* To choose a specific handler, this is the reference named passed
* to {@link ezcImageHandlerSettings}.
* @return void
*
*
* @throws ezcImageHandlerNotAvailableException
* If fitting handler is not available.
* @throws ezcImageFilterNotAvailableException
* If filter is not available.
* @throws ezcImageFileNameInvalidException
* If an invalid character (", ', $) is found in the file name.
*/
public function applyFilter( ezcImageFilter $filter, $inFile, $outFile, $handlerName = null )
{
$handlerObj = false;
// Do we have an explicit handler given?
if ( $handlerName !== null )
{
if ( !isset( $this->handlers[$handlerName] ) )
{
throw new ezcImageHandlerNotAvailableException( $handlerName );
}
if ( $this->handlers[$handlerName]->hasFilter( $filter->name ) === true )
{
$handlerObj = $this->handlers[$handlerName];
}
}
// Either no handler explicitly given or try to fall back.
if ( $handlerObj === false )
{
foreach ( $this->handlers as $regHandler )
{
if ( $regHandler->hasFilter( $filter->name ) )
{
$handlerObj = $regHandler;
break;
}
}
}
// No handler found to apply filter with.
if ( $handlerObj === false )
{
throw new ezcImageFilterNotAvailableException( $filter->name );
}
$imgRef = $handlerObj->load( $inFile );
$handlerObj->applyFilter( $imgRef, $filter );
$handlerObj->save( $imgRef, $outFile );
}
/**
* Returns a handler object for direct use.
* Returns the handler with the highest priority, that supports the given
* filter, MIME input type and MIME output type. All parameters are
* optional, if none is specified, the highest prioritized handler is
* returned.
*
* If no handler is found, that supports the criteria named, an exception
* of type {@link ezcImageHandlerNotAvailableException} will be thrown.
*
* @param string $filterName Name of the filter to search for.
* @param string $mimeIn Input MIME type.
* @param string $mimeOut Output MIME type.
*
* @return ezcImageHandler
*
* @throws ezcImageHandlerNotAvailableException
* If a handler for the given specification could not be found.
*/
public function getHandler( $filterName = null, $mimeIn = null, $mimeOut = null )
{
foreach ( $this->handlers as $handler )
{
if ( ( !isset( $filterName ) || $handler->hasFilter( $filterName ) )
&& ( !isset( $mimeIn ) || $handler->allowsInput( $mimeIn ) )
&& ( !isset( $mimeOut ) || $handler->allowsOutput( $mimeOut ) )
)
{
return $handler;
}
}
throw new ezcImageHandlerNotAvailableException( 'unknown' );
}
}
?>