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