If you want to conveniently compose pictures and text programmatically you may want to have a look at ImageMagick. Especially when powered by Pango markup. The markup is SGML and the resulting text line breaks and flows automatically. It gets quite a lot easier to lay your text out this way, than to use ImageMagic’s built in text facilities. And some things do not only get easier, they become possible.
However, first you need to get the ImageMagick + Pango pair installed. Depending on your machine this can get complicated. I still haven’t figured out how to do it on my Mac. (See here for how I worked around it.) As my real use case was to get it to happen in a CI build step, I searched for ImageMagic + Pango Docker images and couldn’t find one that actually worked.
So I created and pushed one: cospaia/magick-pango-babashka
The Dockerfile is super simple. It is based on the babashka/babashka
image, which means I can then just apt install
two things,
and that’s about it.
I guess the real work here was to test that it worked for the purpose. But that was just plain fun! Basing my image on the Babashka image means I can use my favorite programming language to create compositions.
Let’s start with a more boring test. Over at the ImageMagick site we find some Pango usage information and examples. Those were the ones I couldn’t get working. But now this works (Docker required, naturally):
docker run --rm -v $(pwd)/output:/output cospaia/magick-pango-babashka
convert -background lightblue pango:"Anthony Thyssen"
/output/pango.gif
You’ll have to imagine the awesome non-animated GIF. Now to a more fun example. Also demonstrated in this video:
https://www.youtube.com/watch?v=fa5ig2cIWnU
More fun because it is a bit more realistic, and because I get to use
Babashka. To try it yourself: go to this
repository and copy the examples
directory to your
computer. It contains a Babashka script compose.clj
and
some images. The script creates the picture shown above and looks like
so:
#!/usr/bin/env -S bb -cp ./bin
(ns compose
(:require [babashka.fs :as fs]
[babashka.process :as p]))
(defn compose! [output-path]
(let [output-dir (-> output-path fs/file fs/parent)]
(when output-dir
(fs/create-dirs output-dir))
(let [tmp-dir (fs/path (fs/temp-dir) (name (gensym "compose-")))
pango (str (fs/path tmp-dir "pango.png"))
montage-1 (str (fs/path tmp-dir "montage-1.png"))
montage-2 (str (fs/path tmp-dir "montage-2.png"))]
(fs/create-dirs tmp-dir)
(println "Writing pango output to: " pango)
(p/sh "convert" "-background" "white" "-size" "1140x"
(str "pango:<span font_size='36000'>"
"<b>magick-pango-babashka<tt>:latest</tt></b></span>"
"\n"
"<span font_size='28000'>"
"A Docker image to power your pictures + text "
"compositions.</span>"
"\n\nExample:\n"
"<tt>docker run -v \"$(pwd)\":/work -w /work "
"cospaia/magick-pango-babashka examples/compose.clj "
"output/composition.png</tt>")
"-bordercolor" "white" "-border" "30"
pango)
(println "Writing first montage output to: " montage-1)
(p/sh "montage" "-resize" "350x"
"examples/assets/ImageMagick.png" "examples/assets/pango-name.png"
"examples/assets/babashka.png"
"-geometry" "+0+0" "-gravity" "center"
"-background" "white" "-tile" "x1"
"-mode" "Concatenate"
montage-1)
(println "Writing second montage output to: " montage-2)
(p/sh "convert" montage-1
"-gravity" "north" "-extent" "1200x383+0-20"
montage-2)
(println "Writing result output to: " output-path)
(p/sh "montage" montage-2 pango
"-tile" "x2" "-mode" "concatenate"
output-path))))
(comment
(compose! "output/composition.png")
:rcf)
(if (not= *file* (System/getProperty "babashka.file"))
(println "Use the REPL, Padawan")
(if-let [[output-path] *command-line-args*]
(compose! output-path)
(println "Usage: ./examples/compose <output-file>")))
I’m an ImageMagick noob so this can probably be done in much better ways. What I do is to:
Create the text as an image using the
convert
command and Pango markup. This image has a thick border, because I couldn’t figure out how to later compose it with margins.Tile the three logos on a row using the
montage
command. I size them all so that the row should fit well within 1200px width, with some padding. I also center them vertically.Size the image to 1200x383 px adding the 20px of padding, using the
convert
command.Create a new image with the montage and the text images on top of each other using the
montage
command.
Here is how you can run it from the command line, even if you are on a Mac and, like me, fail to install ImageMagick with Pango support on your machine.
docker run -v "$(pwd)":/work -w /work cospaia/magick-pango-babashka
examples/compose.clj output/composition.png
Please use the script as a base for your compositions. And please tell me if you know the smarter ways to compose that image. Why not retweet or comment here? 🙏
I created a missing Docker image. Making it easier to programmatically compose pictures from pictures and marked up text. I based the image off of Babashka's so that it is delightful to do the actual scripting. https://t.co/Jy5p1HdueV
— Peter Strömberg aka PEZ (@pappapez) October 10, 2023