我正在寻找一种使用PHP将文本包装到特定宽度的框中的方法。我有动态的文本字符串,和可变的字体大小。
我找到了一个很好的方法,可以按照我想要的方式从这个线程中剪切文本:用PHP对长单词进行更智能的换行?
使用此代码块:
function smart_wordwrap($string, $width = 10, $break = "'n") {
// split on problem words over the line length
$pattern = sprintf('/([^ ]{%d,})/', $width);
$output = '';
$words = preg_split($pattern, $string, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);
foreach ($words as $word) {
if (false !== strpos($word, ' ')) {
// normal behaviour, rebuild the string
$output .= $word;
} else {
// work out how many characters would be on the current line
$wrapped = explode($break, wordwrap($output, $width, $break));
$count = $width - (strlen(end($wrapped)) % $width);
// fill the current line and add a break
$output .= substr($word, 0, $count) . $break;
// wrap any remaining characters from the problem word
$output .= wordwrap(substr($word, $count), $width, $break, true);
}
}
// wrap the final output
return wordwrap($output, $width, $break);
}
这很好,但我需要找到一种方法,将设置的像素尺寸(约束框)和字体大小输入到上面。上面的函数使用的是字符计数,如果字体很小,显然字符计数需要更大,反之亦然。
如果我有以下变量,我是否可以这样做?
$boxWidth = 200(px);
$text = (dynamic string);
$font = 'customfont.ttf'
$fontSize = (dynamic size);
我在想另一个单词换行函数的循环。或者可能有一种方法可以编辑"爆炸",因为我不完全确定这个函数是如何工作的。
正如@Mark Baker所建议的那样,我已经使用imagettfbbox()
实现了这种行为
- 带有helper函数的完整代码片段可以在这里找到
- 我发布相关代码以供参考
//帮助在图像上进行文本渲染的实用程序功能
// Returns expected width of rendered text in pixels
public static function getWidthPixels(string $text, string $font, int $font_size): int {
// https://www.php.net/manual/en/function.imageftbbox.php#refsect1-function.imageftbbox-returnvalues
$bbox = imageftbbox($font_size, 0, $font, " " . $text);
return $bbox[2] - $bbox[0];
}
// Returns wrapped format (with newlines) of a piece of text (meant to be rendered on an image)
// using the width of rendered bounding box of text
public static function wrapTextByPixels(
string $text,
int $line_max_pixels,
int $font_size,
string $font
): string {
$words = explode(' ', $text); // tokenize the text into words
$lines = []; // Array[Array[string]]: array to store lines of words
$crr_line_idx = 0; // (zero-based) index of current lines in which words are being added
$crr_line_pixels = 0; // width of current line (in which words are being added) in pixels
foreach ($words as $word) {
// determine the new width of current line (in pixels) if the current word is added to it (including space)
$crr_line_new_pixels = $crr_line_pixels + ImageTextRenderUtils::getWidthPixels(' ' . $word, $font, $font_size);
// determine the width of current word in pixels
$crr_word_pixels = ImageTextRenderUtils::getWidthPixels($word, $font, $font_size);
if ($crr_word_pixels > $line_max_pixels) {
// if the current word itself is too long to fit in single line
// then we have no option: it must still be put in oneline only
if ($crr_line_pixels == 0) {
// but it is put into current line only if current line is empty
$lines[$crr_line_idx] = array($word);
$crr_line_idx++;
} else {
// otherwise if current line is non-empty, then the extra long word is put into a newline
$crr_line_idx++;
$lines[$crr_line_idx] = array($word);
$crr_line_idx++;
$crr_line_pixels = 0;
}
} else if ($crr_line_new_pixels > $line_max_pixels) {
// otherwise if new width of current line (including current word and space)
// exceeds the maximum permissible width, then force the current word into newline
$crr_line_idx++;
$lines[$crr_line_idx] = array($word);
$crr_line_pixels = $crr_word_pixels;
} else {
// else if the current word (including space) can fit in the current line, then put it there
$lines[$crr_line_idx][] = $word;
$crr_line_pixels = $crr_line_new_pixels;
}
}
// after the above foreach loop terminates, the $lines 2-d array Array[Array[string]]
// would contain words segregated into lines to preserve the $line_max_pixels
// now we just need to stitch together lines (array of word strings) into a single continuous piece of text with
$concatenated_string = array_reduce(
$lines,
static function (string $wrapped_text, array $crr_line): string {
return $wrapped_text . PHP_EOL . implode(' ', $crr_line);
},
''
);
// the above process of concatenating lines into single piece of text will inadvertently
// add an extra newline ''n' character in the beginning; so we must remove that
return StringUtils::removeFirstOccurrence($concatenated_string, "'n");
}