1
0
mirror of https://github.com/Intervention/image.git synced 2025-09-03 10:53:01 +02:00

Merge remote-tracking branch 'upstream/master' into fix/minetype

This commit is contained in:
tchiotludo
2017-09-04 12:36:06 +02:00
15 changed files with 201 additions and 49 deletions

View File

@@ -1,5 +1,9 @@
language: php
sudo: false
dist: trusty
php:
- 5.4
- 5.5
@@ -14,15 +18,9 @@ matrix:
- php: nightly
- php: hhvm
before_install:
- sudo add-apt-repository -y ppa:moti-p/cc
- sudo apt-get update
- sudo apt-get -y --reinstall install imagemagick
- yes | pecl install imagick-beta
- if [[ ${TRAVIS_PHP_VERSION:0:3} == "5.4" ]]; then echo "extension = imagick.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini; fi
before_script:
- composer self-update
- composer install --prefer-source --no-interaction --dev
- printf "\n" | pecl install imagick
- composer self-update || true
- composer install --prefer-dist --no-interaction --no-progress --no-suggest --optimize-autoloader
script: vendor/bin/phpunit

View File

@@ -2,7 +2,9 @@
Intervention Image is a **PHP image handling and manipulation** library providing an easier and expressive way to create, edit, and compose images. The package includes ServiceProviders and Facades for easy **Laravel** integration.
[![Latest Version](https://img.shields.io/packagist/v/intervention/image.svg)](https://packagist.org/packages/intervention/image)
[![Build Status](https://travis-ci.org/Intervention/image.png?branch=master)](https://travis-ci.org/Intervention/image)
[![Monthly Downloads](https://img.shields.io/packagist/dm/intervention/image.svg)](https://packagist.org/packages/intervention/image/stats)
## Requirements
@@ -18,7 +20,7 @@ Intervention Image is a **PHP image handling and manipulation** library providin
- [Installation](http://image.intervention.io/getting_started/installation)
- [Laravel Framework Integration](http://image.intervention.io/getting_started/installation#laravel)
- [Official Documentation](http://image.intervention.io/)
- [Basic Usage](http://image.intervention.io/use/basics)
## Code Examples
@@ -36,7 +38,7 @@ $img->insert('public/watermark.png');
$img->save('public/bar.jpg');
```
Refer to the [documentation](http://image.intervention.io/) to learn more about Intervention Image.
Refer to the [official documentation](http://image.intervention.io/) to learn more about Intervention Image.
## Contributing
@@ -50,4 +52,4 @@ Contributions to the Intervention Image library are welcome. Please note the fol
Intervention Image is licensed under the [MIT License](http://opensource.org/licenses/MIT).
Copyright 2014 [Oliver Vogel](http://olivervogel.net/)
Copyright 2017 [Oliver Vogel](http://olivervogel.com/)

View File

@@ -7,8 +7,8 @@
"authors": [
{
"name": "Oliver Vogel",
"email": "oliver@olivervogel.net",
"homepage": "http://olivervogel.net/"
"email": "oliver@olivervogel.com",
"homepage": "http://olivervogel.com/"
}
],
"require": {
@@ -17,7 +17,7 @@
"guzzlehttp/psr7": "~1.1"
},
"require-dev": {
"phpunit/phpunit": "3.*",
"phpunit/phpunit": "^4.8 || ^5.7",
"mockery/mockery": "~0.9.2"
},
"suggest": {
@@ -33,6 +33,14 @@
"extra": {
"branch-alias": {
"dev-master": "2.3-dev"
},
"laravel": {
"providers": [
"Intervention\\Image\\ImageServiceProvider"
],
"aliases": {
"Image": "Intervention\\Image\\Facades\\Image"
}
}
},
"minimum-stability": "stable"

View File

@@ -2,6 +2,9 @@
namespace Intervention\Image;
use GuzzleHttp\Psr7\Stream;
use Psr\Http\Message\StreamInterface;
abstract class AbstractDecoder
{
/**
@@ -85,22 +88,35 @@ abstract class AbstractDecoder
/**
* Init from given stream
*
* @param $stream
* @param StreamInterface|resource $stream
* @return \Intervention\Image\Image
*/
public function initFromStream($stream)
{
$offset = ftell($stream);
$shouldAndCanSeek = $offset !== 0 && $this->isStreamSeekable($stream);
if ($shouldAndCanSeek) {
rewind($stream);
if (!$stream instanceof StreamInterface) {
$stream = new Stream($stream);
}
$data = @stream_get_contents($stream);
try {
$offset = $stream->tell();
} catch (\RuntimeException $e) {
$offset = 0;
}
$shouldAndCanSeek = $offset !== 0 && $stream->isSeekable();
if ($shouldAndCanSeek) {
fseek($stream, $offset);
$stream->rewind();
}
try {
$data = $stream->getContents();
} catch (\RuntimeException $e) {
$data = null;
}
if ($shouldAndCanSeek) {
$stream->seek($offset);
}
if ($data) {
@@ -112,18 +128,6 @@ abstract class AbstractDecoder
);
}
/**
* Checks if we can move the pointer for this stream
*
* @param resource $stream
* @return bool
*/
private function isStreamSeekable($stream)
{
$metadata = stream_get_meta_data($stream);
return $metadata['seekable'];
}
/**
* Determines if current source data is GD resource
*
@@ -213,6 +217,7 @@ abstract class AbstractDecoder
*/
public function isStream()
{
if ($this->data instanceof StreamInterface) return true;
if (!is_resource($this->data)) return false;
if (get_resource_type($this->data) !== 'stream') return false;
@@ -332,6 +337,7 @@ abstract class AbstractDecoder
case $this->isFilePath():
return $this->initFromPath($this->data);
// isBase64 has to be after isFilePath to prevent false positives
case $this->isBase64():
return $this->initFromBinary(base64_decode($this->data));

View File

@@ -74,6 +74,13 @@ abstract class AbstractEncoder
*/
abstract protected function processIco();
/**
* Processes and returns image as WebP encoded string
*
* @return string
*/
abstract protected function processWebp();
/**
* Process a given image
*
@@ -146,6 +153,12 @@ abstract class AbstractEncoder
$this->result = $this->processPsd();
break;
case 'webp':
case 'image/webp':
case 'image/x-webp':
$this->result = $this->processWebp();
break;
default:
throw new \Intervention\Image\Exception\NotSupportedException(
"Encoding format ({$format}) is not supported."

View File

@@ -50,6 +50,7 @@ class Constraint
/**
* Fix the given argument in current constraint
*
* @param integer $type
* @return void
*/

View File

@@ -14,37 +14,54 @@ class Decoder extends \Intervention\Image\AbstractDecoder
*/
public function initFromPath($path)
{
$info = @getimagesize($path);
if ($info === false) {
if ( ! file_exists($path)) {
throw new \Intervention\Image\Exception\NotReadableException(
"Unable to read image from file ({$path})."
"Unable to find file ({$path})."
);
}
// get mime type of file
$mime = finfo_file(finfo_open(FILEINFO_MIME_TYPE), $path);
// define core
switch ($info[2]) {
case IMAGETYPE_PNG:
switch (strtolower($mime)) {
case 'image/png':
case 'image/x-png':
$core = @imagecreatefrompng($path);
break;
case IMAGETYPE_JPEG:
case 'image/jpg':
case 'image/jpeg':
case 'image/pjpeg':
$core = @imagecreatefromjpeg($path);
if (!$core) {
$core= @imagecreatefromstring(file_get_contents($path));
}
break;
case IMAGETYPE_GIF:
case 'image/gif':
$core = @imagecreatefromgif($path);
break;
case 'image/webp':
case 'image/x-webp':
if ( ! function_exists('imagecreatefromwebp')) {
throw new \Intervention\Image\Exception\NotReadableException(
"Unsupported image type. GD/PHP installation does not support WebP format."
);
}
$core = @imagecreatefromwebp($path);
break;
default:
throw new \Intervention\Image\Exception\NotReadableException(
"Unable to read image type. GD driver is only able to decode JPG, PNG or GIF files."
"Unsupported image type. GD driver is only able to decode JPG, PNG, GIF or WebP files."
);
}
if (empty($core)) {
throw new \Intervention\Image\Exception\NotReadableException(
"Unable to read image from file ({$path})."
"Unable to decode image from file ({$path})."
);
}
@@ -52,7 +69,7 @@ class Decoder extends \Intervention\Image\AbstractDecoder
// build image
$image = $this->initFromGdResource($core);
$image->mime = $info['mime'];
$image->mime = $mime;
$image->setFileInfoFromPath($path);
return $image;

View File

@@ -55,6 +55,23 @@ class Encoder extends \Intervention\Image\AbstractEncoder
return $buffer;
}
protected function processWebp()
{
if ( ! function_exists('imagewebp')) {
throw new \Intervention\Image\Exception\NotSupportedException(
"Webp format is not supported by PHP installation."
);
}
ob_start();
imagewebp($this->image->getCore(), null, $this->quality);
$this->image->mime = defined('IMAGETYPE_WEBP') ? image_type_to_mime_type(IMAGETYPE_WEBP) : 'image/webp';
$buffer = ob_get_contents();
ob_end_clean();
return $buffer;
}
/**
* Processes and returns encoded image as TIFF string
*

View File

@@ -40,7 +40,9 @@ class ImageServiceProvider extends ServiceProvider
*/
public function boot()
{
return $this->provider->boot();
if (method_exists($this->provider, 'boot')) {
return $this->provider->boot();
}
}
/**

View File

@@ -77,7 +77,7 @@ class ImageServiceProviderLaravel5 extends ServiceProvider
// imagecache route
if (is_string(config('imagecache.route'))) {
$filename_pattern = '[ \w\\.\\/\\-\\@]+';
$filename_pattern = '[ \w\\.\\/\\-\\@\(\)]+';
// route to access template applied image file
$app['router']->get(config('imagecache.route').'/{template}/{filename}', array(

View File

@@ -70,6 +70,28 @@ class Encoder extends \Intervention\Image\AbstractEncoder
return $imagick->getImagesBlob();
}
protected function processWebp()
{
if ( ! \Imagick::queryFormats('WEBP')) {
throw new \Intervention\Image\Exception\NotSupportedException(
"Webp format is not supported by Imagick installation."
);
}
$format = 'webp';
$compression = \Imagick::COMPRESSION_JPEG;
$imagick = $this->image->getCore();
$imagick = $imagick->mergeImageLayers(\Imagick::LAYERMETHOD_MERGE);
$imagick->setFormat($format);
$imagick->setImageFormat($format);
$imagick->setCompression($compression);
$imagick->setImageCompression($compression);
$imagick->setImageCompressionQuality($this->quality);
return $imagick->getImagesBlob();
}
/**
* Processes and returns encoded image as TIFF string
*

View File

@@ -61,6 +61,9 @@ class AbstractDecoderTest extends PHPUnit_Framework_TestCase
$source = $this->getTestDecoder(fopen(__DIR__ . '/images/test.jpg', 'r'));
$this->assertTrue($source->isStream());
$source = $this->getTestDecoder(new \GuzzleHttp\Psr7\Stream(fopen(__DIR__ . '/images/test.jpg', 'r')));
$this->assertTrue($source->isStream());
$source = $this->getTestDecoder(null);
$this->assertFalse($source->isStream());
}

View File

@@ -46,6 +46,20 @@ class EncoderTest extends PHPUnit_Framework_TestCase
$this->assertEquals('image/gif; charset=binary', $this->getMime($encoder->result));
}
public function testProcessWebpGd()
{
if (function_exists('imagewebp')) {
$core = imagecreatefromjpeg(__DIR__.'/images/test.jpg');
$encoder = new GdEncoder;
$image = Mockery::mock('\Intervention\Image\Image');
$image->shouldReceive('getCore')->once()->andReturn($core);
$image->shouldReceive('setEncoded')->once()->andReturn($image);
$img = $encoder->process($image, 'webp', 90);
$this->assertInstanceOf('Intervention\Image\Image', $img);
$this->assertEquals('image/webp; charset=binary', $this->getMime($encoder->result));
}
}
/**
* @expectedException \Intervention\Image\Exception\NotSupportedException
*/
@@ -155,6 +169,16 @@ class EncoderTest extends PHPUnit_Framework_TestCase
$this->assertEquals('mock-gif', $encoder->result);
}
/**
* @expectedException \Intervention\Image\Exception\NotSupportedException
*/
public function testProcessWebpImagick()
{
$encoder = new ImagickEncoder;
$image = Mockery::mock('\Intervention\Image\Image');
$img = $encoder->process($image, 'webp', 90);
}
public function testProcessTiffImagick()
{
$core = $this->getImagickMock('tiff');

View File

@@ -28,6 +28,14 @@ class GdSystemTest extends PHPUnit_Framework_TestCase
$this->manager()->make('tests/images/broken.png');
}
/**
* @expectedException \Intervention\Image\Exception\NotReadableException
*/
public function testMakeFromNotExisting()
{
$this->manager()->make('tests/images/not_existing.png');
}
public function testMakeFromString()
{
$str = file_get_contents('tests/images/circle.png');
@@ -75,6 +83,22 @@ class GdSystemTest extends PHPUnit_Framework_TestCase
$this->assertEquals(10, $img->getHeight());
}
public function testMakeFromWebp()
{
if (function_exists('imagecreatefromwebp')) {
$img = $this->manager()->make('tests/images/test.webp');
$this->assertInstanceOf('Intervention\Image\Image', $img);
$this->assertInternalType('resource', $img->getCore());
$this->assertEquals(16, $img->getWidth());
$this->assertEquals(16, $img->getHeight());
$this->assertEquals('image/webp', $img->mime);
$this->assertEquals('tests/images', $img->dirname);
$this->assertEquals('test.webp', $img->basename);
$this->assertEquals('webp', $img->extension);
$this->assertEquals('test', $img->filename);
}
}
public function testCanvas()
{
$img = $this->manager()->canvas(30, 20);
@@ -1474,6 +1498,15 @@ class GdSystemTest extends PHPUnit_Framework_TestCase
$this->assertInternalType('resource', imagecreatefromstring($img->encoded));
}
public function testEncodeWebp()
{
if (function_exists('imagewebp')) {
$img = $this->manager()->make('tests/images/trim.png');
$data = (string) $img->encode('webp');
$this->assertEquals('image/webp; charset=binary', $this->getMime($data));
}
}
public function testEncodeDataUrl()
{
$img = $this->manager()->make('tests/images/trim.png');
@@ -1643,4 +1676,10 @@ class GdSystemTest extends PHPUnit_Framework_TestCase
'driver' => 'gd'
));
}
private function getMime($data)
{
$finfo = new finfo(FILEINFO_MIME);
return $finfo->buffer($data);
}
}

BIN
tests/images/test.webp Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 B