I'm trying to print multiple lines of text on an image and center align them.
i.e.
This isA string of text
Right now, I only have the left position for the whole string. Any shortcuts on getting that to work? I think it might have to be a getttfbox on the whole string, then an explode on the line breaks, then center the new text inside that larger ttfbox. That's a pain in the ass...
EDIT: Came up with a solution:
foreach ( $strings as $index => $string ) {
$parts = explode ( "\n", $string['string'] );
if ( count ( $parts ) > 1 ) {
$bounds = imagettfbbox ( intval($string['fontsize']), 0, $font, $string['string'] );
$width = $bounds[2] - $bounds[0];
$height = $bounds[3] - $bounds[5];
$line_height = $height / count ( $parts );
foreach ( $parts as $index => $part ) {
$bounds = imagettfbbox ( intval($string['fontsize']), 0, $font, $part );
$new_width = $bounds[2] - $bounds[0];
$diff = ( $width - $new_width ) / 2;
$new_left = $string['left'] + $diff;
$new_string = $string;
$new_string['left'] = $new_left;
$new_string['top'] = $string['top'] + ($index * $line_height);
$new_string['string'] = $part;
$new_strings[] = $new_string;
}
}
}
if ( $new_strings )
$strings = $new_strings;
In this case, each $string is an array with some information about how and what to print.
Hope that helps someone.
You can use stil/gd-text class. Disclaimer: I am the author.
<?php
use GDText\Box;
use GDText\Color;
$img = imagecreatefromjpeg('image.jpg');
$textbox = new Box($img);
$textbox->setFontSize(12);
$textbox->setFontFace('arial.ttf');
$textbox->setFontColor(new Color(0, 0, 0));
$textbox->setBox(
50, // distance from left edge
50, // distance from top edge
200, // textbox width
100 // textbox height
);
// now we have to align the text horizontally and vertically inside the textbox
$textbox->setTextAlign('center', 'top');
// it accepts multiline text
$textbox->draw("This is\na string of text");
Demonstration:
Here is a function that will use the imagettfbbox you mentioned, I can't help with the automatic wordwrapping, but maybe as you suggested, split the string ahead of time.
function imagettftext_cr(&$im, $size, $angle, $x, $y, $color, $fontfile, $text)
{
$bbox = imagettfbbox($size, $angle, $fontfile, $text);
$dx = ($bbox[2]-$bbox[0])/2.0 - ($bbox[2]-$bbox[4])/2.0;
$dy = ($bbox[3]-$bbox[1])/2.0 + ($bbox[7]-$bbox[1])/2.0;
$px = $x-$dx;
$py = $y-$dy;
return imagettftext($im, $size, $angle, $px, $py, $color, $fontfile, $text);
}
Edit: Also found this in the PHP documentation comments...
Here's a simple function to wrap text going into an image. It'll wrap
onto as many lines as it needs to, but $angle has to be zero. The
$width parameter is the width of the image.
function wrap($fontSize, $angle, $fontFace, $string, $width)
{
$ret = "";
$arr = explode(' ', $string);
foreach ( $arr as $word )
{
$teststring = $ret.' '.$word;
$testbox = imagettfbbox($fontSize, $angle, $fontFace, $teststring);
if ( $testbox[2] > $width ){
$ret.=($ret==""?"":"\n").$word;
} else {
$ret.=($ret==""?"":' ').$word;
}
}
return $ret;
}
You could use the excellent gd-text library.
If you use composer, an example could be this:
<?php
require __DIR__.'/../vendor/autoload.php';
use GDText\Box;
$im = imagecreatetruecolor(500, 500);
$backgroundColor = imagecolorallocate($im, 0, 18, 64);
imagefill($im, 0, 0, $backgroundColor);
$box = new Box($im);
$box->setFontFace(__DIR__.'/Pacifico.ttf'); // http://www.dafont.com/pacifico.font
$box->setFontSize(80);
$box->setFontColor([255, 255, 255]);
$box->setTextShadow([0, 0, 0, 50], 0, -2);
$box->setLeading(0.7);
$box->setBox(20, 20, 460, 460);
$box->setTextAlign('center', 'center');
$box->draw("Pacifico");
header("Content-type: image/png");
imagepng($im);
This is my code which works:
function textWithImage($text){
$imagePath="/images/fb-share-bg.jpg";
$im = imagecreatefromjpeg($imagePath);
$txtColor = imageColorAllocate($im, 255, 255, 255);
$font="/fonts/OpenSans-Bold.ttf";
$description=$text;
$in = wordwrap($description,50,"|",true);
$st = explode("|",$in);
//$inStrArr=str_split($description,60);
$inStrArr=$st;
$addOnYVal=0;
foreach($inStrArr as $key=>$value){
if($key!=0){
$addOnYVal+=40;
}
list($x, $y) = pc_ImageTTFCenter($im, $value, $font, 20,$addOnYVal);
ImageTTFText($im,20,0, $x, $y, $txtColor, $font, $value);
}
$newImagename="/uploads/".$img;
imagejpeg($im, $newImagename,100);
} // Enf of textWithImage function
function pc_ImageTTFCenter($image, $text, $font, $size, $addOnYVal) {
// find the size of the image
$xi = ImageSX($image);
$yi = ImageSY($image);
// find the size of the text
$box = ImageTTFBBox($size, $angle, $font, $text);
$xr = abs(max($box[2], $box[4]));
$yr = abs(max($box[5], $box[7]));
// compute centering
$x = intval(($xi - $xr) / 2);
$y = intval(($yi + $yr) / 2) + + $addOnYVal;;
return array($x, $y);
}
// Calling function
$text = "This is to center alignn the text on image";
textWithImage($text);
// Output: Image will be saved in the upload folder
Get the length of the string (strlen), and an average width of each letter, multiply the strlen result by the average width and then subtract that from your horizontal position.
$text="center this";
$h=50;
$h=(strlen($text)*15)-$h;
Tell me if it works because I have never tried this
Related
I would like to put on a picture in vertical text in PHP:
function scrivi($scrivi, $p) {
$imgResource = imagecreatefromjpeg($p);
$textcolor = imagecolorallocate($imgResource, 255, 255, 255);
$fontPath = "st.ttf";
$fontSize = "18";
$rotation = "270"; // counter-clockwise rotation
$text = "this is a text";
$textCoords = imagettfbbox($fontSize, $rotation, $fontPath, $text);
$x = 36;
$y = 36;
imagettftext($imgResource, $fontSize, $rotation, $x, $y, $textcolor, $fontPath, $text);
unlink($p);
imagejpeg($imgResource, $p, 100);
imagedestroy($imgResource);
}
It works well only that I would like that the letters are turned this is an example using the function
Instead I would like to
an idea could be to wrap each letter
All you really need to do is split the text into an array, loop it, then offset the y by the height + leading of the font character:
function scrivi($p,$text)
{
$imgResource = imagecreatefromjpeg($p);
$textcolor = imagecolorallocate($imgResource, 255,255, 255);
$fontPath = __DIR__."/st.ttf";
$fontSize = "18";
$x = 36 ;
$y = 36;
foreach(str_split($text) as $char) {
$textCoords = imagettfbbox($fontSize, 0, $fontPath, $char);
imagettftext($imgResource, $fontSize, 0, $x, $y, $textcolor,$fontPath,$char);
$y += 24;
}
unlink($p);
imagejpeg($imgResource,$p,100);
imagedestroy($imgResource);
}
scrivi('http://imgtops.sourceforge.net/bakeoff/bw.jpg',"Cats are great");
Gives you:
(Image credit: http://imgtops.sourceforge.net/bakeoff/)
Today I want to make a function that parses the text into lines, guided by imageTTFBbox(). I created this piece of code but it's limited to 2 lines. I want to make the same, but in infinite lines. Thanks for your help! :D
function printe($image, $image_width, $string, $font_size, $y, $color, $font){
$font = "fonts/" . $font . ".ttf";
$limit = $image_width - 20;
$tsize = #imageTTFBbox($font_size,0, $font, $string);
$twidth = abs($tsize[4] - $tsize[0]);
$words = explode(" ", $string);
$text = ''; $text1 = '';$a = 0;$o = 0;
for($i = 0; $i < count($words); $i++){
$tsize = #imageTTFBbox($font_size,0, $font, $words[$i]." ");
$twidth = abs($tsize[4] - $tsize[0]);
$nw = $twidth + $a;
if($nw > $limit OR $o > 0){
$o++;
$text1 .= $words[$i]." ";
}else{
$text .= $words[$i]. " ";
$a =$a+$twidth;
}
}
$txtcolor = processColor($color);
$t1size = #imageTTFBbox($font_size,0, $font, $text);
$t1width = abs($t1size[4] - $t1size[0]);
$t2size = #imageTTFBbox($font_size, 0, $font, $text1);
$t2width = abs($t2size[4] - $t2size[0]);
$center = ceil($image_width / 2);
$y1 = $y; $y2 = $y;
$xcord1 = ($image_width/2)-($t1width/2)+3;
$xcord2 = ($image_width/2)-($t2width/2)+3;
$y2 = $y + ($font_size * 1.5);
imagettftext($image, $font_size, 0, $xcord1, $y1, $txtcolor, $font, $text);
imagettftext($image, $font_size, 0, $xcord2, $y2, $txtcolor, $font, $text1);
}
Even if not fully tested, I wrote this code starting from an example found on the PHP manual:
<?php
function write_multiline_text($image, $font_size, $color, $font, $text, $start_x, $start_y, $max_width) {
//split the string
//build new string word for word
//check everytime you add a word if string still fits
//otherwise, remove last word, post current string and start fresh on a new line
$words = explode(" ", $text);
$string = "";
$tmp_string = "";
for($i = 0; $i < count($words); $i++) {
$tmp_string .= $words[$i]." ";
//check size of string
$dim = imagettfbbox($font_size, 0, $font, $tmp_string);
if($dim[4] < ($max_width - $start_x)) {
$string = $tmp_string;
$curr_width = $dim[4];
} else {
$i--;
$tmp_string = "";
$start_xx = $start_x + round(($max_width - $curr_width - $start_x) / 2);
imagettftext($image, $font_size, 0, $start_xx, $start_y, $color, $font, $string);
$string = "";
$start_y += abs($dim[5]) * 2;
$curr_width = 0;
}
}
$start_xx = $start_x + round(($max_width - $dim[4] - $start_x) / 2);
imagettftext($image, $font_size, 0, $start_xx, $start_y, $color, $font, $string);
}
// Create a 300x300 image
$im = imagecreatetruecolor(300, 300);
$black = imagecolorallocate($im, 0, 0, 0);
$white = imagecolorallocate($im, 255, 255, 255);
// Set the background to be white
imagefilledrectangle($im, 1, 1, 298, 298, $white);
// Path to our font file
$font = 'c:/windows/fonts/arial.ttf';
$text = "This is a very ";
$text .= "long long long long long long long long long long long long long long long long ";
$text .= "long long long long long long long long long long long long long long long long ";
$text .= "line of text";
write_multiline_text($im, 12, $black, $font, $text, 10, 22, 298);
// Output to browser
header('Content-Type: image/png');
imagepng($im);
imagedestroy($im);
?>
And here is the resulting output image:
If you don't want the text to be centered, you must substitute the variable $start_xx with $start_x in the two calls to the function imagettftext.
Does anybody have a function that draws a ttf string (imagettftext) with specified letter spacing?
I cannot find any build-in GD function so I think that it should be done letter by letter adding some constant width.
Maybe someone have such function already :)
ps. the best font will be arial.ttf
function imagettftextSp($image, $size, $angle, $x, $y, $color, $font, $text, $spacing = 0)
{
if ($spacing == 0)
{
imagettftext($image, $size, $angle, $x, $y, $color, $font, $text);
}
else
{
$temp_x = $x;
for ($i = 0; $i < strlen($text); $i++)
{
$bbox = imagettftext($image, $size, $angle, $temp_x, $y, $color, $font, $text[$i]);
$temp_x += $spacing + ($bbox[2] - $bbox[0]);
}
}
}
and the call:
imagettftextSp($image, 30, 0, 30, 30, $black, 'arial.ttf', $text, 23);
Function parameters order meets standard imagettftext parameters order, and the last parameter is optional $spacing parameter. If not set or the passed value is 0, the kerning / letter spacing is not set.
I know this was answered a while back, but I needed a solution that had letter spacing and maintained the angular offsets.
I modified radzi's code to accomplish this:
function imagettftextSp($image, $size, $angle, $x, $y, $color, $font, $text, $spacing = 0)
{
if ($spacing == 0)
{
imagettftext($image, $size, $angle, $x, $y, $color, $font, $text);
}
else
{
$temp_x = $x;
$temp_y = $y;
for ($i = 0; $i < strlen($text); $i++)
{
imagettftext($image, $size, $angle, $temp_x, $temp_y, $color, $font, $text[$i]);
$bbox = imagettfbbox($size, 0, $font, $text[$i]);
$temp_x += cos(deg2rad($angle)) * ($spacing + ($bbox[2] - $bbox[0]));
$temp_y -= sin(deg2rad($angle)) * ($spacing + ($bbox[2] - $bbox[0]));
}
}
}
Just to complete pidalia's answer (which is the best) to avoid some trouble with special char (like "é" or "à")
static function imagettftextSp($image, $size, $angle, $x, $y, $color, $font, $text, $spacing = 0) {
if ($spacing == 0) {
imagettftext($image, $size, $angle, $x, $y, $color, $font, $text);
} else {
$temp_x = $x;
$temp_y = $y;
//to avoid special char problems
$char_array = preg_split('//u',$text, -1, PREG_SPLIT_NO_EMPTY);
foreach($char_array as $char) {
imagettftext($image, $size, $angle, $temp_x, $temp_y, $color, $font, $char);
$bbox = imagettfbbox($size, 0, $font, $char);
$temp_x += cos(deg2rad($angle)) * ($spacing + ($bbox[2] - $bbox[0]));
$temp_y -= sin(deg2rad($angle)) * ($spacing + ($bbox[2] - $bbox[0]));
}
}
}
GD doesn't support kerning, so you'll have to do it manually. Personally, I wrote a function that would write each letter separately. I can't find it right now, but it's something along the lines of:
function drawText(&$image, $text, $fgColor, $font, $fgColor,
$fontSize = 14, $kerning = 0, $x = 0, $y = 0) {
$letters = explode('', $text);
foreach ($letters as $n => $letter) {
$bbox = imagettftext($image, $fontSize, 0, $x, $y, $fgColor, $font, $letter);
$x += $bbox[2] + $kerning;
}
}
I have tried all the answers here and none seems to do a decent job. If you draw the bounding box, this is what happens:
Clearly they are not evenly spaced. It appears that the bounding box, returned by imagettftext() and imagettfbbox(), only tells you what is drawn. This might seem enough, but it isn't, because of font kerning. This means that even when you say a letter should be draw at (x,y), that that will not be one of the coordinates of the bounding box. A correction for kerning is needed.
I botched together this code for horizontal text:
function getBBoxW($bBox)
{
return $bBox[2] - $bBox[0];
}
function imagettftextSpacing($image, $size, $x, $y, $color, $font, $text, $spacing = 0)
{
$testStr = 'test';
$testW = getBBoxW(imagettfbbox($size, 0, $font, $testStr));
foreach (mb_str_split($text) as $char)
{
$fullBox = imagettfbbox($size, 0, $font, $char . $testStr);
imagettftext($image, $size, 0, $x - $fullBox[0], $y, $color, $font, $char);
$x += $spacing + getBBoxW($fullBox) - $testW;
}
}
The results are much better. Note that the $testStr can have any value.
Here's an example of the result, the first line is normal text, the second line has a negative spacing:
Try this function:
$image = imagecreatetruecolor(500,200);
$text = "Text to print";
$text_color=imagecolorallocate($image,255,255,255);
$font_size = 18;
$space = 8;
$font = "path_to_font/arial.ttf";
$x=20;
$y=20;
for ($i = 0; $i <strlen($text); $i++){
$arr = imagettftext ($image, $font_size,0, $x, $y, $text_color, $font, $text{$i});
$x = $arr[4]+$space;
}
imagejpeg($image);
destroyimage($image);
I was wondering if anyone knows how I could remove or replace these annoying N over L (I'm guessing means newline) from my php string before printing them to an image.
the text looks like this
any help much appreciated as I have wasted hours on this little problem...
<?php
exec('/usr/games/fortune -s', $fortune);
for($i = 0; $i <= count($fortune); $i++) {
$text = "$text $fortune[$i]";
}
$image = imagecreatefrompng("rex.png");
$color = imagecolorallocate($image, 0, 0, 0);
$newtext = wordwrap($text, 35, "\n", true);
$newertext2 = explode ("\n", $newtext);
imagestring ($image, 3, 0, 0, $newertext2[0], $color);
imagestring ($image, 3, 0, 11, $newertext2[1], $color);
imagestring ($image, 3, 0, 22, $newertext2[2], $color);
imagestring ($image, 3, 0, 33, $newertext2[3], $color);
imagestring ($image, 3, 0, 44, $newertext2[4], $color);
imagestring ($image, 3, 0, 55, $newertext2[5], $color);
imagestring ($image, 3, 0, 66, $newertext2[6], $color);
imagestring ($image, 3, 0, 77, $newertext2[7], $color);
header("Content-type: image/png");
imagepng($image);
?>
$newertext2 = explode ("\n", $newtext);
imagestring ($image, 3, 0, 0, $newertext[0], $color);
Shouldn't you print the letters in newertext2 instead of newertext ?
Late answer:
I found out that every whitespace character except of new line and space is replaced by a "NL" symbol. e.g. the tab sign.
A solution could be to replace them like this:
preg_replace all spaces
Of course you should replace by a space instead of a underscore ;)
Here is a function for write the content by respecting whitespaces.
This function will not exceed the image's sizes limits.
If a new line is not able to be added entirely in once, it will be exploded in smallest lines.
/**
* #author Booteille
*
* #param resource $image
* #param int $font
* #param int $x
* #param int $y
* #param string $string
* #param int $color
*/
function whitespaces_imagestring($image, $font, $x, $y, $string, $color) {
$font_height = imagefontheight($font);
$font_width = imagefontwidth($font);
$image_height = imagesy($image);
$image_width = imagesx($image);
$max_characters = (int) ($image_width - $x) / $font_width ;
$next_offset_y = $y;
$i = 0;
$exploded_string = explode("\n", $string);
$i_count = count($exploded_string);
for(; $i < $i_count; ++$i) {
$exploded_wrapped_string = explode("\n", wordwrap(str_replace("\t", " ", $exploded_string[$i]), $max_characters, "\n"));
$j = 0;
$j_count = count($exploded_wrapped_string);
for(; $j < $j_count; ++$j) {
imagestring($image, $font, $x, $next_offset_y, $exploded_wrapped_string[$j], $color);
$next_offset_y += $font_height;
if($next_offset_y >= $image_height - $y) {
return;
}
}
}
}
I have a script that generates images from text using PHP. It's working fine except that I would like it to generate multiline text as well with differing colors. How can it be done using PHP, GD and Freetype? Below is the code I use to generate single line text images.
$textval = 'This is some text to be an image';
$textcolor = '666666';
$font="arial.ttf";
$size = 9;
$padding= 1;
$bgcolor= "ffffff";
$transparent = 0;
$antialias = 0;
$fontfile = $fontpath.$font;
$box= imageftbbox( $size, 0, $fontfile, $textval, array());
$boxwidth= $box[4];
$boxheight= abs($box[3]) + abs($box[5]);
$width= $boxwidth + ($padding*2) + 1;
$height= $boxheight + ($padding) + 0;
$textx= $padding;
$texty= ($boxheight - abs($box[3])) + $padding;
// create the image
$png= imagecreate($width, $height);
$color = str_replace("#","",$bgcolor);
$red = hexdec(substr($bgcolor,0,2));
$green = hexdec(substr($bgcolor,2,2));
$blue = hexdec(substr($bgcolor,4,2));
$bg = imagecolorallocate($png, $red, $green, $blue);
$color = str_replace("#","",$textcolor);
$red = hexdec(substr($textcolor,0,2));
$green = hexdec(substr($textcolor,2,2));
$blue = hexdec(substr($textcolor,4,2));
$tx = imagecolorallocate($png, $red, $green, $blue);
imagettftext( $png, $size, 0, $textx, $texty, $tx, $fontfile, $textval );
header("content-type: image/jpeg");
imagejpeg($png);
imagedestroy($png);
exit;
Add this function to wrap the text before it goes into your function.
function wrap($fontSize, $angle, $fontFace, $string, $width){
$ret = "";
$arr = explode(' ', $string);
foreach ( $arr as $word ){
$teststring = $ret.' '.$word;
$testbox = imagettfbbox($fontSize, $angle, $fontFace, $teststring);
if ( $testbox[2] > $width ){
$ret.=($ret==""?"":"\n").$word;
} else {
$ret.=($ret==""?"":' ').$word;
}
}
return $ret;
}
Source: http://www.php.net/imagettftext