Center text on a curve, PHP GD - php

I'm attempting to wrap text around a curve using the function found below. However, this does not take into account aligning the text centered no matter how wide the text maybe be. As an attempt to solve this problem I calculated the width of the text using a binding box.
$type_space=imagettfbbox($fontSize, 0, $fontname, $text);
$image_width = abs($type_space[4] - $type_space[0]) + 10;
However, from this, I need to work out a way that it can adjust the start $angle based on the width. I've played around with lots of settings, however just can't seem to find the correct maths. Any help would be amazing, thank you!
Curve Function
function imagettftextarc($image, $size, $angle, $x, $y, $r, $color, $fontfile, $text, $dir=false){
$sbox=imagettfbbox($size, 0, $fontfile, ' ');
$sbox=($sbox[2]-$sbox[0])*0.1;
$angle=$angle*M_PI/180;
foreach(preg_split('//u', $text) AS $t){
$px=$x+$r*cos($angle);
$py=$y+$r*sin($angle);
$dirangle=(360-(M_PI/2+$angle)*180/M_PI+($dir?180:0))%360;
imagettftext($image, $size, $dirangle, $px, $py, $color, $fontfile, $t);
$box=imagettfbbox($size, 0, $fontfile, $t);
$dx=$box[2]-$box[0];
$da=abs(asin(($dx+$sbox)/$r));
if($dir){
$angle-=$da;
}else{
$angle+=$da;
}
}
}
Possible Solution
I'm not sure how to execute this but the possibility could be to work out the length of the curve if it was a full 360 circle unwrapped horizontally. Then divide the new width with the unwrapped width to create the value that correlates to how many degrees it needs to be altered.

Related

Imagick annotateImage: how to set text position from the top left

I'm trying to create a wrapper function around annotateImage to be able to set the exact top and left positions of a given text. The default method sets the y-position from the baseline, which means there has to be a lot of experimentation involved if one wants to draw a text at an exact spot on an image. This is what I mean...
$image->annotateImage($draw, 0, 0, 0, 'The quick brown fox');
In the above code, the text is invisible because the y position is 0. So to fix this, I've started with the following function where I add an offset of 40 to y...
function addText($image, $draw, $x, $y, $text) {
$y = $y + 40;
$image->annotateImage($draw, $x, $y, 0, $text);
}
addText($image, $draw, 0, 0, 'The quick brown fox'); // draw at 0, 0
But it's not very reliable because it doesn't take into account any factors such as font size, etc...
What's the best way to achieve this?
I've found a solution that works well. If the gravity is set to 'NorthWest', the text tends to keep its x y position from the top left.
$draw->setGravity(Imagick::GRAVITY_NORTHWEST);
$image->annotateImage($draw, 0, 0, 0, 'The quick brown fox');

PHP imagettftext() center text

I want to center the text I add to my image using :
imagettftext($image, 85, 0, 250, 350, $color, $font, $txt );
I tried something like this :
$fontwidth1 = imagefontwidth($font);
$center1 = (imagesx($image)/2) - ($fontwidth1*(strlen($txt)/2));
However unfortunately it's not working.
The imagefontwidth($font) part does not work :(
Anyone has faced this issue before and know an solution / alternative method ?
The function imagefontwidth works best with fixed-width fonts. Like Austin Brunkhorst said, the most reliable way to get centered text uses imagettfbbox, like so:
$bbox = imagettfbbox(85, 0, $font, $txt);
$center1 = (imagesx($image) / 2) - (($bbox[2] - $bbox[0]) / 2);

What is the correct way to determine text coordinates a from bounding box?

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?
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.
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);
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])
);
}
I know this is a little late but imagettfbbox is in points not pixels.
pixel font size in imagettftext instead of point size

PHP imagettftext baseline workaround

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.
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.
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);
?>

PHP: creating a smooth edged circle, image or font?

I'm making a PHP image script that will create circles at a given radius.
I used:
<?php
imagefilledellipse ( $image, $cx, $cy, $w, $h, $color );
?>
but hate the rough edges it produces. So I was thinking of making or using a circle font that I will output using:
<?php
imagettftext ( $image, $size, $angle, $x, $y, $color, 'fontfile.ttf', $text );
?>
So that the font will produce a circle that has a smooth edge. My problem is making the "font size" match the "radius size".
Any ideas? Or maybe a PHP class that will produce a smooth edge on a circle would be great!
Thank you.
for quick and dirty anti-aliasing, make the image twice the desired size, then down-sample to the desired size.
$circleSize=90;
$canvasSize=100;
$imageX2 = imagecreatetruecolor($canvasSize*2, $canvasSize*2);
$bg = imagecolorallocate($imageX2, 255, 255, 255);
$col_ellipse = imagecolorallocate($imageX2, 204, 0, 0);
imagefilledellipse($imageX2, $canvasSize, $canvasSize, $circleSize*2, $circleSize*2, $col_ellipse);
$imageOut = imagecreatetruecolor($canvasSize, $canvasSize);
imagecopyresampled($imageOut, $imageX2, 0, 0, 0, 0, $canvasSize, $canvasSize, $canvasSize*2, $canvasSize*2);
header("Content-type: image/png");
imagepng($imageOut);
Clever idea, I like that!
But maybe this PHP class already does the trick: Antialiased filled Arcs/Ellipses for PHP (GD)
In many cases websites need dynamically created images: pie charts, rounded corners, menu buttons, etc. This list is endless. PHP, or more precisely the GD library, provides filled elliptical arcs and ellipses, but they are not antialiased. Therefore I have written a PHP function to render filled antialiased elliptical arcs or filled antialiased ellipses (as well as circles..) with PHP easily. Drawing these filled arcs is now a one-liner.
Cairo does antialiasing well.

Categories