9

I am writing to print text to an image using PHP. However, the function imagettftext() uses the baseline, whereas I need the text vertically centered.

So, I either need a method to print text with y not the distance from top to baseline, but from top to top of bounding box OR I need a method using which I could determine the distance between top of bounding box and baseline.

Apparently, I am confusing you. So, to make it clear: I am aware of the function imagettfbbox(). Using that function I can determine height and width of resulting text box. Its height, however, is utterly useless for vertical alignment when printing with imagettftext(), because the Y parameter is not the distance to the top of the box (or even the bottom, but at least something I could have used having the height) but the distance to the baseline of the text within.

EDIT: Why am I not accepting the latest answer?

See my latest comment below the answer, and use this image as a reference.

Kara
  • 5,650
  • 15
  • 48
  • 55
arik
  • 23,480
  • 35
  • 91
  • 147
  • What you have to do is calculate the coordinates of where you want the text to go. Use imagettfbbox to see what size your text will be, then subtract it from the height of the image and divide by 2. – Kavi Siegel Jul 18 '11 at 18:50
  • Well, that's the thing! Imagettfbbox() gives me the bounding box, great but utterly useless for vertical alignment, because imagettftext doesn't use the distance to the top of the bounding box or the bottom, but instead to the baseline, which makes aligning text difficult, for I have no idea of the position of the baseline realtive to the text box. – arik Jul 18 '11 at 18:54
  • Thanks for asking this, I am also looking for an answer, but it doesn't seem that one is available. I am generating some ID tags, and want the name to fill the available space. For me the issue is letters which hang down (qypjg...). Words with those letters take up the available height (calculated using imagettfbbox) at a smaller font size, but don't fill the space because they hang below the bottom of the space. Not the end of the world, but it does make things look inconsistent and off centre. – Dallin Dec 20 '12 at 10:32

2 Answers2

11

I do not know if the answer still interested.However, the imagettfbbox() function give you more information than simply the height and the width of the bounding box. It's designed exactly to return information needed by the imagettftext() to manage the text as you want.

The trick lies in the fact that the coordinates returned from imagettfbbox() are not related to the absolute top left corner, but to the baseline of the font for the particular text. This is the reason because the box is specified in point coordinates, and these are often negative.

In short:

$dims = imagettfbbox($fontsize, 0, $font, $text);

$ascent = abs($dims[7]);
$descent = abs($dims[1]);
$width = abs($dims[0])+abs($dims[2]);
$height = $ascent+$descent;

...

// In the example code, for the vertical centering of the text, consider
// the simple following formula

$y = (($imageHeight/2) - ($height/2)) + $ascent;

This works perfectly for my projects. Hope this help.

Sorry for english. Marco.

Marco Jacovone
  • 248
  • 3
  • 9
  • Wow, Marco, that is exactly what I had been looking for all this time. I thought I had found a solution by adding a third of the font size to the height (because that seemed about the difference between baseline and bottom), but your solution is more precise. Thank you! – arik Feb 21 '13 at 11:55
5

Not entirely sure what your asking...can you give an example? Perhaps imagettfbbox is what you need?

// get bounding box dims
$dims = imagettfbbox($fontsize, 0, $font, $quote);

// do some math to find out the actual width and height
$width = $dims[4] - $dims[6]; // upper-right x minus upper-left x 
$height = $dims[3] - $dims[5]; // lower-right y minus upper-right y

edit: Here is an example of vertically centered text

<?php
$font = 'arial.ttf';
$fontsize = 100;
$imageX = 500;
$imageY = 500;

// text
$text = "FOOBAR";

// create a bounding box for the text
$dims = imagettfbbox($fontsize, 0, $font, $text);

// height of bounding box (your text)
$bbox_height = $dims[3] - $dims[5]; // lower-right y minus upper-right y

// Create image
$image = imagecreatetruecolor($imageX,$imageY);

// background color
$bgcolor = imagecolorallocate($image, 0, 0, 0);

// text color
$fontcolor = imagecolorallocate($image, 255, 255, 255);

// fill in the background with the background color
imagefilledrectangle($image, 0, 0, $imageX, $imageY, $bgcolor);

$x = 0; 
$y = (($imageY/2) - ($bbox_height/2)) + $fontsize;
imagettftext($image, $fontsize, 0, $x, $y , $fontcolor, $font, $text);

// tell the browser that the content is an image
header('Content-type: image/png');
// output image to the browser
imagepng($image);

// delete the image resource 
imagedestroy($image);
?>
Crayon Violent
  • 30,524
  • 3
  • 51
  • 74
  • 2
    No, that's not what I need. I am aware of the function imagettfbbox, but it calculates the bounding box, whereas imagettftext uses the distance to the BASELINE as the Y parameter, not to the top or bottom of the box. – arik Jul 18 '11 at 18:58
  • okay, I think you are confusing yourself or overthinking here. If you want to vertically align text, Yes you do need imagettfbbox(). To get the y argument for imagettftext(), you take the height of the image divided by 2. That is your vertical center, let's call that $ivc. Then you take the height calculated from imagettfbbox() and divide that by 2, lets call that $tvc. Then, $y = $ivc - $tvc; – Crayon Violent Jul 18 '11 at 20:21
  • also, you need to add the fontsize to $y to account for that. edited to add an example for vertically aligned text – Crayon Violent Jul 18 '11 at 20:38
  • 1
    No, I had tried this once. It's wrong, as shown in the graphics I have created: http://img855.imageshack.us/img855/9894/alignment.jpg – arik Jul 19 '11 at 14:43
  • Well I don't know what to tell you, perhaps it is the font you are using or perhaps a charset you are using. Maybe it is buggy or not fully supported or the GD library is otherwise having trouble with it. Because my example code as-is works just fine. – Crayon Violent Jul 19 '11 at 15:02
  • Well, that's the strange. But did you try your example using different texts like 'o', 'L' and 'Ä' as single letters? Because the thing is, with varying height of the letter varies the necessary offset to the baseline, but, which is most important: the offset from the top of the bounding box to the baseline varies depending on the height of the highest letter(s). Your example only works with strings that contain at least one 'tall' character, e. g. 'b', 'd', 'A', 'C' or any other upper-case letter. If it does not contain tall characters, the text is too low, thus not vertically aligned. – arik Jul 19 '11 at 22:01
  • Okay, I see what you're saying, but I don't think there is a built-in "workaround" for that, as it is based on the font template. You would basically need to a) expect that all your text be lowercase or not have any "tall" characters, b) somehow keep a map of "real pixel heights" of every letter/character in the font template vs. font size you use, and calculate that into the offset, instead of the font template font-size. But honestly, IMO I don't see why you are nickel and diming a few pixels. What if your text was an underscore, should that then be completely... – Crayon Violent Jul 20 '11 at 02:25
  • ...centered, looking like a hyphen? different letters aren't supposed to be exactly centered like that, that's not how words are written. I mean I'm not trying to say you aren't "allowed" to do that...just sayin'... try writing out (or typing in a paint program) a test phrase with diff chars, and vertically center all of the chars individually...it makes the text look jagged. – Crayon Violent Jul 20 '11 at 02:26
  • Well, firstly, I did not mean to center each character individually, but the whole text box. The text is not supposed to look jugged, but, as stated earlier, those letters were examples for the whole text, e. g. if the whole text were an 'o', 'L', 'Ä', respectively, ... Surely, it's not overly important (usually) to have the text perfectly centered, but still, I was wondering whether there was such a possibility. Thanks for your effort, however :) – arik Jul 20 '11 at 06:53
  • why did you add $font_size? please explain. The solution works but I cannot understand why do we need + $font_size here to centralize the text vertically? – user2867106 Aug 11 '15 at 04:38