Adding text to an image using PHP and Imagick

March 5th, 2024

Just like my previous article on adding text to an image using GD, this is how to add that same text using Imagick instead

The code

I've added comments in the code below to denote what it does. The code uses Laravel for the public_path() helper, but should be fairly straightforward to use native PHP or another framework instead:

// Get the template image that we want to add text to
$image = public_path('social/social-template.png');

// Create an Imagick object
$imagick = new Imagick();
$imagick->readImageBlob($image);

// Define the color and font size of our text
$navy = 'rgb(20, 45, 111)';
$font_size = 128;
$font_path = public_path('fonts/Jokker-Semibold.woff');

// Wrap the text based on a maximum width (see next code block)
$text = $this->wrapText('This is my title', $font_size, $font_path);

$draw = new ImagickDraw();
$draw->setFillColor($navy);
$draw->setFontSize($font_size);
$draw->setFont($font_path);

// Add the text to the image
$imagick->annotateImage($draw, 210, 600, 0, trim($text));

// Store the image
$imagick->writeImage(public_path("social/images/this-is-my-title.png"));

// Clear memory
$imagick->clear();

Wrapping text

Defining a max width and wrapping the text isn't very straightforward, as each font will result in a different width of the text. Luckily, Imagick also provides some methods to calculate that:

public function wrapText(string $text, int $font_size, string $font_path): string
{
    // A variable to store our result in
    $wrapped = '';
  
    // Split the text into an array of words
    $words = explode(' ', $text);

    foreach ($words as $word) {
        // Calculate the size of the current result + the additional word
        $teststring = "{$wrapped} {$word}";
      
        $draw = new ImagickDraw();
        $draw->setFont($font_path);
        $draw->setFontSize($font_size);
      	
      	$metrics = (new Imagick())->queryFontMetrics($draw, $teststring);

        // If the test box width is larger than our max allowed width,
        // add a line break before the word, otherwise add a space
        if ($metrics['textWidth'] > 1900) {
            $wrapped .= "\n".$word;
        } else {
            $wrapped .= ' '.$word;
        }
    }

    return $wrapped;
}

Alternatives

If you prefer to work with HTML & CSS to generate images, take a look at [browsershot](https://github.com/spatie/browsershot)

MENU