Why are my last words being cut off?

370 Views Asked by At

I'm using imagettftext and imageTTFBbox to convert strings to images of strings.

For example, this string below

This planet has — or rather had — a problem, which was this: most of the people living on it were unhappy for pretty much all of the time. Many solutions were suggested for this problem, but most of these were largely concerned with the movement of small green pieces of paper, which was odd because on the whole it wasn't the small green pieces of paper that were unhappy.

becomes

img

However, the last line or so is being cut off.

My setup consists of the following: a wrapper function to wrap text (working perfectly fine), and the main img2Text function that's shown below:

function imagettfJustifytext($text, $font="../../fonts/Roboto-Light.ttf", $Justify=2, $W=0, $H=0, $X=0, $Y=0, $fsize=12, $color=array(0x0,0x0,0x0), $bgcolor=array(0xFF,0xFF,0xFF)){
    $angle = 0;
    $L_R_C = $Justify; 
    $_bx = imageTTFBbox($fsize,0,$font,$text);

    $W = ($W==0)?abs($_bx[2]-$_bx[0]):$W;
    $H = ($H==0)?abs($_bx[5]-$_bx[3]):$H;

    $im = @imagecreate($W, $H)
    or die("Cannot Initialize new GD image stream");

    $background_color = imagecolorallocate($im, $bgcolor[0], $bgcolor[1], $bgcolor[2]);
    $text_color = imagecolorallocate($im, $color[0], $color[1], $color[2]); 

    if($L_R_C == 0){ //Justify Left
        imagettftext($im, $fsize, $angle, $X, $fsize, $text_color, $font, $text);
    } elseif ($L_R_C == 1) { //Justify Right
        $s = split("[\n]+", $text);
        $__H=0;
        foreach($s as $key=>$val){
            $_b = imageTTFBbox($fsize,0,$font,$val);
            $_W = abs($_b[2]-$_b[0]); 
            $_X = $W-$_W;
            $_H = abs($_b[5]-$_b[3]);  
            $__H += $_H;              
            imagettftext($im, $fsize, $angle, $_X, $__H, $text_color, $font, $val);    
            $__H += 6;
        }
    } elseif ($L_R_C == 2) { //Justify Center
        $s = split("[\n]+", $text);
        $__H=0;
        foreach($s as $key=>$val){
            $_b = imageTTFBbox($fsize,0,$font,$val);
            $_W = abs($_b[2]-$_b[0]); 
            $_X = abs($W/2)-abs($_W/2);
            $_H = abs($_b[5]-$_b[3]);  
            $__H += $_H;              
            imagettftext($im, $fsize, $angle, $_X, $__H, $text_color, $font, $val);    
            $__H += 6;
        }
    }        
    return $im;
  }

I believe the problem lies in the imageTTFBox built-in function or my usage of it to calculate the height of the image. It seems to underestimate the height with longer strings of text. The relevant line of code is line 6, which I have reproduced below for convenience:

$H = ($H==0)?abs($_bx[5]-$_bx[3]):$H;

for reference:

$_bx = imageTTFBbox($fsize,0,$font,$text);

and for the uninitiated imageTTFBbox:

array imagettfbbox ( float $size , float $angle , string $fontfile , string $text )

This function calculates and returns the bounding box in pixels for a TrueType text.

EDIT

timclutton's answer makes sense, and I've tried removing the $__H += 6; line and it does help. Now only the very last bit gets cut off. (see image below)

img2

1

There are 1 best solutions below

2
On BEST ANSWER

The image is created at the start of the function based on the dimensions returned by imagettfbbox(), but then at the end of your foreach loop you add a 'line-spacing' of +6 via $__H += 6;. This causes the vertical text size to increase beyond the originally measured dimensions.

You can test this by removing that line and seeing that the text now fits into the image.

If you want to include additional line-spacing you should create a variable for it at the start of the function and then create the image based on the dimensions returned by imagettfbbox() plus the line-spacing value multiplied by the number of lines.


I've rewritten your function as the following:

function imagettftextalign($text, $align = 0, $fsize = 12, $color = array(0x0, 0x0, 0x0), $bgcolor = array(0xFF, 0xFF, 0xFF), $font = './font/Roboto-Light.ttf', $angle = 0)
{
    // measure input text.
    $box = imagettfbbox($fsize, $angle, $font, $text);
    $w = abs($box[2] - $box[0]);
    $h = abs($box[5] - $box[3]);

    // create resources.
    $im = imagecreatetruecolor($w, $h);
    $background_color = imagecolorallocate($im, $bgcolor[0], $bgcolor[1], $bgcolor[2]);
    $text_color = imagecolorallocate($im, $color[0], $color[1], $color[2]);

    // set background.
    imagefilledrectangle($im, 0, 0, $w, $h, $background_color);

    // split text by line and get line height.
    $lines = explode("\n", $text);
    $lh = floor($h / count($lines));

    // output lines.
    $y = ($lh * .8); // note: this is a guesstimate at the font baseline!
    foreach ($lines as $line) {
        if ($align > 0) {
            $box = imagettfbbox($fsize, $angle, $font, $line);
            $lw = abs($box[2] - $box[0]); // line width.
            switch ($align) {
                case 1: // centre.
                    $x = ($w / 2) - ($lw / 2);
                    break;
                case 2: // right.
                    $x = $w - $lw;
                    break;
            }
        } else {
            $x = 0;
        }

        imagettftext($im, $fsize, $angle, $x, $y, $text_color, $font, $line);
        $y += $lh; // increment line y position.
    }

    return $im;
}

$txt = "This planet has — or rather had — a problem, which
was this: most of the people living on it were unhappy
for pretty much all of the time. Many solutions were
suggested for this problem, but most of these were
largely concerned with the movement of small green
pieces of paper, which was odd because on the whole
it wasn't the small green pieces of paper that were
unhappy.";

$im = imagettftextalign($txt, 1);

header('Content-type: image/png');
imagepng($im);

This gives me the following output, which seems to be what you are aiming for:

example of function output