Commit 3ea57548 authored by Avris's avatar Avris

Merge branch 'v4.0' into 'master'

v4.0

See merge request !1
parents 93a3380f 9ad81b7c
tests/_output/*
vendor/*
.idea/*
/.idea/*
**/.DS_Store
/vendor/*
/tests/_output/*
## Micrus Imagine ##
# Micrus Imagine
This is a module for [Micrus framework](https://micrus.avris.it) that allows you
to integrate it with [Imagine](https://imagine.readthedocs.org/en/latest/)
......@@ -6,44 +6,44 @@ in a similar way that [LiipImagineBundle](https://github.com/liip/LiipImagineBun
This module lets you define image generators that will automatically apply a set of filters to the specified images and cache the result.
To install this module, open the file `app/Config/modules.yml` and add:
## Installation
- Avris\Micrus\Imagine\ImagineModule
Then run:
Run:
composer require avris/micrus-imagine
Also, make sure that `web/imagine` directory is writable by the server.
Then register the module in your `App\App:registerModules`:
yield new \Avris\Micrus\Imagine\ImagineModule;
### Usage ###
## Usage
Let's say you want to allow users to attach images to their posts. Whatever they upload, you will save the originals
in `run/att/` folder, but you only want to display it as a 200x200 thumbnail with a watermark in the bottom left corner.
in `var/attachments/` folder, but you only want to display it as a 200x200 thumbnail with a watermark in the bottom left corner.
Configure the module in the `app/Config/config.yml`, for instance like that:
Configure the module in the `config/imagine.yml`, for instance like that:
imagine:
sourceDir: run
generators:
thumb:
thumbnail: { size: [200, 200], mode: outbound }
watermark: { image: app/Asset/gfx/logo.png, size: 10%, position: bottomright }
source: '%PROJECT_DIR%/var/attachments' # default: '%PROJECT_DIR%/var/pics'
generators:
thumb:
thumbnail: { size: [200, 200], mode: outbound }
watermark: { image: assets/images/logo.png, size: 10%, position: bottomright }
You created a `thumb` generator that applies two filteres to the original image: `thumbnail` and `watermark`.
Now, wherever you wish to display the thumbnail, put such code there:
<img src="{{ ('att/'~post.file)|imagine('thumb') }}"/>
<img src="{{ post.file|imagine('thumb') }}"/>
It will generate a URL which will point either to a generated image (if it already exists) or to a controller that will generate it.
### Extending the module ###
The built-in list of filters is copied from [LiipImagineBundle](https://symfony.com/doc/master/bundles/LiipImagineBundle/filters.html) (MIT).
## Extending the module
To create your own filter, let's say it's called `myfilter`, just implement `Avris\Micrus\Imagine\Filter\LoaderInterface`
and register your service under the name: `imagineFilter_myfilter`.
To create your own filter, just implement `Avris\Micrus\Imagine\Filter\LoaderInterface` (`imagineFilter`).
### Copyright ###
## Copyright
* **Author:** Andrzej Prusinowski [(Avris.it)](https://avris.it)
* **Author:** Andre Prusinowski [(Avris.it)](https://avris.it)
* **Licence:** [MIT](https://mit.avris.it)
......@@ -11,8 +11,8 @@
"homepage": "https://avris.it"
}],
"require": {
"avris/micrus": "^3.0",
"imagine/Imagine": "~0.5,<0.7"
"avris/micrus": "^4.0",
"imagine/imagine": "^0.6.3|^0.7.0,<0.8"
},
"autoload": {
"psr-4": { "Avris\\Micrus\\Imagine\\": "src" }
......
instanceof:
Avris\Micrus\Imagine\Filter\LoaderInterface:
tags: ['imagineFilter']
driver: 'Gd'
source: '%PROJECT_DIR%/var/pics'
target: 'imagine'
generators: []
Avris\Micrus\Imagine\:
dir: '%MODULE_DIR%/src/'
Imagine\Image\ImagineInterface:
resolve: '@Avris\Micrus\Imagine\ImagineManager.imagine'
<?php
/**
* Based on https://github.com/liip/LiipImagineBundle/blob/2.0/Imagine/Filter/Loader
* License attached in LICENSE.md
*/
namespace Avris\Micrus\Imagine\Filter;
use Imagine\Exception\InvalidArgumentException;
use Imagine\Image\ImageInterface;
use Imagine\Image\ImagineInterface;
/**
* AutoRotateFilterLoader - rotates an Image based on its EXIF Data.
*
* @author Robert Schönthal <robert.schoenthal@gmail.com>
*/
class AutoRotateFilterLoader implements LoaderInterface
final class AutoRotateFilterLoader implements LoaderInterface
{
protected $orientationKeys = array(
const ORIENTATION_KEYS = [
'exif.Orientation',
'ifd0.Orientation',
);
];
/**
* {@inheritdoc}
*/
public function load(ImageInterface $image, array $options = array())
public function load(ImagineInterface $imagine, ImageInterface $image, array $options = []): ImageInterface
{
if ($orientation = $this->getOrientation($image)) {
if ($orientation < 1 || $orientation > 8) {
throw new InvalidArgumentException(
sprintf('The image has wrong EXIF orientation tag (%d)', $orientation)
);
return $image;
}
// Rotates if necessary.
......@@ -44,14 +42,7 @@ class AutoRotateFilterLoader implements LoaderInterface
return $image;
}
/**
* calculates to rotation degree from the EXIF Orientation.
*
* @param int $orientation
*
* @return int
*/
private function calculateRotation($orientation)
private function calculateRotation(int $orientation): int
{
switch ($orientation) {
case 1:
......@@ -69,16 +60,11 @@ class AutoRotateFilterLoader implements LoaderInterface
}
}
/**
* @param ImageInterface $image
*
* @return int
*/
private function getOrientation(ImageInterface $image)
private function getOrientation(ImageInterface $image): ?int
{
//>0.6 imagine meta data interface
if (method_exists($image, 'metadata')) {
foreach ($this->orientationKeys as $orientationKey) {
foreach (self::ORIENTATION_KEYS as $orientationKey) {
$orientation = $image->metadata()->offsetGet($orientationKey);
if ($orientation) {
......@@ -92,18 +78,9 @@ class AutoRotateFilterLoader implements LoaderInterface
return isset($data['Orientation']) ? $data['Orientation'] : null;
}
return;
}
/**
* Returns true if the image is flipped, false otherwise.
*
* @param int $orientation
*
* @return bool
*/
private function isFlipped($orientation)
private function isFlipped(int $orientation): bool
{
switch ($orientation) {
case 1:
......@@ -119,4 +96,9 @@ class AutoRotateFilterLoader implements LoaderInterface
return true;
}
}
public function getName(): string
{
return 'autoRotate';
}
}
<?php
/**
* Based on https://github.com/liip/LiipImagineBundle/blob/2.0/Imagine/Filter/Loader
* License attached in LICENSE.md
*/
namespace Avris\Micrus\Imagine\Filter;
use Imagine\Image\Box;
......@@ -7,22 +10,9 @@ use Imagine\Image\ImageInterface;
use Imagine\Image\ImagineInterface;
use Imagine\Image\Point;
class BackgroundFilterLoader implements LoaderInterface
final class BackgroundFilterLoader implements LoaderInterface
{
/**
* @var ImagineInterface
*/
protected $imagine;
public function __construct(ImagineInterface $imagine)
{
$this->imagine = $imagine;
}
/**
* {@inheritdoc}
*/
public function load(ImageInterface $image, array $options = array())
public function load(ImagineInterface $imagine, ImageInterface $image, array $options = array()): ImageInterface
{
$background = $image->palette()->color(
isset($options['color']) ? $options['color'] : '#fff',
......@@ -32,14 +22,63 @@ class BackgroundFilterLoader implements LoaderInterface
$size = $image->getSize();
if (isset($options['size'])) {
list($width, $height) = $options['size'];
$width = isset($options['size'][0]) ? $options['size'][0] : null;
$height = isset($options['size'][1]) ? $options['size'][1] : null;
$position = isset($options['position']) ? $options['position'] : 'center';
switch ($position) {
case 'topleft':
$x = 0;
$y = 0;
break;
case 'top':
$x = ($width - $image->getSize()->getWidth()) / 2;
$y = 0;
break;
case 'topright':
$x = $width - $image->getSize()->getWidth();
$y = 0;
break;
case 'left':
$x = 0;
$y = ($height - $image->getSize()->getHeight()) / 2;
break;
case 'center':
$x = ($width - $image->getSize()->getWidth()) / 2;
$y = ($height - $image->getSize()->getHeight()) / 2;
break;
case 'right':
$x = $width - $image->getSize()->getWidth();
$y = ($height - $image->getSize()->getHeight()) / 2;
break;
case 'bottomleft':
$x = 0;
$y = $height - $image->getSize()->getHeight();
break;
case 'bottom':
$x = ($width - $image->getSize()->getWidth()) / 2;
$y = $height - $image->getSize()->getHeight();
break;
case 'bottomright':
$x = $width - $image->getSize()->getWidth();
$y = $height - $image->getSize()->getHeight();
break;
default:
throw new \InvalidArgumentException("Unexpected position '{$position}'");
break;
}
$size = new Box($width, $height);
$topLeft = new Point(($width - $image->getSize()->getWidth()) / 2, ($height - $image->getSize()->getHeight()) / 2);
$topLeft = new Point($x, $y);
}
$canvas = $this->imagine->create($size, $background);
$canvas = $imagine->create($size, $background);
return $canvas->paste($image, $topLeft);
}
public function getName(): string
{
return 'background';
}
}
<?php
/**
* Based on https://github.com/liip/LiipImagineBundle/blob/2.0/Imagine/Filter/Loader
* License attached in LICENSE.md
*/
namespace Avris\Micrus\Imagine\Filter;
use Imagine\Filter\Basic\Crop;
use Imagine\Image\Box;
use Imagine\Image\ImageInterface;
use Imagine\Image\ImagineInterface;
use Imagine\Image\Point;
class CropFilterLoader implements LoaderInterface
final class CropFilterLoader implements LoaderInterface
{
/**
* {@inheritdoc}
*/
public function load(ImageInterface $image, array $options = array())
public function load(ImagineInterface $imagine, ImageInterface $image, array $options = array()): ImageInterface
{
list($x, $y) = $options['start'];
list($width, $height) = $options['size'];
$x = $options['start'][0] ?? null;
$y = $options['start'][1] ?? null;
$width = $options['size'][0] ?? null;
$height = $options['size'][1] ?? null;
$filter = new Crop(new Point($x, $y), new Box($width, $height));
$image = $filter->apply($image);
return $image;
}
public function getName(): string
{
return 'crop';
}
}
<?php
/**
* Based on https://github.com/liip/LiipImagineBundle/blob/2.0/Imagine/Filter/Loader
* License attached in LICENSE.md
*/
namespace Avris\Micrus\Imagine\Filter;
use Imagine\Filter\Basic\Resize;
use Imagine\Image\Box;
use Imagine\Image\ImageInterface;
/**
* downscale filter.
* Downscale filter.
*
* @author Devi Prasad <https://github.com/deviprsd21>
*/
class DownscaleFilterLoader implements LoaderInterface
final class DownscaleFilterLoader extends ScaleFilterLoader
{
/**
* {@inheritdoc}
*/
public function load(ImageInterface $image, array $options = array())
public function __construct()
{
if (!isset($options['max'])) {
throw new \InvalidArgumentException('Missing max option.');
}
list($width, $height) = $options['max'];
$size = $image->getSize();
$origWidth = $size->getWidth();
$origHeight = $size->getHeight();
if ($origWidth > $width || $origHeight > $height) {
$widthRatio = $width / $origWidth;
$heightRatio = $height / $origHeight;
$ratio = $widthRatio > $heightRatio ? $widthRatio : $heightRatio;
parent::__construct('max', 'by', false);
}
$filter = new Resize(new Box($origWidth * $ratio, $origHeight * $ratio));
protected function calcAbsoluteRatio($ratio)
{
return 1 - ($ratio > 1 ? $ratio - floor($ratio) : $ratio);
}
return $filter->apply($image);
}
protected function isImageProcessable($ratio)
{
return $ratio < 1;
}
return $image;
public function getName(): string
{
return 'downscale';
}
}
<?php
/**
* Based on https://github.com/liip/LiipImagineBundle/blob/2.0/Imagine/Filter/Loader
* License attached in LICENSE.md
*/
namespace Avris\Micrus\Imagine\Filter;
use Imagine\Image\ImageInterface;
use Imagine\Image\ImagineInterface;
final class FlipFilterLoader implements LoaderInterface
{
public function load(ImagineInterface $imagine, ImageInterface $image, array $options = array()): ImageInterface
{
switch ($options['axis'] ?? null) {
case 'x':
case 'horizontal':
return $image->flipHorizontally();
case 'y':
case 'vertical':
return $image->flipVertically();
default:
throw new \InvalidArgumentException('The "axis" option must be set to "x", "horizontal", "y", or "vertical".');
}
}
public function getName(): string
{
return 'flip';
}
}
<?php
/**
* Based on https://github.com/liip/LiipImagineBundle/blob/2.0/Imagine/Filter/Loader
* License attached in LICENSE.md
*/
namespace Avris\Micrus\Imagine\Filter;
use Imagine\Filter\Advanced\Grayscale;
use Imagine\Image\ImageInterface;
use Imagine\Image\ImagineInterface;
/**
* GrayscaleFilterLoader - apply grayscale filter.
*
* @author Gregoire Humeau <gregoire.humeau@gmail.com>
*/
final class GrayscaleFilterLoader implements LoaderInterface
{
public function load(ImagineInterface $imagine, ImageInterface $image, array $options = array()): ImageInterface
{
$filter = new Grayscale();
return $filter->apply($image);
}
public function getName(): string
{
return 'grayscale';
}
}
<?php
/**
* Based on https://github.com/liip/LiipImagineBundle/blob/2.0/Imagine/Filter/Loader
* License attached in LICENSE.md
*/
namespace Avris\Micrus\Imagine\Filter;
use Imagine\Image\ImageInterface;
use Imagine\Image\ImagineInterface;
class InterlaceFilterLoader implements LoaderInterface
final class InterlaceFilterLoader implements LoaderInterface
{
/**
* {@inheritdoc}
*/
public function load(ImageInterface $image, array $options = array())
public function load(ImagineInterface $imagine, ImageInterface $image, array $options = array()): ImageInterface
{
$mode = ImageInterface::INTERLACE_LINE;
if (!empty($options['mode'])) {
......@@ -20,4 +21,9 @@ class InterlaceFilterLoader implements LoaderInterface
return $image;
}
public function getName(): string
{
return 'interlace';
}
}
The MIT License (MIT)
Copyright © 2010-2016 Liip & Contributors <https://github.com/liip/LiipImagineBundle/graphs/contributors>
Copyright © 2004-2011 Bulat Shakirzyanov <https://github.com/avalanche123/AvalancheImagineBundle/graphs/contributors>
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
......@@ -3,16 +3,11 @@
namespace Avris\Micrus\Imagine\Filter;
use Imagine\Image\ImageInterface;
use Imagine\Image\ImagineInterface;
interface LoaderInterface
{
/**
* Loads and applies a filter on the given image.
*
* @param ImageInterface $image
* @param array $options
*
* @return ImageInterface
*/
public function load(ImageInterface $image, array $options = array());
public function load(ImagineInterface $imagine, ImageInterface $image, array $options = []): ImageInterface;
public function getName(): string;
}
<?php
/**
* Based on https://github.com/liip/LiipImagineBundle/blob/2.0/Imagine/Filter/Loader
* License attached in LICENSE.md
*/
namespace Avris\Micrus\Imagine\Filter;
use Imagine\Image\ImageInterface;
use Imagine\Image\ImagineInterface;
use Imagine\Image\Point;
class PasteFilterLoader implements LoaderInterface
final class PasteFilterLoader implements LoaderInterface
{
/**
* @var ImagineInterface
*/
protected $imagine;
/**
* @var string
*/
protected $rootPath;
/** @var string */
private $rootPath;
public function __construct(ImagineInterface $imagine, $rootPath)
public function __construct(string $envProjectDir)
{
$this->imagine = $imagine;
$this->rootPath = $rootPath;
$this->rootPath = $envProjectDir;
}
/**
* @see Liip\ImagineBundle\Imagine\Filter\Loader\LoaderInterface::load()
*/
public function load(ImageInterface $image, array $options = array())
public function load(ImagineInterface $imagine, ImageInterface $image, array $options = array()): ImageInterface
{
list($x, $y) = $options['start'];
$destImage = $this->imagine->open($this->rootPath.'/'.$options['image']);
$x = $options['start'][0] ?? null;
$y = $options['start'][1] ?? null;
$destImage = $imagine->open($this->rootPath.'/'.$options['image']);
return $image->paste($destImage, new Point($x, $y));
}
public function getName(): string
{
return 'paste';
}
}
<?php
/**
* Based on https://github.com/liip/LiipImagineBundle/blob/2.0/Imagine/Filter/Loader
* License attached in LICENSE.md
*/
namespace Avris\Micrus\Imagine\Filter;
use Imagine\Exception\InvalidArgumentException;
use Imagine\Image\ImageInterface;
use Liip\ImagineBundle\Imagine\Filter\RelativeResize;
use Imagine\Image\ImagineInterface;
/**
* Loader for this bundle's relative resize filter.
*
* @author Jeremy Mikola <jmikola@gmail.com>
*/
class RelativeResizeFilterLoader implements LoaderInterface
final class RelativeResizeFilterLoader implements LoaderInterface
{
/**
* {@inheritdoc}
*/
public function load(ImageInterface $image, array $options = array())
public function load(ImagineInterface $imagine, ImageInterface $image, array $options = array()): ImageInterface
{
if (list($method, $parameter) = each($options)) {
$filter = new RelativeResize($method, $parameter);
foreach ($options as $method => $parameter) {
if (!in_array($method, ['heighten', 'increase', 'scale', 'widen'])) {
throw new InvalidArgumentException(sprintf('Unsupported method: ', $method));
}
return $filter->apply($image);
return $image->resize(call_user_func([$image->getSize(), $method], $parameter));
}
throw new InvalidArgumentException('Expected method/parameter pair, none given');
}
public function getName(): string
{
return 'relativeResize';
}
}
<?php
/**
* Based on https://github.com/liip/LiipImagineBundle/blob/2.0/Imagine/Filter/Loader
* License attached in LICENSE.md
*/
namespace Avris\Micrus\Imagine\Filter;
use Imagine\Filter\Basic\Resize;
use Imagine\Image\Box;
use Imagine\Image\ImageInterface;
use Imagine\Image\ImagineInterface;
/**
* Loader for Imagine's basic resize filter.
*
* @author Jeremy Mikola <jmikola@gmail.com>
*/
class ResizeFilterLoader implements LoaderInterface
final class ResizeFilterLoader implements LoaderInterface
{
/**
* {@inheritdoc}
*/
public function load(ImageInterface $image, array $options = array())
public function load(ImagineInterface $imagine, ImageInterface $image, array $options = array()): ImageInterface
{
list($width, $height) = $options['size'];
$width = $options['size'][0] ?? null;
$height = $options['size'][1] ?? null;
$filter = new Resize(new Box($width, $height));
return $filter->apply($image);
}
public function getName(): string
{
return 'resize';
}
}
<?php
/**
* Based on https://github.com/liip/LiipImagineBundle/blob/2.0/Imagine/Filter/Loader
* License attached in LICENSE.md
*/
namespace Avris\Micrus\Imagine\Filter;
use Imagine\Image\ImageInterface;
use Imagine\Image\ManipulatorInterface;
use Imagine\Image\ImagineInterface;
/**
* Loader for Imagine's basic rotate method.
*
* @author Bocharsky Victor <bocharsky.bw@gmail.com>
*/
class RotateFilterLoader implements LoaderInterface