- Fixed bug #1189, which caused wide Asian UTF8 characters to be measured as
  narrow characters (thanks to Roy Zuo).
This commit is contained in:
Paul Beckingham 2013-03-02 18:22:21 -05:00
parent 914447c885
commit 6aa0277749
10 changed files with 45 additions and 36 deletions

View file

@ -168,4 +168,5 @@ suggestions:
trHD trHD
Benjamin Weber Benjamin Weber
alparo alparo
Roy Zuo

View file

@ -83,6 +83,8 @@ Bugs
package (thanks to Jakub Wilk). package (thanks to Jakub Wilk).
+ Fixed bug #1183, correcting error message typos (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 #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). + Improved hyphenation by splitting on commas (even if no whitespace after).
Leads to better output of, for example, 'task show', where comma-separated Leads to better output of, for example, 'task show', where comma-separated
lists are common. lists are common.

View file

@ -146,7 +146,7 @@ std::string ViewTask::render (std::vector <Task>& data, std::vector <int>& seque
if (print_empty_columns || global_min != 0) 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_min) global_min = label_length;
if (label_length > global_ideal) global_ideal = label_length; if (label_length > global_ideal) global_ideal = label_length;
minimal.push_back (global_min); minimal.push_back (global_min);

View file

@ -122,7 +122,7 @@ std::string ViewText::render ()
for (unsigned int col = 0; col < _columns.size (); ++col) for (unsigned int col = 0; col < _columns.size (); ++col)
{ {
// Headers factor in to width calculations. // 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; unsigned int global_ideal = global_min;
for (unsigned int row = 0; row < _data.size (); ++row) for (unsigned int row = 0; row < _data.size (); ++row)

View file

@ -32,6 +32,7 @@
#include <Date.h> #include <Date.h>
#include <ColDescription.h> #include <ColDescription.h>
#include <text.h> #include <text.h>
#include <utf8.h>
#include <util.h> #include <util.h>
#include <i18n.h> #include <i18n.h>
@ -106,14 +107,14 @@ void ColumnDescription::measure (Task& task, unsigned int& minimum, unsigned int
int min_desc = longestWord (description); int min_desc = longestWord (description);
int min_anno = indent + Date::length (format); int min_anno = indent + Date::length (format);
minimum = std::max (min_desc, min_anno); minimum = std::max (min_desc, min_anno);
maximum = description.length (); maximum = utf8_width (description);
std::map <std::string, std::string> annos; std::map <std::string, std::string> annos;
task.getAnnotations (annos); task.getAnnotations (annos);
std::map <std::string, std::string>::iterator i; std::map <std::string, std::string>::iterator i;
for (i = annos.begin (); i != annos.end (); 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) if (len > maximum)
maximum = len; maximum = len;
} }
@ -122,7 +123,7 @@ void ColumnDescription::measure (Task& task, unsigned int& minimum, unsigned int
// Just the text // Just the text
else if (_style == "desc") else if (_style == "desc")
{ {
maximum = description.length (); maximum = utf8_width (description);
minimum = longestWord (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_desc = longestWord (description);
int min_anno = Date::length (format); int min_anno = Date::length (format);
minimum = std::max (min_desc, min_anno); minimum = std::max (min_desc, min_anno);
maximum = description.length (); maximum = utf8_width (description);
std::map <std::string, std::string> annos; std::map <std::string, std::string> annos;
task.getAnnotations (annos); task.getAnnotations (annos);
std::map <std::string, std::string>::iterator i; std::map <std::string, std::string>::iterator i;
for (i = annos.begin (); i != annos.end (); i++) for (i = annos.begin (); i != annos.end (); i++)
maximum += i->second.length () + minimum + 1; maximum += utf8_width (i->second) + minimum + 1;
} }
// The te... // The te...
else if (_style == "truncated") else if (_style == "truncated")
{ {
minimum = 4; minimum = 4;
maximum = description.length (); maximum = utf8_width (description);
} }
// The text [2] // The text [2]
@ -159,7 +160,7 @@ void ColumnDescription::measure (Task& task, unsigned int& minimum, unsigned int
task.getAnnotations (annos); task.getAnnotations (annos);
// <description> + ' ' + '[' + <count> + ']' // <description> + ' ' + '[' + <count> + ']'
maximum = description.length () + 3 + format ((int)annos.size ()).length (); maximum = utf8_width (description) + 3 + utf8_width (format ((int)annos.size ()));
minimum = longestWord (description); minimum = longestWord (description);
} }
@ -249,7 +250,7 @@ void ColumnDescription::render (
// This is a des... // This is a des...
else if (_style == "truncated") else if (_style == "truncated")
{ {
int len = description.length (); int len = utf8_width (description);
if (len > width) if (len > width)
lines.push_back (color.colorize (description.substr (0, width - 3) + "...")); lines.push_back (color.colorize (description.substr (0, width - 3) + "..."));
else else

View file

@ -30,6 +30,7 @@
#include <Context.h> #include <Context.h>
#include <ColProject.h> #include <ColProject.h>
#include <text.h> #include <text.h>
#include <utf8.h>
#include <util.h> #include <util.h>
#include <i18n.h> #include <i18n.h>
@ -87,7 +88,7 @@ void ColumnProject::measure (Task& task, unsigned int& minimum, unsigned int& ma
throw format (STRING_COLUMN_BAD_FORMAT, _name, _style); throw format (STRING_COLUMN_BAD_FORMAT, _name, _style);
minimum = longestWord (project); minimum = longestWord (project);
maximum = project.length (); maximum = utf8_width (project);
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////

View file

@ -32,6 +32,7 @@
#include <ColTags.h> #include <ColTags.h>
#include <text.h> #include <text.h>
#include <i18n.h> #include <i18n.h>
#include <utf8.h>
extern Context context; extern Context context;
@ -85,14 +86,14 @@ void ColumnTags::setStyle (const std::string& value)
// Set the minimum and maximum widths for the value. // Set the minimum and maximum widths for the value.
void ColumnTags::measure (Task& task, unsigned int& minimum, unsigned int& maximum) 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 == "count") minimum = maximum = 3;
else if (_style == "default" || else if (_style == "default" ||
_style == "list") _style == "list")
{ {
std::string tags = task.get (_name); std::string tags = task.get (_name);
minimum = 0; minimum = 0;
maximum = tags.length (); maximum = utf8_width (tags);
if (maximum) if (maximum)
{ {
@ -101,9 +102,9 @@ void ColumnTags::measure (Task& task, unsigned int& minimum, unsigned int& maxim
std::vector <std::string>::iterator i; std::vector <std::string>::iterator i;
for (i = all.begin (); i != all.end (); ++i) for (i = all.begin (); i != all.end (); ++i)
{ {
unsigned int length_ = i->length (); unsigned int length = utf8_width (*i);
if (length_ > minimum) if (length > minimum)
minimum = i->length () + 1; minimum = length;
} }
} }
} }
@ -121,7 +122,18 @@ void ColumnTags::render (
std::string tags = task.get (_name); std::string tags = task.get (_name);
if (tags != "") if (tags != "")
{ {
if (_style == "indicator") if (_style == "default" ||
_style == "list")
{
std::replace (tags.begin (), tags.end (), ',', ' ');
std::vector <std::string> all;
wrapText (all, tags, width, _hyphenate);
std::vector <std::string>::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 ( lines.push_back (
color.colorize ( color.colorize (
@ -135,17 +147,6 @@ void ColumnTags::render (
color.colorize ( color.colorize (
rightJustify ("[" + format ((int)all.size ()) + "]", width))); rightJustify ("[" + format ((int)all.size ()) + "]", width)));
} }
else if (_style == "default" ||
_style == "list")
{
std::replace (tags.begin (), tags.end (), ',', ' ');
std::vector <std::string> all;
wrapText (all, tags, width, _hyphenate);
std::vector <std::string>::iterator i;
for (i = all.begin (); i != all.end (); ++i)
lines.push_back (color.colorize (leftJustify (*i, width)));
}
} }
} }

View file

@ -32,6 +32,7 @@
#include <Date.h> #include <Date.h>
#include <ColUDA.h> #include <ColUDA.h>
#include <text.h> #include <text.h>
#include <utf8.h>
#include <i18n.h> #include <i18n.h>
#include <stdlib.h> #include <stdlib.h>
@ -99,11 +100,11 @@ void ColumnUDA::measure (Task& task, unsigned int& minimum, unsigned int& maximu
if (format == "") if (format == "")
format = context.config.get ("dateformat"); format = context.config.get ("dateformat");
minimum = maximum = date.toString (format).length (); minimum = maximum = utf8_width (date.toString (format));
} }
else if (_type == "duration") else if (_type == "duration")
{ {
minimum = maximum = Duration (value).formatCompact ().length (); minimum = maximum = utf8_width (Duration (value).formatCompact ());
} }
else if (_type == "string") else if (_type == "string")
{ {
@ -113,7 +114,7 @@ void ColumnUDA::measure (Task& task, unsigned int& minimum, unsigned int& maximu
} }
else if (_type == "numeric") else if (_type == "numeric")
{ {
minimum = maximum = value.length (); minimum = maximum = utf8_width (value);
} }
} }
} }

View file

@ -1049,7 +1049,7 @@ std::string leftJustify (const int input, const int width)
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
std::string leftJustify (const std::string& 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) 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;
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////

View file

@ -40,15 +40,17 @@ if (open my $fh, '>', '455.rc')
ok (-r '455.rc', 'Created 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 abc pro:Bar\x{263A} 2>&1};
qx{../src/task rc:455.rc add def pro:Foo! 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}; 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'); # ' ' + 'Pri' + ' ' == 5
like ($output, qr/\s{7}def/ms, 'bug 455 - correct spacing in non utf8 task'); 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. # Cleanup.
unlink qw(pending.data completed.data undo.data backlog.data synch.key 455.rc); unlink qw(pending.data completed.data undo.data backlog.data synch.key 455.rc);