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.