mowgli
mowgli

Reputation: 2869

Smarter word-wrap in PHP for long words?

I'm looking for a way to make word-wrap in PHP a bit smarter. So it doesn't pre-break long words leaving any prior small words alone on one line.

Let's say I have this (the real text is always completely dynamic, this is just to show):

wordwrap('hello! heeeeeeeeeeeeeeereisaverylongword', 25, '<br />', true);

This outputs:

hello!
heeeeeeeeeeeeeeereisavery
longword

See, it leaves the small word alone on the first line. How can I get it to ouput something more like this:

hello! heeeeeeeeeeee
eeereisaverylongword

So it utilizes any available space on each line. I have tried several custom functions, but none have been effective (or they had some drawbacks).

Upvotes: 21

Views: 41513

Answers (6)

Emanuele Coppola
Emanuele Coppola

Reputation: 304

I encountered a similar issue while developing a PNG rendering engine in PHP using the GD library. I needed to correctly break text with monospace fonts and faced the same challenge.

To address it, I created a small utility: https://github.com/EmanueleCoppola/smartwrap

As documented in the README, with the smartwrap utility:

$output = wordwrap('hello! heeeeeeeeeeeeeeereisaverylongword', 20, "\n", true);
// The output will be ↓
$output === "hello!\nheeeeeeeeeeeeeeereis\naverylongword";

$output = smartwrap('hello! heeeeeeeeeeeeeeereisaverylongword', 20, "\n", true);
// The output will be ↓
$output === "hello! heeeeeeeeeeee\neeereisaverylongword";

Upvotes: 0

mowgli
mowgli

Reputation: 2869

This is also a solution (for browsers etc.):

$string = 'hello! heeeeeeeeeeeeeeeeeeeeeereisaverylongword';
echo preg_replace('/([^\s]{20})(?=[^\s])/', '$1'.'<wbr>', $string);

It puts a <wbr> at words with 20 or more characters

<wbr> means "word break opportunity" so it only breaks if it has to (dictated by width of element/browser/viewer/other). It's invisible otherwise.

Good for fluid/responsive layout where there is no fixed width. And does not wrap odd like php's wordwrap

Upvotes: 13

cmbuckley
cmbuckley

Reputation: 42547

I've had a go at the custom function for this smart wordwrap:

function smart_wordwrap($string, $width = 75, $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);
}

$string = 'hello! too long here too long here too heeeeeeeeeeeeeereisaverylongword but these words are shorterrrrrrrrrrrrrrrrrrrr';
echo smart_wordwrap($string, 11) . "\n";

EDIT: Spotted a couple of caveats. One major caveat with this (and also with the native function) is the lack of multibyte support.

Upvotes: 17

frglps
frglps

Reputation: 721

How about

$string = "hello! heeeeeeeeeeeeeeereisaverylongword";
$break = 25;

echo implode(PHP_EOL, str_split($string, $break));

Which outputs

hello! heeeeeeeeeeeeeeere                                                                                                                                                           
isaverylongword

str_split() converts the string to an array of $break size chunks.

implode() joins the array back together as a string using the glue which in this case is an end of line marker (PHP_EOL) although it could as easily be a '<br/>'

Upvotes: 17

brenjt
brenjt

Reputation: 16297

You can use CSS to accomplish this.

word-wrap: break-word;

That will break the word for you. Here is a link to see it in action:

http://www.css3.info/preview/word-wrap/

Upvotes: 7

Paul Dessert
Paul Dessert

Reputation: 6389

This should do the trick...

$word = "hello!" . wordwrap('heeeeeeeeeeeeeeereisaverylongword', 25, '<br />', true);
echo $word;

Upvotes: 4

Related Questions