Automatic Magazine Layout
Issue № 219

Automatic Magazine Layout

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#section2

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#section3

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#section4

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?#section5

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#section6

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#section7

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#section8

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#section9

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#section10

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#section11

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#section12

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#section13

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#section14

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#section15

The full source code and examples are downloadable.

Thanks and credits#section16

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

Harvey Kane

Harvey Kane is a PHP Web developer living in Auckland, New Zealand who publishes SEO Articles and tools. By day, he produces CMS websites for small businesses.

35 Reader Comments

  1. I find it truly incredible that no sooner do I run into the challenge of “I know I want to put a small image gallery on the home page to improve it, but I don’t have the time (or want to go through the pain, yet again) of having to manually size the images to exactly the size they need to be to get them into an aesthetically pleasing layout” then ALA rises to the occasion and delivers what appears to be an elegant solution to exactly my problem. Just think…in a day or two, when I get to use this, I’ll get my problem solved sooner than I had expected.

    Thank you.

  2. I think your article is very interesting and useful. What do you think about an idea to speed things up by allowing to manualy enter values for image size?

  3. This is a good idea and its nice to see the integration of math and webdesign. However, I think the idea can be taken alot further. How about specifying both the maximum width and height and also images which are given a greater “real estate” preference. How about different layouts like larger photo on left, top etc. Even a option make a polaroid photo-montage a-la Picassa would make this script alot more useful. Also an easy way to mabye refrence the pictures in the footnotes would be interesting (i.e. overlaying a figure number on the image).

    I would be interested in a RoR version for this, if only I had the time at the moment. But again, well done on the novel and useful idea.

  4. I’d like to have this script integrated into the gallery extension of Typo3. The preview page looks really ugly when you combine landscape and portrait pictures.

  5. On the issue of using getimagesize, you may be able to use imagesx and imagesy to get the width and height of the images. They work a little different than getimagesize, whereas getimagesize takes a filename while imagesx & imagesy take an image resource. This may be worth checking in to though, as getimagesize is currently providing your script with information that is not being used.

    More information can be found at “getimagesize”:http://us2.php.net/manual/en/function.getimagesize.php , “imagesx”:http://us2.php.net/manual/en/function.imagesx.php, and “imagesy”:http://us2.php.net/manual/en/function.imagesy.php .

  6. It’s an elegant solution, as mentioned before, and it _can_ be taken further. I’m thinking some interesting plugins for WordPress/TextPattern/etc could be based off of this.

  7. Thanks everyone for the feedback. Yes, there are plenty of ways to improve this, and a WordPress / Typo plugin seemed logical to me, as well as adding support for alt / title attributes on the images.

    The speed of getImageSize() hadn’t worried me too much as benchmarking didn’t show too much of a performance hit (compared with my first attempt at least). I’ll certainly look into getting the data from the image resource if this saves some processing power.

    It currently tries to use the highest resolution image as the large one, but an obvious enhancement would be to add an extra flag for priority on each image.

    I guess the whole point of the script for me is to deal with user uploaded images, or images that change often – The script works very well for a homepage rotation, where 3 random images are chosen randomly and arranged nicely.

    I also use the script in my CMS – users can add a simple piece of custom BBCode into their pages – this is the sort of thing I can see working for WordPress, which I’ll be getting started on soon.

  8. Very nicely done. Take a good note folks–Leonardo da Vinci would be proud. The best designs take into account so many different disciplines. This is a superb integration of relatively complex algebra and visual design to fulfill a very practical design application.

  9. Good stuff. Great math 🙂
    But why not skip the whole html/css bit and generate the compilation in one single jpeg image.
    This way one eliminates a few HTTP requests, the jpeg-file overhead. The html will also be smaller and one doesn’t need to call getImageSize. Also no browser-bug to take into account. The only problem whould be the color of the padding.

  10. @Gerben

    Appreciate your points about reducing HTTP requests and browser bugs, but there are some good reasons to implement this as seperate images too.

    The main one for me is that by putting the final output into one larger image, this will break the “right click, save target as” functionality that the user expects of images on the web. When I say “break”, I mean the user will need to crop their image out of the layout manually, which won’t be what they were expecting.

    Also means that individual images wouldn’t be clickable to a full size version without using an image map. Although I didn’t mention it in the article, the script does allow for you to make images clickable so that you can include a full size version in a popup, or whatever takes your fancy.

  11. A while ago, I’ve spent way too much time trying to solve a similar problem. I’m just not too good at mathematics myself, so you can imagine how grateful I am for this writing!
    Thanks!

  12. You kiwis are keeping us on our toes.

    I recently wrote a PHP script to automatically create thumbnails on upload and place each one centered both vertically and horizontally inside square divs. The overall effect ended up making the thumbnails look a bit like mounted slides. I may be able to combine the two and have a magazine style thumbnail layout. That would rock.

    Thanks a lot for the work you’ve done, particularly trudging through the algebra and representing it all visually. I think math gets a bad reputation simply because it is so often presented in a way that appeals to a very narrow slice of learning styles.

  13. After reading your article on this, I considered two generalized solutions:

    n images across (all the same height, but within a specified width)
    and
    n images in the left column and m images in the right.

    After a small amount of math, the generalized solution falls out.

    If anyone is interested in this general solution, I can post it.

  14. Harvey,

    Thanks for an excellent script. I love applied mathematics and automation in web pages. Rules rule!

    I have incorporated the script in a slightly modified form in some of my pages. I needed to be able to link from the generated images to a page with a full size copy.

    By adding a [link] parameter in the template and an extra argument to addImage() plus a few other modifications, I could fairly easily accomplish this.

    You can see it in work here:
    http://500th.net/index.htm?id=55&day=20060712
    http://500th.net/index.htm?id=55&day=20060713

    I’d be happy to share this code. It’s quite simple.

    Thanks again

    Martin

  15. Harvey – Great script, really useful. Martin and Alex, I’d love to see what you worked up. And Martin, those are some great pictures.

    Thanks to all three of you!

  16. Excellent article.

    In addition to what Harvey already mentioned, I wouldn’t be surprised if the file size of a combined image would actually be larger then the sum of the small ones. Simply getting the padding/transitions between the images looking sharp enough would necessitate using very little compression on the JPG. In most cases a sliced up image will produce better looking results with a lower overall file size.

    That said there are probably going to be to be times when it is more appropriate to use the single image scheme.

  17. http://bentley.110mb.com/?mag

    has example layouts of the general solutions. To simplify the code for my purposes the images are simply scaled by the browser based on the height and width calculated. Alt and title attributes are supported as are links for each of the images. I’ve also reduced the methods to only two (one for an across layout and one for a block layout).

    The first example is 7 across and the second is 4 on the left and 4 on the right.

    Any orientation is allowed in any order. Let me know what you think.

  18. I’ve created a Python version of the PHP class. The “project page is here”:http://freecog.net/2006/magazinelayout/ and a “test/demo here”:http://freecog.net/2006/magazinelayout/tests/test.py?width=600&padding=3&02.jpg=1&04.jpg=1&06.jpg=1&09.jpg=1&11.jpg=1

    Right now it’s pretty much just a line-by line translation, with some minor improvements. I’ll look at adding link, alt text and title options–it should be trivial to do so.

    Alex, how about you show us your code?

  19. Great idea, but it seems that the alignment is not working with example 4. It looks way off in IE and stills slightly off in FF.

  20. I changed the script, so that it returns width AND height for each image and also ALT parameter. By doing so I can not to enlarge images physicaly (I am using phpThumb to automaticaly change size of uploaded images) if they are too small (which saves processing power) and fill WIDTH, HEIGHT and ALT parameters in IMG tag, which I believe is more standarts compilant.

  21. Division is expensive, and while the math site gave you A solution to the equations, it probably wasn’t the most readable or efficient solution. Working with the equations by hand (which means I could have a bug) I came up with the following got 4 images:

    w2 = (t * r1 * r2 + t * r2 * r3) / (r1 * r2 + r2 * r3 + r1 * r3)

  22. Looks like something in the comments system screwed up my formula. I’ll use ‘x’ for multiplication since that seems to be the issue:

    w2 = (t x r1 x r2 + t x r1 x r3) / (r1 x r2 + r2 x r3 + r1 x r3)

  23. Hi, I thought it was a really great and informative article. Yet, I couldn’t help but notice that on every page at least one image was a single pixel out. I’m sure that wasn’t intentional.

    I think the only solution would be to make sure that every image has an even width by using:

    if (a % b == 1) {}

    Something like that could solve the problem.

  24. It would be great if the order of the images could be fixed as to the order they were inserted into the array. I have a layout of one portrait and one landscape image that I have a need to be in a specific order (like before/after shots with before always on the left). The layout works but sometimes the order is reversed.

    Is there a way to fix the order in a situation like this?

  25. I love the script! I was looking for an interesting way to put some photos on my site. I’ve modified the script slightly so that it picks images at random from a directory, and I modified image.php to use caching. I like it so much I’ve put it on the “home page”:http://www.ouafc.com

  26. I’ve found this very useful and i’d certainly never in a million years (with my single gcse in maths )have come up with the algebra for this myself. It’d be handy if it could be reversed so that one could specify the max height rather than the width, or possiblly both. I think the former could be achieved very easily if only i could get my head round the maths. it’s be a simple matter of reversing the formula, right?

    Should have paid more attention in school. Algebra IS usefull, who knew it?

  27. This script is great! So great that my own CMS had to have one… only it’s an ASP cms, and ASP 3.0 doesn’t have facilities to read/write image dimensions! Anyway, I collected a couple of scripts from third-parties, and here it is: a fully working magazine layout in Classic ASP (ASP 3.0)!, under the form of a simple ASP class. It doesn’t include any “resize image” script, but this can easily be done with an external resize.PHP or .ASPX script.

  28. Outstanding work; however, I wanted to suggest an even more general solution, capable of arranging arbitrarily many images, using recursion. Any possible arrangement can be derived from two basic layouts: vertical (v) and horizontal (h). For example, the layout you call “3a” could be achieved with h(h(i1, i2), i3). Similarly, “3b” is h(v(i1, i2), i3). “4a” could be derived a number of ways, one of them being h(h(i1, i2), h(i3, i4)).

    The algorithm would randomly divide the set of images into smaller and smaller subsets, then work its way back up, applying one of the two layouts to the intermediate results. At each level, it could either choose the layout randomly, resulting in some interesting patterns, or using some aesthetic rules based on the portrait/landscape orientation of the subresults.

  29. This is a wonderful, insightful and uplifting case of a revealing infrastructure Automatic Magazine Layout implementation. Keep up these useful pieces. Your help would be appreciated.
    Thanks.

Got something to say?

We have turned off comments, but you can see what folks had to say before we did so.

More from ALA

I am a creative.

A List Apart founder and web design OG Zeldman ponders the moments of inspiration, the hours of plodding, and the ultimate mystery at the heart of a creative career.
Career