phplucidframe / console-table

ConsoleTable helps you to display tabular data in a terminal/shell/console
Other
99 stars 22 forks source link

Cell width problem on multibyte characters #12

Closed drmalus closed 5 years ago

drmalus commented 5 years ago

Hi!

First of all this package is awesome, i really like it so thanks for it!

I found an error on multibyte characters. I've listed records from a database table and there was some Hungarian names.

I fixed this with mb_strlen() and a new strPadUnicode method. Here is the new method with the two fixed methods.

   /**
     * Calculate maximum width of each column
     *
     * @return array
     */
    private function calculateColumnWidth()
    {
        foreach ($this->data as $y => $row) {
            if (is_array($row)) {
                foreach ($row as $x => $col) {
                    $content = preg_replace('#\x1b[[][^A-Za-z]*[A-Za-z]#', '', $col);
                    if (!isset($this->columnWidths[$x])) {
                        $this->columnWidths[$x] = mb_strlen($content, 'UTF-8');
                    } else {
                        if (mb_strlen($content, 'UTF-8') > $this->columnWidths[$x]) {
                            $this->columnWidths[$x] = mb_strlen($content, 'UTF-8');
                        }
                    }
                }
            }
        }

        return $this->columnWidths;
    }

    /**
     * Get the printable cell content
     *
     * @param integer $index The column index
     * @param array   $row   The table row
     * @return string
     */
    private function getCellOutput($index, $row = null)
    {
        $cell = $row ? $row[$index] : '-';
        $width = $this->columnWidths[$index];
        $pad = $row ? $width - mb_strlen($cell, 'UTF-8') : $width;
        $padding = str_repeat($row ? ' ' : '-', $this->padding);

        $output = '';

        if ($index === 0) {
            $output .= str_repeat(' ', $this->indent);
        }

        if ($this->border) {
            $output .= $row ? '|' : '+';
        }

        $output .= $padding; # left padding
        $cell = trim(preg_replace('/\s+/', ' ', $cell)); # remove line breaks
        $content = preg_replace('#\x1b[[][^A-Za-z]*[A-Za-z]#', '', $cell);
        $delta = mb_strlen($cell, 'UTF-8') - mb_strlen($content, 'UTF-8');
        $output .= $this->strPadUnicode($cell, $width + $delta, $row ? ' ' : '-'); # cell content
        $output .= $padding; # right padding
        if ($row && $index == count($row) - 1 && $this->border) {
            $output .= $row ? '|' : '+';
        }

        return $output;
    }

    /**
     * Multibyte version of str_pad() function
     * @source http://php.net/manual/en/function.str-pad.php
     */
    private function strPadUnicode($str, $padLength, $padString = ' ', $dir = STR_PAD_RIGHT)
    {
        $strLen = mb_strlen($str, 'UTF-8');
        $padStrLen = mb_strlen($padString, 'UTF-8');
        if (!$strLen && ($dir == STR_PAD_RIGHT || $dir == STR_PAD_LEFT)) {
            $strLen = 1;
        }
        if (!$padLength || !$padStrLen || $padLength <= $strLen) {
            return $str;
        }

        $result = null;
        $repeat = ceil($strLen - $padStrLen + $padLength);
        if ($dir == STR_PAD_RIGHT) {
            $result = $str . str_repeat($padString, $repeat);
            $result = mb_substr($result, 0, $padLength, 'UTF-8');
        } else if ($dir == STR_PAD_LEFT) {
            $result = str_repeat($padString, $repeat) . $str;
            $result = mb_substr($result, -$padLength, null, 'UTF-8');
        } else if ($dir == STR_PAD_BOTH) {
            $length = ($padLength - $strLen) / 2;
            $repeat = ceil($length / $padStrLen);
            $result = mb_substr(str_repeat($padString, $repeat), 0, floor($length), 'UTF-8')
                . $str
                . mb_substr(str_repeat($padString, $repeat), 0, ceil($length), 'UTF-8');
        }

        return $result;
    }

With this fix everything is work great for me!

Thanks, Frank Szklenár

cithukyaw commented 5 years ago

I'm glad to hear it is helpful to you and thanks for your reporting.

I think it is better you create a pull request with your code so that I can test and merge it. And I also need to know your input characters.

ethaniel commented 5 years ago

@drmalus thank you so much for your fix. @cithukyaw it actually works!

Before: image

After: image

drmalus commented 5 years ago

@ethaniel I'am really happy to help you with my fix! :)

cithukyaw commented 5 years ago

released version 1.2.4 with this fix. Thanks @drmalus.

HappyVibes commented 5 years ago

hey :) i have the same exact problem with version 1.4.2 when i use hebrew with that. maybe you know what can i do about that?