Practical PHP: Watermark or manipulate images without tempfiles.

For various reasons, you might find that you wish to find your whole data stream, and post-process it before you send it to the browser. A practical reason is to support ‘Content-Length:’ to increase speed, and cachability of your program, or to manipulate the data again before it’s fully sent to the client. Sometimes, this can be difficult.

For this example, we’ll examine my silly little ‘who are you’ image generator. (Note that this old version does not have features of the new one, but they’re highly semantical (the image buffering and code to display the user’s country, as well as cleaning the code up a bit. I love tenary operators!

In PHP, you want to buffer your content with ob_start().

Here’s a snippit of my local copy’s functioning code: ...
ob_start();
imagejpeg($im);
$imageBuffer = ob_get_contents();
ob_end_clean();
header(‘Last-Modified: ’.gmdate(‘D, d M Y H:i:s’).’ GMT’);
header(‘Content-Length: ’. strlen($imageBuffer));
header(“Content-type: image/jpeg”);
echo(”$imageBuffer”);
imagedestroy($im);

As you see, I tell PHP to begin buffering my output. Then, I tell PHP to create my image with the data stored in $im. I then read that data into a temporary buffer, rather than wasting overhead with a temporary file. I tell it to cleanly end the buffer, and begin the process of displaying my image. I obtain the size of the image, which will be used to specify the length of the image, display it, and cleanly exit.

It is possible (and rather often) for images and other data to be created with no buffering, but then the only trivial solution (I can think of) would be to cause further overhead to obtain the size of our generated image. In this case, it’s not overly necessary, but for a product, as say, a thumbnail generator, I’d strongly suggest buffering, as opposed to creating an interm tempfile.

A sample function for such (say, adding watermarks) might look like:

function waterMark($originalFile, $watermarkFile) { $watermarkSource = imagecreatefromjpeg($watermarkFile); $originalSource = imagecreatefromjpeg($originalFile); ... imagecopymerge($originalSource, $watermarkSource, $originalSourceSizeX, $originalSourceSizeY, 0, 0, $fileWidth, $fileHeight, 100); ob_start(); imagejpeg($originalSource); $imageBuffer = ob_get_contents(); ob_end_clean(); imagedestroy($originalSource); imagedestroy($watermarkSource); return $imageBuffer; }

Thus, you’ll have your watermarked image returned to you as a string; to write out to a file, stuff into an array, or if you’re really sadistic, dumped into a SQL database! :)

Design notes: I chose to set the Last-Modified header as to not cache the image; after all, it is entirely dynamic. I also decided to use ‘echo’ rather than ‘print’ for a rather marginal speed increase, as it does not set a return value.