• PHP
    October 21, 2009

Typographic headlines with PHP (Part 1)

Guest Author

My recent blog post about scaling images with PHP gave me the idea to write something about creating typographic headlines with PHP. At Apache Friends we're using this technique since many years to get rid of the usual boring and everywhere available "web fonts" like Helvetica, Times and Verdana.

For this example I chose the font Tusj by Norwegian graphic designer Magnus Cederholm. Okay, this font will only work for very large headlines, but it's looks cool and it's perfect for this demo's purposes because the TTF file is very huge (1.5 MB) and that makes the processing in PHP quite slow. (Yes, in this case, I want to slow down my PHP script.)

The Basics

First, I define some basic parameter: TTF font file, the font size, and an example text.

$font_file = "./FFF Tusj.ttf";
$font_size = 64;
$text = "An Example";

In the next step I've to find out, how big my image needs to be to take the rendered text. That's not so trivial because it strongly depends on the used characters, the choosen size and of course the font itself. To solve this problem PHP offers the imagettfbbox() function:

$bb = imagettfbbox($font_size, 0, $font_file, $text);
$bb_width = $bb[4]-$bb[6];
$bb_height = $bb[3]-$bb[5];

With imagecreate() I now can create the image using $bb_width and $bb_width for the size:

$image = imagecreate($bb_width, $bb_height);    

Define two colors: one for the background and one for the foreground.

$fillcolor = imagecolorallocate($image, 255, 255, 255);    
$fontcolor = imagecolorallocate($image, 69, 138, 186);

Fill the background using $fillcolor:

imagefill($image, 0, 0, $fillcolor);    

Render the text to the image:

imagettftext($image, $font_size, 0, abs($bb[6]),$bb_height-$bb[3], $fontcolor,

$font_file, $text);

I don't want to go into the details of this function, for a detailed explanation of all parameters please refer to the PHP manual.

And finally send it to the browser:

header("Content-Type: image/png");   

Basically that's all you need to do. Here's the output of the above PHP script:


Because I used imagecreate() and imagepng() the file I got is an indexed-colored 8-bit PNG with a file size of 8 KB.

Some Variations

If I use imagecreatetruecolor() and imagepng() I will get a truecolored 24-bit PNG with a file size of 41 KB:


And if I use imagejpeg() instead of imagepng() I will get a truecolored 24-bit
JPEG with a quality setting of 100 and a file size of 41 KB:


All three variations look exactly the same, but the indexd-colored 8-bit PNG is the smallest one. So for this purpose an 8-bit PNG seems to be the best choice.

Turning off antialiasing?

By default imagettftext() uses an antialiasing algorithm to smooth the output. Using the negative of a color index turns this feature off:

imagettftext ($image, $font_size, 0, abs($bb[6]),$bb_height-$bb[3], -$font_color, 

$font_file, $text);

Sometimes (usually in case of small font sizes) this will give you a sharper and better looking result, but in this special case it definitely looks worse:


Welcome to the Real World

As I mentioned in the beginning, I intentionally chose a font based on a very large TTF file, which makes it very expensive for PHP to render a headline. Let's take a look at some quick benchmark results:

% ab -n 1000 http://demo/headline.php
This is ApacheBench, Version 2.3
Benchmarking demo (be patient)
Document Path: /headline.php
Document Length: 7888 bytes
Requests per second: 33.52 [#/sec] (mean)

Autsch... 33 requests per second is far to slow for a real world scenario. Yes, if I had chosen a smaller font the results would be much better, but probably the script will still be not suitable for use in a production environment. However, a simple caching mechanism should easily solve this issue.

But not today, stay tuned for part 2 of this tutorial. Live long and prosper.

Be the first to comment

Comments ( 0 )
Please enter your name.Please provide a valid email address.Please enter a comment.CAPTCHA challenge response provided was incorrect. Please try again.