问题
Does anyone know a simple way to do letter spacing/kerning using imagettftext? I have my script working just as I need now, but I could really do with the generated text having the CSS style
letter-spacing: -0.01em;
so it matches the standard text on the page, but I don't see any way to do this easily. I did find the following thread relating to this, but I've tried to fit the answers into my code and none of them had the desired effect.
php imagettftext letter spacing
My current code is as follows
<?php
include 'deliverytimes.php';
$date = new DateTime();
$now = date("Y-m-d H:i:s");
$h = date("H:i:s");
$days = explode(",", $businessDaysToAdd);
if (count($days) > 1) {
$two_weekdays_later_1 = strtotime(date("Y-m-d H:i:s", strtotime($now)) . " +" . $days[0] . " weekdays $h");
$date_1 = new DateTime("@$two_weekdays_later_1");
$formattedDeliveryDate_1 = $date_1->format('jS M');
$formattedDeliveryDate_3 = $date_1->format('jS \o\f F');
$two_weekdays_later_2 = strtotime(date("Y-m-d H:i:s", strtotime($now)) . " +" . $days[1] . " weekdays $h");
$date_2 = new DateTime("@$two_weekdays_later_2");
$formattedDeliveryDate_2 = $date_2->format('jS M.');
$formattedDeliveryDate_4 = $date_2->format('jS \o\f F');
$formattedDeliveryDate1 = $formattedDeliveryDate_3;
$formattedDeliveryDate2 = $formattedDeliveryDate_4;
$formattedDeliveryDate = "If ordered today we estimate delivery to be approximately between " . $formattedDeliveryDate_1 . " and " . $formattedDeliveryDate_2;
} else {
$h = date("H:i:s");
$two_weekdays_later = strtotime(date("Y-m-d H:i:s", strtotime($now)) . " +" . $businessDaysToAdd . " weekdays $h");
$date = new DateTime("@$two_weekdays_later");
$formattedDeliveryDate = "If ordered today we estimate delivery approximately by " . $date->format('l, jS M.');
}
$defaultOutput = 'main';
$textMobile = isset($_REQUEST['mobile']) ? $_REQUEST['mobile'] : $defaultOutput;
switch($textMobile) {
case "main":
$textToUse = $formattedDeliveryDate;
break;
case "p1":
$textToUse = $formattedDeliveryDate1;
break;
case "p2":
$textToUse = $formattedDeliveryDate2;
break;
}
// Path to our font file
$font = './Inter-SemiBold.ttf';
$fontBold = './Inter-Bold.ttf';
$size = 24;
$size2 = 83;
$bbox = imageftbbox($size2, 0, $fontBold, $textToUse);
$width = 1020;
$height = 110;
$im = imagecreatetruecolor($width, $height);
$x = ($width - ($bbox[4] - $bbox [0])) / 2;
imagealphablending($im, false);
imagesavealpha($im, true);
$white = imagecolorallocate($im, 255, 255, 255);
$black = imagecolorallocate($im, 0, 0, 0);
$grey = imagecolorallocate($im, 161, 161, 168);
$trans = imagecolorallocatealpha($im, 255, 255, 255, 127);
imagefilledrectangle($im, 0, 0, $width, $height, $trans);
$defaultTextColour = 'white';
$textColour = isset($_REQUEST['colour']) ? $_REQUEST['colour'] : $defaultTextColour;
switch($textColour) {
case "white":
$textColourUse = $white;
break;
case "black":
$textColourUse = $black;
break;
case "grey":
$textColourUse = $grey;
break;
}
// Write it
imagettftext($im, $size2, 0, $x, -$bbox[7], $textColourUse, $fontBold, $textToUse);
// Output to browser
header('Content-Type: image/png');
header("Cache-Control: no-store, no-cache, must-revalidate, max-age=0");
header("Cache-Control: post-check=0, pre-check=0", false);
header("Pragma: no-cache");
imagepng($im);
imagedestroy($im);
回答1:
I can see what you mean. Without any kerning your output looks like this:
and when I take the accepted answer, which is good enough for your purposes, it looks like this for spacing set to -0.5
:
Notice how the space between the f
and e
is still quite big whereas in other places the letters can even overlap.
What causes this, and is there a way to prevent it?
Let's start by drawing the box, returned by imagettfbbox()
around the letters:
Clearly that routine doesn't quite work as planned. You would expect the horizontal overlap, if it is there, to be the same for all boxes.
To explain this is somewhat difficult, it has to do with font kerning. Kerning can subtract space before and after a letter:
It's important to realize that the $x
position you give to imagettftext()
takes this kerning into account. So even though you say a letter should be draw at (x,y)
that is not one of the coordinates returned by imagettfbbox()
. That function returns the bounding box of the drawn letter, as shown by the red rectangles.
Now lets see if, with this knowledge, we can draw evenly spaced letters:
Clearly that is possible. The code I used was this:
function imagettftextSpacing($image, $size, $x, $y, $color, $font, $text, $spacing = 0)
{
foreach (mb_str_split($text) as $char)
{
$frontBox = imagettfbbox($size, 0, $font, $char);
$charBox = imagettftext($image, $size, 0, $x - $frontBox[0], $y, $color, $font, $char);
$charW = $charBox[2] - $charBox[0];
$x += $charW + $spacing;
}
}
This gets the horizontal offset of a character and corrects for that when drawing the character.
Although this is an improvement, it is not yet completely satisfactory. Some letters touch, and others do not. This is because we corrected for the kerning at the front of a character, but not for the kerning at the back. We somehow need to find out what the kerning at the back is, and then correct for it. I botched together this code:
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;
}
}
I made a separate function to get the width of a bounding box, because I compute it a lot, and the function name makes better clear what is being done. I use a test string, and check how wide that is, so I can later compute what a character in front of it does. I finally compensate for that. The result is this:
This is clearly better, you have a hard time spotting that this is spaced closer than the example at the beginning of this answer, but it is.
I have to admit this is rather complex, and I have no idea why you would want to do this. Using basic HTML and CSS could do the same job better.
来源:https://stackoverflow.com/questions/65239769/php-imageftbbox-imagettftext-simple-letter-spacing-kerning