Commit 8cfc089e authored by alexpott's avatar alexpott

Issue #2033669 by tim.plunkett, larowlan: Image file objects should be classed.

parent b1624c56
......@@ -496,6 +496,9 @@ services:
class: Drupal\system\Plugin\ImageToolkitInterface
factory_method: getDefaultToolkit
factory_service: image.toolkit.manager
image.factory:
class: Drupal\Core\Image\ImageFactory
arguments: ['@image.toolkit']
breadcrumb:
class: Drupal\Core\Breadcrumb\BreadcrumbManager
token:
......
......@@ -3086,7 +3086,6 @@ function _drupal_bootstrap_code() {
require_once __DIR__ . '/tablesort.inc';
require_once __DIR__ . '/file.inc';
require_once __DIR__ . '/unicode.inc';
require_once __DIR__ . '/image.inc';
require_once __DIR__ . '/form.inc';
require_once __DIR__ . '/mail.inc';
require_once __DIR__ . '/ajax.inc';
......
This diff is collapsed.
......@@ -2,10 +2,10 @@
/**
* @file
* Contains \Drupal\Component\Image\Image.
* Contains \Drupal\Component\Utility\Image.
*/
namespace Drupal\Component\Image;
namespace Drupal\Component\Utility;
/**
* Provides helpers to operate on images.
......
<?php
/**
* @file
* Contains \Drupal\Core\Image\Image.
*/
namespace Drupal\Core\Image;
use Drupal\system\Plugin\ImageToolkitInterface;
use Drupal\Component\Utility\Image as ImageUtility;
/**
* Defines an image object to represent an image file.
*
* @see \Drupal\system\Plugin\ImageToolkitInterface
* @see \Drupal\image\ImageEffectInterface
*
* @ingroup image
*/
class Image implements ImageInterface {
/**
* String specifying the path of the image file.
*
* @var string
*/
protected $source;
/**
* An image toolkit object.
*
* @var \Drupal\system\Plugin\ImageToolkitInterface
*/
protected $toolkit;
/**
* An image file handle.
*
* @var resource
*/
protected $resource;
/**
* Height, in pixels.
*
* @var int
*/
protected $height = 0;
/**
* Width, in pixels.
*
* @var int
*/
protected $width = 0;
/**
* Commonly used file extension for the image.
*
* @var string
*/
protected $extension = '';
/**
* MIME type ('image/jpeg', 'image/gif', 'image/png').
*
* @var string
*/
protected $mimeType = '';
/**
* File size in bytes.
*
* @var int
*/
protected $fileSize = 0;
/**
* If this image file has been processed.
*
* @var bool
*/
protected $processed = FALSE;
/**
* Constructs a new Image object.
*
* @param string $source
* The path to an image file.
* @param \Drupal\system\Plugin\ImageToolkitInterface $toolkit
* The image toolkit.
*/
public function __construct($source, ImageToolkitInterface $toolkit) {
$this->source = $source;
$this->toolkit = $toolkit;
}
/**
* {@inheritdoc}
*/
public function getExtension() {
$this->processInfo();
return $this->extension;
}
/**
* {@inheritdoc}
*/
public function getHeight() {
$this->processInfo();
return $this->height;
}
/**
* {@inheritdoc}
*/
public function setHeight($height) {
$this->height = $height;
return $this;
}
/**
* {@inheritdoc}
*/
public function getWidth() {
$this->processInfo();
return $this->width;
}
/**
* {@inheritdoc}
*/
public function setWidth($width) {
$this->width = $width;
return $this;
}
/**
* {@inheritdoc}
*/
public function getFileSize() {
$this->processInfo();
return $this->fileSize;
}
/**
* {@inheritdoc}
*/
public function getMimeType() {
$this->processInfo();
return $this->mimeType;
}
/**
* {@inheritdoc}
*/
public function setResource($resource) {
$this->resource = $resource;
return $this;
}
/**
* {@inheritdoc}
*/
public function hasResource() {
return (bool) $this->resource;
}
/**
* {@inheritdoc}
*/
public function getResource() {
if (!$this->hasResource()) {
$this->processInfo();
$this->toolkit->load($this);
}
return $this->resource;
}
/**
* {@inheritdoc}
*/
public function setSource($source) {
$this->source = $source;
return $this;
}
/**
* {@inheritdoc}
*/
public function getSource() {
return $this->source;
}
/**
* {@inheritdoc}
*/
public function getToolkitId() {
return $this->toolkit->getPluginId();
}
/**
* {@inheritdoc}
*/
public function save($destination = NULL) {
if (empty($destination)) {
$destination = $this->getSource();
}
if ($return = $this->toolkit->save($this, $destination)) {
// Clear the cached file size and refresh the image information.
clearstatcache(TRUE, $destination);
$this->setSource($destination);
$this->processInfo();
// @todo Use File utility when https://drupal.org/node/2050759 is in.
if ($this->chmod($destination)) {
return $return;
}
}
return FALSE;
}
/**
* Prepares the image information.
*
* Drupal supports GIF, JPG and PNG file formats when used with the GD
* toolkit, and may support others, depending on which toolkits are
* installed.
*
* @return bool
* FALSE, if the file could not be found or is not an image. Otherwise, the
* image information is populated.
*/
protected function processInfo() {
if ($this->processed) {
return TRUE;
}
$destination = $this->getSource();
if (!is_file($destination) && !is_uploaded_file($destination)) {
return FALSE;
}
if ($details = $this->toolkit->getInfo($this)) {
$this->height = $details['height'];
$this->width = $details['width'];
$this->extension = $details['extension'];
$this->mimeType = $details['mime_type'];
$this->fileSize = filesize($destination);
$this->processed = TRUE;
}
return TRUE;
}
/**
* {@inheritdoc}
*/
public function scale($width = NULL, $height = NULL, $upscale = FALSE) {
$dimensions = array(
'width' => $this->getWidth(),
'height' => $this->getHeight(),
);
// Scale the dimensions - if they don't change then just return success.
if (!ImageUtility::scaleDimensions($dimensions, $width, $height, $upscale)) {
return TRUE;
}
return $this->resize($dimensions['width'], $dimensions['height']);
}
/**
* {@inheritdoc}
*/
public function scaleAndCrop($width, $height) {
$scale = max($width / $this->getWidth(), $height / $this->getHeight());
$x = ($this->getWidth() * $scale - $width) / 2;
$y = ($this->getHeight() * $scale - $height) / 2;
if ($this->resize($this->getWidth() * $scale, $this->getHeight() * $scale)) {
return $this->crop($x, $y, $width, $height);
}
return FALSE;
}
/**
* {@inheritdoc}
*/
public function crop($x, $y, $width, $height) {
$aspect = $this->getHeight() / $this->getWidth();
if (empty($height)) $height = $width * $aspect;
if (empty($width)) $width = $height / $aspect;
$width = (int) round($width);
$height = (int) round($height);
return $this->toolkit->crop($this, $x, $y, $width, $height);
}
/**
* {@inheritdoc}
*/
public function resize($width, $height) {
$width = (int) round($width);
$height = (int) round($height);
return $this->toolkit->resize($this, $width, $height);
}
/**
* {@inheritdoc}
*/
public function desaturate() {
return $this->toolkit->desaturate($this);
}
/**
* {@inheritdoc}
*/
public function rotate($degrees, $background = NULL) {
return $this->toolkit->rotate($this, $degrees, $background);
}
/**
* Provides a wrapper for drupal_chmod() to allow unit testing.
*
* @param string $uri
* A string containing a URI file, or directory path.
* @param int $mode
* Integer value for the permissions. Consult PHP chmod() documentation for
* more information.
*
* @see drupal_chmod()
*
* @todo Remove when https://drupal.org/node/2050759 is in.
*
* @return bool
* TRUE for success, FALSE in the event of an error.
*/
protected function chmod($uri, $mode = NULL) {
return drupal_chmod($uri, $mode);
}
}
<?php
/**
* @file
* Contains Drupal\Core\Image\ImageFactory.
*/
namespace Drupal\Core\Image;
use Drupal\system\Plugin\ImageToolkitInterface;
/**
* Provides a factory for image objects.
*/
class ImageFactory {
/**
* The image toolkit to use for this factory.
*
* @var \Drupal\system\Plugin\ImageToolkitInterface
*/
protected $toolkit;
/**
* Constructs a new ImageFactory object.
*
* @param \Drupal\system\Plugin\ImageToolkitInterface $toolkit
* The image toolkit to use for this image factory.
*/
public function __construct(ImageToolkitInterface $toolkit) {
$this->toolkit = $toolkit;
}
/**
* Sets a custom image toolkit.
*
* @param \Drupal\system\Plugin\ImageToolkitInterface $toolkit
* The image toolkit to use for this image factory.
*
* @return self
* Returns this image.
*/
public function setToolkit(ImageToolkitInterface $toolkit) {
$this->toolkit = $toolkit;
return $this;
}
/**
* Constructs a new Image object.
*
* @param string $source
* The path to an image file.
*
* @return \Drupal\Core\Image\ImageInterface
* The new Image object.
*/
public function get($source) {
return new Image($source, $this->toolkit);
}
}
<?php
/**
* @file
* Contains Drupal\Core\Image\ImageInterface.
*/
namespace Drupal\Core\Image;
/**
* Provides an interface for image objects.
*/
interface ImageInterface {
/**
* Returns the extension of the image file.
*
* @return string
* The extension of the file, or an empty string if the file is invalid.
*/
public function getExtension();
/**
* Returns the height of the image file.
*
* @return int
* The height of the file, or 0 if the file is invalid.
*/
public function getHeight();
/**
* Sets the height of the image file.
*
* @param int $height
*
* @return self
* Returns this image file.
*/
public function setHeight($height);
/**
* Returns the width of the image file.
*
* @return int
* The width of the file, or 0 if the file is invalid.
*/
public function getWidth();
/**
* Sets the width of the image file.
*
* @param int $width
*
* @return self
* Returns this image file.
*/
public function setWidth($width);
/**
* Returns the size of the image file.
*
* @return int
* The size of the file in bytes, or 0 if the file is invalid.
*/
public function getFileSize();
/**
* Returns the MIME type of the image file.
*
* @return string
* The MIME type of the file, or an empty string if the file is invalid.
*/
public function getMimeType();
/**
* Sets the image file resource.
*
* @param resource $resource
* The image file handle.
*
* @return self
* Returns this image file.
*/
public function setResource($resource);
/**
* Determines if this image file has a resource set.
*
* @return bool
* TRUE if this image file has a resource set, FALSE otherwise.
*/
public function hasResource();
/**
* Retrieves the image file resource.
*
* @return resource
* The image file handle.
*/
public function getResource();
/**
* Sets the source path of the image file.
*
* @param string $source
* A string specifying the path of the image file.
*
* @return self
* Returns this image file.
*/
public function setSource($source);
/**
* Retrieves the source path of the image file.
*
* @return string
* The source path of the image file.
*/
public function getSource();
/**
* Returns the ID of the image toolkit used for this image file.
*
* @return string
* The ID of the image toolkit.
*/
public function getToolkitId();
/**
* Closes the image and saves the changes to a file.
*
* @param string|null $destination
* (optional) Destination path where the image should be saved. If it is empty
* the original image file will be overwritten.
*
* @return bool
* TRUE on success, FALSE on failure.
*
* @see \Drupal\system\Plugin\ImageToolkitInterface::save()
*/
public function save($destination = NULL);
/**
* Scales an image while maintaining aspect ratio.
*
* The resulting image can be smaller for one or both target dimensions.
*
* @param int $width
* (optional) The target width, in pixels. This value is omitted then the
* scaling will based only on the height value.
* @param int $height
* (optional) The target height, in pixels. This value is omitted then the
* scaling will based only on the width value.
* @param bool $upscale
* (optional) Boolean indicating that files smaller than the dimensions will
* be scaled up. This generally results in a low quality image.
*
* @return bool
* TRUE on success, FALSE on failure.
*/
public function scale($width = NULL, $height = NULL, $upscale = FALSE);
/**
* Scales an image to the exact width and height given.
*
* This function achieves the target aspect ratio by cropping the original image
* equally on both sides, or equally on the top and bottom. This function is
* useful to create uniform sized avatars from larger images.
*
* The resulting image always has the exact target dimensions.
*
* @param int $width
* The target width, in pixels.
* @param int $height
* The target height, in pixels.
*
* @return bool
* TRUE on success, FALSE on failure.
*/
public function scaleAndCrop($width, $height);
/**
* Crops an image to a rectangle specified by the given dimensions.
*
* @param int $x
* The top left coordinate, in pixels, of the crop area (x axis value).
* @param int $y
* The top left coordinate, in pixels, of the crop area (y axis value).
* @param int $width
* The target width, in pixels.
* @param int $height
* The target height, in pixels.
*
* @return bool
* TRUE on success, FALSE on failure.
*
* @see \Drupal\system\Plugin\ImageToolkitInterface::crop()
*/
public function crop($x, $y, $width, $height);
/**
* Resizes an image to the given dimensions (ignoring aspect ratio).
*
* @param int $width
* The target width, in pixels.
* @param int $height
* The target height, in pixels.
*
* @return bool
* TRUE on success, FALSE on failure.
*
* @see \Drupal\system\Plugin\ImageToolkitInterface::resize()
*/
public function resize($width, $height);
/**
* Converts an image to grayscale.
*
* @return bool
* TRUE on success, FALSE on failure.
*
* @see \Drupal\system\Plugin\ImageToolkitInterface::desaturate()
*/
public function desaturate();
/**
* Rotates an image by the given number of degrees.
*
* @param int $degrees
* The number of (clockwise) degrees to rotate the image.
* @param string $background
* (optional) An hexadecimal integer specifying the background color to use
* for the uncovered area of the image after the rotation. E.g. 0x000000 for
* black, 0xff00ff for magenta, and 0xffffff for white. For images that
* support transparency, this will default to transparent. Otherwise it will
* be white.
*
* @return bool
* TRUE on success, FALSE on failure.
*
* @see \Drupal\system\Plugin\ImageToolkitInterface::rotate()
*/
public function rotate($degrees, $background = NULL);
}
......@@ -9,7 +9,6 @@
use Drupal\Component\Utility\NestedArray;
use Drupal\Core\Template\Attribute;
use Symfony\Component\HttpFoundation\JsonResponse;
use Drupal\file\FileUsage\DatabaseFileUsageBackend;
use Drupal\file\FileUsage\FileUsageInterface;
use Drupal\Core\Ajax\AjaxResponse;
use Drupal\Core\Ajax\ReplaceCommand;
......@@ -406,7 +405,7 @@ function file_validate_size(File $file, $file_limit = 0, $user_limit = 0) {
}
/**
* Checks that the file is recognized by image_get_info() as an image.
* Checks that the file is recognized by Image::getInfo() as an image.
*
* @param Drupal\file\File $file
* A file entity.
......@@ -419,8 +418,8 @@ function file_validate_size(File $file, $file_limit = 0, $user_limit = 0) {
function file_validate_is_image(File $file) {
$errors = array();
$info = image_get_info($file->getFileUri());
if (!$info || empty($info['extension'])) {
$image = Drupal::service('image.factory')->get($file->getFileUri());
if (!$image->getExtension()) {
$errors[] = t('Only JPEG, PNG and GIF images are allowed.');
}
......@@ -454,16 +453,19 @@ function file_validate_image_resolution(File $file, $maximum_dimensions = 0, $mi
$errors = array();
// Check first that the file is an image.
if ($info = image_get_info($file->getFileUri())) {
$image_factory = Drupal::service('image.factory');
$image = $image_factory->get($file->getFileUri());
if ($image->getExtension()) {
if ($maximum_dimensions) {
// Check that it is smaller than the given dimensions.
list($width, $height) = explode('x', $maximum_dimensions);
if ($info['width'] > $width || $info['height'] > $height) {
if ($image->getWidth() > $width || $image->getHeight() > $height) {
// Try to resize the image to fit the dimensions.
if ($image = image_load($file->