From 7370aa77323190eee2a3ebc31367d2e3e74ef0cb Mon Sep 17 00:00:00 2001 From: Claudiu Cristea Date: Mon, 16 Sep 2013 19:49:59 +0300 Subject: [PATCH] Issue #2066219 by claudiu.cristea: Decouple image type from image extension. --- core/lib/Drupal/Core/Image/Image.php | 34 ++++++++++++++++++++-- core/lib/Drupal/Core/Image/ImageInterface.php | 17 +++++++++++ core/modules/file/file.module | 12 ++++++-- core/modules/image/image.field.inc | 2 +- core/modules/image/image.module | 2 +- .../image/Plugin/field/field_type/ImageItem.php | 2 +- .../system/Plugin/ImageToolkit/GDToolkit.php | 27 +++++++++-------- .../Drupal/system/Plugin/ImageToolkitInterface.php | 33 +++++++++++++-------- .../Drupal/system/Tests/Image/ToolkitGdTest.php | 6 ++-- .../image_test/Plugin/ImageToolkit/TestToolkit.php | 12 ++++++-- core/tests/Drupal/Tests/Core/Image/ImageTest.php | 8 +++++ 11 files changed, 117 insertions(+), 38 deletions(-) diff --git a/core/lib/Drupal/Core/Image/Image.php b/core/lib/Drupal/Core/Image/Image.php index a515fe4..0f2fc85 100644 --- a/core/lib/Drupal/Core/Image/Image.php +++ b/core/lib/Drupal/Core/Image/Image.php @@ -63,7 +63,14 @@ class Image implements ImageInterface { 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 */ @@ -99,6 +106,13 @@ public function __construct($source, ImageToolkitInterface $toolkit) { /** * {@inheritdoc} */ + public function isSupported() { + return in_array($this->getType(), $this->toolkit->supportedTypes()); + } + + /** + * {@inheritdoc} + */ public function getExtension() { $this->processInfo(); return $this->extension; @@ -147,6 +161,14 @@ public function getFileSize() { /** * {@inheritdoc} */ + public function getType() { + $this->processInfo(); + return $this->type; + } + + /** + * {@inheritdoc} + */ public function getMimeType() { $this->processInfo(); return $this->mimeType; @@ -245,9 +267,17 @@ protected function processInfo() { if ($details = $this->toolkit->getInfo($this)) { $this->height = $details['height']; $this->width = $details['width']; - $this->extension = $details['extension']; + $this->type = $details['type']; $this->mimeType = $details['mime_type']; $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; } return TRUE; diff --git a/core/lib/Drupal/Core/Image/ImageInterface.php b/core/lib/Drupal/Core/Image/ImageInterface.php index b83c578..5966ab2 100644 --- a/core/lib/Drupal/Core/Image/ImageInterface.php +++ b/core/lib/Drupal/Core/Image/ImageInterface.php @@ -13,6 +13,14 @@ 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. * * @return string @@ -65,6 +73,15 @@ public function setWidth($width); 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. * * @return string diff --git a/core/modules/file/file.module b/core/modules/file/file.module index 68b33cf..65b3ca0 100644 --- a/core/modules/file/file.module +++ b/core/modules/file/file.module @@ -7,6 +7,7 @@ use Drupal\file\Entity\File; use Drupal\Component\Utility\NestedArray; +use Drupal\Component\Utility\Unicode; use Drupal\Core\Entity\EntityStorageControllerInterface; use Drupal\Core\Template\Attribute; use Drupal\file\FileUsage\FileUsageInterface; @@ -411,8 +412,13 @@ function file_validate_is_image(File $file) { $errors = array(); $image = \Drupal::service('image.factory')->get($file->getFileUri()); - if (!$image->getExtension()) { - $errors[] = t('Only JPEG, PNG and GIF images are allowed.'); + if (!$image->isSupported()) { + $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; @@ -447,7 +453,7 @@ function file_validate_image_resolution(File $file, $maximum_dimensions = 0, $mi // Check first that the file is an image. $image_factory = \Drupal::service('image.factory'); $image = $image_factory->get($file->getFileUri()); - if ($image->getExtension()) { + if ($image->isSupported()) { if ($maximum_dimensions) { // Check that it is smaller than the given dimensions. list($width, $height) = explode('x', $maximum_dimensions); diff --git a/core/modules/image/image.field.inc b/core/modules/image/image.field.inc index 1111b8a..4e26f4d 100644 --- a/core/modules/image/image.field.inc +++ b/core/modules/image/image.field.inc @@ -41,7 +41,7 @@ function image_field_widget_process($element, &$form_state, $form) { } else { $image = \Drupal::service('image.factory')->get($file->getFileUri()); - if ($image->getExtension()) { + if ($image->isSupported()) { $variables['width'] = $image->getWidth(); $variables['height'] = $image->getHeight(); } diff --git a/core/modules/image/image.module b/core/modules/image/image.module index b93bc53..aa13940 100644 --- a/core/modules/image/image.module +++ b/core/modules/image/image.module @@ -242,7 +242,7 @@ function image_file_download($uri) { // Check that the file exists and is an image. $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. $headers = \Drupal::moduleHandler()->invokeAll('file_download', array($original_uri)); // Confirm there's at least one module granting access and none denying access. diff --git a/core/modules/image/lib/Drupal/image/Plugin/field/field_type/ImageItem.php b/core/modules/image/lib/Drupal/image/Plugin/field/field_type/ImageItem.php index a6d8972..6073377 100644 --- a/core/modules/image/lib/Drupal/image/Plugin/field/field_type/ImageItem.php +++ b/core/modules/image/lib/Drupal/image/Plugin/field/field_type/ImageItem.php @@ -293,7 +293,7 @@ public function preSave() { // Determine the dimensions if necessary. if (empty($width) || empty($height)) { $image = \Drupal::service('image.factory')->get($this->entity->getFileUri()); - if ($image->getExtension()) { + if ($image->isSupported()) { $this->width = $image->getWidth(); $this->height =$image->getHeight(); } diff --git a/core/modules/system/lib/Drupal/system/Plugin/ImageToolkit/GDToolkit.php b/core/modules/system/lib/Drupal/system/Plugin/ImageToolkit/GDToolkit.php index 3219edb..f8ba4d2 100644 --- a/core/modules/system/lib/Drupal/system/Plugin/ImageToolkit/GDToolkit.php +++ b/core/modules/system/lib/Drupal/system/Plugin/ImageToolkit/GDToolkit.php @@ -98,7 +98,7 @@ public function rotate(ImageInterface $image, $degrees, $background = NULL) { // Images are assigned a new color palette when rotating, removing any // 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()); if ($transparent_index != 0) { $transparent_gif_color = imagecolorsforindex($image->getResource(), $transparent_index); @@ -155,8 +155,7 @@ public function desaturate(ImageInterface $image) { * {@inheritdoc} */ public function load(ImageInterface $image) { - $extension = str_replace('jpg', 'jpeg', $image->getExtension()); - $function = 'imagecreatefrom' . $extension; + $function = 'imagecreatefrom' . image_type_to_extension($image->getType(), FALSE); if (function_exists($function) && $resource = $function($image->getSource())) { $image->setResource($resource); if (!imageistruecolor($resource)) { @@ -190,17 +189,16 @@ public function save(ImageInterface $image, $destination) { $destination = drupal_realpath($destination); } - $extension = str_replace('jpg', 'jpeg', $image->getExtension()); - $function = 'image' . $extension; + $function = 'image' . image_type_to_extension($image->getType(), FALSE); if (!function_exists($function)) { return FALSE; } - if ($extension == 'jpeg') { + if ($image->getType() == IMAGETYPE_JPEG) { $success = $function($image->getResource(), $destination, \Drupal::config('system.image.gd')->get('jpeg_quality')); } else { // Always save PNG images with full transparency. - if ($extension == 'png') { + if ($image->getType() == IMAGETYPE_PNG) { imagealphablending($image->getResource(), FALSE); imagesavealpha($image->getResource(), TRUE); } @@ -221,12 +219,10 @@ public function getInfo(ImageInterface $image) { $data = getimagesize($image->getSource()); if (isset($data) && is_array($data)) { - $extensions = array('1' => 'gif', '2' => 'jpg', '3' => 'png'); - $extension = isset($extensions[$data[2]]) ? $extensions[$data[2]] : ''; $details = array( 'width' => $data[0], 'height' => $data[1], - 'extension' => $extension, + 'type' => $data[2], 'mime_type' => $data['mime'], ); } @@ -250,7 +246,7 @@ public function getInfo(ImageInterface $image) { public function createTmp(ImageInterface $image, $width, $height) { $res = imagecreatetruecolor($width, $height); - if ($image->getExtension() == 'gif') { + if ($image->getType() == IMAGETYPE_GIF) { // Grab transparent color index from image resource. $transparent = imagecolortransparent($image->getResource()); @@ -264,7 +260,7 @@ public function createTmp(ImageInterface $image, $width, $height) { imagecolortransparent($res, $transparent); } } - elseif ($image->getExtension() == 'png') { + elseif ($image->getType() == IMAGETYPE_PNG) { imagealphablending($res, FALSE); $transparency = imagecolorallocatealpha($res, 0, 0, 0, 127); imagefill($res, 0, 0, $transparency); @@ -290,4 +286,11 @@ public static function isAvailable() { } return FALSE; } + + /** + * {@inheritdoc} + */ + public static function supportedTypes() { + return array(IMAGETYPE_PNG, IMAGETYPE_JPEG, IMAGETYPE_GIF); + } } diff --git a/core/modules/system/lib/Drupal/system/Plugin/ImageToolkitInterface.php b/core/modules/system/lib/Drupal/system/Plugin/ImageToolkitInterface.php index 774f050..5918160 100644 --- a/core/modules/system/lib/Drupal/system/Plugin/ImageToolkitInterface.php +++ b/core/modules/system/lib/Drupal/system/Plugin/ImageToolkitInterface.php @@ -50,14 +50,14 @@ * * @see system_image_toolkit_settings() */ - function settingsForm(); + public function settingsForm(); /** * Handles submissions for toolkit's settings form. * * @see system_image_toolkit_settings_submit() */ - function settingsFormSubmit($form, &$form_state); + public function settingsFormSubmit($form, &$form_state); /** * Scales an image to the specified size. @@ -73,7 +73,7 @@ function settingsFormSubmit($form, &$form_state); * @return bool * 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. @@ -93,7 +93,7 @@ function resize(ImageInterface $image, $width, $height); * @return bool * TRUE or FALSE, based on success. */ - function rotate(ImageInterface $image, $degrees, $background = NULL); + public function rotate(ImageInterface $image, $degrees, $background = NULL); /** * Crops an image. @@ -115,7 +115,7 @@ function rotate(ImageInterface $image, $degrees, $background = NULL); * * @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. @@ -129,7 +129,7 @@ function crop(ImageInterface $image, $x, $y, $width, $height); * @return bool * TRUE or FALSE, based on success. */ - function desaturate(ImageInterface $image); + public function desaturate(ImageInterface $image); /** * Creates an image resource from a file. @@ -140,7 +140,7 @@ function desaturate(ImageInterface $image); * @return bool * TRUE or FALSE, based on success. */ - function load(ImageInterface $image); + public function load(ImageInterface $image); /** * Writes an image resource to a destination file. @@ -153,7 +153,7 @@ function load(ImageInterface $image); * @return bool * TRUE or FALSE, based on success. */ - function save(ImageInterface $image, $destination); + public function save(ImageInterface $image, $destination); /** * Gets details about an image. @@ -166,12 +166,12 @@ function save(ImageInterface $image, $destination); * keyed array containing information about the image: * - "width": Width, in pixels. * - "height": Height, in pixels. - * - "extension": Commonly used file extension for the image. - * - "mime_type": MIME type ('image/jpeg', 'image/gif', 'image/png'). + * - "type": Image type represented as an IMAGETYPE_* constant. + * - "mime_type": MIME type (e.g. 'image/jpeg', 'image/gif', 'image/png'). * * @see \Drupal\Core\Image\ImageInterface::processInfo() */ - function getInfo(ImageInterface $image); + public function getInfo(ImageInterface $image); /** * Verifies Image Toolkit is set up correctly. @@ -179,5 +179,14 @@ function getInfo(ImageInterface $image); * @return bool * 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(); } diff --git a/core/modules/system/lib/Drupal/system/Tests/Image/ToolkitGdTest.php b/core/modules/system/lib/Drupal/system/Tests/Image/ToolkitGdTest.php index 1797b78..0e81a77 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Image/ToolkitGdTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Image/ToolkitGdTest.php @@ -230,7 +230,7 @@ function testManipulations() { $image_truecolor = imageistruecolor($image->getResource()); $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') { // Transparent GIFs and the imagefilter function don't work together. $values['corners'][3][3] = 0; @@ -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))); // 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. foreach ($values['corners'] as $key => $corner) { // Get the location of the corner. @@ -293,6 +293,6 @@ function testManipulations() { } } } - } + } diff --git a/core/modules/system/tests/modules/image_test/lib/Drupal/image_test/Plugin/ImageToolkit/TestToolkit.php b/core/modules/system/tests/modules/image_test/lib/Drupal/image_test/Plugin/ImageToolkit/TestToolkit.php index eede36a..836ef7c 100644 --- a/core/modules/system/tests/modules/image_test/lib/Drupal/image_test/Plugin/ImageToolkit/TestToolkit.php +++ b/core/modules/system/tests/modules/image_test/lib/Drupal/image_test/Plugin/ImageToolkit/TestToolkit.php @@ -46,12 +46,10 @@ public function getInfo(ImageInterface $image) { $data = getimagesize($image->getSource()); if (isset($data) && is_array($data)) { - $extensions = array('1' => 'gif', '2' => 'jpg', '3' => 'png'); - $extension = isset($extensions[$data[2]]) ? $extensions[$data[2]] : ''; $details = array( 'width' => $data[0], 'height' => $data[1], - 'extension' => $extension, + 'type' => $data[2], 'mime_type' => $data['mime'], ); } @@ -133,4 +131,12 @@ protected function logCall($op, $args) { public static function isAvailable() { return TRUE; } + + /** + * {@inheritdoc} + */ + public static function supportedTypes() { + return array(IMAGETYPE_PNG, IMAGETYPE_JPEG, IMAGETYPE_GIF); + } + } diff --git a/core/tests/Drupal/Tests/Core/Image/ImageTest.php b/core/tests/Drupal/Tests/Core/Image/ImageTest.php index c96f4c9..700973e 100644 --- a/core/tests/Drupal/Tests/Core/Image/ImageTest.php +++ b/core/tests/Drupal/Tests/Core/Image/ImageTest.php @@ -54,6 +54,7 @@ protected function setUp() { 'width' => 88, 'height' => 100, 'extension' => 'png', + 'type' => IMAGETYPE_PNG, 'mime_type' => 'image/png', ))); @@ -107,6 +108,13 @@ public function testGetFileSize() { } /** + * Tests \Drupal\Core\Image\Image::getType(). + */ + public function testGetType() { + $this->assertEquals($this->image->getType(), IMAGETYPE_PNG); + } + + /** * Tests \Drupal\Core\Image\Image::getMimeType(). */ public function testGetMimeType() { -- 1.8.3.1