Vithun's Blog Notes on Software

Grails Image Crop Using Jcrop

You might have seen many websites nowadays have a profile picture. And when you upload a profile picture, you are usually prompted to crop the picture (sometimes with certain forced resolution), and it is this cropped picture that is used from then onwards. I was required to implement a similar functionality in the Grails project I’m working on. I decided to use Jcrop for the JavaScript side of the cropping mechanism as it was based on jQuery and we were already using jQuery in our application. Here, I illustrate how I did the job.

I first downloaded Jcrop from here. The downloaded zip file contains, among other things, two folders: /css and /js. I copied the contents of these two folders into /web-app/css and /web-app/js/jcrop directories (respectively) of my grails application.

Now I have the required tools for cropping. I had previously saved the image uploaded by a user (see here to read about file uploads in grails). In my gsp (view), I first render this image and give it an id.

<img id="picture" src="${resource(file: imageSource)}" />

I then added the following within a script tag in the same gsp to initialize Jcrop:

$(function() {
    $('#picture').Jcrop({
        aspectRatio: 1,
        setSelect: [0, 0, 100, 100],
        minSize: [100, 100],
        onSelect: updateCoords,
        onChange: updateCoords
    });
});

Since I use Jcrop (and in turn jQuery), the required javascript and css files should be included. So, I added the following lines within the head section of the gsp:

<link rel="stylesheet" href="${resource(dir:'css',file:'jquery.Jcrop.css')}" />
<g:javascript plugin="jquery" />
<g:javascript src="jcrop/jquery.Jcrop.min.js" />

Now if you look back at the script to initialize Jcrop, you can see that I have mentioned updateCoords as the method to be called on selecting or changing the cropper element. So we should now provide the updateCoords method. I added this within a script tag in the head section of my gsp:

function updateCoords(c) {
    $('#x1').val(c.x);
    $('#y1').val(c.y);
    $('#x2').val(c.x2);
    $('#y2').val(c.y2);
}

x1, y1, x2 and y2 above are ids of hidden fields that will save the co-ordinates of the crop layout. This is how I have included them in my gsp within a form:

<g:form action="savePicture">
    <g:hiddenField name="x1" value="0" />
    <g:hiddenField name="y1" value="0" />
    <g:hiddenField name="x2" value="100" />
    <g:hiddenField name="y2" value="100" />
    <g:submitButton name="Crop and Save" />
</g:form>

Now, when the user adjusts the cropper and presses the submit button, the co-ordinates of this cropped layout will be available to the controller method. Using this we can crop the image in our controller (using various ways) and save it. This is one way to do it:

def savePicture = {
    def x1 = params.x1 as Integer
    def y1 = params.y1 as Integer
    def x2 = params.x2 as Integer
    def y2 = params.y2 as Integer
    File tempPicture = new File("/path/to/original/file")
    BufferedImage image = ImageIO.read(tempPicture)
    BufferedImage croppedImage = image.getSubimage(x1, y1, x2 - x1, y2 - y1)
    File profilePicture = new File("/path/to/new/cropped/file")
    ImageIO.write(croppedImage, "jpg", profilePicture);
    FileUtils.deleteQuietly(tempPicture)
}

Note: The fully qualified names of classes used above are java.io.File, java.awt.image.BufferedImage, javax.imageio.ImageIO and org.apache.commons.io.FileUtils.