PHP imageftbbox imagettftext – простое расстояние между буквами/кернинг?

Кто-нибудь знает простой способ сделать межбуквенный интервал/кернинг с помощью imagettftext? У меня есть мой скрипт, работающий так, как мне нужно сейчас, но я действительно мог бы сделать сгенерированный текст, имеющий стиль CSS

letter-spacing: -0.01em;

поэтому он соответствует стандартному тексту на странице, но я не вижу способа сделать это легко. Я нашел следующий поток, относящийся к этому, но я попытался вписать ответы в свой код, и ни один из них не дал желаемого эффекта.

расстояние между буквами php imagettftext

Мой текущий код выглядит следующим образом

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

person Danny Shepherd    schedule 10.12.2020    source источник


Ответы (1)


Я понимаю, что вы имеете в виду. Без какого-либо кернинга ваш вывод выглядит так:

введите здесь описание изображения

и когда я беру принятый ответ, который достаточно хорош для ваших целей, это выглядит так для интервала, установленного на -0.5:

введите здесь описание изображения

Обратите внимание, что расстояние между f и e по-прежнему довольно велико, тогда как в других местах буквы могут даже перекрываться.

Что вызывает это и есть ли способ предотвратить это?

Давайте начнем с рисования прямоугольника, возвращаемого imagettfbbox() вокруг букв:

введите здесь описание изображения

Очевидно, что эта рутина работает не совсем так, как планировалось. Вы ожидаете, что горизонтальное перекрытие, если оно есть, будет одинаковым для всех блоков.

Объяснить это несколько сложно, это связано с кернингом шрифтов. Кернинг может вычитать пробел до и после буквы:

введите здесь описание изображения

Важно понимать, что позиция $x, которую вы указываете для imagettftext(), учитывает этот кернинг. Таким образом, даже если вы говорите, что буква должна быть нарисована в (x,y), это не одна из координат, возвращаемых imagettfbbox(). Эта функция возвращает ограничивающую рамку нарисованной буквы, показанную красными прямоугольниками.

Теперь давайте посмотрим, сможем ли мы с помощью этих знаний рисовать равномерно расположенные буквы:

введите здесь описание изображения

Ясно, что это возможно. Код, который я использовал, был следующим:

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

Я сделал отдельную функцию для получения ширины ограничительной рамки, потому что я много ее вычисляю, а имя функции лучше объясняет, что делается. Я использую тестовую строку и проверяю, насколько она широка, чтобы позже я мог вычислить, что делает символ перед ней. Я, наконец, компенсирую это. Результат таков:

введите здесь описание изображения

Это явно лучше, вам трудно заметить, что это расположено ближе, чем пример в начале этого ответа, но это так.

Я должен признать, что это довольно сложно, и я понятия не имею, почему вы захотите это сделать. Использование базовых HTML и CSS может сделать ту же работу лучше.

person KIKO Software    schedule 11.12.2020
comment
Привет, Кико, еще раз спасибо за ваш удивительный и информативный ответ - теперь мне нужно попробовать вставить его в свой код. Вы правы, это сложно, и причина в том, что я использую его для списков eBay, где у нас нет ни javascript, ни jquery, ничего, кроме чистого HTML и CSS, поэтому для создания динамического бита информации единственный хак - это используйте PHP, выплевывая файл изображения. Определенно не идея, но работа в очень жестких условиях. - person Danny Shepherd; 13.12.2020
comment
На самом деле, КИКО, я здесь заблудился - я только копался в каком-то коде, который сделал для меня друг - как мне поместить код, который вы сделали, в тот, который я опубликовал, чтобы создать желаемое? - person Danny Shepherd; 13.12.2020
comment
@DannyShepherd Просто замените imagettftext() этим новым imagettftextSpacing() и обратите внимание, что два аргумента изменились, угол исчез, и я добавил интервал. Другое дело центрирование текста, я этого не делал. Вы должны подсчитать количество символов, и вместе с указанным вами интервалом вы можете центрировать его. - person KIKO Software; 13.12.2020
comment
я пробовал следующее, и я обновил $ для тех, которые используются в моем скрипте, но я не получаю результата в окне изображения, это потому, что оно исчезает или что-то еще не так? пример - person Danny Shepherd; 13.12.2020
comment
@DannyShepherd Вы не изменили аргументы, 0 для угла все еще там, я удалил это, потому что я его не использую. - person KIKO Software; 13.12.2020
comment
Извините за тупость, я не очень понимаю - я изменил это на это (удалить 0 из результата) и все равно ничего не получил. Нужно ли удалять 0 везде в скрипте? pastebin.com/gwLZKYBd - person Danny Shepherd; 13.12.2020
comment
imagettftextSpacing($im, $size2, $x, -$bbox[7], $textColourUse, $fontBold, $textToUse, $spacing); это все еще неправильно? - person Danny Shepherd; 13.12.2020
comment
@DannyShepherd Я не могу проверить ваш код, не хватает include 'deliverytimes.php';, но выглядит хорошо, должно работать. Если он не пытается выполнить отладку. Включите отчет об ошибках и проверьте журнал ошибок. Поиграйте с ним немного, это не слишком сложный код. Я заметил одну вещь: вы используете переменную $textColourUse в моей программе, тогда как у меня ее нет, я называю ее $color. Если цвета не совпадают, вы, вероятно, ничего не видите, и вы должны получить предупреждение в журнале ошибок. - person KIKO Software; 13.12.2020
comment
Я получаю `Неустранимая ошибка: вызов неопределенной функции mb_str_split() в /var/www/html/deecies/js/deliverym1c.php в строке 96`, которая равна foreach (mb_str_split($textToUse) as $char) - person Danny Shepherd; 13.12.2020
comment
@DannyShepherd Хорошо, вы используете более старую версию PHP. Это была многобайтовая версия, но вы, вероятно, можете использовать обычную версию, поэтому замените mb_str_split() на str_split(), и она должна работать, при условии, что вы исправили имя переменной цвета. - person KIKO Software; 13.12.2020
comment
Ах, отлично, это работает - вы потрясающие! Большое спасибо за ваше время и помощь KIKO!! - person Danny Shepherd; 13.12.2020
comment
@DannyShepherd Пожалуйста. ???? - person KIKO Software; 13.12.2020