Imagemagick.php

Created Diff never expires
37 removals
Lines
Total
Removed
Words
Total
Removed
To continue using this feature, upgrade to
Diffchecker logo
Diffchecker Pro
397 lines
92 additions
Lines
Total
Added
Words
Total
Added
To continue using this feature, upgrade to
Diffchecker logo
Diffchecker Pro
451 lines
<?php
<?php
/**
/**
* @brief Image Class - ImageMagick
* @brief Image Class - ImageMagick
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @copyright (c) Invision Power Services, Inc.
* @license https://www.invisioncommunity.com/legal/standards/
* @license https://www.invisioncommunity.com/legal/standards/
* @package Invision Community
* @package Invision Community
* @since 06 Mar 2014
* @since 06 Mar 2014
*/
*/


namespace IPS\Image;
namespace IPS\Image;


/* To prevent PHP errors (extending class does not exist) revealing path */
/* To prevent PHP errors (extending class does not exist) revealing path */


use Imagick;
use Imagick;
use ImagickDraw;
use ImagickDraw;
use ImagickException;
use ImagickException;
use ImagickPixel;
use ImagickPixel;
use InvalidArgumentException;
use InvalidArgumentException;
use IPS\Image;
use IPS\Image;
use IPS\Settings;
use IPS\Settings;
use function count;
use function count;
use function defined;
use function defined;
use function file_put_contents;
use function file_put_contents;
use function in_array;
use function in_array;
use const IPS\TEMP_DIRECTORY;
use const IPS\TEMP_DIRECTORY;


if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
{
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
exit;
}
}


/**
/**
* Image Class - ImageMagick
* Image Class - ImageMagick
*/
*/
class Imagemagick extends Image
class Imagemagick extends Image
{
{
/**
/**
* @brief Temporary filename
* @brief Temporary filename
*/
*/
protected string|null|false $tempFile = NULL;
protected string|null|false $tempFile = NULL;
/**
/**
* @brief Imagick object
* @brief Imagick object
*/
*/
protected Imagick $imagick;
protected Imagick $imagick;
/**
/**
* Constructor
* Constructor
*
*
* @param string|null $contents Contents
* @param string|null $contents Contents
* @param bool $noImage We are creating a new instance of the object internally and are not passing an image string
* @param bool $noImage We are creating a new instance of the object internally and are not passing an image string
* @return void
* @return void
* @throws InvalidArgumentException
* @throws InvalidArgumentException
*/
*/
public function __construct( ?string $contents, bool $noImage=FALSE )
public function __construct( ?string $contents, bool $noImage=FALSE )
{
{
/* If we are just creating an instance of the object without passing image contents as a string, return now */
/* If we are just creating an instance of the object without passing image contents as a string, return now */
if( $noImage === TRUE )
if( $noImage === TRUE )
{
{
return;
return;
}
}


$this->tempFile = tempnam( TEMP_DIRECTORY, 'imagick' );
$this->tempFile = tempnam( TEMP_DIRECTORY, 'imagick' );
if ( $this->tempFile === FALSE )
{
throw new \RuntimeException('Unable to create temporary file for ImageMagick');
}
file_put_contents( $this->tempFile, $contents );
file_put_contents( $this->tempFile, $contents );
try
try
{
{
$this->imagick = new Imagick( $this->tempFile );
$this->imagick = new Imagick( $this->tempFile );


/* Set quality (if image format is JPEG) */
/* Set quality (if image format is JPEG, WEBP, or AVIF) */
if ( in_array( mb_strtolower( $this->imagick->getImageFormat() ), array( 'jpg', 'jpeg', 'webp', 'avif' ) ) )
if ( in_array( mb_strtolower( $this->imagick->getImageFormat() ), array( 'jpg', 'jpeg', 'webp', 'avif' ) ) )
{
{
$this->imagick->setImageCompressionQuality( (int) Settings::i()->image_jpg_quality ?: 85 );
$this->imagick->setImageCompressionQuality( (int) Settings::i()->image_jpg_quality ?: 85 );
}
}
}
}
catch ( ImagickException $e )
catch ( ImagickException $e )
{
{
if ( $this->tempFile !== NULL && @is_file( $this->tempFile ) )
{
@unlink( $this->tempFile );
}
throw new InvalidArgumentException( $e->getMessage(), $e->getCode() );
throw new InvalidArgumentException( $e->getMessage(), $e->getCode() );
}
}


/* Set width/height */
/* Set width/height */
$this->setDimensions();
$this->setDimensions();
}
}
/**
/**
* Destructor
* Destructor
*
*
* @return void
* @return void
*/
*/
public function __destruct()
public function __destruct()
{
{
if( $this->tempFile !== NULL )
if( $this->tempFile !== NULL && @is_file( $this->tempFile ) )
{
{
unlink( $this->tempFile );
@unlink( $this->tempFile );
}
}
}
}
/**
/**
* Get Contents
* Get Contents
*
*
* @return string
* @return string
*/
*/
public function __toString()
public function __toString()
{
{
/* If possible, retain the color profiles when stripping EXIF data */
/* If possible, retain the color profiles when stripping EXIF data */
if( Settings::i()->imagick_strip_exif )
if( Settings::i()->imagick_strip_exif )
{
{
$imageColorProfiles = array();
$imageColorProfiles = array();


try
try
{
{
$imageColorProfiles = $this->imagick->getImageProfiles( 'icc' );
$imageColorProfiles = $this->imagick->getImageProfiles( 'icc' );
}
}
catch( ImagickException $e ){}
catch( ImagickException $e ){}


$this->imagick->stripImage();
$this->imagick->stripImage();


if( count( $imageColorProfiles ) )
if( count( $imageColorProfiles ) )
{
{
foreach( $imageColorProfiles as $type => $profile )
foreach( $imageColorProfiles as $type => $profile )
{
{
try
try
{
{
$this->imagick->profileImage( $type, $profile );
$this->imagick->profileImage( $type, $profile );
}
}
catch( ImagickException $e ){}
catch( ImagickException $e ){}
}
}
}
}
}
}


return $this->imagick->getImagesBlob();
return $this->imagick->getImagesBlob();
}
}
/**
/**
* Automatically orient the image based on EXIF data
*
* @return void
*/
public function autoOrient(): void
{
try
{
$this->imagick->autoOrient();
$this->setDimensions();
error_log("Après autoOrient - Largeur: {$this->width}, Hauteur: {$this->height}");
}
catch ( ImagickException $e )
{
error_log("Erreur autoOrient: " . $e->getMessage());
}
}

/**
* Resize
* Resize
*
*
* @param int $width Width (in pixels)
* @param int $width Width (in pixels)
* @param int $height Height (in pixels)
* @param int $height Height (in pixels)
* @return void
* @return void
*/
*/
public function resize( int $width, int $height ) : void
public function resize( int $width, int $height ) : void
{
{
$format = $this->imagick->getImageFormat();
$format = mb_strtolower( $this->imagick->getImageFormat() );


if( mb_strtolower( $format ) == 'gif' )
if( $format == 'gif' )
{
{
$this->imagick = $this->imagick->coalesceImages();
$this->imagick = $this->imagick->coalesceImages();


foreach( $this->imagick as $frame )
foreach( $this->imagick as $frame )
{
{
$frame->thumbnailImage( $width, $height );
$frame->thumbnailImage( $width, $height );
}
}


/* Needs ImageMagick 6.2.9 or higher for optimizeImageLayers */
/* Needs ImageMagick 6.2.9 or higher for optimizeImageLayers */
try
try
{
{
$this->imagick->optimizeImageLayers();
$this->imagick->optimizeImageLayers();
}
}
catch( ImagickException $e )
catch( ImagickException $e )
{
{
$this->imagick->deconstructImages();
$this->imagick->deconstructImages();
}
}
}
}
else
else
{
{
$this->imagick->thumbnailImage( $width, $height );
$this->imagick->thumbnailImage( $width, $height );
}
}


/* Set width/height */
/* Set width/height */
$this->setDimensions();
$this->setDimensions();
}
}


/**
/**
* Crop to a given width and height (will attempt to downsize first)
* Crop to a given width and height
*
*
* @param int $width Width (in pixels)
* @param int $width Width (in pixels)
* @param int $height Height (in pixels)
* @param int $height Height (in pixels)
* @return void
* @return void
*/
*/
public function crop( int $width, int $height ) : void
public function crop( int $width, int $height ) : void
{
{
$this->imagick->cropThumbnailImage( $width, $height );
$this->imagick->cropThumbnailImage( $width, $height );


/* Set width/height */
/* Set width/height */
$this->setDimensions();
$this->setDimensions();
}
}
/**
/**
* Crop at specific points
* Crop at specific points
*
*
* @param int $point1X x-point for top-left corner
* @param int $point1X x-point for top-left corner
* @param int $point1Y y-point for top-left corner
* @param int $point1Y y-point for top-left corner
* @param int $point2X x-point for bottom-right corner
* @param int $point2X x-point for bottom-right corner
* @param int $point2Y y-point for bottom-right corner
* @param int $point2Y y-point for bottom-right corner
* @return void
* @return void
*/
*/
public function cropToPoints( int $point1X, int $point1Y, int $point2X, int $point2Y ) : void
public function cropToPoints( int $point1X, int $point1Y, int $point2X, int $point2Y ) : void
{
{
if( mb_strtolower( $this->imagick->getImageFormat() ) === 'gif' )
$format = mb_strtolower( $this->imagick->getImageFormat() );

if( $format === 'gif' )
{
{
$this->imagick = $this->imagick->coalesceImages();
$this->imagick = $this->imagick->coalesceImages();
foreach( $this->imagick as $frame )
foreach( $this->imagick as $frame )
{
{
$frame->cropImage( $point2X - $point1X, $point2Y - $point1Y, $point1X, $point1Y );
$frame->cropImage( $point2X - $point1X, $point2Y - $point1Y, $point1X, $point1Y );
$frame->setImagePage($point2X - $point1X, $point2Y - $point1Y, 0, 0);
$frame->setImagePage( $point2X - $point1X, $point2Y - $point1Y, 0, 0 );
}
}
/* Needs ImageMagick 6.2.9 or higher for optimizeImageLayers */
/* Needs ImageMagick 6.2.9 or higher for optimizeImageLayers */
try
try
{
{
$this->imagick->optimizeImageLayers();
$this->imagick->optimizeImageLayers();
}
}
catch( ImagickException $e )
catch( ImagickException $e )
{
{
$this->imagick->deconstructImages();
$this->imagick->deconstructImages();
}
}
}
}
else
else
{
{
$this->imagick->cropImage( $point2X - $point1X, $point2Y - $point1Y, $point1X, $point1Y );
$this->imagick->cropImage( $point2X - $point1X, $point2Y - $point1Y, $point1X, $point1Y );
}
}


/* Set width/height */
/* Set width/height */
$this->setDimensions();
$this->setDimensions();
}
}
/**
/**
* Impose image
* Impose image
*
*
* @param Image $image Image to impose
* @param Image $image Image to impose
* @param int $x Location to impose to, x axis
* @param int $x Location to impose to, x axis
* @param int $y Location to impose to, y axis
* @param int $y Location to impose to, y axis
* @return void
* @return void
*/
*/
public function impose( Image $image, int $x=0, int $y=0 ) : void
public function impose( Image $image, int $x=0, int $y=0 ) : void
{
{
$this->imagick->compositeImage( $image->imagick, Imagick::COMPOSITE_DEFAULT, $x, $y );
if ( $image instanceof Imagemagick )
{
$this->imagick->compositeImage( $image->imagick, Imagick::COMPOSITE_DEFAULT, $x, $y );
}
}
}


/**
/**
* Rotate image
* Rotate image
*
*
* @param int $angle Angle of rotation
* @param int $angle Angle of rotation
* @return void
* @return void
*/
*/
public function rotate( int $angle ) : void
public function rotate( int $angle ) : void
{
{
$this->imagick->rotateImage( new ImagickPixel('#00000000'), $angle );
$this->imagick->rotateImage( new ImagickPixel('#00000000'), $angle );


/* Set width/height */
/* Set width/height */
$this->setDimensions();
$this->setDimensions();
}
}


/**
/**
* Flip image horizontally
*
* @return void
* @return void
*/
*/
public function flip(): void
public function flip(): void
{
{
$this->imagick->flopImage();
$this->imagick->flopImage();


/* Set width/height */
/* Set width/height */
$this->setDimensions();
$this->setDimensions();
}
}


/**
/**
* Set the image width and height
* Set the image width and height
*
*
* @return void
* @return void
*/
*/
protected function setDimensions() : void
protected function setDimensions() : void
{
{
/* If this is a gif, we need to coalesce the image in order to get the proper dimensions */
/* If this is a gif, we need to coalesce the image in order to get the proper dimensions */
if ( mb_strtolower( $this->imagick->getImageFormat() ) === 'gif' )
if ( mb_strtolower( $this->imagick->getImageFormat() ) === 'gif' )
{
{
$this->imagick = $this->imagick->coalesceImages();
$this->imagick = $this->imagick->coalesceImages();
}
}
/* Set width/height */
/* Set width/height */
$this->width = $this->imagick->getImageWidth();
$this->width = $this->imagick->getImageWidth();
$this->height = $this->imagick->getImageHeight();
$this->height = $this->imagick->getImageHeight();
}
}
/**
/**
* Get Image Orientation
* Get Image Orientation
*
*
* @return int|NULL
* @return int|null
*/
*/
public function getImageOrientation(): int|NULL
public function getImageOrientation(): ?int
{
{
try
try
{
{
if ( $orientation = parent::getImageOrientation() )
if ( $orientation = parent::getImageOrientation() )
{
{
return $orientation;
return $orientation;
}
}
/* This method does not exist in ImageMagick < 6.6.4 */
/* This method does not exist in ImageMagick < 6.6.4 */
return ( method_exists( $this->imagick, 'getImageOrientation' ) ) ? $this->imagick->getImageOrientation() : NULL;
return ( method_exists( $this->imagick, 'getImageOrientation' ) ) ? $this->imagick->getImageOrientation() : null;
}
}
catch( ImagickException $e )
catch( ImagickException $e )
{
{
return NULL;
return null;
}
}
}
}


/**
/**
* Set image orientation
* Set image orientation
*
*
* @param int $orientation The orientation
* @param int $orientation The orientation
* @return void
* @return void
*/
*/
public function setImageOrientation( int $orientation ) : void
public function setImageOrientation( int $orientation ) : void
{
{
if( method_exists( $this->imagick, 'getImageOrientation' ) )
if( method_exists( $this->imagick, 'setImageOrientation' ) )
{
{
$this->imagick->setImageOrientation($orientation);
$this->imagick->setImageOrientation( $orientation );
}
}
}
}


/**
/**
* Get Image Width
*
* @return int|null
*/
public function getWidth(): ?int
{
return $this->imagick->getImageWidth();
}

/**
* Get Image Height
*
* @return int|null
*/
public function getHeight(): ?int
{
return $this->imagick->getImageHeight();
}

/**
* Can we write text reliably on an image?
* Can we write text reliably on an image?
*
*
* @return bool
* @return bool
*/
*/
public static function canWriteText(): bool
public static function canWriteText(): bool
{
{
return TRUE;
return true;
}
}


/**
/**
* Create a new blank canvas image
* Create a new blank canvas image
*
*
* @param int $width Width
* @param int $width Width
* @param int $height Height
* @param int $height Height
* @param array $rgb Color to use for bg
* @param array $rgb Color to use for bg
* @return Image
* @return Image
*/
*/
public static function newImageCanvas( int $width, int $height, array $rgb ): Image
public static function newImageCanvas( int $width, int $height, array $rgb ): Image
{
{
$obj = new static(NULL, TRUE);
$obj = new static( null, true );
$obj->imagick = new Imagick();
$obj->imagick = new Imagick();
$obj->width = $width;
$obj->width = $width;
$obj->height = $height;
$obj->height = $height;
$obj->type = 'png';
$obj->type = 'png';
$pixel = new ImagickPixel( "rgba({$rgb[0]}, {$rgb[1]}, {$rgb[2]}, 1)" );
$pixel = new ImagickPixel( "rgba({$rgb[0]}, {$rgb[1]}, {$rgb[2]}, 1)" );


$obj->imagick->newImage( $width, $height, $pixel );
$obj->imagick->newImage( $width, $height, $pixel );
$obj->imagick->setImageFormat( "png" );
$obj->imagick->setImageFormat( 'png' );


return $obj;
return $obj;
}
}


/**
/**
* Write text on our image
* Write text on our image
*
*
* @param string $text Text
* @param string $text Text
* @param string $font Path to font to use
* @param string $font Path to font to use
* @param int $size Size of text
* @param int $size Size of text
* @return void
* @return void
*/
*/
public function write( string $text, string $font, int $size ) : void
public function write( string $text, string $font, int $size ) : void
{
{
$draw = new ImagickDraw();
$draw = new ImagickDraw();
$draw->setTextAntialias( true );
$draw->setTextAntialias( true );
$draw->setGravity( Imagick::GRAVITY_CENTER );
$draw->setGravity( Imagick::GRAVITY_CENTER );
$draw->setFont( $font );
$draw->setFont( $font );
$draw->setFontSize( $size );
$draw->setFontSize( $size );


$draw->setFillColor( new ImagickPixel( "rgba(255,255,255,1)" ) );
$draw->setFillColor( new ImagickPixel( 'rgba(255,255,255,1)' ) );


$this->imagick->annotateImage( $draw, 0, 0, 0, $text );
$this->imagick->annotateImage( $draw, 0, 0, 0, $text );
}
}
/**
/**
* Return an array of supported extensions
* Return an array of supported extensions
*
*
* @return array
* @return array
*/
*/
public static function supportedExtensions(): array
public static function supportedExtensions(): array
{
{
$extensions = static::$imageExtensions;
$extensions = static::$imageExtensions;
if( in_array( 'WEBP', Imagick::queryFormats() ) )
if( in_array( 'WEBP', Imagick::queryFormats() ) )
{
{
$extensions[] = 'webp';
$extensions[] = 'webp';
}
}


if( in_array( 'AVIF', Imagick::queryFormats() ) )
if( in_array( 'AVIF', Imagick::queryFormats() ) )
{
{
$extensions[] = 'avif';
$extensions[] = 'avif';
}
}


return $extensions;
return $extensions;
}
}
}
}