Cropping an image with Jcrop

Image editing and manipulation can sometimes be a difficult thing to implement in our application. Using Laravel and the Jcrop JavaScript library, we can make the task much simpler.

Tip

Downloading the example code

You can download the example code files for all Packt books you have purchased from your account at http://www.packtpub.com. If you purchased this book elsewhere, you can visit http://www.packtpub.com/support and register to have the files e-mailed directly to you.

Getting ready

We need to download the Jcrop library from http://deepliquid.com/content/Jcrop_Download.html and unzip it. Put the file jquery.Jcrop.min.js into our public/js directory, and the jquery.Jcrop.min.css and Jcrop.gif files into our public/css directory. We'll use the Google CDN version of jQuery. We also need to make sure we have the GD library installed on our server, so we can do image manipulation. In our public directory, we'll need an images folder to store the images, and should have the permission set for it to be writable.

How to do it...

Follow these steps to finish this recipe:

  1. Let's create a route in our routes.php file to hold our form:
    Route::get('imageform', function()
    {
        return View::make('imageform');
    });
  2. Create the form for uploading an image, in app/views with the filename imageform.php:
    <h1>Laravel and Jcrop</h1>
    <?= Form::open(array('files' => true)) ?>
    <?= Form::label('image', 'My Image') ?>
    <br>
    <?= Form::file('image') ?>
    <br>
    <?= Form::submit('Upload!') ?>
    <?= Form::close() ?>
  3. Make a route to handle the image upload and validation:
    Route::post('imageform', function()
    {
        $rules = array(
            'image' => 'required|mimes:jpeg,jpg|max:10000'
        );
    
        $validation = Validator::make(Input::all(), $rules);
    
        if ($validation->fails())
        {
            return Redirect::to('imageform')->withErrors($validation);
        }
        else
        {
            $file = Input::file('image');
            $file_name = $file->getClientOriginalName();
            if ($file->move('images', $file_name))
            {
                return Redirect::to('jcrop')->with('image',$file_name);
            }
            else
            {
                return "Error uploading file";
            }
        }
    });
  4. Create a route for our Jcrop form:
    Route::get('jcrop', function()
    {
        return View::make('jcrop')->with('image', 'images/'. Session::get('image'));
    });
  5. Make a form, where we can crop the image, in our app/views directory with the filename jcrop.php:
    <html>
        <head>
            <title>Laravel and Jcrop</title>
            <meta charset="utf-8">
            <link rel="stylesheet" href="css/jquery.Jcrop.min.css" />
            <script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
            <script src="js/jquery.Jcrop.min.js"></script>
        </head>
        <body>
            <h2>Image Cropping with Laravel and Jcrop</h2>
            <img src="<?php echo $image ?>" id="cropimage">
    
            <?= Form::open() ?>
            <?= Form::hidden('image', $image) ?>
            <?= Form::hidden('x', '', array('id' => 'x')) ?>
            <?= Form::hidden('y', '', array('id' => 'y')) ?>
            <?= Form::hidden('w', '', array('id' => 'w')) ?>
            <?= Form::hidden('h', '', array('id' => 'h')) ?>
            <?= Form::submit('Crop it!') ?>
            <?= Form::close() ?>
    
            <script type="text/javascript">
                $(function() {
                    $('#cropimage').Jcrop({
                        onSelect: updateCoords
                    });
                });
                function updateCoords(c) {
                    $('#x').val(c.x);
                    $('#y').val(c.y);
                    $('#w').val(c.w);
                    $('#h').val(c.h);
                };
            </script>
        </body>
    </html>
  6. Create a route that will process the image and display it:
    Route::post('jcrop', function()
    {
        $quality = 90;
    
        $src  = Input::get('image');
        $img  = imagecreatefromjpeg($src);
        $dest = ImageCreateTrueColor(Input::get('w'),
            Input::get('h'));
    
        imagecopyresampled($dest, $img, 0, 0, Input::get('x'),
            Input::get('y'), Input::get('w'), Input::get('h'),
            Input::get('w'), Input::get('h'));
        imagejpeg($dest, $src, $quality);
    
        return "<img src='" . $src . "'>";
    });
    

How it works...

We start with a basic file upload; to make it easier, we'll only be using .jpg files. We use the validation to check for the image type as well as making sure the file size is under 10,000 kilobytes. After the file is uploaded, we send the path to our Jcrop route.

In the HTML for the Jcrop route, we create a form with hidden fields that will hold the dimensions of the cropping. The JavaScript function updateCoords takes the cropping dimensions and updates the values of those hidden fields.

When we're done cropping, we submit the form and our route gets the POST data. The image is run through the GD library and cropped, based on the dimensions that were posted. We then overwrite the image and display the updated and cropped file.

There's more...

While this recipe only covers cropping a jpg image, adding in gif and png images wouldn't be very difficult. We'd just need to get the file extension by passing the file name to Laravel using File::extension(). Then, we could either do a switch or if statement to use the appropriate PHP function. For example, if the extension is .png, we'd use imagecreatefrompng() and imagepng(). More information can be found at http://www.php.net/manual/en/ref.image.php.