从边界框确定文本坐标a的正确方法是什么?

9
给定调用imagettfbbox()的结果,什么是正确的像素完美点,可以提供给imagettftext(),使得文本不会超出其边界框?
我正在从边框中确定宽度/高度和基线的x/y,如下所示:
$box = imagettfbbox($size, $angle, $font, $text);
$boxXCoords = array($box[0], $box[2], $box[4], $box[6]);
$boxYCoords = array($box[1], $box[3], $box[5], $box[7]);
$boxWidth = max($boxXCoords) - min($boxXCoords);
$boxHeight = max($boxYCoords) - min($boxYCoords);
$boxBaseX = abs(min($boxXCoords));
$boxBaseY = abs(min($boxYCoords));

接下来,我会在我的图像上绘制一个填充的矩形,其尺寸与边界框相同:

imagefilledrectangle($image, 0, 0, $boxWidth - 1, $boxHeight - 1, $color);

之后,我绘制了文本:
imagettftext($image, $size, $angle, $boxBaseX, $boxBaseY, $color, $font, $text);

然而,这会导致文本超出矩形框一两个像素。我看过 PHP 的 imagettfbbox() 文档中对此问题的几次尝试,但它们都只是建议在这里或那里减去一两个像素,这对我来说似乎是一种欺骗行为。发生了什么事?为什么我们需要虚拟数字才能做到正确呢?

这显然是一个非常古老的问题,但我遇到了类似的问题,想知道您是否解决了这个问题? - ndg
4个回答

7

我认为没有完美的方法可以根据imagettfbbox()返回的结果以及使用.ttf非等宽字体在图像上精确地放置单像素文本。在PHP手册中,许多用户已经发布了完成此操作的方法(有和没有调整数字的),我建议使用PHP手册中的jodybrabec的简单函数,该函数计算出精确的边界框。我测试过这个函数,在极端情况下,文本只会在一个方向上最多偏移1个像素。尽管如此,如果您给您的图像添加一些填充(即使只有2或3个像素),您的文本将始终在图像的尺寸范围内。


3
这不回答我的问题。我在我的问题中已经引用了php.net评论中的解决方法,而这个答案只是把我指向那些相同的解决方法之一。我不认同GD库无法确定正确的边界框,因为很多其他库都能做到这一点而没有这个问题。要么有本地解决方法(包括可能是我误解了这些函数的工作原理),要么是PHP实现出了问题。 - FtDRbwLXw6

0

如果在这行代码中,你没有从每个维度中减去一个,会发生什么?

imagefilledrectangle($image, 0, 0, $boxWidth - 1, $boxHeight - 1, $color);

而不是这样做:

imagefilledrectangle($image, 0, 0, $boxWidth, $boxHeight, $color);

我觉得这可能是一条注释? - uınbɐɥs
1
我最初也是这么想的,但如果它解决了问题,那么它可以作为一个答案。此外,源代码的格式对于注释来说有点太严格了。我选择了清晰易懂的方式,以便能够快速理解。 - Homer6
imagefilledrectangle() 创建的是文本背后的矩形,而不是文本本身。我只是这样做是为了显示应该是文本边界框的尺寸。从框的宽度/高度中减去1是必要的,因为我们需要绝对的起始/结束像素(例如,对于一个200x200像素的框,我们想要从0,0到199,199包括绘制一个矩形)。 - FtDRbwLXw6

0

SlightlyMagic HQ Card Generator 项目用于渲染策略卡牌游戏《魔法:集换式卡牌游戏》的卡牌。该生成器由 PHP 驱动,并内置了先进的文本渲染引擎。我不知道计算背后的逻辑,但对于此应用程序而言,渲染器非常准确。以下是计算正确边界框的函数(HQ Card Generator 8.x/scripts/classes/font.php):

private function convertBoundingBox ($bbox) {
    // Transform the results of imagettfbbox into usable (and correct!) values.
    if ($bbox[0] >= -1)
        $xOffset = -abs($bbox[0] + 1);
    else
        $xOffset = abs($bbox[0] + 2);
    $width = abs($bbox[2] - $bbox[0]);
    if ($bbox[0] < -1) $width = abs($bbox[2]) + abs($bbox[0]) - 1;
    $yOffset = abs($bbox[5] + 1);
    if ($bbox[5] >= -1) $yOffset = -$yOffset;
    $height = abs($bbox[7]) - abs($bbox[1]);
    if ($bbox[3] > 0) $height = abs($bbox[7] - $bbox[1]) - 1;
    return array(
        'width' => $width,
        'height' => $height,
        'xOffset' => $xOffset, // Using xCoord + xOffset with imagettftext puts the left most pixel of the text at xCoord.
        'yOffset' => $yOffset, // Using yCoord + yOffset with imagettftext puts the top most pixel of the text at yCoord.
        'belowBasepoint' => max(0, $bbox[1])
    );
}

3
谢谢,但是这个函数基本上与在php.net评论中找到的那些函数相同,并且正好做了我不想做的事情 -- 它通过稍微调整数字来使它们正确。 我想知道1)为什么一开始就不正确,可能还有2)如何在没有hacky(繁琐)代码的情况下正确获取它。 - FtDRbwLXw6

-1

我不确定你所说的“imagettfbbox是按点而非像素计算”的意思。如果你的意思是它返回的坐标是以点为单位的,那这是不正确的(也没有意义)。如果你的意思是$size参数是按点而非像素计算的,那与我的问题并没有太大关系(并且仅适用于GD2)。 - FtDRbwLXw6

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接