Skip to content

ImageMagick Bug Note: Stroke State Persists Across Annotate Calls

Created 2026-04-10
Tags technical-noteimagemagickcarouselbug-fix

Discovered: 2026-04-10
Fixed in: instagram-carousel-generate.sh
Symptom: Text appearing with visible outlines in rendered carousel slides


The carousel preview showed text with outlines (a “stroked” appearance) on several slides. The affected slides were 1, 3, 4, 5, 6, and 7. Slide 2 had no issue.

In ImageMagick, drawing properties like -stroke and -strokewidth are global state within a single magick command. Once set, they apply to every subsequent operation — including -annotate (text) commands — until explicitly reset.

The shell script used a pattern like this to draw decorative divider lines:

Terminal window
-stroke "${DARK}" -strokewidth 1.5 -fill none \
-draw "line 390,660 690,660" \
-fill "${SEC}" -font "${JOST}" -pointsize 28 \
-annotate +0+60 "Some text here"

The -fill "${SEC}" call updates the fill color, but it does not reset the stroke. The -annotate command that follows therefore renders the text with both a fill and a stroke — producing a visible outline around each character.

The stroke color was ${DARK} (#1A1A1A) applied on top of dark text, so the outline appeared as a subtle thickening rather than an obvious colored border. It was only noticeable when viewing the rendered JPG at full resolution.

Add -stroke none -strokewidth 0 immediately after every -draw "line..." call, before the next -annotate:

Terminal window
-stroke "${DARK}" -strokewidth 1.5 -fill none \
-draw "line 390,660 690,660" \
-stroke none -strokewidth 0 \ reset here
-fill "${SEC}" -font "${JOST}" -pointsize 28 \
-annotate +0+60 "Some text here"

Eight resets were added across slides 1, 3 (×2), 4 (×2), 5, 6, and 7.

Always reset -stroke none -strokewidth 0 after any -draw that uses a stroke, before the next text annotation. This applies even when you reassign -fill — fill and stroke are independent state variables.

Other properties that carry state across operations and may need explicit resets:

PropertyReset with
Stroke color-stroke none
Stroke width-strokewidth 0
Fill color-fill <new-color>
Font-font <new-font>
Point size-pointsize <n>
Gravity-gravity <direction>