imagettfbbox() returns wrong dimensions when using space characters inside text

1.5k Views Asked by At

I'm creating a graphical menu dynamically from PHP, it results in only 1 image that looks like this:

One    Two    Three    Four

The problem however is, that I have to determine the x-offset and the width of each page title (e.g. the left offset and the width of "One" in pixels) to position the image with css.

All works fine, except for page titles that contain spaces - imagettfbbox() returns the wrong positions. I am using an arial font (TTF)

Any suggestions on how I get around this issue?

Edit: I got it working now, using the following function to determine the text's bounds:

function calculateTextBox($font_size, $font_angle, $font_file, $text) {
    $box = imagettfbbox($font_size, $font_angle, $font_file, $text);

    $min_x = min(array($box[0], $box[2], $box[4], $box[6]));
    $max_x = max(array($box[0], $box[2], $box[4], $box[6]));
    $min_y = min(array($box[1], $box[3], $box[5], $box[7]));
    $max_y = max(array($box[1], $box[3], $box[5], $box[7]));

    return array(
        'left' => ($min_x >= -1) ? -abs($min_x + 1) : abs($min_x + 2),
        'top' => abs($min_y),
        'width' => $max_x - $min_x,
        'height' => $max_y - $min_y,
        'box' => $box
    );
}

Edit 2: Unfortunately I keep getting wrong dimensions when using different font sizes and font files...

2

There are 2 best solutions below

0
On

Actually, its real bounds can be calculated from imagettftext function's return values.

1
On

One way to do it would be to use the imagecolorat function to work out the colour of a pixel at particular point in the image, its not going to be the quickest or even necessarily the best way to do it but it should work.

Something like this (searching for black pixels) should work, and will return the bounds you're looking for which you can then translate into x/ co-ordinates if you want:

function get_bounds($image)
{
    $height = imagesy($image);
    $width = imagesx($image);


    $to_return = new stdClass();
    $to_return->top = null;
    $to_return->bottom = null;
    $to_return->left = null;
    $to_return->right = null;

    for ($x = 0; $x < $width; $x++)
    {
        for ($y = 0; $y < $height; $y++)
        {
            $color = imagecolorat($image, $x, $y);
            $rgb = imagecolorsforindex($image, $color);

            // If its black
            if (($rgb['red'] == 0) && ($rgb['green'] == 0) && ($rgb['blue'] == 0))
            {
                if (($y < $to_return->top) || is_null($to_return->top))
                {
                    $to_return->top = $y;
                }

                if (($y > $to_return->bottom) || is_null($to_return->bottom))
                {
                    $to_return->bottom = $y;
                }

                if (($x < $to_return->left) || is_null($to_return->left))
                {
                    $to_return->left = $x;
                }

                if (($x > $to_return->right) || is_null($to_return->right))
                {
                    $to_return->right = $x;
                }
            }
        }
    }

    return $to_return;
}