Fork me on GitHub

How to set up Jawr to generate base64 encoded images

Overview

The goal of this tutorial is to explain how to set up Jawr to generate base64 encoded images in the CSS bundles.
As you know, one of the main rules to increase performance in your web application is to reduce the number of HTTP requests.
One of the way to achieve this is to create image sprites, which combines multiple images in a single image. Jawr already provides a way to handle this case. Please take a look at the Jawr sprite documentation
So, with sprite all the reference of the images defined in your CSS will point to one or a few sprites, which will reduce the number of HTTP requests.
There is another way to reduce the number of HTTP requests by encoding the content of the images in base64.
It means that in you generated bundle, the URL references to the image will be replaced by the content of your images.
So your images will be embedded in your CSS. This will increase the size of your CSS files, but will reduce drastically the number of HTTP requests, as there will be no HTTP request required for your images.

Here is an example of the base64 encoding of a CSS image reference:

        background-image:url("../../images/logo.png");

Will be transformed like this :

        background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAA...AAElFTkSuQmCC");

So here, the content of the image is set directly in the CSS file.
If we take a look at the generated content, we have:

  • data - the name of the scheme
  • image/png - the content type
  • base64 - the type of encoding used to encode the data
  • iVBORw0KGgo….lFTkSuQmCC - the encoded image content

MHTML to the rescue for IE6 and IE7

The base64 encoded resources is handled by all major browser except for IE6 and IE7, but fortunately there is a workaround for this, which is the use of MHTML references.

MHTML stands for MIME HTML. It’s a file format which allows you to combine multiple contents like images in one file.

So for these browsers, instead of using the data URI, we will use MHTML references. Here is an example of MHTML use in a CSS file :

/*
Content-Type: multipart/related; boundary="_ANY_SEPARATOR"

--_ANY_SEPARATOR
Content-Type:image/png
Content-Location:logo
Content-Transfer-Encoding:base64

iVBORw0KGgoAAAANSUhEUgAAA...AAElFTkSuQmCC
*/

#logo {
  background-image: url(mhtml:http://www.mycomp.com/css/home.css!logo);
}

Here we can see that the MHTML part is set at the top of the file in a commented section.
In the first line, we define the boundary used to separate the declaration of each resource.
Then for each resource, we have some information: - Content-Type : the content type - Content-Transfer-Encoding : the encoding used for this resource - Content-Location : the property which will allow us to reference this particular resource in the CSS file

If we take a look at the image URL, we have:

  • mhtml - the name of the scheme
  • http://www.mycomp.com/css/home.css - the absolute URL of the current CSS
  • ! - a separator
  • logo - the identifier of our resource in the MHTML part.

As we are using absolute URL, Jawr will generate a bundle version for HTTP requests and another one for HTTPS ones. Otherwise IE will raise an warning saying that you have SSL and non SSL content in your page.

  • How Jawr handles the absolute URL for MHTML references

As we’ve seen, we must use absolute URL to reference the current CSS which contains the MHTML content. As you know, Jawr generates the bundle at server startup or at build time.
At this stage, Jawr doesn’t know the application absolute URL. So Jawr will use a placeholder named JAWR_BUNDLE_PATH in the generated bundle.

  • For bundles generated at server startup

The resolution of the absolute path will be done at runtime for the first request, then it will be put in cache.

  • For bundles generated at build time

When you generate the Jawr bundles at build time, if you have defined absolute paths for the following properties :

- jawr.url.contextpath.override : The context path of the application for HTTP - jawr.url.contextpath.ssl.override : The context path of the application for HTTPS

The values of these properties will be used to reference the absolute path of the CSS bundles.

Otherwise Jawr will set the reference to the CSS bundles and will let a place holder for the web application URL.

        #logo {
          background-image: url(mhtml:http://{WEBAPP_URL}/gzip_a08d115cf05a5ef92f289b0a7510057f.ie6@/css/home.css!logo);
        }

You will have to process the content of these bundles to replace WEBAPP_URL by the right web application URL.

Set up Jawr in your project

Please check the “quickstart” tutorial for the instruction about Jawr installation in your project.

Jawr configuration file

Since version 3.3, Jawr is able to generate variant bundles from postprocessors. This means that by just setting the correct postprocessor in your bundle, Jawr will generate the content of the CSS for the different browsers.

The id of the postprocessor to use is base64ImageEncoder.\ There are 3 properties related to the base64 encoding available in the Jawr configuration :

Property name Type Purpose Default value
jawr.css.postprocessor.base64ImageEncoder.encode.by.default Boolean Enable/disable the base64 image encoding by default true
jawr.css.postprocessor.base64ImageEncoder.maxFileLength Integer [ The maximum size (in bytes) of the image to encode in base64 30000
jawr.css.postprocessor.base64ImageEncoder.encode.sprite Boolean Enable/disable the base64 image encode on generated sprite image False
  • The property jawr.css.postprocessor.base64ImageEncoder.encode.by.default will define if the postprocessor should encode the CSS image in base64 by default or not. If the jawr.css.postprocessor.base64ImageEncoder.encode.by.default is set to true, by default all the CSS images will be encoded, except if the are followed by the annotation : jawr:base64-skip

            .style1 {
                    background: url('../img/img1.png') ; 
                    height : 20px;
            }
            .style2 {
                    background: url('../img/img2.png') ; /** jawr:base64-skip */
                    height : 20px;
            }
    

In this example, if jawr.css.postprocessor.base64ImageEncoder.encode.by.default is set to true, only the first image URL will be encoded, the second one will be skipped.

If the jawr.css.postprocessor.base64ImageEncoder.encode.by.default is set to false, by default all the CSS images will be encoded, except if the are followed by the annotation : jawr:base64

            .style1 {
                    background: url('../img/img1.png') ; 
                    height : 20px;
            }
            .style2 {
                    background: url('../img/img2.png') ; /** jawr:base64 */
                    height : 20px;
            }

In this example, if jawr.css.postprocessor.base64ImageEncoder.encode.by.default is set to false, only the second image URL will be encoded, the first one will be skipped.

  • The property jawr.css.postprocessor.base64ImageEncoder.maxFileLength will define the maximum image file size allowed to encode the image. If an image has a size greater than the maxFileLength> property, the post processor will not encode this image.\ You should be aware that there is a limitation in IE8, which is not able to handle base64 encoded image which are greater than 32Kb.
  • The property jawr.css.postprocessor.base64ImageEncoder.encode.sprite defines if the post processor must encode sprite images generated by Jawr using Smartsprites or not.

In our example, we will define our bundle like this :

        jawr.css.bundle.base64Bundle.id=/css/base64Bundle.css
        jawr.css.bundle.base64Bundle.mappings=/css/one.css
        jawr.css.bundle.base64Bundle.bundlepostprocessors=cssminify,base64ImageEncoder
        jawr.css.bundle.base64Bundle.filepostprocessors=base64ImageEncoder

Warning:
You should noticed that we have set the CSS minifier before our base64ImageEncoder. We use this configuration because the CSS minifier would have removed the MHTML part which in comment.

It is also important to set the base64ImageEncoder not only as a bundle post processor but as a file post processor AND a bundle post processor, because this post processor needs to prepend the MHTML part to the bundle and also to rewrite the URL for each file.
So never use this post processor only as a bundle post processor or only as a file post processor.

To use the base64ImageEncoder with composite bundle, you need to define the base64ImageEncoder as file postprocessor for the child bundles, and as bundle postprocessor for the composite bundle.

jawr.css.bundle.base64CompositeBundle.id=/css/compositeBundle.css
jawr.css.bundle.base64CompositeBundle.composite=true
jawr.css.bundle.base64CompositeBundle.child.names=childOne,childTwo
jawr.css.bundle.base64CompositeBundle.filepostprocessors=none
jawr.css.bundle.base64CompositeBundle.bundlepostprocessors=cssminify,base64ImageEncoder

jawr.css.bundle.childOne.mappings=/css/one.css
jawr.css.bundle.childOne.filepostprocessors=base64ImageEncoder
jawr.css.bundle.childOne.bundlepostprocessors=none
jawr.css.bundle.childTwo.mappings=/css/dir/two.css
jawr.css.bundle.childTwo.filepostprocessors=base64ImageEncoder
jawr.css.bundle.childTwo.bundlepostprocessors=none

Test the base64 image encoding

Create an /img directory at the root of your web application and copy 2 png images in it and rename them to img1.png and img2.png .

Write a test CSS file named /css/one.css and add the following content:

.style1 {
        background: url('../img/img1.png') ;
        height : 20px;
}
.style2 {
        background: url('../img/img2.png') ; /** jawr:base64-skip */
        height : 20px;
}

Here you should noticed that we have added an annotation to image reference of /img/img2.png : /** jawr:base64-skip */. This annotation tells to the base64ImageEncoder to not encode this image in the process but rewrites the URL so it goes through the Jawr image servlet.

Write a test JSP page and add the following content:

<%@ taglib uri="http://jawr.net/tags" prefix="jawr" %>
<%@ page contentType="text/html;charset=UTF-8" %>
<html>
<head>
        <jawr:style src="/css/base64Bundle.css" />
</head>
<body>
        <div class="style1"></div>
        <div class="style2"></div>
</body>
</html>

Deploy your application to a server. Open the JSP you created with a firefox browser. If you take a look to the content of the CSS bundle, you should find the data URI reference of your image. You can also check with Firebug that there is no extra HTTP request made for your image.

Then open the JSP you created with an IE6 or IE7 browser. If you take a look to the content of the CSS bundle, you should find the MHTML part at the top of your bundle.