How to account for font swash with PHP and GD

和自甴很熟 提交于 2019-12-10 13:07:30

问题


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


回答1:


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.



来源:https://stackoverflow.com/questions/34094643/how-to-account-for-font-swash-with-php-and-gd

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!