9
votes

Given the result of a call to imagettfbbox(), what is the correct, pixel-perfect point to provide to imagettftext() such that the text will not extend beyond its bounding box?

I am determining the width/height and x/y of the baseline from the bounding box like this:

$box = imagettfbbox($size, $angle, $font, $text);
$boxXCoords = array($box[0], $box[2], $box[4], $box[6]);
$boxYCoords = array($box[1], $box[3], $box[5], $box[7]);
$boxWidth = max($boxXCoords) - min($boxXCoords);
$boxHeight = max($boxYCoords) - min($boxYCoords);
$boxBaseX = abs(min($boxXCoords));
$boxBaseY = abs(min($boxYCoords));

I then draw a filled rectangle on my image of the dimensions of the bounding box:

imagefilledrectangle($image, 0, 0, $boxWidth - 1, $boxHeight - 1, $color);

After that, I draw the text:

imagettftext($image, $size, $angle, $boxBaseX, $boxBaseY, $color, $font, $text);

However, this causes the text to extend beyond the rectangle by a pixel or two. I have seen several attempts to fix this issue on PHP's imagettfbbox() documentation, but they all just suggest substracting a pixel or two here and there, which seems like a hack to me. What's happening here, and why should we need to fudge the numbers to get things right?

4
This is obviously an incredibly old question, but I'm experiencing a similar issue and was wondering if you resolved the issue?ndg

4 Answers

7
votes

I believe there is no perfect way to place text with single-pixel precision on an image based on what imagettfbbox() returns and also using .ttf non-monospaced fonts. Over at the PHP manual many users have posted ways to accomplish this (with and without fudging the numbers); I recommend using jodybrabec's simple function over at the PHP manual, which calculates the exact bounding box. I have tested this one and only in extreme cases is the text positioned at most 1 pixel off in one direction. Nonetheless, if you add some padding (even if it is just 2 or 3 pixels) to your image your text will be within the dimensions of the image 100% of the time.

0
votes

What happens when you don't subtract one from each of the dimensions in this line:

imagefilledrectangle($image, 0, 0, $boxWidth - 1, $boxHeight - 1, $color);

and instead do this:

imagefilledrectangle($image, 0, 0, $boxWidth, $boxHeight, $color);
0
votes

The SlightlyMagic HQ Card Generator project renders cards for the strategy card game Magic: the Gathering. The generator is powered by PHP with an advanced text rendering engine built in. I don't know about logic behind the calculations, but the renderer is dead accurate for the purposes of this application. Here's the function that calculates proper bounding boxes (HQ Card Generator 8.x/scripts/classes/font.php):

private function convertBoundingBox ($bbox) {
    // Transform the results of imagettfbbox into usable (and correct!) values.
    if ($bbox[0] >= -1)
        $xOffset = -abs($bbox[0] + 1);
    else
        $xOffset = abs($bbox[0] + 2);
    $width = abs($bbox[2] - $bbox[0]);
    if ($bbox[0] < -1) $width = abs($bbox[2]) + abs($bbox[0]) - 1;
    $yOffset = abs($bbox[5] + 1);
    if ($bbox[5] >= -1) $yOffset = -$yOffset;
    $height = abs($bbox[7]) - abs($bbox[1]);
    if ($bbox[3] > 0) $height = abs($bbox[7] - $bbox[1]) - 1;
    return array(
        'width' => $width,
        'height' => $height,
        'xOffset' => $xOffset, // Using xCoord + xOffset with imagettftext puts the left most pixel of the text at xCoord.
        'yOffset' => $yOffset, // Using yCoord + yOffset with imagettftext puts the top most pixel of the text at yCoord.
        'belowBasepoint' => max(0, $bbox[1])
    );
}
-1
votes

I know this is a little late but imagettfbbox is in points not pixels.

pixel font size in imagettftext instead of point size