PHP imageftbbox imagettftext - 简单字母 spacing/kerning?

PHP imageftbbox imagettftext - simple letter spacing/kerning?

有谁知道使用 imagettftext 制作字母 spacing/kerning 的简单方法?我的脚本现在可以正常工作,但我真的可以使用具有 CSS 样式

的生成文本

letter-spacing: -0.01em;

所以它与页面上的标准文本相匹配,但我看不出有什么方法可以轻松做到这一点。我确实找到了与此相关的以下线程,但我已尝试将答案放入我的代码中,并且 none 达到了预期的效果。

php imagettftext letter spacing

我现在的代码如下

<?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);

我明白你的意思了。在没有任何字距调整的情况下,您的输出如下所示:

当我采用 the accepted answer 时,这对您的目的来说已经足够了,间距设置为 -0.5:

时看起来像这样

请注意 fe 之间的 space 仍然很大,而在其他地方字母甚至可以重叠。

是什么原因造成的,有什么办法可以预防吗?

让我们从绘制由 imagettfbbox() 围绕字母返回的方框开始:

很明显,例行公事并没有按计划进行。您会期望水平重叠(如果存在)对于所有框都是相同的。

要解释这个有点困难,它与font kerning有关。字距可以在一个字母前后减去space:

重要的是要认识到,您为 imagettftext() 设置的 $x 位置考虑了此字距调整。因此,即使您说应该在 (x,y) 绘制一个字母,它也不是 imagettfbbox() 返回的坐标之一。该函数 returns 绘制字母的边界框,如红色矩形所示。

现在让我们看看,利用这些知识,我们是否可以均匀地绘制 spaced 个字母:

显然这是可能的。我使用的代码是这样的:

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;
    }
}

这会获取字符的水平偏移并在绘制字符时对其进行更正。

虽然这是一个改进,但还不能完全令人满意。有些字母接触,有些则没有。这是因为我们校正了字符前面的字距,但没有校正后面的字距。我们不知何故需要找出后面的字距调整是什么,然后对其进行更正。我把这段代码搞砸了:

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;
    }
}

我做了一个单独的函数来获取边界框的宽度,因为我经常计算它,而且函数名更清楚地说明了正在做什么。我使用一个测试字符串,并检查它有多宽,这样我可以稍后计算它前面的字符的作用。我终于弥补了这一点。结果是这样的:

这显然更好,您很难发现这比本答案开头的示例更接近 spaced,但确实如此。

我不得不承认这很复杂,我不知道你为什么要这样做。使用基本的 HTML 和 CSS 可以更好地完成同样的工作。