Commit 156bdc43 authored by alexpott's avatar alexpott

Issue #2066219 by claudiu.cristea, fietserwin: Decouple image type from image extension.

parent af6cd3da
...@@ -63,7 +63,14 @@ class Image implements ImageInterface { ...@@ -63,7 +63,14 @@ class Image implements ImageInterface {
protected $extension = ''; protected $extension = '';
/** /**
* MIME type ('image/jpeg', 'image/gif', 'image/png'). * Image type represented by a PHP IMAGETYPE_* constant (e.g. IMAGETYPE_JPEG).
*
* @var int
*/
protected $type;
/**
* MIME type (e.g. 'image/jpeg', 'image/gif', 'image/png').
* *
* @var string * @var string
*/ */
...@@ -96,6 +103,13 @@ public function __construct($source, ImageToolkitInterface $toolkit) { ...@@ -96,6 +103,13 @@ public function __construct($source, ImageToolkitInterface $toolkit) {
$this->toolkit = $toolkit; $this->toolkit = $toolkit;
} }
/**
* {@inheritdoc}
*/
public function isSupported() {
return in_array($this->getType(), $this->toolkit->supportedTypes());
}
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
...@@ -144,6 +158,14 @@ public function getFileSize() { ...@@ -144,6 +158,14 @@ public function getFileSize() {
return $this->fileSize; return $this->fileSize;
} }
/**
* {@inheritdoc}
*/
public function getType() {
$this->processInfo();
return $this->type;
}
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
...@@ -245,9 +267,17 @@ protected function processInfo() { ...@@ -245,9 +267,17 @@ protected function processInfo() {
if ($details = $this->toolkit->getInfo($this)) { if ($details = $this->toolkit->getInfo($this)) {
$this->height = $details['height']; $this->height = $details['height'];
$this->width = $details['width']; $this->width = $details['width'];
$this->extension = $details['extension']; $this->type = $details['type'];
$this->mimeType = $details['mime_type']; $this->mimeType = $details['mime_type'];
$this->fileSize = filesize($destination); $this->fileSize = filesize($destination);
$this->extension = pathinfo($destination, PATHINFO_EXTENSION);
// It may be a temporary file, without extension, or an image created from
// an image resource. Fallback to default extension for this image type.
if (empty($this->extension)) {
$this->extension = image_type_to_extension($this->type, FALSE);
}
$this->processed = TRUE; $this->processed = TRUE;
} }
return TRUE; return TRUE;
......
...@@ -12,6 +12,14 @@ ...@@ -12,6 +12,14 @@
*/ */
interface ImageInterface { interface ImageInterface {
/**
* Checks if the image format is supported.
*
* @return bool
* Returns TRUE if the image format is supported by the toolkit.
*/
public function isSupported();
/** /**
* Returns the extension of the image file. * Returns the extension of the image file.
* *
...@@ -64,6 +72,15 @@ public function setWidth($width); ...@@ -64,6 +72,15 @@ public function setWidth($width);
*/ */
public function getFileSize(); public function getFileSize();
/**
* Returns the type of the image.
*
* @return int
* The image type represented by a PHP IMAGETYPE_* constant (e.g.
* IMAGETYPE_JPEG).
*/
public function getType();
/** /**
* Returns the MIME type of the image file. * Returns the MIME type of the image file.
* *
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
use Drupal\file\Entity\File; use Drupal\file\Entity\File;
use Drupal\Component\Utility\NestedArray; use Drupal\Component\Utility\NestedArray;
use Drupal\Component\Utility\Unicode;
use Drupal\Core\Entity\EntityStorageControllerInterface; use Drupal\Core\Entity\EntityStorageControllerInterface;
use Drupal\Core\Template\Attribute; use Drupal\Core\Template\Attribute;
use Drupal\file\FileUsage\FileUsageInterface; use Drupal\file\FileUsage\FileUsageInterface;
...@@ -411,8 +412,13 @@ function file_validate_is_image(File $file) { ...@@ -411,8 +412,13 @@ function file_validate_is_image(File $file) {
$errors = array(); $errors = array();
$image = \Drupal::service('image.factory')->get($file->getFileUri()); $image = \Drupal::service('image.factory')->get($file->getFileUri());
if (!$image->getExtension()) { if (!$image->isSupported()) {
$errors[] = t('Only JPEG, PNG and GIF images are allowed.'); $toolkit = \Drupal::service('image.toolkit');
$extensions = array();
foreach ($toolkit->supportedTypes() as $image_type) {
$extensions[] = Unicode::strtoupper(image_type_to_extension($image_type));
}
$errors[] = t('Image type not supported. Allowed types: @types.', array('@types' => implode(', ', $extensions)));
} }
return $errors; return $errors;
...@@ -447,7 +453,7 @@ function file_validate_image_resolution(File $file, $maximum_dimensions = 0, $mi ...@@ -447,7 +453,7 @@ function file_validate_image_resolution(File $file, $maximum_dimensions = 0, $mi
// Check first that the file is an image. // Check first that the file is an image.
$image_factory = \Drupal::service('image.factory'); $image_factory = \Drupal::service('image.factory');
$image = $image_factory->get($file->getFileUri()); $image = $image_factory->get($file->getFileUri());
if ($image->getExtension()) { if ($image->isSupported()) {
if ($maximum_dimensions) { if ($maximum_dimensions) {
// Check that it is smaller than the given dimensions. // Check that it is smaller than the given dimensions.
list($width, $height) = explode('x', $maximum_dimensions); list($width, $height) = explode('x', $maximum_dimensions);
......
...@@ -41,7 +41,7 @@ function image_field_widget_process($element, &$form_state, $form) { ...@@ -41,7 +41,7 @@ function image_field_widget_process($element, &$form_state, $form) {
} }
else { else {
$image = \Drupal::service('image.factory')->get($file->getFileUri()); $image = \Drupal::service('image.factory')->get($file->getFileUri());
if ($image->getExtension()) { if ($image->isSupported()) {
$variables['width'] = $image->getWidth(); $variables['width'] = $image->getWidth();
$variables['height'] = $image->getHeight(); $variables['height'] = $image->getHeight();
} }
......
...@@ -233,7 +233,7 @@ function image_file_download($uri) { ...@@ -233,7 +233,7 @@ function image_file_download($uri) {
// Check that the file exists and is an image. // Check that the file exists and is an image.
$image = \Drupal::service('image.factory')->get($uri); $image = \Drupal::service('image.factory')->get($uri);
if ($image->getExtension()) { if ($image->isSupported()) {
// Check the permissions of the original to grant access to this image. // Check the permissions of the original to grant access to this image.
$headers = \Drupal::moduleHandler()->invokeAll('file_download', array($original_uri)); $headers = \Drupal::moduleHandler()->invokeAll('file_download', array($original_uri));
// Confirm there's at least one module granting access and none denying access. // Confirm there's at least one module granting access and none denying access.
......
...@@ -293,7 +293,7 @@ public function preSave() { ...@@ -293,7 +293,7 @@ public function preSave() {
// Determine the dimensions if necessary. // Determine the dimensions if necessary.
if (empty($width) || empty($height)) { if (empty($width) || empty($height)) {
$image = \Drupal::service('image.factory')->get($this->entity->getFileUri()); $image = \Drupal::service('image.factory')->get($this->entity->getFileUri());
if ($image->getExtension()) { if ($image->isSupported()) {
$this->width = $image->getWidth(); $this->width = $image->getWidth();
$this->height =$image->getHeight(); $this->height =$image->getHeight();
} }
......
...@@ -98,7 +98,7 @@ public function rotate(ImageInterface $image, $degrees, $background = NULL) { ...@@ -98,7 +98,7 @@ public function rotate(ImageInterface $image, $degrees, $background = NULL) {
// Images are assigned a new color palette when rotating, removing any // Images are assigned a new color palette when rotating, removing any
// transparency flags. For GIF images, keep a record of the transparent color. // transparency flags. For GIF images, keep a record of the transparent color.
if ($image->getExtension() == 'gif') { if ($image->getType() == IMAGETYPE_GIF) {
$transparent_index = imagecolortransparent($image->getResource()); $transparent_index = imagecolortransparent($image->getResource());
if ($transparent_index != 0) { if ($transparent_index != 0) {
$transparent_gif_color = imagecolorsforindex($image->getResource(), $transparent_index); $transparent_gif_color = imagecolorsforindex($image->getResource(), $transparent_index);
...@@ -155,8 +155,7 @@ public function desaturate(ImageInterface $image) { ...@@ -155,8 +155,7 @@ public function desaturate(ImageInterface $image) {
* {@inheritdoc} * {@inheritdoc}
*/ */
public function load(ImageInterface $image) { public function load(ImageInterface $image) {
$extension = str_replace('jpg', 'jpeg', $image->getExtension()); $function = 'imagecreatefrom' . image_type_to_extension($image->getType(), FALSE);
$function = 'imagecreatefrom' . $extension;
if (function_exists($function) && $resource = $function($image->getSource())) { if (function_exists($function) && $resource = $function($image->getSource())) {
$image->setResource($resource); $image->setResource($resource);
if (!imageistruecolor($resource)) { if (!imageistruecolor($resource)) {
...@@ -190,17 +189,16 @@ public function save(ImageInterface $image, $destination) { ...@@ -190,17 +189,16 @@ public function save(ImageInterface $image, $destination) {
$destination = drupal_realpath($destination); $destination = drupal_realpath($destination);
} }
$extension = str_replace('jpg', 'jpeg', $image->getExtension()); $function = 'image' . image_type_to_extension($image->getType(), FALSE);
$function = 'image' . $extension;
if (!function_exists($function)) { if (!function_exists($function)) {
return FALSE; return FALSE;
} }
if ($extension == 'jpeg') { if ($image->getType() == IMAGETYPE_JPEG) {
$success = $function($image->getResource(), $destination, \Drupal::config('system.image.gd')->get('jpeg_quality')); $success = $function($image->getResource(), $destination, \Drupal::config('system.image.gd')->get('jpeg_quality'));
} }
else { else {
// Always save PNG images with full transparency. // Always save PNG images with full transparency.
if ($extension == 'png') { if ($image->getType() == IMAGETYPE_PNG) {
imagealphablending($image->getResource(), FALSE); imagealphablending($image->getResource(), FALSE);
imagesavealpha($image->getResource(), TRUE); imagesavealpha($image->getResource(), TRUE);
} }
...@@ -221,12 +219,10 @@ public function getInfo(ImageInterface $image) { ...@@ -221,12 +219,10 @@ public function getInfo(ImageInterface $image) {
$data = getimagesize($image->getSource()); $data = getimagesize($image->getSource());
if (isset($data) && is_array($data)) { if (isset($data) && is_array($data)) {
$extensions = array('1' => 'gif', '2' => 'jpg', '3' => 'png');
$extension = isset($extensions[$data[2]]) ? $extensions[$data[2]] : '';
$details = array( $details = array(
'width' => $data[0], 'width' => $data[0],
'height' => $data[1], 'height' => $data[1],
'extension' => $extension, 'type' => $data[2],
'mime_type' => $data['mime'], 'mime_type' => $data['mime'],
); );
} }
...@@ -250,7 +246,7 @@ public function getInfo(ImageInterface $image) { ...@@ -250,7 +246,7 @@ public function getInfo(ImageInterface $image) {
public function createTmp(ImageInterface $image, $width, $height) { public function createTmp(ImageInterface $image, $width, $height) {
$res = imagecreatetruecolor($width, $height); $res = imagecreatetruecolor($width, $height);
if ($image->getExtension() == 'gif') { if ($image->getType() == IMAGETYPE_GIF) {
// Grab transparent color index from image resource. // Grab transparent color index from image resource.
$transparent = imagecolortransparent($image->getResource()); $transparent = imagecolortransparent($image->getResource());
...@@ -264,7 +260,7 @@ public function createTmp(ImageInterface $image, $width, $height) { ...@@ -264,7 +260,7 @@ public function createTmp(ImageInterface $image, $width, $height) {
imagecolortransparent($res, $transparent); imagecolortransparent($res, $transparent);
} }
} }
elseif ($image->getExtension() == 'png') { elseif ($image->getType() == IMAGETYPE_PNG) {
imagealphablending($res, FALSE); imagealphablending($res, FALSE);
$transparency = imagecolorallocatealpha($res, 0, 0, 0, 127); $transparency = imagecolorallocatealpha($res, 0, 0, 0, 127);
imagefill($res, 0, 0, $transparency); imagefill($res, 0, 0, $transparency);
...@@ -290,4 +286,11 @@ public static function isAvailable() { ...@@ -290,4 +286,11 @@ public static function isAvailable() {
} }
return FALSE; return FALSE;
} }
/**
* {@inheritdoc}
*/
public static function supportedTypes() {
return array(IMAGETYPE_PNG, IMAGETYPE_JPEG, IMAGETYPE_GIF);
}
} }
...@@ -50,14 +50,14 @@ interface ImageToolkitInterface extends PluginInspectionInterface { ...@@ -50,14 +50,14 @@ interface ImageToolkitInterface extends PluginInspectionInterface {
* *
* @see system_image_toolkit_settings() * @see system_image_toolkit_settings()
*/ */
function settingsForm(); public function settingsForm();
/** /**
* Handles submissions for toolkit's settings form. * Handles submissions for toolkit's settings form.
* *
* @see system_image_toolkit_settings_submit() * @see system_image_toolkit_settings_submit()
*/ */
function settingsFormSubmit($form, &$form_state); public function settingsFormSubmit($form, &$form_state);
/** /**
* Scales an image to the specified size. * Scales an image to the specified size.
...@@ -73,7 +73,7 @@ function settingsFormSubmit($form, &$form_state); ...@@ -73,7 +73,7 @@ function settingsFormSubmit($form, &$form_state);
* @return bool * @return bool
* TRUE or FALSE, based on success. * TRUE or FALSE, based on success.
*/ */
function resize(ImageInterface $image, $width, $height); public function resize(ImageInterface $image, $width, $height);
/** /**
* Rotates an image the given number of degrees. * Rotates an image the given number of degrees.
...@@ -93,7 +93,7 @@ function resize(ImageInterface $image, $width, $height); ...@@ -93,7 +93,7 @@ function resize(ImageInterface $image, $width, $height);
* @return bool * @return bool
* TRUE or FALSE, based on success. * TRUE or FALSE, based on success.
*/ */
function rotate(ImageInterface $image, $degrees, $background = NULL); public function rotate(ImageInterface $image, $degrees, $background = NULL);
/** /**
* Crops an image. * Crops an image.
...@@ -115,7 +115,7 @@ function rotate(ImageInterface $image, $degrees, $background = NULL); ...@@ -115,7 +115,7 @@ function rotate(ImageInterface $image, $degrees, $background = NULL);
* *
* @see image_crop() * @see image_crop()
*/ */
function crop(ImageInterface $image, $x, $y, $width, $height); public function crop(ImageInterface $image, $x, $y, $width, $height);
/** /**
* Converts an image resource to grayscale. * Converts an image resource to grayscale.
...@@ -129,7 +129,7 @@ function crop(ImageInterface $image, $x, $y, $width, $height); ...@@ -129,7 +129,7 @@ function crop(ImageInterface $image, $x, $y, $width, $height);
* @return bool * @return bool
* TRUE or FALSE, based on success. * TRUE or FALSE, based on success.
*/ */
function desaturate(ImageInterface $image); public function desaturate(ImageInterface $image);
/** /**
* Creates an image resource from a file. * Creates an image resource from a file.
...@@ -140,7 +140,7 @@ function desaturate(ImageInterface $image); ...@@ -140,7 +140,7 @@ function desaturate(ImageInterface $image);
* @return bool * @return bool
* TRUE or FALSE, based on success. * TRUE or FALSE, based on success.
*/ */
function load(ImageInterface $image); public function load(ImageInterface $image);
/** /**
* Writes an image resource to a destination file. * Writes an image resource to a destination file.
...@@ -153,7 +153,7 @@ function load(ImageInterface $image); ...@@ -153,7 +153,7 @@ function load(ImageInterface $image);
* @return bool * @return bool
* TRUE or FALSE, based on success. * TRUE or FALSE, based on success.
*/ */
function save(ImageInterface $image, $destination); public function save(ImageInterface $image, $destination);
/** /**
* Gets details about an image. * Gets details about an image.
...@@ -166,12 +166,12 @@ function save(ImageInterface $image, $destination); ...@@ -166,12 +166,12 @@ function save(ImageInterface $image, $destination);
* keyed array containing information about the image: * keyed array containing information about the image:
* - "width": Width, in pixels. * - "width": Width, in pixels.
* - "height": Height, in pixels. * - "height": Height, in pixels.
* - "extension": Commonly used file extension for the image. * - "type": Image type represented as an IMAGETYPE_* constant.
* - "mime_type": MIME type ('image/jpeg', 'image/gif', 'image/png'). * - "mime_type": MIME type (e.g. 'image/jpeg', 'image/gif', 'image/png').
* *
* @see \Drupal\Core\Image\ImageInterface::processInfo() * @see \Drupal\Core\Image\ImageInterface::processInfo()
*/ */
function getInfo(ImageInterface $image); public function getInfo(ImageInterface $image);
/** /**
* Verifies Image Toolkit is set up correctly. * Verifies Image Toolkit is set up correctly.
...@@ -179,5 +179,14 @@ function getInfo(ImageInterface $image); ...@@ -179,5 +179,14 @@ function getInfo(ImageInterface $image);
* @return bool * @return bool
* True if the GD toolkit is available on this machine. * True if the GD toolkit is available on this machine.
*/ */
static function isAvailable(); public static function isAvailable();
/**
* Returns a list of image types supported by the toolkit.
*
* @return array
* An array of available image types. An image type is represented by a PHP
* IMAGETYPE_* constant (e.g. IMAGETYPE_JPEG, IMAGETYPE_PNG, etc.).
*/
public static function supportedTypes();
} }
...@@ -230,7 +230,7 @@ function testManipulations() { ...@@ -230,7 +230,7 @@ function testManipulations() {
$image_truecolor = imageistruecolor($image->getResource()); $image_truecolor = imageistruecolor($image->getResource());
$this->assertTrue($image_truecolor, format_string('Image %file after load is a truecolor image.', array('%file' => $file))); $this->assertTrue($image_truecolor, format_string('Image %file after load is a truecolor image.', array('%file' => $file)));
if ($image->getExtension() == 'gif') { if ($image->getType() == IMAGETYPE_GIF) {
if ($op == 'desaturate') { if ($op == 'desaturate') {
// Transparent GIFs and the imagefilter function don't work together. // Transparent GIFs and the imagefilter function don't work together.
$values['corners'][3][3] = 0; $values['corners'][3][3] = 0;
...@@ -264,7 +264,7 @@ function testManipulations() { ...@@ -264,7 +264,7 @@ function testManipulations() {
$this->assertTrue($correct_dimensions_object, format_string('Image %file object after %action action is reporting the proper height and width values.', array('%file' => $file, '%action' => $op))); $this->assertTrue($correct_dimensions_object, format_string('Image %file object after %action action is reporting the proper height and width values.', array('%file' => $file, '%action' => $op)));
// JPEG colors will always be messed up due to compression. // JPEG colors will always be messed up due to compression.
if ($image->getExtension() != 'jpg') { if ($image->getType() != IMAGETYPE_JPEG) {
// Now check each of the corners to ensure color correctness. // Now check each of the corners to ensure color correctness.
foreach ($values['corners'] as $key => $corner) { foreach ($values['corners'] as $key => $corner) {
// Get the location of the corner. // Get the location of the corner.
...@@ -293,6 +293,6 @@ function testManipulations() { ...@@ -293,6 +293,6 @@ function testManipulations() {
} }
} }
} }
} }
} }
...@@ -46,12 +46,10 @@ public function getInfo(ImageInterface $image) { ...@@ -46,12 +46,10 @@ public function getInfo(ImageInterface $image) {
$data = getimagesize($image->getSource()); $data = getimagesize($image->getSource());
if (isset($data) && is_array($data)) { if (isset($data) && is_array($data)) {
$extensions = array('1' => 'gif', '2' => 'jpg', '3' => 'png');
$extension = isset($extensions[$data[2]]) ? $extensions[$data[2]] : '';
$details = array( $details = array(
'width' => $data[0], 'width' => $data[0],
'height' => $data[1], 'height' => $data[1],
'extension' => $extension, 'type' => $data[2],
'mime_type' => $data['mime'], 'mime_type' => $data['mime'],
); );
} }
...@@ -133,4 +131,12 @@ protected function logCall($op, $args) { ...@@ -133,4 +131,12 @@ protected function logCall($op, $args) {
public static function isAvailable() { public static function isAvailable() {
return TRUE; return TRUE;
} }
/**
* {@inheritdoc}
*/
public static function supportedTypes() {
return array(IMAGETYPE_PNG, IMAGETYPE_JPEG, IMAGETYPE_GIF);
}
} }
...@@ -54,6 +54,7 @@ protected function setUp() { ...@@ -54,6 +54,7 @@ protected function setUp() {
'width' => 88, 'width' => 88,
'height' => 100, 'height' => 100,
'extension' => 'png', 'extension' => 'png',
'type' => IMAGETYPE_PNG,
'mime_type' => 'image/png', 'mime_type' => 'image/png',
))); )));
...@@ -106,6 +107,13 @@ public function testGetFileSize() { ...@@ -106,6 +107,13 @@ public function testGetFileSize() {
$this->assertEquals($this->image->getFileSize(), 3905); $this->assertEquals($this->image->getFileSize(), 3905);
} }
/**
* Tests \Drupal\Core\Image\Image::getType().
*/
public function testGetType() {
$this->assertEquals($this->image->getType(), IMAGETYPE_PNG);
}
/** /**
* Tests \Drupal\Core\Image\Image::getMimeType(). * Tests \Drupal\Core\Image\Image::getMimeType().
*/ */
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment