I read an article by Zach Leatherman about bulk og images generation. He explains how he used ImageMagick to generate a bunch of Open Graph images.
More precisely, he wrote a simple bash script with two calls to ImageMagick. The script converts this:
To this:
Even if ImageMagick is convenient, I think it's not the best tool for this particular task. Let's see why, and what to do about it.
Why not ImageMagick?
ImageMagick is an awesome tool. I used it a lot for RealFavicon and bulk jobs. However, I see a few issues here.
Not just a CLI tool: a new language
Usually, we use ImageMagick from the command line. Command line? You might think cd
, ls
or echo
. But ImageMagick is not of that kind. It's more like sed
or awk
: as complex as it is powerful.
Look at Zach's code:
# resize image
convert "$i" -resize x530 -background $bgColor \
-gravity center -extent 1200x630 "tmp/${i##*/}"
# add watermark
composite -gravity SouthEast -geometry +20+20 \
"tmp/watermark.png" "tmp/${i##*/}" "${outputDir}${i##*/}"
convert
, -resize
, -gravity
, -extent
... This is new vocabulary we have to learn.
ImageMagick has a great documentation, with a lot of examples we can start from. Yet, be ready to go on an adventure.
Don't get me wrong: I'm sure Zach is fluent with ImageMagick and was able to write this script in a few minutes. However, I can't do this. I know ImageMagick just enough to use its doc effectively. In other words, I'm good at finding the right examples and copying them wisely. 😅
Inconvenient trial and error
When creating something visual with code, we can't expect to achieve the perfect design on the first try, whatever the technology. "Text should be a bit larger", "We won't need that much padding", and so on. So we need a convenient feedback loop.
ImageMagick is not ideal here.
Zach's script iterates over a lot of images. That's the point. But during the tuning phase, this is counterproductive. Basically, we need to hack in a way or another:
- Replace
*.png
withrocket.png
to select only one image. - Prepare the ImageMagick command lines from a terminal and copy/paste them to the script when we're ready.
- ...
ImageMagick doesn't scale well
This one it a bit out of scope here because we create rather simple images. But in general, ImageMagick is not convenient for complex compositions.
Either we need to produce several intermediate images, heading for the final image at each step. Zach creates one in his demo. Or we write a single super command which does everything. I'm sure Zach's commands could be merged.
In both cases, it's hard to write and maintain.
The HTML way
In this section, we are going to address these issues.
First, we use HTML and CSS. The reason is obvious: we already know them. No more ImageMagick multiverse.
We also use the Resoc image templating system. It will help us build a template, with reload-as-you-type feature for instant feedback loop.
Can't wait? You can check the demo repo.
Create the template
Let's go:
mkdir 1-million-devs
cd 1-million-devs
npx itdk init resoc-template
The last command creates a sample image template in resoc-template
and opens it in the browser:
On the right panel, we see what the template expects as parameters. Our template will mimic Zach's script:
- Path to the main image
- Path to the watermark image
- Background color
Let's edit 1-million-devs/resoc-template/resoc.manifest.json
. We can adapt the existing content to define our own manifest:
{
"partials": {
"content": "./content.html.mustache",
"styles": "./styles.css.mustache"
},
"parameters": [
{
"label": "Main image",
"name": "mainImage",
"type": "imageUrl",
"demoValue": "rocket.png"
},
{
"name": "logo",
"type": "imageUrl",
"demoValue": "netlify-logo.png"
},
{
"name": "backgroundColor",
"type": "color",
"demoValue": "#00dc9e"
}
]
}
Back to the browser, we can see that these new parameters were taken into account:
To get a working preview of the template, we needs a demo image and a logo. Save the rocket to 1-million-devs/resoc-template/rocket.png
and Netlify logo to 1-million-devs/resoc-template/netlify-logo.png
.
The previews of the left panel are quite broken at this point. It's time to create our own template.
Edit 1-million-devs/resoc-template/content.html.mustache
:
<div class="wrapper">
<img id="main-image" src="{{ mainImage }}" />
<img id="logo" src="{{ logo }}" />
</div>
The content is straightforward, expect for the strange {{ mainImage }}
and {{ logo }}
. This is Mustache, the templating system Resoc uses to inject the final values.
Now for the CSS, fill 1-million-devs/resoc-template/styles.css.mustache
with:
.wrapper {
background-color: {{ backgroundColor }};
display: flex;
justify-content: center;
align-items: center;
position: relative;
}
#main-image {
max-height: 84vh;
max-width: 84vh;
}
#logo {
position: absolute;
right: 4vh;
bottom: 4vh;
height: 10vh;
}
Nothing fancy. Just a matter of centering the main image and put the logo to the bottom left. Background color is set by a parameter, via {{ backgroundColor }}
. We use the vh
unit to speak relatively to the view port height. As we plan to generate only 1200x630 images, we could have just used pixels.
Take a look at the preview in your browser:
Cool! You could think this was made with ImageMagick 😆
Use the template
We are now going to build the command line that will create the images. We don't need to start from scratch, though, because the template viewer already provides one below the previews:
Let's shamelessly steal Zach's bash script, replace the two ImageMagick invocations with Resoc's, and name this new script create-og-images.sh
:
#!/bin/sh
echo "Uses Resoc"
images="input/*.png"
watermark="resoc-template/netlify-logo.png"
outputDir="output/"
bgColor="#00dc9e"
mkdir -p $outputDir
for i in $images
do
create-img resoc-template/resoc.manifest.json \
-o "${outputDir}${i##*/}" \
--params \
mainImage="$i" \
logo="$watermark" \
backgroundColor="$bgColor" \
-w 1200 -h 630
done
We need to drop a few samples images in a new input
directory.
create-img
could be called with npx
, but this is way too slow. Install it once for all and go create!
npm install -g create-img
./create-og-images.sh
A few seconds later, the script turned this:
To this:
Conclusion
ImageMagick is an amazing tool. It can perform effects which are beyond HTML and CSS capabilities. Regarding composition, the balance is reversed. Web technologies are more suitable and better known.
Yet, HTML is somehow not available from the command line. Puppeteer has been here for a while, but it is too raw to be used in this context.
I'm creating the Resoc image template dev kit to fill this gap. HTML and CSS are incredibly powerful and we know them so well. Just like JS entered the desktop world via Electron, HTML should join the command line universe.
And there are plenty of other usages for image templates, like static social image generation for NextJS.
Top comments (0)