diff --git a/AUTHORS b/AUTHORS index 8099eab3b..f50c9da04 100644 --- a/AUTHORS +++ b/AUTHORS @@ -168,4 +168,5 @@ suggestions: trHD Benjamin Weber alparo + Roy Zuo diff --git a/ChangeLog b/ChangeLog index 8092dc729..01f295025 100644 --- a/ChangeLog +++ b/ChangeLog @@ -83,6 +83,8 @@ Bugs package (thanks to Jakub Wilk). + Fixed bug #1183, correcting error message typos (thanks to Jakub Wilk). + Fixed bug #1184, correcting man page formatting (thanks to Jakub Wilk). + + Fixed bug #1189, which caused wide Asian UTF8 characters to be measured as + narrow characters (thanks to Roy Zuo). + Improved hyphenation by splitting on commas (even if no whitespace after). Leads to better output of, for example, 'task show', where comma-separated lists are common. diff --git a/src/ViewTask.cpp b/src/ViewTask.cpp index 5379c323d..562ba0edb 100644 --- a/src/ViewTask.cpp +++ b/src/ViewTask.cpp @@ -146,7 +146,7 @@ std::string ViewTask::render (std::vector & data, std::vector & seque if (print_empty_columns || global_min != 0) { - unsigned int label_length = utf8_length ((*i)->label ()); + unsigned int label_length = utf8_width ((*i)->label ()); if (label_length > global_min) global_min = label_length; if (label_length > global_ideal) global_ideal = label_length; minimal.push_back (global_min); diff --git a/src/ViewText.cpp b/src/ViewText.cpp index 348bf3a42..1d82d0771 100644 --- a/src/ViewText.cpp +++ b/src/ViewText.cpp @@ -122,7 +122,7 @@ std::string ViewText::render () for (unsigned int col = 0; col < _columns.size (); ++col) { // Headers factor in to width calculations. - unsigned int global_min = utf8_length (_columns[col]->label ()); + unsigned int global_min = utf8_width (_columns[col]->label ()); unsigned int global_ideal = global_min; for (unsigned int row = 0; row < _data.size (); ++row) diff --git a/src/columns/ColDescription.cpp b/src/columns/ColDescription.cpp index 0e9646771..68d8cbb7d 100644 --- a/src/columns/ColDescription.cpp +++ b/src/columns/ColDescription.cpp @@ -32,6 +32,7 @@ #include #include #include +#include #include #include @@ -106,14 +107,14 @@ void ColumnDescription::measure (Task& task, unsigned int& minimum, unsigned int int min_desc = longestWord (description); int min_anno = indent + Date::length (format); minimum = std::max (min_desc, min_anno); - maximum = description.length (); + maximum = utf8_width (description); std::map annos; task.getAnnotations (annos); std::map ::iterator i; for (i = annos.begin (); i != annos.end (); i++) { - unsigned int len = min_anno + 1 + i->second.length (); + unsigned int len = min_anno + 1 + utf8_width (i->second); if (len > maximum) maximum = len; } @@ -122,7 +123,7 @@ void ColumnDescription::measure (Task& task, unsigned int& minimum, unsigned int // Just the text else if (_style == "desc") { - maximum = description.length (); + maximum = utf8_width (description); minimum = longestWord (description); } @@ -136,20 +137,20 @@ void ColumnDescription::measure (Task& task, unsigned int& minimum, unsigned int int min_desc = longestWord (description); int min_anno = Date::length (format); minimum = std::max (min_desc, min_anno); - maximum = description.length (); + maximum = utf8_width (description); std::map annos; task.getAnnotations (annos); std::map ::iterator i; for (i = annos.begin (); i != annos.end (); i++) - maximum += i->second.length () + minimum + 1; + maximum += utf8_width (i->second) + minimum + 1; } // The te... else if (_style == "truncated") { minimum = 4; - maximum = description.length (); + maximum = utf8_width (description); } // The text [2] @@ -159,7 +160,7 @@ void ColumnDescription::measure (Task& task, unsigned int& minimum, unsigned int task.getAnnotations (annos); // + ' ' + '[' + + ']' - maximum = description.length () + 3 + format ((int)annos.size ()).length (); + maximum = utf8_width (description) + 3 + utf8_width (format ((int)annos.size ())); minimum = longestWord (description); } @@ -249,7 +250,7 @@ void ColumnDescription::render ( // This is a des... else if (_style == "truncated") { - int len = description.length (); + int len = utf8_width (description); if (len > width) lines.push_back (color.colorize (description.substr (0, width - 3) + "...")); else diff --git a/src/columns/ColProject.cpp b/src/columns/ColProject.cpp index d2bbdbd1c..4acc6453a 100644 --- a/src/columns/ColProject.cpp +++ b/src/columns/ColProject.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include #include @@ -87,7 +88,7 @@ void ColumnProject::measure (Task& task, unsigned int& minimum, unsigned int& ma throw format (STRING_COLUMN_BAD_FORMAT, _name, _style); minimum = longestWord (project); - maximum = project.length (); + maximum = utf8_width (project); } //////////////////////////////////////////////////////////////////////////////// diff --git a/src/columns/ColTags.cpp b/src/columns/ColTags.cpp index 0b716db87..a8a6f2f82 100644 --- a/src/columns/ColTags.cpp +++ b/src/columns/ColTags.cpp @@ -32,6 +32,7 @@ #include #include #include +#include extern Context context; @@ -85,14 +86,14 @@ void ColumnTags::setStyle (const std::string& value) // Set the minimum and maximum widths for the value. void ColumnTags::measure (Task& task, unsigned int& minimum, unsigned int& maximum) { - if (_style == "indicator") minimum = maximum = context.config.get ("tag.indicator").length (); + if (_style == "indicator") minimum = maximum = utf8_width (context.config.get ("tag.indicator")); else if (_style == "count") minimum = maximum = 3; else if (_style == "default" || _style == "list") { std::string tags = task.get (_name); minimum = 0; - maximum = tags.length (); + maximum = utf8_width (tags); if (maximum) { @@ -101,9 +102,9 @@ void ColumnTags::measure (Task& task, unsigned int& minimum, unsigned int& maxim std::vector ::iterator i; for (i = all.begin (); i != all.end (); ++i) { - unsigned int length_ = i->length (); - if (length_ > minimum) - minimum = i->length () + 1; + unsigned int length = utf8_width (*i); + if (length > minimum) + minimum = length; } } } @@ -121,7 +122,18 @@ void ColumnTags::render ( std::string tags = task.get (_name); if (tags != "") { - if (_style == "indicator") + if (_style == "default" || + _style == "list") + { + std::replace (tags.begin (), tags.end (), ',', ' '); + std::vector all; + wrapText (all, tags, width, _hyphenate); + + std::vector ::iterator i; + for (i = all.begin (); i != all.end (); ++i) + lines.push_back (color.colorize (leftJustify (*i, width))); + } + else if (_style == "indicator") { lines.push_back ( color.colorize ( @@ -135,17 +147,6 @@ void ColumnTags::render ( color.colorize ( rightJustify ("[" + format ((int)all.size ()) + "]", width))); } - else if (_style == "default" || - _style == "list") - { - std::replace (tags.begin (), tags.end (), ',', ' '); - std::vector all; - wrapText (all, tags, width, _hyphenate); - - std::vector ::iterator i; - for (i = all.begin (); i != all.end (); ++i) - lines.push_back (color.colorize (leftJustify (*i, width))); - } } } diff --git a/src/columns/ColUDA.cpp b/src/columns/ColUDA.cpp index facd44b17..36ccb732e 100644 --- a/src/columns/ColUDA.cpp +++ b/src/columns/ColUDA.cpp @@ -32,6 +32,7 @@ #include #include #include +#include #include #include @@ -99,11 +100,11 @@ void ColumnUDA::measure (Task& task, unsigned int& minimum, unsigned int& maximu if (format == "") format = context.config.get ("dateformat"); - minimum = maximum = date.toString (format).length (); + minimum = maximum = utf8_width (date.toString (format)); } else if (_type == "duration") { - minimum = maximum = Duration (value).formatCompact ().length (); + minimum = maximum = utf8_width (Duration (value).formatCompact ()); } else if (_type == "string") { @@ -113,7 +114,7 @@ void ColumnUDA::measure (Task& task, unsigned int& minimum, unsigned int& maximu } else if (_type == "numeric") { - minimum = maximum = value.length (); + minimum = maximum = utf8_width (value); } } } diff --git a/src/text.cpp b/src/text.cpp index 6ce964945..3098e32fd 100644 --- a/src/text.cpp +++ b/src/text.cpp @@ -1049,7 +1049,7 @@ std::string leftJustify (const int input, const int width) //////////////////////////////////////////////////////////////////////////////// std::string leftJustify (const std::string& input, const int width) { - return input + std::string (width - utf8_text_length (input), ' '); + return input + std::string (width - utf8_text_width (input), ' '); } //////////////////////////////////////////////////////////////////////////////// @@ -1071,7 +1071,7 @@ std::string rightJustify (const int input, const int width) //////////////////////////////////////////////////////////////////////////////// std::string rightJustify (const std::string& input, const int width) { - return std::string (width - utf8_text_length (input), ' ') + input; + return std::string (width - utf8_text_width (input), ' ') + input; } //////////////////////////////////////////////////////////////////////////////// diff --git a/test/bug.455.t b/test/bug.455.t index a27488cbc..8f696eee7 100755 --- a/test/bug.455.t +++ b/test/bug.455.t @@ -40,15 +40,17 @@ if (open my $fh, '>', '455.rc') ok (-r '455.rc', 'Created 455.rc'); } -# Bug #455 - Text alignment in reports is broken when text contains utf8 characters +# Bug #455 - Text alignment in reports is broken when text contains wide utf8 +# characters qx{../src/task rc:455.rc add abc pro:Bar\x{263A} 2>&1}; qx{../src/task rc:455.rc add def pro:Foo! 2>&1}; my $output = qx{../src/task rc:455.rc ls 2>&1}; -like ($output, qr/\s{7}abc/ms, 'bug 455 - correct spacing in utf8 task'); -like ($output, qr/\s{7}def/ms, 'bug 455 - correct spacing in non utf8 task'); +# ' ' + 'Pri' + ' ' == 5 +like ($output, qr/\S\s{5}abc/ms, 'bug 455 - correct spacing in utf8 task'); +like ($output, qr/\S\s{5}def/ms, 'bug 455 - correct spacing in non utf8 task'); # Cleanup. unlink qw(pending.data completed.data undo.data backlog.data synch.key 455.rc);