PHP 中的 Imagick 库允许您在图像上绘制文本。
我如何告诉 Imagick 基于一些有界文本框来换行文本,以便单词显示为多行文本而不是单行?
用法:
list($lines, $lineHeight) = wordWrapAnnotation($image, $draw, $msg, 140);
for($i = 0; $i < count($lines); $i++)
$image->annotateImage($draw, $xpos, $ypos + $i*$lineHeight, 0, $lines[$i]);
功能:
/* Implement word wrapping... Ughhh... why is this NOT done for me!!!
OK... I know the algorithm sucks at efficiency, but it's for short messages, okay?
Make sure to set the font on the ImagickDraw Object first!
@param image the Imagick Image Object
@param draw the ImagickDraw Object
@param text the text you want to wrap
@param maxWidth the maximum width in pixels for your wrapped "virtual" text box
@return an array of lines and line heights
*/
function wordWrapAnnotation(&$image, &$draw, $text, $maxWidth)
{
$words = explode(" ", $text);
$lines = array();
$i = 0;
$lineHeight = 0;
while($i < count($words) )
{
$currentLine = $words[$i];
if($i+1 >= count($words))
{
$lines[] = $currentLine;
break;
}
//Check to see if we can add another word to this line
$metrics = $image->queryFontMetrics($draw, $currentLine . ' ' . $words[$i+1]);
while($metrics['textWidth'] <= $maxWidth)
{
//If so, do it and keep doing it!
$currentLine .= ' ' . $words[++$i];
if($i+1 >= count($words))
break;
$metrics = $image->queryFontMetrics($draw, $currentLine . ' ' . $words[$i+1]);
}
//We can't add the next word to this line, so loop to the next line
$lines[] = $currentLine;
$i++;
//Finally, update line height
if($metrics['textHeight'] > $lineHeight)
$lineHeight = $metrics['textHeight'];
}
return array($lines, $lineHeight);
}
我已经成功使用@Sarke的版本有一段时间了,但我注意到如果一个单词长于$maxWidth,就会出现无限循环。这是修复无限循环的版本:
function wordWrapAnnotation($image, $draw, $text, $maxWidth)
{
$text = trim($text);
$words = preg_split('%\s%', $text, -1, PREG_SPLIT_NO_EMPTY);
$lines = array();
$i = 0;
$lineHeight = 0;
while (count($words) > 0)
{
$metrics = $image->queryFontMetrics($draw, implode(' ', array_slice($words, 0, ++$i)));
$lineHeight = max($metrics['textHeight'], $lineHeight);
// check if we have found the word that exceeds the line width
if ($metrics['textWidth'] > $maxWidth or count($words) < $i)
{
// handle case where a single word is longer than the allowed line width (just add this as a word on its own line?)
if ($i == 1)
$i++;
$lines[] = implode(' ', array_slice($words, 0, --$i));
$words = array_slice($words, $i);
$i = 0;
}
}
return array($lines, $lineHeight);
}
我发现@BMiner 的函数存在一个错误,当只有一个单词时,它返回的行高为 0。
我最终使用数组函数在一个循环中重写了它。我保持参数不变,因此它适用于当前的实现。
我改用 preg_split,所以它可以很好地处理额外或双空格、制表符和换行符。
function wordWrapAnnotation($image, $draw, $text, $maxWidth)
{
$words = preg_split('%\s%', $text, -1, PREG_SPLIT_NO_EMPTY);
$lines = array();
$i = 0;
$lineHeight = 0;
while (count($words) > 0)
{
$metrics = $image->queryFontMetrics($draw, implode(' ', array_slice($words, 0, ++$i)));
$lineHeight = max($metrics['textHeight'], $lineHeight);
if ($metrics['textWidth'] > $maxWidth or count($words) < $i)
{
$lines[] = implode(' ', array_slice($words, 0, --$i));
$words = array_slice($words, $i);
$i = 0;
}
}
return array($lines, $lineHeight);
}
这是我的单行文本容器版本
function GetTextSize($font,$text,$max_weight,$max_width){
$size = $max_weight;
$imagick=new Imagick();
while (true){
$draw = new ImagickDraw();
$draw->setFontSize($size);
$draw->setfont($font);
$bbox2=$imagick->queryFontMetrics($draw,$text);
$width_of_text = $bbox2[textWidth];
if ($width_of_text > $max_width){
$size -= 1;
}else{
break;
}
}
return $size;
}
$draw = new ImagickDraw();
$font="path_to_font.ttf";
$text="Love Happyness Freedom";
$output = new Imagick('path_to_image.jpg');
$output->setGravity(Imagick::GRAVITY_CENTER);
$fontsize=GetTextSize($font,$text,70,600);
$draw->setfont($font);
$draw->setFontSize($fontsize);
$draw->annotation(000, 000, $text);
$output->drawImage($draw);
$output->setImageFormat('jpg');
header('Content-Type: image/jpg');
print $output;
嗨,我为 BMinner 的代码找到了一些解决方案,我编辑了他的代码并获得了良好的工作解决方案
用法
<?
$w = 210;
$h = 520;
$canvas = new Imagick();
$canvas->newImage($w,$h,new ImagickPixel('green'),'png');
$draw = new ImagickDraw();
$draw->setFontSize(25);
$text="SomeTextWithoutSpacesAndGoingOn..xxxxx <br><br>some short words with spaces <br><br>and some text<br>with manuel page<br>break <br><br>and also multiple spaces spaces end. also w i t o n e c ha ra c ter";
list($lines, $lineHeight)= wordWrapAnnotation($canvas, $draw, $text, $w-20);
$canvas->annotateImage($draw, 10, $lineHeight , 0, $lines);
header("Content-Type: image/png");
echo $canvas;
?>
BMinner 引用的函数
<?
//this is unicode split method for out of english latin characters
function str_split_unicode($str, $l = 0) {
if ($l > 0) {
$ret = array();
$len = mb_strlen($str, "UTF-8");
for ($i = 0; $i < $len; $i += $l) {
$ret[] = mb_substr($str, $i, $l, "UTF-8");
}
return $ret;
}
return preg_split("//u", $str, -1, PREG_SPLIT_NO_EMPTY);
}
//this is my function detects long words and split them
function check_long_words($image,$draw,$text,$maxWidth) {
$metrics = $image->queryFontMetrics($draw, $text);
if($metrics['textWidth'] <= $maxWidth)
return array($text);
$words = str_split_unicode($text);
$i = 0;
while($i < count($words) )
{
$currentLine = $words[$i];
if($i+1 >= count($words))
{
$lines[] = $currentLine;
//$lines = $lines + $checked;
break;
}
//Check to see if we can add another word to this line
$metrics = $image->queryFontMetrics($draw, $currentLine . $words[$i+1]);
while($metrics['textWidth'] <= $maxWidth)
{
//If so, do it and keep doing it!
$currentLine .= $words[++$i];
if($i+1 >= count($words))
break;
$metrics = $image->queryFontMetrics($draw, $currentLine . ' ' . $words[$i+1]);
$t++;
}
//We can't add the next word to this line, so loop to the next line
$lines[] = $currentLine;
$i++;
}
return $lines;
}
//this is BMiner code some fixes for manule breaks
function wordWrapAnnotation(&$image, &$draw, $text, $maxWidth)
{
$brler = explode("<br>", $text);
$lines = array();
foreach($brler as $br)
{
$i = 0;
$words = explode(" ", $br);
while($i < count($words) )
{
$currentLine = $words[$i];
$metrics = $image->queryFontMetrics($draw, $currentLine . ' ' . $words[$i+1]);
if($i+1 >= count($words))
{
$checked=check_long_words($image,$draw,$currentLine,$maxWidth);
$lines = array_merge($lines, $checked);
if($metrics['textHeight'] > $lineHeight)
$lineHeight = $metrics['textHeight'];
//$lines = $lines + $checked;
break;
}
//Check to see if we can add another word to this line
while($metrics['textWidth'] <= $maxWidth)
{
//If so, do it and keep doing it!
$currentLine .= ' ' . $words[++$i];
if($i+1 >= count($words))
break;
$metrics = $image->queryFontMetrics($draw, $currentLine . ' ' . $words[$i+1]);
$t++;
}
//We can't add the next word to this line, so loop to the next line
$checked=check_long_words($image,$draw,$currentLine,$maxWidth);
$lines = array_merge($lines, $checked);
$i++;
//Finally, update line height
if($metrics['textHeight'] > $lineHeight)
$lineHeight = $metrics['textHeight'];
}
}
return array(join("\n",$lines), $lineHeight);
}
?>
和输出