Oh fiddlesticks
Something's gone wrong here. Try again and see if it works, if it doesn't you're allowed to shout at me.

Generating previews with imagettftext

Posted: November 15th, 2013
Tagged: ,

Long time no blog!

I’ve been so busy at work recently I haven’t had any time to work on other stuff, and for the record, I’ve now got plans to rewrite my sort-of-game engine under Haxe 3 and OpenFL, so whenever I get around to that expect some new articles!

But today I have a little php nugget I had to put together recently that I think someone will find useful!

In the past I’ve dabbled with generating images dynamically in php – usually basic stuff, website statistics etc. But recently I had to do something a little more advanced in generating preview images of letters that people would be paying for. This involved placing a large amount of text within a series of template images – including ones with oddly shaped spaces where the text would have to go, which presents the problem of getting it to fit in the gap. I guess you could brute force it – manually position each line – but that’s frankly ludicrous and not future-friendly at all.

So, let’s make a solution.

The inspiration for my solution eventually came from a Stack Overflow question, which essentially explains there’s no nice way in php to do it and you have to implement a solution yourself. I knew I had existing Haxe code which calculated the intersection point of a pair of line segment and figured I could recycle it to help me out. Let’s look at how we’re storing data first:

A simple array of points defining a polygon defines the area we want our text to fill, with a set of points to for each design you’re populating – from this data we use another simple function to find the min and max X and Y values:

That’s awesome and all, but let’s get to the really interesting functions: getWidthAtPoint takes the current Y value in the and returns an array of the form [target width, start X], which it does by iterating around the array of polygon points and finding the line segments on the left and right side which intersect with the current Y value. Those line segments are then passed through _line_seg_intersection which finds the X coordinate along those lines where the Y value crosses.

That’s most of it, so let’s wrap up with the last few things you need to make this work. You have to run explode() on your text to seperate in to paragraphs – which allows you to space those differently to ordinary lines (I double space them), and then explode() again in to seperate words so you can wrap them. If you wanted to wrap by letter and not word, just run over each letter. Now we need to loop through this mess of arrays and build each line of text word by word – checking the length at each step until we overstep the limit. This code probably isn’t as clean as it could be, but it works…

(Helpful note: the getStartXY function is basically the same as the minMaxXY function, but only returns the minimums)

I’m sure there’s a better way to work out the font height, but as you can see I’ve just estimated it based on the same metric as the font size – just mess around and find the right value for the font you’re using, or suggest something better in the comments.

I ran this code on a reasonably complex polygon with around 12 vertices and it worked perfectly (and reasonably fast), so this code is pretty suitable for whatever you might want to do. I’ve also covered the case of the text being larger than the box in my code thanks to the minMaxXY function, essentially clamping the Y value to the maximum so it just maintains the same width after the end of the box. Here’s an example of the sort of output this script generates in the task I designed it for:

letter_preview

That’s all folks, hope this has been a useful post for someone!
— w