Associate a banner image with each post.
Generate multiple versions (sizes) of these banner images.
Provide the generated images in variables for use in post templates.
Each post in the site is represented by a directory containing an index.md
Markdown file and, optionally, a banner.png
image file. This repository
contains two posts, one with and one without an image:
posts/2013-10-23-post-without-banner/index.md
posts/2013-10-24-post-with-banner/banner.png
posts/2013-10-24-post-with-banner/index.md
The site.hs
Haskell program contains only the most basic Hakyll
directives to process the Markdown files (indeed, I haven’t even bothered to
override the date code; you’ll need to put the date in the Markdown file
metadata). In addition, it contains the proof-of-concept code to handle the
banner images.
The imageProcessor
function is a helper to construct the Hakyll Rules ()
to
process a set of banner images and the Context a
allowing them to be used in
the associated posts and templates. This function takes a Pattern
which
matches the banner images and a list describing the different versions to
generate of each image.
let (postImages, postImageField) = imageProcessor "posts/*/banner.png"
"small" , Just (200,100))
[ ("medium", Just (600,300))
, ("full" , Nothing)
, ( ]
The first argument to imageProcessor
is the pattern identifying the image to
be processed. This pattern must end in a full filename. The second argument
is a list of versions to create. Each version has a name (the String
) and
image processing instructions (Nothing
to copy the image, Just (x,y)
to
scale and crop the image to the given dimensions using ImageMagick’s convert
command). This generates a Rules ()
value to process the images (postImages
in the code) and a Context a
to make them available in posts and templates.
The Rules ()
value can be “run” just like any create
or match
statement
and the Context a
value can be used in the context of post with a path that
matches the image pattern (ignoring the filename). This context defines one
variable for each of the image versions being generated. In this code, the
variables are:
banner-small
is a 200x100 image.banner-medium
is a 600x300 image.banner-full
is the original image.These names are generated from the filename banner.png
in the image pattern
(this is why the pattern must have a filename) and the name of each version.
With this configuration, the posts included in the repository result in the following files being generated:
posts/2013-10-23-post-without-banner/index.html
posts/2013-10-24-post-with-banner/index.html
posts/2013-10-24-post-with-banner/full-banner.png
posts/2013-10-24-post-with-banner/medium-banner.png
posts/2013-10-24-post-with-banner/small-banner.png
As expected, each post has an index.html
file and the single banner image has
small
, medium
, and full
versions.
The implementation of imageField
and the assumption that the pattern ends in
a file name are pretty crappy but, overall, I’m quite happy with this approach.
mod_php
embedded in Apache. This is fine in most cases, but sometimes it’s definitely
the wrong thing to do; in my case, I’m processing large image files (~4M or
so) and keeping my memory limit down. The easiest way to do this is to offload
the processing to another process. Something like ImageMagick perhaps? With a
custom action from ImageCache Actions, this is simple!
A quick Google led me to an article called Create PDF thumbnails with imagecache and ImageMagick while GD is still the default toolkit which supplies the following code (simplified a little by removing the PDF-y bits):
<?php
$w = 246; // change to your preferred thumbnail width
if (!_imageapi_imagemagick_convert($image->source.'[0]', $image->source.'.png', array(0 => '-thumbnail '.$w))) return FALSE;
$img = imagecreatefrompng($image->source.'.png');
$image->source.'.png');
file_delete($image->resource = $img;
$image->toolkit = 'imageapi_gd';
$image->info = array('extension' => 'jpeg');
return TRUE;
?>
Alas, this code it pretty useless: because it stomps on $image->info
any
further actions on this $image
will probably break.
Thankfully, there’s an easy fix: when you update $image
, make sure you
update everything that needs fixing. Here’s the amended code:
<?php
// "Thumbnail" the image
$width = 600;
if (!_imageapi_imagemagick_convert($image->source, $image->source.'.png', array(0 => '-thumbnail '.$width))) return FALSE;
// Load it back in as a GD resource
$img = imagecreatefrompng($image->source.'.png');
// Get the "deets" on the new image
$info = getimagesize($image->source.'.png');
$image->resource = $img;
$image->toolkit = 'imageapi_gd';
$image->info = array(
'width' => $info[0],
'height' => $info[1],
'extension' => 'png',
'file_size' => filesize($image->source.'.png'),
'mime_type' => $info['mime'],
;
)
// Clean up
$image->source.'.png');
file_delete(
return $image;
?>
Make sure that you’ve enabled the ImageMagick toolkit, drop this code in a custom action and you’ll be on externally processing images in no time!
]]>