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
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/)
I was hoping to get in touch with someone on a situation that I cannot find the solution to anywhere.
I am trying to create a captcha on my website using php and although I was able to create an image and create the random captcha text.
I am unable to over lay the two. Here is my code:
<?PHP
session_start();
function generateRandomString($length = 10) {
$letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
$len = strlen($letters);
$letter = $letters[rand(0, $len - 1)];
$text_color = imagecolorallocate($image, 0, 0, 0);
$word = "";
for ($i = 0; $i < 6; $i++) {
$letter = $letters[rand(0, $len - 1)];
imagestring($image, 7, 5 + ($i * 30), 20, $letter, $text_color);
$word .= $letter;
}
$_SESSION['captcha_string'] = $word;
}
function security_image(){
// $code = isset($_SESSION['captcha']) ? $_SESSION['captcha'] : generate_code();
//$font = 'content/fonts/comic.ttf';
$width = '110';
$height = '20';
$font_size = $height * 0.75;
// $image = #imagecreate($width, $height) or die('GD not installed');
global $image;
$image = imagecreatetruecolor($width, $height) or die("Cannot Initialize new GD image stream");
$background_color = imagecolorallocate($image, 255, 255, 255);
imagefilledrectangle($image,0,0,200,50,$background_color);
$line_color = imagecolorallocate($image, 64,64,64);
for($i=0;$i<10;$i++) {
imageline($image,0,rand()%50,200,rand()%50,$line_color);
}
$pixel_color = imagecolorallocate($image, 0,0,255);
for($i=0;$i<1000;$i++) {
imagesetpixel($image,rand()%200,rand()%50,$pixel_color);
}
//$textbox = imagettfbbox($font_size, 0, $font, $code);
//$textbox = imagettfbbox($font_size, 0, $randomString);
$x = ($width - $textbox[4]) / 2;
$y = ($height - $textbox[5]) / 2;
// imagettftext($image, $font_size, 0, $x, $y, $text_color, $font , $code);
imagettftext($image, $font_size, 0, $x, $y, $text_color , $word);
$images = glob("*.png");
foreach ($images as $image_to_delete) {
#unlink($image_to_delete);
}
imagepng($image, "image" . $_SESSION['count'] . ".png");
header('Content-Type: image/png');
imagepng($image);
imagedestroy($image);
}
security_image();
?>
I have no idea what I’m doing wrong. I’ve spent over 10 hours on this “display text” issue. I don’t understand and I am desperate for help. I even downloaded working captcha version from other resources that break once I upload it to my server. I have no idea whats going on. At first I thought there was something wrong with my server but the fact that I can even create the pixels, lines image means that it is at least working.
Please help!!!!
UPDATE---------------------------------------------
Thank you for your suggestions. Here is the edited code. I'm still getting the same issue.
<?PHP
session_start();
function security_image(){
global $image;
// $code = isset($_SESSION['captcha']) ? $_SESSION['captcha'] : generate_code();
$font = 'content/fonts/comic.ttf';
$width = '110';
$height = '20';
$font_size = $height * 0.75;
$image = imagecreatetruecolor($width, $height) or die("Cannot Initialize new GD image stream");
$background_color = imagecolorallocate($image, 255, 255, 255);
imagefilledrectangle($image,0,0,200,50,$background_color);
$line_color = imagecolorallocate($image, 64,64,64);
for($i=0;$i<10;$i++) {
imageline($image,0,rand()%50,200,rand()%50,$line_color);
}
$pixel_color = imagecolorallocate($image, 0,0,255);
for($i=0;$i<1000;$i++) {
imagesetpixel($image,rand()%200,rand()%50,$pixel_color);
}
$letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
$len = strlen($letters);
$letter = $letters[rand(0, $len - 1)];
$text_color = imagecolorallocate($image, 0, 0, 0);
$word = "";
for ($i = 0; $i < 6; $i++) {
$letter = $letters[rand(0, $len - 1)];
imagestring($image, 7, 5 + ($i * 30), 20, $letter, $text_color);
$word .= $letter;
}
$_SESSION['captcha_string'] = $word;
/*texbox unitinitialized (removed for the sake of just showing the word size doesnt matter)
$x = ($width - $textbox[4]) / 2;
$y = ($height - $textbox[5]) / 2;
*/
$x = ($width) / 2;
$y = ($height) / 2;
imagettftext($image,$font, 4, $x, $y, $word);
header('Content-Type: image/png');
imagepng($image);
imagedestroy($image);
}
security_image();?>
i made some suggested changes but the code seems to still do the same thing. Just display the lines and pixels as expected but the text still is missing... ?
There are some several "errors" in your functions, let's fix them:
In generateRandomString()
generateRandomString($length = 10)
$lenght is not used its scope.
$text_color = imagecolorallocate($image, 0, 0, 0);
$image is uninitialized
In security_image() function:
$textbox is uninitialized
$text_color and $word is uninitialized.
And Wrong parameter count for imagettftext() You add 7 parameters, and forget the font file parameter.
found the problem. using this:
http://php.net/manual/en/function.imagettftext.php
i was able to see that the font location was incorrect. using the example on that page and editing it to my needs worked.
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.
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
The PHP code below generates text as a dynamically created image, how would I be able to get the image to only be as large as the text? Thanks.
<?php
header('Content-Type: image/jpeg');
$text='Test';
$img = imageCreate(200,200);
imagecolorallocate($img, 255, 255, 255);
$textColor = imagecolorallocate($img, 0, 0, 0);
imagefttext($img, 15, 0, 0, 55, $textColor, 'bgtbt.ttf', $text);
imagejpeg($img);
imagedestroy($img);
?>
UPDATE 1: I found the answer here with the example of the original poster - Creating IMage from Text in PHP - how can I make multiline?
UPDATE 2: Martin Geisler's version also works well
When using a TrueType font, you use the imageftbbox function to obtain the bounding box for a string typeset with your font. The bounding box gives the offsets from the base-point to the four corners in the rectangle occupied by the text. So if you store the bounding box in $bb and use imagefttext to put text at ($x, $y), then the corners will have these coordinates:
($x + $bb[6], $y + $bb[7]) ($x + $bb[4], $y + $bb[5])
+-------+
| Hello |
+-------+
($x + $bb[0], $y + $bb[1]) ($x + $bb[2], $y + $bb[3])
That tells us that we want an image width of ($x + $bb[2]) - ($x + $bb[6]) = $bb[2] - $bb[6] and similarly an image height of $bb[3] - $bb[7]. The text should then be rendered at coordinates (-$bb[6], -$bb[7]) inside that picture since we want to have
(0, 0) = ($x + $bb[6], $y + $bb[7]) ==> $x = -$bb[6] and $y = -$bb[7]
You can try it out with this code. Put it into a file called img.php and browse to img.php?q=Hello to test:
<?php
header("Content-type: image/png");
$q = $_REQUEST['q'];
$font = "Impact.ttf";
$size = 30;
$bbox = imageftbbox($size, 0, $font, $q);
$width = $bbox[2] - $bbox[6];
$height = $bbox[3] - $bbox[7];
$im = imagecreatetruecolor($width, $height);
$green = imagecolorallocate($im, 60, 240, 60);
imagefttext($im, $size, 0, -$bbox[6], -$bbox[7], $green, $font, $q);
imagepng($im);
imagedestroy($im);
?>
If you use the bitmap fonts instead, then look at the imagefontwidth and imagefontheight functions.
#Martin Geisler's answer is almost correct, but I couldn't get my text to fit completely inside the image. I tried this instead, which works perfectly!
From the PHP Manual's User Contributed Notes:
$text = "<?php echo \"hello, world\"; ?>";
$font = "./arial.ttf";
$size = "60";
$bbox = imagettfbbox($size, 0, $font, $text);
$width = abs($bbox[2] - $bbox[0]);
$height = abs($bbox[7] - $bbox[1]);
$image = imagecreatetruecolor($width, $height);
$bgcolor = imagecolorallocate($image, 255, 255, 255);
$color = imagecolorallocate($image, 0, 0, 0);
$x = $bbox[0] + ($width / 2) - ($bbox[4] / 2);
$y = $bbox[1] + ($height / 2) - ($bbox[5] / 2);
imagefilledrectangle($image, 0, 0, $width - 1, $height - 1, $bgcolor);
imagettftext($image, $size, 0, $x, $y, $color, $font, $text);
$last_pixel= imagecolorat($image, 0, 0);
for ($j = 0; $j < $height; $j++)
{
for ($i = 0; $i < $width; $i++)
{
if (isset($blank_left) && $i >= $blank_left)
{
break;
}
if (imagecolorat($image, $i, $j) !== $last_pixel)
{
if (!isset($blank_top))
{
$blank_top = $j;
}
$blank_left = $i;
break;
}
$last_pixel = imagecolorat($image, $i, $j);
}
}
$x -= $blank_left;
$y -= $blank_top;
imagefilledrectangle($image, 0, 0, $width - 1, $height - 1, $bgcolor);
imagettftext($image, $size, 0, $x, $y, $color, $font, $text);
header('Content-type: image/png');
imagepng($image);
imagedestroy($image);