Adding Text Watermarks with Imagick

In a previous article, Timothy Boronczyk wrote about how to create watermarks with Imagick using an overlay image. In this article, I’ll show you how to achieve a similar effect using plain text.

At the time of this writing, virtually no documentation exists on PHP’s Imagick API documentation, although there are plenty of command-line examples to be found on the ImageMagick website, so that is where we shall begin. Converting the command-line code from the examples into PHP is simply a tedious matter of finding the appropriate methods that perform the same functionality.

For my examples I’ll be using the following image of some completely random handsome looking gentleman.

Drawing Text on the Image

The easiest type of textual watermark to create is a string overlay on top of the image.

The command line example is:

convert image.png  -font Arial -pointsize 20 
    -draw "gravity south
        fill black  text 0,12 'Copyright'
        fill white  text 1,11 'Copyright'" 
    result.png

And the PHP equivalent is:

<?php
// Create objects
$image = new Imagick('image.png');

// Watermark text
$text = 'Copyright';

// Create a new drawing palette
$draw = new ImagickDraw();

// Set font properties
$draw->setFont('Arial');
$draw->setFontSize(20);
$draw->setFillColor('black');

// Position text at the bottom-right of the image
$draw->setGravity(Imagick::GRAVITY_SOUTHEAST);

// Draw text on the image
$image->annotateImage($draw, 10, 12, 0, $text);

// Draw text again slightly offset with a different color
$draw->setFillColor('white');
$image->annotateImage($draw, 11, 11, 0, $text);

// Set output image format
$image->setImageFormat('png');

// Output the new image
header('Content-type: image/png');
echo $image;

The result is:

This example is pretty straight-forward and the code’s comments make it so you shouldn’t need much extra explanation. But while this works, the text stands out too drastically from the image and the effect should probably be more subtle.

Transparent Text Using a Font Mask

For a smoother effect on the watermark text, you can make the text string transparent using a font mask.

Command line example:

convert -size 300x50 xc:grey30 -font Arial -pointsize 20 
    -gravity center -draw "fill grey70  text 0,0  'Copyright'" 
    fgnd.png
convert -size 300x50 xc:black -font Arial -pointsize 20 -gravity center 
    -draw "fill white  text  1,1  'Copyright'
        text  0,0  'Copyright'
        fill black  text -1,-1 'Copyright'" 
    +matte mask.png
composite -compose CopyOpacity  mask.png  fgnd.png  stamp.png 
    mogrify -trim +repage stamp.png
composite -gravity south -geometry +0+10 stamp.png  image.png 
    result.png

PHP equivalent:

<?php
// Create objects
$image = new Imagick('image.png');
$watermark = new Imagick();
$mask = new Imagick();
$draw = new ImagickDraw();

// Define dimensions
$width = $image->getImageWidth();
$height = $image->getImageHeight();

// Create some palettes
$watermark->newImage($width, $height, new ImagickPixel('grey30'));
$mask->newImage($width, $height, new ImagickPixel('black'));

// Watermark text
$text = 'Copyright';

// Set font properties
$draw->setFont('Arial');
$draw->setFontSize(20);
$draw->setFillColor('grey70');

// Position text at the bottom right of the image
$draw->setGravity(Imagick::GRAVITY_SOUTHEAST);

// Draw text on the watermark palette
$watermark->annotateImage($draw, 10, 12, 0, $text);

// Draw text on the mask palette
$draw->setFillColor('white');
$mask->annotateImage($draw, 11, 13, 0, $text);
$mask->annotateImage($draw, 10, 12, 0, $text);
$draw->setFillColor('black');
$mask->annotateImage($draw, 9, 11, 0, $text);

// This is needed for the mask to work
$mask->setImageMatte(false);

// Apply mask to watermark
$watermark->compositeImage($mask, Imagick::COMPOSITE_COPYOPACITY, 0, 0);

// Overlay watermark on image
$image->compositeImage($watermark, Imagick::COMPOSITE_DISSOLVE, 0, 0);

// Set output image format
$image->setImageFormat('png');

// Output the new image
header('Content-type: image/png');
echo $image;

The result is:

Several images are created in this example. The first image $watermark is grey-scale, and the second image $mask uses pure black for the parts I want to be transparent and white for the parts I want to keep. When I apply the mask by combining the images, any grey shades found in $mask as a result of anti-aliasing will be semi-transparent and result in smoother edges.

In the command-line version of the code, the outside edges of the transparent image are clipped before the watermark is overlayed, but there appears to be a bug that prevents the compositeImage() method from preserving the position defined by setGravity(). This basically means that if I were to clip the edges then my watermark would lose its place at the bottom right and be re-positioned in the top-left corner. To get around this foolish behavior, I’ve created my palettes with the same dimensions as the source image so no clipping takes place.

Tiled Text

This final example tiles text over the entire image which makes it far more difficult to remove.

Command line:

convert -size 140x80 xc:none -fill grey 
    -gravity NorthWest -draw "text 10,10 'Copyright'" 
    -gravity SouthEast -draw "text 5,15 'Copyright'" 
    miff:- | 
composite -tile - image.png  result.png

PHP:

<?php
// Create objects
$image = new Imagick('image.png');
$watermark = new Imagick();

// Watermark text
$text = 'Copyright';

// Create a new drawing palette
$draw = new ImagickDraw();
$watermark->newImage(140, 80, new ImagickPixel('none'));

// Set font properties
$draw->setFont('Arial');
$draw->setFillColor('grey');
$draw->setFillOpacity(.5);

// Position text at the top left of the watermark
$draw->setGravity(Imagick::GRAVITY_NORTHWEST);

// Draw text on the watermark
$watermark->annotateImage($draw, 10, 10, 0, $text);

// Position text at the bottom right of the watermark
$draw->setGravity(Imagick::GRAVITY_SOUTHEAST);

// Draw text on the watermark
$watermark->annotateImage($draw, 5, 15, 0, $text);

// Repeatedly overlay watermark on image
for ($w = 0; $w < $image->getImageWidth(); $w += 140) {
    for ($h = 0; $h < $image->getImageHeight(); $h += 80) {
        $image->compositeImage($watermark, Imagick::COMPOSITE_OVER, $w, $h);
    }
}

// Set output image format
$image->setImageFormat('png');

// Output the new image
header('Content-type: image/png');
echo $image;

Result:

Notice that I’ve set the transparency with setFillOpacity() rather than using an image mask.

Summary

For me, image manipulation in PHP has become one of the most enjoyable aspects of the language, and I can only hope that Imagick will become bundled in future versions. If you’re looking for a way to contribute, I encourage you to convert other command line examples to PHP and then post your results on the official PHP manual so others can learn and enjoy.

Image via Fotolia

Free book: Jump Start HTML5 Basics

Grab a free copy of one our latest ebooks! Packed with hints and tips on HTML5's most powerful new features.

  • Mohamed Zahran

    Just in time :)
    Thank You.

  • http://www.avforums.com Stuart Wright

    Thanks Martin,
    how would you create an inner drop shadow in text?
    The outer one is documented in php
    e.g. outer light drop shadow to create ‘cut-out’ text :
    $layer = $text_layer->clone();
    $layer->setImageBackgroundColor( new ImagickPixel( ‘white’ ) );
    $layer->shadowImage( 70, .3, 1, 1 );
    But how would you create an inner drop shadow?
    Thanks

    • http://www.psinas.com Martin Psinas

      Stuart, I’m not sure about that. For that type of effect I would probably use the CSS box-shadow property which is supported in all major browsers.

  • http://www.dresjkdsj.co Robertus

    whether it can be applied in wordpress?

  • Mohan

    Hi Martin,
    I am trying to write text on image, but it can’t display. I look like black page.