BlackDragonIV Posted October 29, 2019 Share Posted October 29, 2019 I need to draw various font sizes onto a canvas to create a web service. Wanting to protect my HD assets, the intent is to use PHP to populate the image with the necessary text, and then scale down the image before presenting it to the user.A similar service already exists, a tool to allow the public to make user generated content for a card game. The problem ever, is that PHP does not seem to handle font sizes very well. I created this sample code to demonstrate my issue.Here is a sample output from the code below <?php $img = imagecreatetruecolor(750, 530); $black = imagecolorallocate($img, 0, 0, 0); $gray = imagecolorallocate($img, 125, 125, 125); $white = imagecolorallocate($img, 255, 255, 255); $font = realpath('../fonts/micross.ttf'); $size = 12; $spacing = 20; for ($x = 0; $x <= 25; $x++) { $box = imageftbbox($size + $x/10, 0, $font, "The longer the phase the more apparent the size difference should be"); $boxWidth = $box[2] - $box[0]; imagefilledrectangle($img, 5, $x*$spacing+5, $boxWidth+5, ($x+1)*$spacing+5, $gray); imagefttext($img, $size + $x/10, 0, 5, $spacing*($x+1), $white, $font, "The longer the phase the more apparent the size difference should be Font Size ".($size+($x/10))); } imagejpeg($img); imagedestroy($img); ?> You can see how despite the font size steadily increasing by 0.1, it sporadically jumps at what seem at first like random intervals, however if you increase the number of loops and log the data, you can see that it alternates between increasing every 0.7, and 0.8. Unfortunately that doesn't help me any, just some insight. if($last != $boxWidth) { $last = $boxWidth; echo $boxWidth." ".($size+($x/10))."<br>"; } This outputs the current width and font size each time the text width changes.I've compiled a sample output for you guys to look at here Hopefully I've provided enough information to get some help, this has been very frustrating. Quote Link to comment https://forums.phpfreaks.com/topic/309429-having-font-size-troubles-with-imagettftext/ Share on other sites More sharing options...
BlackDragonIV Posted October 29, 2019 Author Share Posted October 29, 2019 Apologies for how poorly written the post is. I would fix it, but I am unable to edit the post. -- Sorry about that. Quote Link to comment https://forums.phpfreaks.com/topic/309429-having-font-size-troubles-with-imagettftext/#findComment-1571070 Share on other sites More sharing options...
requinix Posted October 29, 2019 Share Posted October 29, 2019 2 hours ago, BlackDragonIV said: The problem ever, is that PHP does not seem to handle font sizes very well. Yeah. Though technically it's libgd doing the work, not PHP itself. Stick with integral font sizes. Or you can upscale the image even further. But you haven't mentioned what you're trying to get out of this. What is your actual goal here? Quote Link to comment https://forums.phpfreaks.com/topic/309429-having-font-size-troubles-with-imagettftext/#findComment-1571072 Share on other sites More sharing options...
BlackDragonIV Posted October 29, 2019 Author Share Posted October 29, 2019 4 hours ago, requinix said: But you haven't mentioned what you're trying to get out of this. What is your actual goal here? I want better sub pixel rendering so that I can render text to an accuracy of 0.1pt size. Quote Link to comment https://forums.phpfreaks.com/topic/309429-having-font-size-troubles-with-imagettftext/#findComment-1571082 Share on other sites More sharing options...
requinix Posted October 29, 2019 Share Posted October 29, 2019 libgd isn't the highest quality stuff. Look into imagemagick, or else use a 10x upscale/downscale. Quote Link to comment https://forums.phpfreaks.com/topic/309429-having-font-size-troubles-with-imagettftext/#findComment-1571083 Share on other sites More sharing options...
BlackDragonIV Posted October 29, 2019 Author Share Posted October 29, 2019 (edited) Well, now that I'm armed with the information at hand, I've got a few options: Settle with what I've got and work in increments of 0.75pt size Try the imagemagick functions Upscale & Downscale Use an entirely different language to build the card image with a better font renderer and then pull the image into the site via PHP Any thoughts or opinions? Edited October 29, 2019 by BlackDragonIV Accidentally posted prematurely with CTRL+ENTER Quote Link to comment https://forums.phpfreaks.com/topic/309429-having-font-size-troubles-with-imagettftext/#findComment-1571085 Share on other sites More sharing options...
requinix Posted October 29, 2019 Share Posted October 29, 2019 You can settle for 0.75pt, if that's an option. Honestly I would still stick with 1pt increments, given that I wasn't able to see an explanation when I checked the libgd source (which actually shells out this work to the FreeType library). ImageMagick is rather good, but requires a bit more stuff installed on the server, and using it tends to involve calling the program - it's not often used as a library like GD. If you need to do this "seriously" then I recommend it. Upscaling would be easy and get you the 0.1pt resolution you want. At the expense of higher memory usage, but that may not be a problem. Going to an entirely different language seems like a lot of effort and is, frankly, an overreaction. Quote Link to comment https://forums.phpfreaks.com/topic/309429-having-font-size-troubles-with-imagettftext/#findComment-1571086 Share on other sites More sharing options...
Barand Posted October 29, 2019 Share Posted October 29, 2019 (edited) Don't know what you're trying to produce but have you considered SVG instead of bitmapped graphics? They rescale better. Edited October 29, 2019 by Barand Quote Link to comment https://forums.phpfreaks.com/topic/309429-having-font-size-troubles-with-imagettftext/#findComment-1571088 Share on other sites More sharing options...
BlackDragonIV Posted October 29, 2019 Author Share Posted October 29, 2019 (edited) I decided to make a sample program using ImageMagick to test it's font-size loss and got significantly better results. I've updated the spreadsheet from earlier to include my ImageMagick results, GD has a font-size pool of only 13.33%, where ImageMagick has a font-size pool of 54.5%. That's a 4x increase! <?php function setFont($fillColor, $backgroundColor) { $draw = new \ImagickDraw(); $imagick = new \Imagick(); $draw->setStrokeColor('none'); $draw->setFillColor($fillColor); $draw->setTextAntialias(true); $draw->setFont("../fonts/pala.ttf"); //$draw->setFont("../fonts/micross.ttf"); $text = "The longer the phrase the more apparent the size should be"; $startSize = 12; $loops = 200; $draw->setFontSize($startSize + ($loops*0.1)); $fontMetrics = $imagick->queryFontMetrics($draw, $text." Width: 9999"); //var_dump($fontMetrics); //echo "Length of array: ".sizeof($fontMetrics)."<br>"; $width = $fontMetrics["textWidth"];//->textWidth(); $spacing = $fontMetrics["textHeight"];//->textWidth(); $last = 0; for($x = 0; $x < $loops; $x++) { $draw->setFontSize($startSize + ($x*0.1)); $fontMetrics = $imagick->queryFontMetrics($draw, $text); $width = $fontMetrics["textWidth"]; if($last != $width) { $last = $width; //echo $width." ".($startSize+($x*0.1))."<br>"; } $draw->annotation(0, $spacing*($x+1), $text." Width: ".$fontMetrics["textWidth"]); } $fontMetrics = $imagick->queryFontMetrics($draw, $text." Width: ".$fontMetrics["textWidth"]); $width = $fontMetrics["textWidth"]; $imagick->newImage($width, $spacing*$loops, $backgroundColor); $imagick->setImageFormat("png"); $imagick->drawImage($draw); header("Content-Type: image/png"); echo $imagick->getImageBlob(); } setFont("black","white"); ?> Here's that new sample code. 3 hours ago, Barand said: Don't know what you're trying to produce but have you considered SVG instead of bitmapped graphics? They rescale better. Unfortunately since I am only a community member of this card game I am unable to acquire SVG graphics -- Needless to say, if I can just draw SVG text onto my bitmaps, then I don't see why that wouldn't work. 3 hours ago, requinix said: Upscaling would be easy and get you the 0.1pt resolution you want. At the expense of higher memory usage, but that may not be a problem. I think re-scaling would introduce artifacts into the background images (But I haven't tested it). I considered re-scaling the font by itself before layering it onto the card, but I don't think GD supports a transparent canvas. Edited October 29, 2019 by BlackDragonIV Fixed code Quote Link to comment https://forums.phpfreaks.com/topic/309429-having-font-size-troubles-with-imagettftext/#findComment-1571097 Share on other sites More sharing options...
requinix Posted October 29, 2019 Share Posted October 29, 2019 3 hours ago, BlackDragonIV said: I think re-scaling would introduce artifacts into the background images (But I haven't tested it). I considered re-scaling the font by itself before layering it onto the card, but I don't think GD supports a transparent canvas. Not upscale the whole image - just the parts that involve rendering text. Scale up, add text and draw stuff, scale down, layer on images and whatever other assets. And GD does support transparency, but it's a bit tricky to get working. All in all ImageMagick is just generally better quality. GD is more about quick and dirty. Quote Link to comment https://forums.phpfreaks.com/topic/309429-having-font-size-troubles-with-imagettftext/#findComment-1571102 Share on other sites More sharing options...
Barand Posted October 30, 2019 Share Posted October 30, 2019 On the whole the dimensions returned by imagettfbbox() are pretty accurate. In the image below, the rectangles and baseline position are the dimensions returned by imagettfbbox(). The text then output into the box at the baseline position to check. ("Broadway" font is a couple of pixels out but the rest are spot on) Note that all coordinates returned for the bbox are integers. The code used: <?php $fonts = [ 'arial' => 'c:/windows/fonts/arial.ttf', 'verdana' => 'c:/windows/fonts/verdana.ttf', 'broadway' => 'c:/windows/fonts/broadw.ttf', 'gabriola' => 'c:/windows/fonts/gabriola.ttf', 'vivaldi' => 'c:/windows/fonts/vivaldii.ttf', ]; $fsize = 42.5; $str = 'The quick brown lazy fox'; foreach ($fonts as $fam => $font) { echo textInABox($fam, $font, $fsize, $str) . '<br><br>'; } function textInABox($fam, $font, $fsize, $str) { list($wd, $ht, $asc, $tx) = array_values(metrics($font,$fsize,$str)); return <<<SVG <svg width='$wd' height='$ht' viewBox='0 0 $wd $ht'> <style type='text/css'> .txt { fill:#A91723; } </style> <rect x='0' y='0' width='$wd' height='$ht' fill='#FFF' stroke='#000'/> <text class='txt' x='$tx' y='$asc' style='font-family:"{$fam}"; font-size:{$fsize}pt'>$str</text> <path d='M 0 $asc l $wd 0' stroke='#ccc'/> </svg>  $fam {$fsize}pt $wd x $ht SVG; } function metrics($font, $fsize, $str) { $box = imagettfbbox($fsize, 0, $font, $str); $ht = abs($box[5] - $box[1]); $wd = abs($box[4] - $box[0]); $base = -$box[5]; $tx = -$box[0]; return [ 'width' => $wd, 'height' => $ht, 'ascent' => $base, 'offsetx' => $tx ]; } ?> Quote Link to comment https://forums.phpfreaks.com/topic/309429-having-font-size-troubles-with-imagettftext/#findComment-1571125 Share on other sites More sharing options...
Barand Posted October 31, 2019 Share Posted October 31, 2019 P.S. ... but not so good with some other languages Quote Link to comment https://forums.phpfreaks.com/topic/309429-having-font-size-troubles-with-imagettftext/#findComment-1571131 Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.