Add proper support for double width characters to legacy mode and few related formatting functions

This commit is contained in:
Asmageddon 2012-03-07 19:55:34 +01:00
parent 412d0e7be9
commit 16f62bbcfe
3 changed files with 57 additions and 6 deletions

View File

@ -33,6 +33,8 @@
# #
# #
from deluge.ui.console.modes import format_utils
try: try:
import curses import curses
except ImportError: except ImportError:
@ -150,6 +152,24 @@ def get_line_length(line, encoding="UTF-8"):
line = replace_tabs(line) line = replace_tabs(line)
return len(line) return len(line)
def get_line_width(line, encoding="UTF-8"):
"""
Get width of string considering double width characters
"""
if line.count("{!") != line.count("!}"):
raise BadColorString("Number of {! is not equal to number of !}")
if isinstance(line, unicode):
line = line.encode(encoding, "replace")
# Remove all the color tags
line = strip_colors(line)
# Replace tabs with the appropriate amount of spaces
line = replace_tabs(line)
return format_utils.strwidth(line)
def parse_color_string(s, encoding="UTF-8"): def parse_color_string(s, encoding="UTF-8"):
""" """
Parses a string and returns a list of 2-tuples (color, string). Parses a string and returns a list of 2-tuples (color, string).

View File

@ -201,3 +201,33 @@ def wrap_string(string,width,min_lines=0,strip_colors=True):
ret.append(" ") ret.append(" ")
return ret return ret
from unicodedata import east_asian_width
def strwidth(string):
"""
Measure width of a string considering asian double width characters
"""
if not isinstance(string, unicode):
string = unicode(string, 'utf-8')
eaw = east_asian_width
length = sum( [1 + (eaw(c) in ['W','F']) for c in string] )
#Using list comprehenstion for improved performance
#The code above is equal to:
#length = 0
#for char in string:
#length += 1
#if east_asian_width(char) in ['W','F']:
#length += 1
return length
def pad_string(string, length, character=" ", side="right"):
"""
Pad string with specified character to desired length, considering double width characters.
"""
w = strwidth(string)
diff = length - w
if side == "left":
return "%s%s" % (character * diff, string)
elif side == "right":
return "%s%s" % (string, character * diff)

View File

@ -47,6 +47,7 @@ from deluge.ui.client import client
import deluge.component as component import deluge.component as component
from deluge.ui.console.modes import format_utils from deluge.ui.console.modes import format_utils
strwidth = format_utils.strwidth
import logging,os import logging,os
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@ -353,7 +354,7 @@ class Legacy(BaseMode):
for line in text.splitlines(): for line in text.splitlines():
# We need to check for line lengths here and split as necessary # We need to check for line lengths here and split as necessary
try: try:
line_length = colors.get_line_length(line) line_length = colors.get_line_width(line)
except colors.BadColorString: except colors.BadColorString:
log.error("Passed a bad colored line: %s", line) log.error("Passed a bad colored line: %s", line)
continue continue
@ -364,11 +365,11 @@ class Legacy(BaseMode):
s_len = 0 s_len = 0
# We need to split this over multiple lines # We need to split this over multiple lines
for chunk in get_line_chunks(line): for chunk in get_line_chunks(line):
if (len(chunk[1]) + s_len) < (self.cols - 1): if (strwidth(chunk[1]) + s_len) < (self.cols - 1):
# This chunk plus the current string in 's' isn't over # This chunk plus the current string in 's' isn't over
# the maximum width, so just append the color tag and text # the maximum width, so just append the color tag and text
s += chunk[0] + chunk[1] s += chunk[0] + chunk[1]
s_len += len(chunk[1]) s_len += strwidth(chunk[1])
else: else:
# The chunk plus the current string in 's' is too long. # The chunk plus the current string in 's' is too long.
# We need to take as much of the chunk and put it into 's' # We need to take as much of the chunk and put it into 's'
@ -379,7 +380,7 @@ class Legacy(BaseMode):
self.lines.append(s) self.lines.append(s)
# Start a new 's' with the remainder chunk # Start a new 's' with the remainder chunk
s = chunk[0] + chunk[1][remain:] s = chunk[0] + chunk[1][remain:]
s_len = len(chunk[1][remain:]) s_len = strwidth(chunk[1][remain:])
# Append the final string which may or may not be the full width # Append the final string which may or may not be the full width
if s: if s:
self.lines.append(s) self.lines.append(s)
@ -411,13 +412,13 @@ class Legacy(BaseMode):
for index, (color, s) in enumerate(parsed): for index, (color, s) in enumerate(parsed):
if index + 1 == len(parsed): if index + 1 == len(parsed):
# This is the last string so lets append some " " to it # This is the last string so lets append some " " to it
s += " " * (self.cols - (col + len(s)) - 1) s += " " * (self.cols - (col + strwidth(s)) - 1)
try: try:
self.stdscr.addstr(row, col, s, color) self.stdscr.addstr(row, col, s, color)
except curses.error: except curses.error:
pass pass
col += len(s) col += strwidth(s)
def do_command(self, cmd): def do_command(self, cmd):