我正在尝试将文字转换为图片。我已经这样做了,但有些情况下文字不在图像框
" e" ""被削减。我尝试过减小字体大小或增加图像的宽度,但在某些情况下,这会再次发生在另一个文本中。这是代码:
$new_line_position = 61;
$angle = 0;
$left = 20;
$top = 45;
$image_width = 1210;
$image_line_height = 45;
$content_input = wordwrap($content_input, $new_line_position, "\n", true);
$lineas = preg_split('/\\n/', $content_input);
$lines_breaks = count($lineas);
$image_height = $image_line_height * $lines_breaks;
$im = imagecreatetruecolor($image_width, $image_height);
// Create some colors
$white = imagecolorallocate($im, 255, 255, 255);
$black = imagecolorallocate($im, 0, 0, 0);
imagefilledrectangle($im, 0, 0, $image_width, $image_height, $white);
$font_ttf = public_path().'/fonts/'.$ttf_font;
foreach($lineas as $linea){
imagettftext($im, $font_size, $angle, $left, $top, $black, $font_ttf, $linea);
$top = $top + $image_line_height;
}
// Add the text
imagepng($im);
imagedestroy($im);
谢谢。
答案 0 :(得分:9)
问题是每个字符的宽度可能略有不同,例如//functions are using #defined values
(...)
和//functions are using #defined values
(...)
。因为你不能按行数字符串分割字符串,所以你需要一个更准确的方法。
主要技巧是使用
W
使用TrueType字体给出文本的边界框,从中可以获得文本将使用的实际宽度。
这是http://php.net/manual/en/function.wordwrap.php找到的像素完美分割功能 使用它而不是wordwrap并传递额外的值,如图像宽度,字体大小和字体路径
i
下面是工作代码示例:
我稍微修改了这个函数imagettfbbox
。此外,修改了代码中的一些计算。现在正在给我一个正确计算边距的完美图像。我不是很满意代码注意到有一个$调整变量,当你使用更大的字体大小时应该更大。我认为这归结于<?php
/**
* Wraps a string to a given number of pixels.
*
* This function operates in a similar fashion as PHP's native wordwrap function; however,
* it calculates wrapping based on font and point-size, rather than character count. This
* can generate more even wrapping for sentences with a consider number of thin characters.
*
* @static $mult;
* @param string $text - Input string.
* @param float $width - Width, in pixels, of the text's wrapping area.
* @param float $size - Size of the font, expressed in pixels.
* @param string $font - Path to the typeface to measure the text with.
* @return string The original string with line-breaks manually inserted at detected wrapping points.
*/
function pixel_word_wrap($text, $width, $size, $font)
{
# Passed a blank value? Bail early.
if (!$text)
return $text;
# Check if imagettfbbox is expecting font-size to be declared in points or pixels.
static $mult;
$mult = $mult ?: version_compare(GD_VERSION, '2.0', '>=') ? .75 : 1;
# Text already fits the designated space without wrapping.
$box = imagettfbbox($size * $mult, 0, $font, $text);
if ($box[2] - $box[0] / $mult < $width)
return $text;
# Start measuring each line of our input and inject line-breaks when overflow's detected.
$output = '';
$length = 0;
$words = preg_split('/\b(?=\S)|(?=\s)/', $text);
$word_count = count($words);
for ($i = 0; $i < $word_count; ++$i) {
# Newline
if (PHP_EOL === $words[$i])
$length = 0;
# Strip any leading tabs.
if (!$length)
$words[$i] = preg_replace('/^\t+/', '', $words[$i]);
$box = imagettfbbox($size * $mult, 0, $font, $words[$i]);
$m = $box[2] - $box[0] / $mult;
# This is one honkin' long word, so try to hyphenate it.
if (($diff = $width - $m) <= 0) {
$diff = abs($diff);
# Figure out which end of the word to start measuring from. Saves a few extra cycles in an already heavy-duty function.
if ($diff - $width <= 0)
for ($s = strlen($words[$i]); $s; --$s) {
$box = imagettfbbox($size * $mult, 0, $font, substr($words[$i], 0, $s) . '-');
if ($width > ($box[2] - $box[0] / $mult) + $size) {
$breakpoint = $s;
break;
}
}
else {
$word_length = strlen($words[$i]);
for ($s = 0; $s < $word_length; ++$s) {
$box = imagettfbbox($size * $mult, 0, $font, substr($words[$i], 0, $s + 1) . '-');
if ($width < ($box[2] - $box[0] / $mult) + $size) {
$breakpoint = $s;
break;
}
}
}
if ($breakpoint) {
$w_l = substr($words[$i], 0, $s + 1) . '-';
$w_r = substr($words[$i], $s + 1);
$words[$i] = $w_l;
array_splice($words, $i + 1, 0, $w_r);
++$word_count;
$box = imagettfbbox($size * $mult, 0, $font, $w_l);
$m = $box[2] - $box[0] / $mult;
}
}
# If there's no more room on the current line to fit the next word, start a new line.
if ($length > 0 && $length + $m >= $width) {
$output .= PHP_EOL;
$length = 0;
# If the current word is just a space, don't bother. Skip (saves a weird-looking gap in the text).
if (' ' === $words[$i])
continue;
}
# Write another word and increase the total length of the current line.
$output .= $words[$i];
$length += $m;
}
return $output;
}
;
?>
功能的不完美。但这是一种实用的方法,适用于大多数字体大小。
pixel_word_wrap
这是一个例子
您也可以使用等宽字体。等宽字体是一种字体,其字母和字符各自占据相同数量的水平空间。
答案 1 :(得分:4)
问题是你的字体是每个字母的可变宽度,但你是根据字母数而不是字体宽度截断的。
采取以下示例,10&#34;我&#34; vs&#34; W&#34;,第二个将超过两倍。
IIIIIIIIII
WWWWWWWWWW
&#34;简单&#34;选项是使用等宽字体,例如Courier,它在下面的块中使用:
iiiiiiiiii
WWWWWWWWWW
但那是一个无聊的字体!所以你需要的是在每一行上使用ìmagettfbbox
(图像真实类型字体边界框&#34;函数http://php.net/manual/en/function.imagettfbbox.php)来获得宽度。你需要一次一行地运行这个函数,然后逐渐减小,直到你得到你需要的大小。
一段pseduo代码(请注意:手写而未经过测试,你需要兼顾它以使其完美):
$targetPixelWidth = 300;
$maximumChactersPerLine = 200; // Make this larger then you expect, but too large will slow it down!
$textToDisplay = "Your long bit of text goes here"
$aLinesToDisplay = array();
while (strlen(textToDisplay) > 0) {
$hasTextToShow = false;
$charactersToTest = $maximumChactersPerLine;
while (!$hasTextToShow && $maximumChactersPerLine>0) {
$wrappedText = wordwrap($textToDisplay, $maximumChactersPerLine);
$aSplitWrappedText = explode("\n", $wrappedText);
$firstLine = trim($aSplitWrappedText[0]);
if (strlen($firstLine) == 0) {
// Fallback to "default"
$charactersToTest = 0;
} else {
$aBoundingBox = imagettfbbox($fontSize, 0, $firstLine, $yourTTFFontFile);
$width = abs($aBoundingBox[2] - $aBoundingBox[0]);
if ($width <= $targetPixelWidth) {
$hasTextToShow = true;
$aLinesToDisplay[] = $firstLine;
$textToDisplay = trim(substr($textToDisplay, strlen($firstLine));
} else {
--$charactersToTest;
}
}
}
if (!$hasTextToShow) {
// You need to handle this by getting SOME text (e.g. first word) and decreasing the length of $textToDisplay, otherwise you'll stay in the loop forever!
$firstLine = ???; // Suggest split at first "space" character (Use preg_split on \s?)
$aLinesToDisplay[] = $firstLine;
$textToDisplay = trim(substr($textToDisplay, strlen($firstLine));
}
}
// Now run the "For Each" to print the lines.
警告:TTF边界框功能也不完美 - 所以允许一些&#34; lee way&#34;但是你仍然会得到远远好于你上面做的更好的结果(即+ -10像素)。它还取决于字体文件字距调整(字母之间的间隙)信息。如果您需要,有点Goggling并阅读手册中的注释将帮助您获得更准确的结果。
你还应该优化上面的功能(以10个字符开头并增加,取适合的最后一个字符串可以让你得到一个更快的答案,直到某些东西适合,并减少strlen
个调用的数量,例如)
回复评论的附录&#34;你可以扩展&#34; TTF边界框功能也不完美&#34;?&#34; (回复太长,无法发表评论)
该功能依赖于&#34;字距调整&#34;字体中的信息。例如,你希望V更接近A(VA - 看他们如何&#34;重叠&#34;稍微),而不是V和W(VW - 看看W在V之后如何开始)。关于该间距的字体中嵌入了许多规则。其中一些规则也说'#34;我知道&#39;框&#39;从0开始,但是对于这个字母,你需要开始绘制-3像素&#34;。
PHP最好阅读规则,但有时会出错,因此会给你错误的维度。这就是为什么你可以告诉PHP写出&#34; 0,0&#34;但它实际上始于&#34; -3,0&#34;并似乎切断了字体。最简单的解决方案是允许几个像素宽限。
是的,这是一个引人注目的问题&#34; (https://www.google.com/webhp?q=php%20bounding%20box%20incorrect)