如何在 PHP 中使用 Imagick 换行文本,以便将其绘制为多行文本?

发布于 2024-11-02 10:47:14 字数 90 浏览 4 评论 0原文

PHP 中的 Imagick 库允许您在图像上绘制文本。

我如何告诉 Imagick 根据某个有界文本框来换行文本,以便单词显示为多行文本而不是单行?

The Imagick library in PHP allows you to draw text on top of an image.

How can I tell Imagick to wrap the text based upon some bounded text box, so that the words appear as multiline text rather than a single line?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

孤君无依 2024-11-09 10:47:14

用途:

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);
}

Usage:

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]);

Function:

/* 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);
}
带刺的爱情 2024-11-09 10:47:14

我已经成功使用 @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);
}

I have been using @Sarke's version successfully for a while, but I noticed there is an infinite loop if a word is longer than the $maxWidth. Here is a version that fixes the infinite loop:

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);
}
不甘平庸 2024-11-09 10:47:14

我发现 @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);
}

I found a bug with @BMiner's function where it returns a lineheight of 0 when there is only one word.

I ended up re-writing it in one loop using array functions. I kept the parameters the same so it works with current implementations.

I used preg_split instead so it works well with extra or double spaces, tabs, and line-breaks.

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);
}
不必在意 2024-11-09 10:47:14

这是我的单行文本容器版本

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;

here is my version for one line text container

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;
骄傲 2024-11-09 10:47:14

嗨,我找到了一些解决方案,感谢 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);



}

?>

和输出

在此处输入图像描述

Hi i find some sollution thanks for BMinner for his code
i edit his code and get good working sollution

USAGE

<?
   $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;

?>

FUNCTIONS REFERANCED FROM 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);



}

?>

AND OUTPUT

enter image description here

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文