Symfony2: Using Assetic for Image Optimisation
Symfony2 uses Assetic for asset management, I have previously written about its use for JavaScript and CSS files. In this post I am going to look at another of its uses, optimising images. Amongst its many filters, Assetic currently has four which can be used for on-the-fly image optimisation. This way you can get the benefits of smaller file sizes without having to use an image editor to process each image. The results are cached and can be saved for production so there is no performance hit for your end users.
I am going to look at two of these filters in this post OptiPNG and jpegtran, There is also support for PNGOUT and Jpegoptim. The former two were available for install through my package manager so they won.
OptiPNG
This can be used to compress PNG images, all you need to do is add this to the Assetic section in your app's config.yml file:
assetic:
filters:
optipng:
bin: /path/to/optipng
apply_to: "\.png$"
The bin setting is only needed if OptiPNG is not in the default location of /usr/bin/optipng
.
All PNG files that you include in your Twig templates will now be automatically optimise providing you use Assetic to the serve them. This means including them in this way:
{% image output="/images/test.png"
'@LimeThinkingSpringBundle/Resources/images/test.png'
%}
<img src="{{ asset_url }}" alt="The alt text" />
{% endimage %}
The default optimisation level used by OptiPNG depends on the version, you can get more information on this and what the levels actually mean in the options section of the OptiPNG manual. You can set the level in the config.yml:
assetic:
filters:
optipng:
bin: /path/to/optipng
apply_to: "\.png$"
level: 3
Setting the level option is only available in the latest version of Symfony2 from Git at the time of writing and was not in the latest release (beta 2). Edit: It is possible with the beta 3 release now available.
jpegtran
jpegtran can be used for JPEG optimisation, in this case there are two main things you can do. The first is to optimise the file by removing meta information stored in the file that is not used for display. To do this you just need to specify optimisation in the config, since jpegtran has other uses:
assetic:
filters:
jpegtran:
bin: /path/to/jpegtran
apply_to: "\.jpe?g$"
optimisation: true
and the following in Twig:
{% image output="/images/test.jpg"
'@LimeThinkingSpringBundle/Resources/images/test.jpg'
%}
<img src="{{ asset_url }}" alt="The alt text" />
{% endimage %}
Note the use of apply_to: ".jpe?g$"
in the config to target both .jpg amd .jpeg file extensions. You may find this optimisation makes no difference though as some image editors will strip out this information by default when saving as a JPEG, this was the case in Gimp for me anyway.
You can also use jpegtran to convert JPEGs to progressive JPEGs, this does not always make the file size smaller though so it is not an automatic gain, there is a lot more information about this in this YUI blog article on image optimisation. To convert all your JPEGs change the config settings to
assetic:
filters:
jpegtran:
bin: /path/to/jpegtran
apply_to: "\.jpe?g$"
progressive: true
Again, at the time of writing this, both of these options will only work with the latest Symfony2 from git and not with beta 2. Edit: they will work with the beta 3 release now available.
Saving for Production
For production, rather than using Assetic to serve the files up as requested, checking the cache each time, it is better to save them as static files. In fact by default Assetic is not routed to in the prod environment so the images will not appear if you do not do this. Dumping the files is easy though using the Symfony2 console:
app/console --env=prod assetic:dump
The files will now be served up as static files and PHP will not be used at all. You just need to remember to regenerate the files whenever they have changed. Making this part of an automated build process with a tool like Phing or Ant will ensure this happens.