I have the following code to print text on an image. I am also adding a debug box around the text. However, I noticed the text on the left lies outside of the box that PHP gives me with imagettfbbox
.
This looks like an issue with the font swash. Is there anyway to account for this? Can I figure out the distance between the start of the swash and the actual position imagettfbbox
gives to me?
I don't think this is an issue with the font, as I tried it with a few script style fonts and the results were similar.
<?php
$font = 'scriptin.ttf';
$text = 'Ipsum';
$size = 30;
$image = imagecreatetruecolor(200, 200);
$fontColour = imagecolorallocate($image, hexdec('11'), hexdec('11'), hexdec('11'));
$bgColour = imagecolorallocate($image, hexdec('CC'), hexdec('CC'), hexdec('CC'));
imagefilledrectangle($image, 0, 0, 200, 200, $bgColour);
$dimensions = imagettfbbox($size, 0, $font, $text);
imagefilledrectangle(
$image,
$dimensions[0] + 40,
$dimensions[7] + 50,
$dimensions[2] + 40,
$dimensions[3] + 50,
imagecolorallocate($image, mt_rand(1, 180), mt_rand(1, 180), mt_rand(1, 180))
);
imagettftext(
$image,
$size,
0,
40,
50,
$fontColour,
$font,
$text
);
header('Content-Type: image/png');
imagepng($image);
The code and font is available here: https://github.com/AydinHassan/image-swash-example
If you point a VHOST at the repository, you can just hit swash.php
Edit: This appears to be fixed in PHP 7.0.12 (bug #53504) so the code below shouldn't be required.
Based on a comment in the PHP manual I've written the following function to calculate and return the difference between where GD thinks the left side of the bounding box is and where the leftmost pixel is found:
function xadjust($size, $angle, $fontfile, $text)
{
$bbox = imagettfbbox($size, $angle, $fontfile, $text);
$width = $bbox[4] - $bbox[6]; // upper right x - upper left x;
$height = $bbox[1] - $bbox[7]; // lower left y - upper left y;
// create an image with height and width doubled to fit any 'swash'.
$im = imagecreatetruecolor($width * 2, $height * 2);
// set background color to opaque black.
imagefill($im, 0, 0, 0x00000000);
// draw the text in opaque white.
imagettftext(
$im,
$size,
0,
$width / 2,
$height,
0x00ffffff,
$fontfile,
$text
);
// set the min-width to its possible maximum.
$min_x = $width * 2;
for ($x = 0; $x < $width * 2; $x++) {
// each x-pixel (horizontal)
for ($y = 0; $y < $height * 2; $y++) {
// each y-pixel (vertical)
if (imagecolorat($im, $x, $y) > 0) {
// non-black pixel found!
$min_x = min($x, $min_x);
}
}
}
imagedestroy($im);
// return the difference between where GD thinks the bounding box is and
// where we found the leftmost non-black pixel.
return (($width / 2) - $min_x) - abs($bbox[0]);
}
This can be integrated to your script fairly easily:
$font = 'scriptin.ttf';
$text = 'Ipsum';
$size = 30;
$image = imagecreatetruecolor(200, 200);
$fontColour = imagecolorallocate($image, hexdec('11'), hexdec('11'), hexdec('11'));
$bgColour = imagecolorallocate($image, hexdec('CC'), hexdec('CC'), hexdec('CC'));
imagefilledrectangle($image, 0, 0, 200, 200, $bgColour);
$xadjust = xadjust($size, 0, $font, $text); // 1. get the adjust value.
$dimensions = imagettfbbox($size, 0, $font, $text);
imagefilledrectangle(
$image,
$dimensions[0] + 40 - $xadjust, // 2. move the left-side of the box to the left.
$dimensions[7] + 50,
$dimensions[2] + 40 - $xadjust, // 3. move the right-side of the box to the left.
$dimensions[3] + 50,
imagecolorallocate($image, mt_rand(1, 180), mt_rand(1, 180), mt_rand(1, 180))
);
imagettftext(
$image,
$size,
0,
40,
50,
$fontColour,
$font,
$text
);
header('Content-Type: image/png');
imagepng($image);
This gives me the following output:
I've run it with a few other fonts and sizes and it seems to be accurate to within 1 pixel.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With