【问题标题】:What is the correct way to determine text coordinates a from bounding box?从边界框确定文本坐标的正确方法是什么?
【发布时间】:2012-07-25 04:05:24
【问题描述】:

鉴于调用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() 文档中看到了一些解决此问题的尝试,但它们都只是建议在这里和那里减去一两个像素,这对我来说似乎是一个 hack。这里发生了什么,为什么我们需要捏造数字才能让事情变得正确?

【问题讨论】:

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

标签: php text gd bounding-box


【解决方案1】:

我知道这有点晚了,但 imagettfbbox 的单位是点而不是像素。

pixel font size in imagettftext instead of point size

【讨论】:

  • 我不确定您所说的“imagettfbbox 是以点而不是像素为单位”是什么意思。如果您的意思是它以点为单位返回坐标,那是不正确的(并且没有意义)。如果您的意思是 $size 参数以点为单位,而不是像素,那么这与我的问题并不相关(也仅适用于 GD2)。
【解决方案2】:

SlightlyMagic HQ Card Generator 项目为策略纸牌游戏 Magic: the Gathering 渲染纸牌。生成器由内置高级文本渲染引擎的 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])
    );
}

【讨论】:

  • 谢谢,但是这个函数与 php.net cmets 中的函数基本相同,并且完全符合我的意愿——它在这里和那里将数字捏造一个像素让他们正确。我想知道 1)为什么一开始就不正确,可能还有 2)如果没有像这样的 hacky 代码,我如何才能让它正确。
【解决方案3】:

如果您不从该行中的每个维度中减去 1,会发生什么:

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

改为这样做:

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

【讨论】:

  • 我认为这可能是一条评论?
  • 一开始我也是这么想的,但是如果它解决了问题,那么它可以作为一个答案。此外,源代码的格式对于评论来说有点过于严格。我选择了清晰,以便可以快速理解。
  • imagefilledrectangle() 在文本后面创建矩形,而不是实际文本本身。我这样做只是为了显示应该是文本边界框的尺寸。从盒子的宽度/高度中减去 1 是必要的,因为我们需要绝对的开始/结束像素(例如,对于 200x200 像素的盒子,我们想要绘制一个从 0,0 到 199,199 的矩形)。
【解决方案4】:

我相信没有完美的方法可以根据imagettfbbox() 返回的内容以及使用 .ttf 非等宽字体在图像上放置单像素精度的文本。在 PHP 手册中,许多用户已经发布了实现此目的的方法(无论是否捏造数字);我建议在 PHP 手册中使用jodybrabec's simple function over,它会计算精确的边界框。我已经对此进行了测试,只有在极端情况下,文本在一个方向上最多相差 1 个像素。尽管如此,如果您在图像中添加一些填充(即使只是 2 或 3 个像素),您的文本将 100% 的时间在图像的尺寸范围内。

【讨论】:

  • 这不能回答我的问题。我已经在我的问题中引用了 php.net cmets 中的变通方法,而这个答案只是将我指向这些相同的变通方法之一。我不接受 GD 库无法确定正确的边界框,因为大量其他库能够在没有这个怪癖的情况下做同样的事情。要么是原生解决方案(包括我可能误解了这些函数的工作原理),要么是 PHP 实现中出现了问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-11-26
  • 2022-01-07
  • 2012-11-15
  • 1970-01-01
  • 2020-11-24
  • 2011-03-17
  • 1970-01-01
相关资源
最近更新 更多