A List Apart

Menu

Automatic Magazine Layout

Issue № 219

Automatic Magazine Layout

by Published in CSS, HTML, The Server Side · 35 Comments

Auto-resized images are a common feature on the web now. Automating the process of resizing images saves a lot of time—it’s certainly quicker than manually resizing images in Photoshop—and is a good way to manage images on a site. But when a good designer crops your images manually, they always looks better.

Article Continues Below

The problem with automatic uploads is particularly evident when several images of completely different dimensions are displayed together on a page, such as four product images that have been uploaded through a content management system (CMS). Finding an attractive way of displaying any two, three, or four images together (regardless of shape and size) has always been difficult without manual resizing or cropping.

This article covers a PHP-based technique for automatically resizing—and more importantly, positioning—between two and eight images in what I call a magazine-style layout (images in a magazine are always carefully positioned—usually one image takes pride of place and several smaller images surround it).

Example 1 shows the final output of this technique. The only information we need to supply to the script is the width of the container, and the file name of each image. Amazingly, everything else can be calculated.

Different dimensions

Consider the following three images of different sizes and dimensions, which could have been uploaded via a CMS

three user-uploaded images, all different dimensions

Three user-uploaded images, all of different dimensions.

In their current shape, these images are unlikely to look attractive. A more desireable layout is shown below

The same three images, resized and positioned correctly

The same three images resized and positioned correctly.

Anyone with an image-editing program such as Photoshop can achieve this effect very quickly, but it needs to be done manually. This method uses of some server-side PHP scripting and some rather complicated algebra to achieve the same effect, no manual tweaking involved.

The solution

Using PHP to resize the images on the server, we are able to calculate the exact size that each image should be so they will fit together in a nice square box. This will maintain our aspect ratios, work with almost any image proportion, and allow us to do all the dirty work on the server. Square images are no problem either (see Example 2).

Getting started

First things first, you will need a PHP web host who has the GD2.x extension enabled. Fortunately, this is quite common these days. You will also need a good image resizing script. I have included a basic script here, but it’s always a good idea to use a script that caches the output, as dynamic images can really slow down your server. Copy the supplied class file to your web server and note its location on the server.

Create a basic script as follows… (line wraps marked » —Ed.)

# include the class file
require_once('magazinelayout.class.php');# Define the width for the output area (pixels)
$width = 600;# Define padding around each image - this *must* be included 
#in your stylesheet (pixels)
$padding = 3;# Define your template for outputting images
# (Don't forget to escape the &)
$template = '';# create a new instance of the class
$mag = new magazinelayout($width,$padding,$template);# Add the images in any order
$mag->addImage( 'landscape1.jpg' );
$mag->addImage( 'portrait1.jpg' );
$mag->addImage( 'landscape2.jpg' );# display the outputecho $mag->getHtml();

That’s it. Running the script should display three images in an attractive layout; the exact look of the layout will be determined by whether the images are landscape (longer than tall) or portrait (taller than long).

How does it work?

The above code is basic object-oriented PHP. It sets up a new class, adds some images into an array, and processes them. The getHtml() method is the key. Let’s take a look.

Getting image ratios

As images are added, we detect whether they are landscape or portrait using PHP’s getimagesize function. This gives us the height and width of the image, which we use to find a ratio of width : height. This function is quite processor intensive, but it can’t be avoided.

We don’t need to know the height and width, but we’ll use the ratios later on, so we’ll save them to an array. At this point, we also decide whether we are dealing with landscape or portrait images, as this determines which template the script will use. The template above is based on two landscape images and one portrait.

Calculating sizes

Our goal is to get the three images to look right within an overall fixed width—the overall height will vary depending on the image dimensions. The complicated part of all this is figuring out the right size for each image. If we did it in Photoshop, it would involve resizing each image until it “looks right,” and every change will potentially upset the balance of the other images. My first attempt at this script wasn’t too different from a Photoshop trial-and-error process; the script started a counter at 1 and tried every size up to 500px until it found a working combination. The sound of my processor fan kicking on convinced me there had to be a better way.

Algebra

It turns out my problem can be solved using algebra—something I spent a lot of time learning at university, but never managed to find a practical application for. Ten years on, it’s coming into use for the first time.

Certain parts of our equation we know:

  • t = Total width—The total width of all images specified when the class is called.
  • r1, r2, r3 = Ratios—We have already calculated the ratios of each image based on the width and height.


What we don’t know…

  • w1—The width of the left column—one required piece of information.
  • w2—The width of the right portrait image, also required.

The following diagram shows the values we know and those we don’t. Known values have a checkmark, unknown have a question mark.

Our equation values explained

A diagram showing the relation of our equation values to the layout.

Given these widths, our image script can figure out the heights because we already know the aspect ratio of each image. If we can use algebra to find w1 and w2, we have all we need.

What we also know

We also know that the height of the portrait image is equal to the combined height of the two landscape images, expressed as…

h1 = h2 + h3

And we know that the same height is also equal to the portrait image’s width divided by its ratio:

h1 = w2 / r1

We also know the height of the landscape images is their width divided by their ratio.

h2 = w1 / r2
h3 = w1 / r3

After some rearranging of elements, I am starting to put everything into one equation, with the goal of finding w1. A trip to the Quickmath calculator gives me the result I need.

W1 = t / ( r1 * ( 1/r2 + 1/r3 + 1/r4 ) + 1 )

Some padding, please

At this point, it became apparent that the first designer to look at the output would be wanting some padding between the images. This is not as simple as adding a few pixels onto each image, because the left-hand side is going to have more padding overall than the right which will throw the formatting. Example 3 shows the layout without any padding.

With a little help from the calculator, I arrived at the following equations:

w1 = - ((2*r1*r2*r3*p + 4*r2*r3*p - r2*r3*t) / (r1*r2 + r3*r2 + r1*r3)) and w2 = (r1* (-4r2*p + 2*r2*r3*p - 4*r3*p + r2*t + r3*t)) / (r1*r2 + r3*r2 + r1*r3)

The formula for one of the layouts.

Things are starting to look a little more complicated here, but this does work.

Enough maths, back to PHP

This was never meant to be a lesson in mathematics, but we now have enough information to plug back into our PHP script. By replacing the algebra variables with PHP variables, we create a formula for finding the information we are missing.

Given the ratios of the images and the overall container width, we can calculate the width of all images—enough for the image resizing script to do its job.

CSS

Because we don’t use tables for positioning anymore, I had to find a way of making the layout look good using CSS. Because we are dealing with squares, this is quite straightforward using the images in a floating div. All was working well until I tried the version with padding between the images in IE. Because IE handles padding differently than other browsers, it offset the images by a few pixels and ruined the effect.

One way to work around this problem is to place the image inside a container div and apply margins to the image. This will force the container to the right size, which gives the much needed effect of padding between the images. Default CSS is as follows:

.magazine-image {
  background: #fff;
  border: 1px #eee solid;
}
.magazine-image img {
  padding: 0px;
  background: #fff;
  margin: 2px;
  border: 1px #eee solid;
}

Different layouts

This script contains six different layouts based on different formulas. The layouts accomodate from one to four images each. If you need to display six images, the script simply uses the four-image layout followed by the two-image layout (see Example 4). Some of the layouts are more appropriate for certain combinations of image size, such as 2x landscape 1x portrait; others are more generic. Example 5 shows how the script can be used at different widths to fit any space of your page.

Possible uses

The obvious use for this script is anywhere where more than one user-submitted image needs to be presented in a HTML page. I’m thinking product databases, forum image uploads, random image rotations, etc, etc.

Once you have ten or so images, you are better off using an AJAX based image gallery, but this script will fill the gap nicely up until that point.

Download

The full source code and examples are downloadable.

Thanks and credits

Thanks to Alexander Burkhardt for the use of the demo images. The images were taken on the lovely Hokianga Harbour in Northland New Zealand.

About the Author

35 Reader Comments

Load Comments