Aleksander Machniak
2013-04-21 7439d3ee14ea8b9e61f656ab092b8d83c72e0dc9
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");
        }
    }
}