Aleksander Machniak
2013-04-21 102b08c6a23a1388c95fae00d2ff850f1fa7e2c4
Fix incorrect handling of leading spaces in text wrapping
3 files modified
193 ■■■■ changed files
CHANGELOG 1 ●●●● patch | view | raw | blame | history
program/lib/Roundcube/rcube_mime.php 146 ●●●●● patch | view | raw | blame | history
tests/Framework/Mime.php 46 ●●●●● patch | view | raw | blame | history
CHANGELOG
@@ -1,6 +1,7 @@
CHANGELOG Roundcube Webmail
===========================
- Fix incorrect handling of leading spaces in text wrapping
- Fix unintentional messages list jumps on click in Internet Explorer (#1489056)
- Fix list of required configuration options (#1489055)
- Fix DB error when creating a new contact and a group is selected (#1489051)
program/lib/Roundcube/rcube_mime.php
@@ -564,82 +564,122 @@
    /**
     * Improved wordwrap function.
     * Improved wordwrap function with multibyte support.
     * The code is based on Zend_Text_MultiByte::wordWrap().
     *
     * @param string $string  Text to wrap
     * @param int    $width   Line width
     * @param string $break   Line separator
     * @param bool   $cut     Enable to cut word
     * @param string $charset Charset of $string
     * @param string $string      Text to wrap
     * @param int    $width       Line width
     * @param string $break       Line separator
     * @param bool   $cut         Enable to cut word
     * @param string $charset     Charset of $string
     * @param bool   $wrap_quoted When enabled quoted lines will not be wrapped
     *
     * @return string Text
     */
    public static function wordwrap($string, $width=75, $break="\n", $cut=false, $charset=null)
    public static function wordwrap($string, $width=75, $break="\n", $cut=false, $charset=null, $wrap_quoted=true)
    {
        if ($charset && function_exists('mb_internal_encoding')) {
            mb_internal_encoding($charset);
        if (!$charset) {
            $charset = RCUBE_CHARSET;
        }
        $para   = preg_split('/\r?\n/', $string);
        $string = '';
        // detect available functions
        $strlen_func  = function_exists('iconv_strlen') ? 'iconv_strlen' : 'mb_strlen';
        $strpos_func  = function_exists('iconv_strpos') ? 'iconv_strpos' : 'mb_strpos';
        $strrpos_func = function_exists('iconv_strrpos') ? 'iconv_strrpos' : 'mb_strrpos';
        $substr_func  = function_exists('iconv_substr') ? 'iconv_substr' : 'mb_substr';
        while (count($para)) {
            $line = array_shift($para);
            if ($line[0] == '>') {
                $string .= $line . (count($para) ? $break : '');
                continue;
            }
        // Convert \r\n to \n, this is our line-separator
        $string       = str_replace("\r\n", "\n", $string);
        $separator    = "\n"; // must be 1 character length
        $result       = array();
            $list = explode(' ', $line);
            $len = 0;
            while (count($list)) {
                $line   = array_shift($list);
                $l      = mb_strlen($line);
                $space  = $len ? 1 : 0;
                $newlen = $len + $l + $space;
        while (($stringLength = $strlen_func($string, $charset)) > 0) {
            $breakPos = $strpos_func($string, $separator, 0, $charset);
                if ($newlen <= $width) {
                    $string .= ($space ? ' ' : '').$line;
                    $len += ($space + $l);
            // quoted line (do not wrap)
            if ($wrap_quoted && $string[0] == '>') {
                if ($breakPos === $stringLength - 1 || $breakPos === false) {
                    $subString = $string;
                    $cutLength = null;
                }
                else {
                    if ($l > $width) {
                        if ($cut) {
                            $start = 0;
                            while ($l) {
                                $str = mb_substr($line, $start, $width);
                                $strlen = mb_strlen($str);
                                $string .= ($len ? $break : '').$str;
                                $start += $strlen;
                                $l -= $strlen;
                                $len = $strlen;
                    $subString = $substr_func($string, 0, $breakPos, $charset);
                    $cutLength = $breakPos + 1;
                }
            }
            // next line found and current line is shorter than the limit
            else if ($breakPos !== false && $breakPos < $width) {
                if ($breakPos === $stringLength - 1) {
                    $subString = $string;
                    $cutLength = null;
                }
                else {
                    $subString = $substr_func($string, 0, $breakPos, $charset);
                    $cutLength = $breakPos + 1;
                }
            }
            else {
                $subString = $substr_func($string, 0, $width, $charset);
                // last line
                if ($subString === $string) {
                    $cutLength = null;
                }
                else {
                    $nextChar = $substr_func($string, $width, 1, $charset);
                    if ($nextChar === ' ' || $nextChar === $separator) {
                        $afterNextChar = $substr_func($string, $width + 1, 1, $charset);
                        if ($afterNextChar === false) {
                            $subString .= $nextChar;
                        }
                        $cutLength = $strlen_func($subString, $charset) + 1;
                    }
                    else {
                        if ($strrpos_func[0] == 'm') {
                            $spacePos = $strrpos_func($subString, ' ', 0, $charset);
                        }
                        else {
                            $spacePos = $strrpos_func($subString, ' ', $charset);
                        }
                        if ($spacePos !== false) {
                            $subString = $substr_func($subString, 0, $spacePos, $charset);
                            $cutLength = $spacePos + 1;
                        }
                        else if ($cut === false) {
                            $spacePos = $strpos_func($string, ' ', 0, $charset);
                            if ($spacePos !== false) {
                                $subString = $substr_func($string, 0, $spacePos, $charset);
                                $cutLength = $spacePos + 1;
                            }
                            else {
                                $subString = $string;
                                $cutLength = null;
                            }
                        }
                        else {
                            $string .= ($len ? $break : '').$line;
                            if (count($list)) {
                                $string .= $break;
                            }
                            $len = 0;
                            $subString = $substr_func($subString, 0, $width, $charset);
                            $cutLength = $width;
                        }
                    }
                    else {
                        $string .= $break.$line;
                        $len = $l;
                    }
                }
            }
            if (count($para)) {
                $string .= $break;
            $result[] = $subString;
            if ($cutLength !== null) {
                $string = $substr_func($string, $cutLength, ($stringLength - $cutLength), $charset);
            }
            else {
                break;
            }
        }
        if ($charset && function_exists('mb_internal_encoding')) {
            mb_internal_encoding(RCUBE_CHARSET);
        }
        return $string;
        return implode($break, $result);
    }
tests/Framework/Mime.php
@@ -142,4 +142,50 @@
        $this->assertEquals($unfolded, rcube_mime::unfold_flowed($flowed), "Test correct unfolding of quoted lines");
    }
    /**
     * Test wordwrap()
     */
    function test_wordwrap()
    {
        $samples = array(
            array(
                array("aaaa aaaa\n           aaaa"),
                "aaaa aaaa\n           aaaa",
            ),
            array(
                array("123456789 123456789 123456789 123", 29),
                "123456789 123456789 123456789\n123",
            ),
            array(
                array("123456789   3456789 123456789", 29),
                "123456789   3456789 123456789",
            ),
            array(
                array("123456789 123456789 123456789   123", 29),
                "123456789 123456789 123456789\n  123",
            ),
            array(
                array("abc", 1, "\n", true),
                "a\nb\nc",
            ),
            array(
                array("ąść", 1, "\n", true, 'UTF-8'),
                "ą\nś\nć",
            ),
            array(
                array(">abc\n>def", 2, "\n", true),
                ">abc\n>def",
            ),
            array(
                array("abc def", 3, "-"),
                "abc-def",
            ),
        );
        foreach ($samples as $sample) {
            $this->assertEquals($sample[1], call_user_func_array(array('rcube_mime', 'wordwrap'), $sample[0]), "Test text wrapping");
        }
    }
}