Warum imagettfbbox nichts taugt

Wer in PHP mit der GD2-Extension Schriftzüge als Bilder generiert, wird früher oder später mit dem Problem der Bounding Box konfrontiert. Um die Größe des Schriftzugs zu berechnen, stellt PHP die Funktion imagettfbbox bereit.

Größe der Bounding Box

Verlässt man sich auf diese Funktion und benutzt deren Rückgabewerte, um die Bildgröße festzulegen, sieht das in etwa so aus wie hier. Der gerahmte Bereich stellt die Bounding Box dar, wie sie mit imagettfbbox berechnet wurde. Würde man das Bild ohne zusätzlichen Rand dimensionieren, wären viele Buchstaben verstümmelt.

x p k H Y f Ä \ Äpfel

Breite der Bounding Box und Schrifthöhe

Da man die Schriftgröße aber ohnehin festlegen muss, könnte man annehmen, dass man einfach diese verwendet und von der errechneten Bounding Box lediglich die horizontalen Koordinaten benutzt. Dass diese Variante ebenso wenig aufgeht, zeigt die folgende Bildserie. Selbst wenn man die doppelte Schriftgröße als Höhe ansetzt, hätte man immer noch das Problem, dass Unterschneidungen (Kerning) nicht berücksichtigt werden und der erste oder der letzte Buchstabe über den Rand hinausragt. Die gesamte Breite hingegen ist korrekt.

x p k H Y f Ä \ Äpfel

Korrigierte Bounding Box

Wenn man aufwändige Korrekturmaßahmen in Kauf nimmt, kann man jedoch ein absolut brauchbares Resultat erzielen. Man muss nur Ober- und Unterlängen einzeln berechnen und die Unter- und Überschneidung berücksichtigen. Die dazu notwendigen Funktionen und deren Anwendung werden nachfolgend vorgestellt.

x p k H Y f Ä \ Äpfel

Die Funktionen

function getTypographicHeights ($pFont, $pSize) {
	$bBox = imagettfbbox($pSize, 0, $pFont, "x");
	$xLine = $bBox[1] - $bBox[7];
	$bBox = imagettfbbox($pSize, 0, $pFont, "p");
	$pLine = $xLine - $bBox[1] + $bBox[7];
	$bBox = imagettfbbox($pSize, 0, $pFont, "k");
	$kLine = $bBox[1] - $bBox[7];
	$bBox = imagettfbbox($pSize, 0, $pFont, "H");
	$hLine = $bBox[1] - $bBox[7];
	$bBox = imagettfbbox($pSize, 0, $pFont, "ÁÂÃÄÅĂČ");
	$accLine = $bBox[1] - $bBox[7];
	return array($pLine, 0, $xLine, $hLine, $kLine, $accLine);
}
function getKerningOffset ($pFont, $pSize, $pText) {
	$bBox = imagettfbbox($pSize, 0, $pFont, "  ");
	$sWidth = $bBox[2] - $bBox[0];
	$bBox = imagettfbbox($pSize, 0, $pFont, "  " . $pText);
	$width = $bBox[2] - $bBox[0] - $sWidth;
	$bBox = imagettfbbox($pSize, 0, $pFont, $pText);
	$kerning = $bBox[2] - $bBox[0] - $width;
	return $kerning;
}

Die Anwendung

$text = "Äpfel";
$font = "c:/windows/fonts/georgiaz.ttf";
$size = 50;

$bBox = imagettfbbox($size, 0, $font, $text);
$lines = getTypographicHeights($font, $size);
$kerning = getKerningOffset($font, $size, $text);
$accentLine = $lines[5];
$pLine = $lines[0];
$img = imagecreatetruecolor($bBox[2] - $bBox[0], $accentLine - $pLine);
$backgnd = imagecolorallocate($img, 255, 255, 160);
$color = imagecolorallocate($img, 128, 0, 0);

imagefill($img, 0, 0, $backgnd);
imagettftext($img, $size, 0, $kerning, $accentLine, $color, $font, $text);
header("Content-Type: image/png");
imagepng($img);