Библиотека Imagick в PHP позволяет рисовать текст поверх изображения. Как я могу сообщить 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); }
Я нашел ошибку с функцией @ 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); }
Я успешно использовал версию @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); }
вот моя версия для текстового контейнера с одной строкой
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 witonec 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); } ?>
И ВЫХОД