Make image manipulation simple using Imagine in a Symfony2 application
Charles Pourcel7 min read
I recently read an interesting article on the pros and cons of two of the most known PHP image manipulation libraries: GD and ImageMagick. As interesting as this reading was, it did not mention a very useful object oriented image manipulation library and its corresponding bundle to all those waiting for the simplest solution to edit, transform and store pictures on their Symfony2 web application: The Imagine library and the LiipImagineBundle. I will try to fill this gap by explaining how these two tools can greatly help you managing pictures easily.
The Imagine library
This library is the perfect PHP 5.3+ object oriented tool to interact with your pictures in a simple way. It provides a common implementation for three of the most known image libraries (GD2, IMagick, Gmagick). So, pick your favorite one (using for example the aforementioned article), install Imagine using the official (well written) documentation and keep going! You still have a lot to discover…
The basics
Let’s start with opening an existing picture using Imagine:
$imagine = new Imagine\Gd\Imagine(); //or $imagine = new Imagine\Imagick\Imagine();
$image = $imagine->open(‘/path/to/image.jpg’);
Note: Depending on the name of the driver you are using, you will have to switch the namespace of the Imagine class to use: one of the rare drawbacks of Imagine. But let’s not worry to much as this drawback can be overcome (check the Imagine Bundle main features below).
As you may have already noticed, Imagine follows some very simple and intuitive rules. For example, if you want to resize or crop an image (one of the most common and basic functionalities in Imagine) you will have to deal with “coordinates” and “dimensions”. To crop you will need to specify the top left corner coordinates and the desired dimensions of the crop. You will therefore have to implement the PointInterface as follows:
//Indicates coordinates (x: 15, y: 30) $coordinates = new Imagine\Image\Point(15, 30);
Note: The coordinates always start from the top left corner and cannot have negative values.
And to specify dimensions implement the BoxInterface like this:
//Indicates dimensions (width: 400, height: 300) $dimensions = new Imagine\Image\Box(400, 300);
Since most of these code excerpts are actually extracted from the official Imagine documentation I will focus on the main interests of the library rather than duplicating code examples you could easily find on the corresponding page in the official documentation.
Overview of the main features
- Drawing shapes: “Drawer” class allows you to easily draw geometric figures or add text using a specific font you like
- Editing colors: Helps you define and alter colors on your pictures using a different “Palette” depending on your needs (CMYK, RGB or more recently Grayscale)
- Managing layers: If you are not using GD2, you can use “Layers” as an object oriented way to interact with picture layers. Among other things it can help you flatten a layered picture or create animated gif image.
- Adding effects: Depending on the driver you picked (and at the time this article has been written), you can use all or a subset of the “Effects” Imagine puts at your disposal (Gamma, Negative, Grayscale, Colorize, Sharpen, Blur)
- Applying filters and transformations: Many “Filters” are available in Imagine (ApplyMask, Copy, Crop, Fill, FlipHorizontally, FlipVertically, Paste, Resize, Rotate, Save, Show, Strip and Thumbnail). What each of them do is well explained in the “ManipulatorInterface”. Besides applying filters directly to an image, Imagine provides a way to stack filters into a “Transformation” instance which can be applied later on one or multiple images (very convenient to decouple your image manipulation from the filter creation).
The Imagine library is already considered a reference when it comes to clean and decoupled image manipulation. I have briefly described its main features above but it contains more including some advanced “Filters” and a clean “Exception” management system. As you can see, it already covers most of the basic and even advanced use cases, and since it has been built to be easily extensible, you will not have too much trouble implementing any missing behaviour.
For those of you wanting to integrate it into a Symfony2 application, there is a Symfony2 bundle for that: the LiipImagineBundle. It uses the Imagine library and completes it with additional useful features.
The Imagine bundle
The LiipImagineBundle wraps the Imagine library into a Symfony2 bundle and includes it into a larger (and yet simple) workflow. As the library itself, this bundle has been made highly extendable and therefore you can customize almost each part of the workflow’s behaviour.
The basic workflow
1. First, you will need to install and configure your bundle. Lucky for you, the Liip team thought about that and provides a quite understandable semantic configuration for the bundle.
2. Then use one of the Twig filters you defined in your configuration by calling it on a picture asset in one of your Twig templates.
3. If you now request the corresponding page (for example to display it in your browser) the ImagineBundle will detect if the filter has already been applied to this specific picture:
- If yes: the already generated (and cached) picture will be retrieved and provided to the template
- If not: the filtered picture will be generated and stored and then provided to the template, in a completely transparent way for the end user.
Overview of its main features
- Semantic bundle configuration: Already well documented and with sensible defaults, this configuration allows you to use the bundle almost out of the box. Besides by defining here the driver you use, you get rid of the Imagine aforementioned drawback on “driver specific Imagine namespaces”.
- Twig and PHP template filter helper: As explained in the “Basic usage” section of the documentation homepage, each filter defined in the bundle configuration will then be available in your Twig templates using the “imagine_filter” Twig filter, making it easy to apply filters you configured (also available for PHP templates).
- Controller as a service: If you need to apply filters anywhere else than in templates you can, using the available service controller.
- Image loaders: You can customize the way you retrieve your images. The default configuration will provide you with a basic filesystem loader but you also have two other built-in loaders: Stream and MongoDB GridFS. The documentation also explains how you can chain data transformers to a custom loader to get an image from virtually any kind of file (PDF in the example).
- Cache resolvers: The bundle uses its own caching system. It provides you with a bunch of different resolvers out of the box (Amazon S3, AWS, with a Filesystem default cache resolver) and allows you to add your own and extend the existing ones.
Summary
Using GD or Imagick to transform the pictures of your Symfony2 application (well even any web application) is good but using the Imagine library is better. It provides you with an object oriented and extensible code structure through a simple but powerful API. Using it allows you to easily switch from GD to Imagick for instance, if you started using GD and find a major drawback later. Still not convinced? Think about the next time a new developer will have to read and maintain the code you are currently so proud of.
And if you are in a Symfony2 application context you can get an even better solution depending on your needs: the LiipImagineBundle. Based on Imagine, it adds many useful features such as cache handling, template integration, many different strategies to store and retrieve your images, and all of this is highly customizable. Could you reasonably ask for more (except for coffee)?
Finally, if you want to know more on which driver you should use and see a basic but functional example of how Imagine can be used you can check this presentation I recently made. Its code focuses on the advantages of the “Transformation” approach over the more basic “Filter” one.