Creating images on the fly and cropping

Posted 26th Jan 2018

#api #bootstrap4 #images

Creating images on the fly is awesome. Need an image with different dimensions? Cropped? Lower quality? No problem, just do it right in your template.

I presume by now you've looked over the other tutorials and you know about templates, fields and creating pages.

Home (hover) > New

Create an 'Image variations' page using the 'basic-page' template. This template has a single image field called 'featuredImage'. If you need a refresher, you can check out details about the images field type. Upload an image in there so we can do some stuff with it in our template file.

Basic page with single image field
Basic page with single image field Zoom

/site/templates/basic-page.php

We'll just create a basic skeleton of the page using the Bootstrap 4 CDN.

<?php namespace ProcessWire; ?>

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">

  <title>
    <?php
      // print the current page field altTitle value
      // if blank or not present, print title field value
      echo $page->get("altTitle|title");
    ?>
  </title>

  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/css/bootstrap.min.css" integrity="sha384-/Y6pD6FV/Vv2HJnA6t+vslU6fwYXjCFtcEpHbNJ0lyAFsXTsjBbfaDjzALeQsN6M" crossorigin="anonymous">

</head>

<body>


  -- OUR IMAGE CODE WILL GO HERE --


  <script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.11.0/umd/popper.min.js" integrity="sha384-b/U6ypiBEHpOf/4+1nzFpr53nxSS+GLCkfwBdFNTxtclqqenISfwAzpKaMNFNmj4" crossorigin="anonymous"></script>
  <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/js/bootstrap.min.js" integrity="sha384-h0AbiXch4ZDo7tp9hKZ4TsHbi047NrKGLO3SEJAg45jXxnGIfYzk4Si90RDIqNm1" crossorigin="anonymous"></script>

</body>

</html>

Ok, let's create some images! Replace the '-- OUR IMAGE CODE WILL GO HERE --' section with:

<?php
  // if page has single image field called 'featuredImage' and has an
  // image uploaded to it. This alternate syntax 'if' wraps this whole block of PHP/HTML
  if ($page->featuredImage):
    
  // save some typing...
  $image = $page->featuredImage;

  // create four images using image resize functions
  $widthOnly = $image->width(150);
  $heightOnly = $image->height(150);
  $square = $image->size(150, 150);
  $rec = $image->size(150, 75);
?>

<div class="container py-5">
  <div class="row">

    <div class="col">
      <img src="<?php echo $widthOnly->url; ?>" />
      <p>
        Changed width only</br>
        Width: <?php echo $widthOnly->width() . "px"; ?></br>
        Height: <?php echo $widthOnly->height() . "px"; ?>
      </p>
    </div>

    <div class="col">
      <img src="<?php echo $heightOnly->url; ?>" />
      <p>
        Changed height only</br>
        Width: <?php echo $heightOnly->width() . "px"; ?></br>
        Height: <?php echo $heightOnly->height() . "px"; ?>
      </p>
    </div>

    <div class="col">
      <img src="<?php echo $square->url; ?>" />
      <p>
        Changed width and height</br>
        Width: <?php echo $square->width() . "px"; ?></br>
        Height: <?php echo $square->height() . "px"; ?>
      </p>
    </div>

    <div class="col">
      <img src="<?php echo $rec->url; ?>" />
      <p>
        Changed width and height</br>
        Width: <?php echo $rec->width() . "px"; ?></br>
        Height: <?php echo $rec->height() . "px"; ?>
      </p>
    </div>

  </div>
</div>

<?php
  // close alternate syntax if statement
  endif;
?>

Now view your page:

Images created on the fly
Images created on the fly Zoom

Good job! Now you've created some images on the fly. Now they're created, they're in your filesystem at:

/YOUR-SITE-ROOT/site/assets/files/PAGE-ID/

The page id is the ID of the page where the image upload field is, so for example, my files are at:

C:\laragon\www\demosite.pwtuts\site\assets\files\1073

Inside there, I now have:

yet_another_cat_photo.0x150.jpeg
yet_another_cat_photo.0x260.jpeg
yet_another_cat_photo.150x0.jpeg
yet_another_cat_photo.150x75.jpeg
yet_another_cat_photo.150x150.jpeg
yet_another_cat_photo.jpeg

But there's six of them? You only created four. Let's step through it:

// Original image
// yet_another_cat_photo.jpeg

$widthOnly = $image->width(150);
// creates yet_another_cat_photo.150x0.jpeg

$heightOnly = $image->height(150);
// creates yet_another_cat_photo.0x150.jpeg

$square = $image->size(150, 150);
// creates yet_another_cat_photo.150x150.jpeg

$rec = $image->size(150, 75);
// creates yet_another_cat_photo.150x75.jpeg

But what about the sixth one? Where does 'yet_another_cat_photo.0x260.jpeg' come from? Change the grid view of the image to 'grid' and click on the image to reveal a larger version with the description field. This is just an auto-generated image based off your original and all of your images will have this variation whether you create images on the fly or not.

Aha, the 0x260.jpeg
Aha, the 0x260.jpeg Zoom

Click on 'Variations':

Showing all the image variations
Showing all the image variations Zoom

There they all are. Now you should have a good idea of how this works. If you delete the images out of the filesystem (ones with '0x260' etc. in the filename, leave the original), the variations disappear.

Variations deleted from the filesystem
Variations deleted from the filesystem Zoom

You can also delete them directly in the variations modal window and a few other ways mentioned below depending on whether you want to remove them or replace them. Either way, they wll be recreated the next time your template file script runs (when your page loads in a browser).

You can also pass an array into some of the image resize functions:

<?php
  // if page has single image field called 'featuredImage' and has an
  // image uploaded to it. This alternate syntax 'if' wraps this whole block of PHP/HTML
  if ($page->featuredImage):

    // set some options
    $options = array(
      "quality" => 5,
      "upscaling" => false,
      "cropping" => true
    );

    // save some typing...
    $image = $page->featuredImage;

    // create four images using image resize functions
    $widthOnly = $image->width(150, $options);
    $heightOnly = $image->height(150, $options);
    $square = $image->size(150, 150, $options);
    $rec = $image->size(150, 75, $options);
?>

So we've changed the quality down to '5' from the default '90'. Refresh your page and you'll notice nothing has changed. You'll need those images to be recreated... so as discussed, you can:

a) Delete the variations in the modal window when you click the 'Variations' button, or

b) Delete the variations from the filesystem, or a new one:

c) run $imagefieldNameHere->removeVariations(); in your script before the new ones are created (but you must remember to remove this line or it'll happen every time the page loads), or

d) since PW v2.5 you can use the 'forceNew' (bool) option in the array which will overwrite the variations. This also needs to be removed once you are happy with your variations.

Let's do it! Change your $options to:

// set some options
$options = array(
  "quality" => 5,
  "upscaling" => false,
  "cropping" => true,
  "forceNew" => true
);

Then refresh your page:

Variations at quality setting of 5
Variations at quality setting of 5 Zoom

Woah! Reminds me of being back on the Amiga in my younger days. Kinda awesome actually, but probably not what you wanted. Anway, you can crop the images here too.

How does default cropping work?

The default setting is 'true'. So, let's say you have an image you want to create at 400px x 300px and the original is 1920px x 1080px, the original image is resized until the first dimension is reached whether it be the width (400px) or the height (300px). Of course, if the original is a square, and you create a smaller square, both dimensions will be reached at the same time when resizing, hence no crop. In a nutshell:

1) If the width is reached first, the height is cropped, so the top and bottom get chopped off to reach the correct final dimension.

2) If the height is reached first, the width is cropped, so the left and right get chopped off to reach the correct final dimension.

We'll change the quality back up and recreate the images and crop them towards the bottom left (southwest) i.e. you're aiming to keep the bottom left of the image. See how this differs from the default of 'true'?

// set some options
$options = array(
  "quality" => 90,
  "upscaling" => false,
  "cropping" => "southwest",
  "forceNew" => true
);

Then refresh your page:

Images cropped southwest
Images cropped southwest Zoom

You'll notice that in the last two images, the cat has moved out of the frame,which is probably undesirable, so knowing whats in the picture, you could probably crop north to be safe.

// set some options
$options = array(
  "quality" => 90,
  "upscaling" => false,
  "cropping" => "north",
  "forceNew" => true
);
Images cropped north
Images cropped north Zoom

That's better. However, if you set this as a default, what happens when an image has a subject that is in the lower half? Then that will be cropped out! You could always manually crop in the page admin area (see the 'Crop' link next to 'Variations') but aint nobody got time for that!

Aint nobody got time for that!
Aint nobody got time for that! Zoom

So, I've got some exciting news for you! You can try the new development version v3.0.89 where 'focus cropping' has been added to the core. I am extrememly excited about this, because, although there have been modules to do predefined crops like croppable image 3 in the past, the focus technique is very natural for content editors. They just choose which part of the image is of most importance, and it will remain visible regardless of variation size. This was a github request just fifteen days ago, that's right, fifteen days! And here it is.

I'll be writing a new tutorial in the coming weeks about setting image defaults sitewide (rather than directly in the template file as we just did) and of course, using this sweet new focus feature.

Feedback & support

I hope you enjoyed this tutorial. You can support pwtuts by following on @pwtuts.

Related tutorials / See all