Forum Moderators: coopster

Message Too Old, No Replies

GD wrapping text inside image example

gd how to wrap text inside an image

         

innoscape

3:53 pm on Aug 16, 2009 (gmt 0)

10+ Year Member



I found this great post here which helped me considerably.
[webmasterworld.com ]

I would like to implement a section of this text wrap function inside the above codes loop.

<?php
function make_wrapped_txt($txt, $color = 000000, $space = 4, $font = 4, $w = 300)
{
if (strlen($color) != 6)
$color = 000000;
$int = hexdec($color);
$h = imagefontheight($font);
$fw = imagefontwidth($font);
$txt = explode("\n", wordwrap($txt, ($w / $fw), "\n"));
$lines = count($txt);
$im = imagecreate($w, (($h * $lines) + ($lines * $space)));
$bg = imagecolorallocate($im, 255, 255, 255);
$color = imagecolorallocate($im, 0xFF & ($int >> 0x10), 0xFF & ($int >> 0x8),
0xFF & $int);
$y = 0;
foreach ($txt as $text)
{
$x = (($w - ($fw * strlen($text))) / 2);
imagestring($im, $font, $x, $y, $text, $color);
$y += ($h + $space);
}
header('Content-type: image/jpeg');
die(imagejpeg($im));
}
?>

Here is the modified version of the code that works for me based on the post I found in the forum here. THANKS AGAIN!
(sorry so verbose, still learning gd)

<?php 
header("Content-type: image/png");

$imgHgt = 300;
$imgWidth= 400;
/*$imgColorHex="487239";*/
$txt1 = $_GET['text1'];
$txt2 = $_GET['text2'];
$txt3 = $_GET['text3'];
$txt4 = $_GET['text4'];
$txt5 = $_GET['text5'];
$txt6 = $_GET['text6'];
$txt = array($txt1,$txt2,$txt3,$txt4,$txt5,$txt6);

$fntSize = array(18,14,12,10,12,24);
$fntHex = array('00FF00','FFFFFF','000000','00FF00','ff00ff','ff0000');
$fntShad = array(1,0,0,0,0,0);
putenv('GDFONTPATH=' . realpath('.'));
$fnt = array('arial','arialbd','verdana','verdanab','verdanab','verdanab');
// Fonts are in the same directory as the script //
// Yes, I will make the arrays properly in the end //
/* eg foreach($_GET as $keyname => $value) { or sim */
// may also just make a function out of it all and call it //
// from the PERL script I made that parses the form data. //
/*did not need $font = "arial"; */
/*did not need $background = @imagecreate($imgWidth, $imgHgt) or die("Cannot Initialize new GD image stream"); */
/* did not need $background_color = imagecolorallocate($background, 0, 0, 254); */
$background = imagecreatefrompng("image/blank.png");
$t = count($txt);
$totY = 5;
$i = 0;
while ($i < $t)
{

if($fntShad[$i] == 1)
{
$bbox = imageftbbox($fntSize[$i], 0, $fnt[$i], $txt[$i], array("linespacing" => 1));
$width = abs($bbox[0]) + abs($bbox[2]);
$height = abs($bbox) + abs($bbox[5]);
$xcoord = (($imgWidth - $width) / 2) + 2;
$ycoord = $height + $totY + 2;
$int = hexdec("000000");
$arr = array("red" => 0xFF & ($int >> 0x10),
"green" => 0xFF & ($int >> 0x8),
"blue" => 0xFF & $int);
$tcolor = ImageColorAllocate($background, $arr["red"], $arr["green"], $arr["blue"]);

imagettftext($background, $fntSize[$i], 0, $xcoord, $ycoord, $tcolor, $fnt[$i], $txt[$i]);
$totY = $ycoord + 5;
$i++;
}
else
{

$bbox = imageftbbox($fntSize[$i], 0, $fnt[$i], $txt[$i], array("linespacing" => 1));
$width = abs($bbox[0]) + abs($bbox[2]);
$height = abs($bbox[1]) + abs($bbox[5]);
$xcoord = (($imgWidth - $width) / 2) + 2;
$ycoord = $height + $totY + 2;
$int = hexdec($fntHex[$i]);
$arr = array("red" => 0xFF & ($int >> 0x10),
"green" => 0xFF & ($int >> 0x8),
"blue" => 0xFF & $int);
$tcolor = ImageColorAllocate($background, $arr["red"], $arr["green"], $arr["blue"]);

imagettftext($background, $fntSize[$i], 0, $xcoord, $ycoord, $tcolor, $fnt[$i], $txt[$i]);
$totY = $ycoord + 5;
$i++;
}
}
imagepng($background);
ImageDestroy($background);
}
?>

All help / suggetion is appreciated... Don't worry about the shaded font part of the loop, just one section and I can take it from there. I think..

Thanks for any assistance...

innoscape

1:11 pm on Aug 17, 2009 (gmt 0)

10+ Year Member



I am still struggling with word wrap in GD.

Here is another snippet of cod I found that I will be using for a reference.

<?php
//the width of the biggest char @
$fontwidth = 11;

//each chargroup has char-ords that have the same proportional displaying width
$chargroup[0] = array(64);
$chargroup[1] = array(37,87,119);
$chargroup[2] = array(65,71,77,79,81,86,89,109);
$chargroup[3] = array(38,66,67,68,72,75,78,82,83,85,88,90);
$chargroup[4] = array(35,36,43,48,49,50,51,52,53,54,55,56,57,60,61,62,63, 69,70,76,80,84,95,97,98,99,100,101,103,104,110,111,112, 113,115,117,118,120,121,122,126);
$chargroup[5] = array(74,94,107);
$chargroup[6] = array(34,40,41,42,45,96,102,114,123,125);
$chargroup[7] = array(44,46,47,58,59,91,92,93,116);
$chargroup[8] = array(33,39,73,105,106,108,124);

//how the displaying width are compared to the biggest char width
$chargroup_relwidth[0] = 1; //is char @
$chargroup_relwidth[1] = 0.909413854;
$chargroup_relwidth[2] = 0.728241563;
$chargroup_relwidth[3] = 0.637655417;
$chargroup_relwidth[4] = 0.547069272;
$chargroup_relwidth[5] = 0.456483126;
$chargroup_relwidth[6] = 0.36589698;
$chargroup_relwidth[7] = 0.275310835;
$chargroup_relwidth[8] = 0.184724689;

//build fast array
$char_relwidth = null;
for ($i=0;$i<count($chargroup);$i++){
for ($j=0;$j<count($chargroup[$i]);$j++){
$char_relwidth[$chargroup[$i][$j]] = $chargroup_relwidth[$i];
}
}

//get the display width (in pixels) of a string
function get_str_width($str){
global $fontwidth,$char_relwidth;
$result = 0;
for ($i=0;$i<strlen($str);$i++){
$result += $char_relwidth[ord($str[$i])];
}
$result = $result * $fontwidth;
return $result;
}

//truncates a string at a certain displaying pixel width
function truncate_str_at_width($str, $width, $trunstr='...'){
global $fontwidth,$char_relwidth;
$trunstr_width = get_str_width($trunstr);
$width -= $trunstr_width;
$width = $width/$fontwidth;
$w = 0;
for ($i=0;$i<strlen($str);$i++){
$w += $char_relwidth[ord($str[$i])];
if ($w > $width)
break;
}
$result = substr($str,0,$i).$trunstr;
return $result;

}
?>

I know I am close to being able to determine the width of the text image based on the font used and breaking it into two strings at a whitespace, then remove that whitespace. Even if I must hard code all the font size vars.

Please help! I am stuck!

innoscape

8:04 pm on Aug 18, 2009 (gmt 0)

10+ Year Member



Still no luck... I am trying to call the wrap routine from inside my original routines loop.

<?php
header("Content-type: image/png");

$imgHgt = 300;
$imgWidth= 400;
$txt1 = $_GET['text1'];
$txt2 = $_GET['text2'];
$txt3 = $_GET['text3'];
$txt4 = $_GET['text4'];
$txt5 = $_GET['text5'];
$txt6 = $_GET['text6'];
$txt = array($txt1,$txt2,$txt3,$txt4,$txt5,$txt6);

$fntSize = array(18,14,12,10,12,24);
$fntHex = array('00FF00','FFFFFF','000000','00FF00','ff00ff','ff0000');
putenv('GDFONTPATH=' . realpath('.'));
$fnt = array('arial','arialbd','verdana','verdanab','verdanab','verdanab');
$background = imagecreatefrompng("images/blank.png");
$t = count($txt);
$totY = 5;
$i = 0;
while ($i < $t) {

$bbox = imageftbbox($fntSize[$i], 0, $fnt[$i], $txt[$i], array("linespacing" => 1));
$width = abs($bbox[0]) + abs($bbox[2]);
$height = abs($bbox[1]) + abs($bbox[5]);
$xcoord = (($imgWidth - $width) / 2) + 2;
$ycoord = $height + $totY + 2;
$int = hexdec($fntHex[$i]);
$arr = array("red" => 0xFF & ($int >> 0x10),
"green" => 0xFF & ($int >> 0x8),
"blue" => 0xFF & $int);
$tcolor = ImageColorAllocate($background, $arr["red"], $arr["green"], $arr["blue"]);

//WOULD I CALL IT HERE?
make_wrapped_txt($txt[$i], $color = $fntHex[$i], $space = 4, $font = $fnt[$i], $w = 380);
//Then do I do with the below line?

imagettftext($background, $fntSize[$i], 0, $xcoord, $ycoord, $tcolor, $fnt[$i], $txt[$i]);
$totY = $ycoord + 5;
$i++;

}
imagepng($background);
ImageDestroy($background);

function make_wrapped_txt($txt, $color = 000000, $space = 4, $font = 4, $w = 300)
{
if (strlen($color) != 6)
$color = 000000;
$int = hexdec($color);
$h = imagefontheight($font);
$fw = imagefontwidth($font);
$txt = explode("\n", wordwrap($txt, ($w / $fw), "\n"));
$lines = count($txt);
$im = imagecreate($w, (($h * $lines) + ($lines * $space)));
$bg = imagecolorallocate($im, 255, 255, 255);
$color = imagecolorallocate($im, 0xFF & ($int >> 0x10), 0xFF & ($int >> 0x8),
0xFF & $int);
$y = 0;
foreach ($txt as $text)
{
$x = (($w - ($fw * strlen($text))) / 2);
imagestring($im, $font, $x, $y, $text, $color);
$y += ($h + $space);
}

//I get rid of these last two?

header('Content-type: image/jpeg');
die(imagejpeg($im));

}
?>

Please help!

innoscape

9:12 pm on Aug 18, 2009 (gmt 0)

10+ Year Member



Ok, I am really trying to figure this out. Maybe I need to go to another forum that is geared more to folks like me, clueless.

Instead of trying to figure out GD with this old a$$ brain, I attempted to just attack it another way.

I decided to try and multiply the character size by the amount of characters to determine the strings length on my own. Instead of hacking up others code, figure it out myself. Then I thought what the heck, now I would need to split it on whitespace, calculate length again and so on. No dice. What if there was no whitespace or other delimiter?

So, I figured I should add up the characters (multiplied by size) until I reached the max width $var up to each whitespace then split. But, if no (white)space delimiter, split at the max set width $var as a last resort.

Then, for each new string created, send it through the GD loop.

(The height is not a problem, the page that sends over the variables displays an html preview of what the generated image should resemble.)

Great right? Sounds feasible! Maybe for someone else, not me. I got as far as:

<?php 
header("Content-type: image/png");

$imgHgt = 300;
$imgWidth= 400;
/*$imgColorHex="487239";*/
$txt1 = $_GET['text1'];
$txt2 = $_GET['text2'];
$txt3 = $_GET['text3'];
$txt4 = $_GET['text4'];
$txt5 = $_GET['text5'];
$txt6 = $_GET['text6'];
$txt = array($txt1,$txt2,$txt3,$txt4,$txt5,$txt6);

$fntSize = array(18,14,12,10,12,24);
$fntHex = array('00FF00','FFFFFF','000000','00FF00','ff00ff','ff0000');
$fntShad = array(1,0,0,0,0,0);
putenv('GDFONTPATH=' . realpath('.'));

$fnt = array('arial','arialbd','verdana','verdanab','verdanab','verdanab');

/*$font = "arial"; */
/*$background = @imagecreate($imgWidth, $imgHgt) or die("Cannot Initialize new GD image stream"); */
/*$background_color = imagecolorallocate($background, 0, 0, 254); */
$background = imagecreatefrompng("images/blank.png");
$t = count($txt);
$totY = 5;
$i = 0;
while ($i < $t) {

$charcount = strlen($txt[$i]);
$sentencewidth = ($charcount * $fntSize[$i]);
if ($sentencewidth > 380) {
/// ARRRGHHH //// I HATE BEING STUPID /////
}

$bbox = imageftbbox($fntSize[$i], 0, $fnt[$i], $txt[$i], array("linespacing" => 1));
$width = abs($bbox[0]) + abs($bbox[2]);
$height = abs($bbox[1]) + abs($bbox[5]);
$xcoord = (($imgWidth - $width) / 2) + 2;
$ycoord = $height + $totY + 2;
$int = hexdec($fntHex[$i]);
$arr = array("red" => 0xFF & ($int >> 0x10),
"green" => 0xFF & ($int >> 0x8),
"blue" => 0xFF & $int);
$tcolor = ImageColorAllocate($background, $arr["red"], $arr["green"], $arr["blue"]);

imagettftext($background, $fntSize[$i], 0, $xcoord, $ycoord, $tcolor, $fnt[$i], $txt[$i]);
$totY = $ycoord + 5;
$i++;
}

imagepng($background);
ImageDestroy($background);

}
?>

I just want our car club members to be able to lay lines of text over an image. I do all this, including the sites hosting, for free. It's a family thing. But, three whole days of trying to figure this out if getting ridiculous!

coopster

12:30 pm on Aug 19, 2009 (gmt 0)

WebmasterWorld Administrator 10+ Year Member



You are on the right track by analyzing and using the coordinates of the bounding box. Wrapping text on an image overlay is not an easy task. Any advanced image work is going to take some time and patience. A quick search [google.com] turns up many of the efforts you likely have already discovered but to show the level of work it may take to perfect the technique, have a look at the perl cpan module [search.cpan.org] for wrapping text using GD, particularly the source code [cpansearch.perl.org]. Perhaps you can use the logic there to complete your own PHP version of the same effort.

innoscape

3:42 pm on Aug 20, 2009 (gmt 0)

10+ Year Member



Yes, I have abandoned PHP all together, again. I am doing it all in PERL where the territory is more familiar to me. I was successful but, not using gd::text:wrap .. I ended up using a very simple 5 line routine.

My attempts using the wrap in gd forced me to create a new "transparent" bounding box and jump through a bunch of other hoops to get it into position and so on...

My solution was crude but, quite effective:
As gd processed the string with the font size already set, I grabbed the $w and $h of the string.
If the string was greater than my max width $var, I counted characters in the string and with a little division I determined how many characters would fit into the max width. Then foreach split on whitespace before that count if it was not already whitespace. Works for my app.

The downfall with my code would be if there was no whitespace in the string or if the whitespace was part of something like a phone number or time. No biggie for my app though and I could solve all that if I wanted to. Since there is an html preview page that points those issues out. Would have to be done intentionally so, GIGO is their prob.

When done, I will post in the proper PERL area of this forum. I should always just stick with PERL, I would have been done a week ago. Really so much better than PHP, for me...