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

BMi*_*ner 7 php text annotate imagick

PHP中的Imagick库允许您在图像上绘制文本.如何告诉Imagick根据一些有界文本框包装文本?

即,使单词显示为多行文本而不是单行.

BMi*_*ner 25

用法:

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]);
Run Code Online (Sandbox Code Playgroud)

功能:

/* 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);
}
Run Code Online (Sandbox Code Playgroud)


Sar*_*rke 7

我发现了@Bobiner函数的一个错误,当只有一个单词时,它会返回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);
}
Run Code Online (Sandbox Code Playgroud)


Joh*_*ord 5

我已经成功使用@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);
}
Run Code Online (Sandbox Code Playgroud)