2011-09-11 15:10:05 +00:00
|
|
|
# Copyright 2010-2011 Florent Le Coz <louiz@louiz.org>
|
2011-03-29 14:11:37 +00:00
|
|
|
#
|
|
|
|
# This file is part of Poezio.
|
|
|
|
#
|
|
|
|
# Poezio is free software: you can redistribute it and/or modify
|
2011-09-11 15:10:05 +00:00
|
|
|
# it under the terms of the zlib license. See the COPYING file.
|
2011-03-29 14:11:37 +00:00
|
|
|
|
|
|
|
"""
|
|
|
|
Various methods to convert
|
|
|
|
shell colors to poezio colors,
|
|
|
|
xhtml code to shell colors,
|
|
|
|
poezio colors to xhtml code
|
|
|
|
"""
|
|
|
|
|
|
|
|
import re
|
2011-11-07 18:43:13 +00:00
|
|
|
import curses
|
2011-04-10 14:44:26 +00:00
|
|
|
from sleekxmpp.xmlstream import ET
|
2011-11-08 01:07:40 +00:00
|
|
|
|
|
|
|
import xml.sax.saxutils
|
|
|
|
|
2011-05-04 20:25:26 +00:00
|
|
|
from sys import version_info
|
2011-03-29 14:11:37 +00:00
|
|
|
|
2011-04-04 17:19:27 +00:00
|
|
|
from config import config
|
2011-03-29 18:36:02 +00:00
|
|
|
import logging
|
|
|
|
|
2011-06-04 18:15:18 +00:00
|
|
|
digits = '0123456789' # never trust the modules
|
2012-05-21 00:14:25 +00:00
|
|
|
|
|
|
|
# HTML named colors
|
2011-09-22 22:09:01 +00:00
|
|
|
colors = {
|
|
|
|
'aliceblue': 231,
|
|
|
|
'antiquewhite': 231,
|
|
|
|
'aqua': 51,
|
|
|
|
'aquamarine': 122,
|
|
|
|
'azure': 231,
|
|
|
|
'beige': 231,
|
|
|
|
'bisque': 230,
|
|
|
|
'black': 232,
|
|
|
|
'blanchedalmond': 230,
|
|
|
|
'blue': 21,
|
|
|
|
'blueviolet': 135,
|
|
|
|
'brown': 124,
|
|
|
|
'burlywood': 223,
|
|
|
|
'cadetblue': 109,
|
|
|
|
'chartreuse': 118,
|
|
|
|
'chocolate': 172,
|
|
|
|
'coral': 209,
|
|
|
|
'cornflowerblue': 111,
|
|
|
|
'cornsilk': 231,
|
|
|
|
'crimson': 197,
|
|
|
|
'cyan': 51,
|
|
|
|
'darkblue': 19,
|
|
|
|
'darkcyan': 37,
|
|
|
|
'darkgoldenrod': 178,
|
|
|
|
'darkgray': 247,
|
|
|
|
'darkgreen': 28,
|
|
|
|
'darkgrey': 247,
|
|
|
|
'darkkhaki': 186,
|
|
|
|
'darkmagenta': 127,
|
|
|
|
'darkolivegreen': 65,
|
|
|
|
'darkorange': 214,
|
|
|
|
'darkorchid': 134,
|
|
|
|
'darkred': 124,
|
|
|
|
'darksalmon': 216,
|
|
|
|
'darkseagreen': 151,
|
|
|
|
'darkslateblue': 61,
|
|
|
|
'darkslategray': 59,
|
|
|
|
'darkslategrey': 59,
|
|
|
|
'darkturquoise': 44,
|
|
|
|
'darkviolet': 128,
|
|
|
|
'deeppink': 199,
|
|
|
|
'deepskyblue': 45,
|
|
|
|
'dimgray': 241,
|
|
|
|
'dimgrey': 241,
|
|
|
|
'dodgerblue': 39,
|
|
|
|
'firebrick': 160,
|
|
|
|
'floralwhite': 231,
|
|
|
|
'forestgreen': 34,
|
|
|
|
'fuchsia': 201,
|
|
|
|
'gainsboro': 252,
|
|
|
|
'ghostwhite': 231,
|
|
|
|
'gold': 226,
|
|
|
|
'goldenrod': 214,
|
|
|
|
'gray': 244,
|
|
|
|
'green': 34,
|
|
|
|
'greenyellow': 191,
|
|
|
|
'grey': 244,
|
|
|
|
'honeydew': 231,
|
|
|
|
'hotpink': 212,
|
|
|
|
'indianred': 174,
|
|
|
|
'indigo': 55,
|
|
|
|
'ivory': 231,
|
|
|
|
'khaki': 229,
|
|
|
|
'lavender': 231,
|
|
|
|
'lavenderblush': 231,
|
|
|
|
'lawngreen': 118,
|
|
|
|
'lemonchiffon': 230,
|
|
|
|
'lightblue': 195,
|
|
|
|
'lightcoral': 217,
|
|
|
|
'lightcyan': 231,
|
|
|
|
'lightgoldenrodyellow': 230,
|
|
|
|
'lightgray': 251,
|
|
|
|
'lightgreen': 157,
|
|
|
|
'lightgrey': 251,
|
|
|
|
'lightpink': 224,
|
|
|
|
'lightsalmon': 216,
|
|
|
|
'lightseagreen': 43,
|
|
|
|
'lightskyblue': 153,
|
|
|
|
'lightslategray': 109,
|
|
|
|
'lightslategrey': 109,
|
|
|
|
'lightsteelblue': 189,
|
|
|
|
'lightyellow': 231,
|
|
|
|
'lime': 46,
|
|
|
|
'limegreen': 77,
|
|
|
|
'linen': 231,
|
|
|
|
'magenta': 201,
|
|
|
|
'maroon': 124,
|
|
|
|
'mediumaquamarine': 115,
|
|
|
|
'mediumblue': 20,
|
|
|
|
'mediumorchid': 170,
|
|
|
|
'mediumpurple': 141,
|
|
|
|
'mediumseagreen': 78,
|
|
|
|
'mediumslateblue': 105,
|
|
|
|
'mediumspringgreen': 49,
|
|
|
|
'mediumturquoise': 80,
|
|
|
|
'mediumvioletred': 163,
|
|
|
|
'midnightblue': 18,
|
|
|
|
'mintcream': 231,
|
|
|
|
'mistyrose': 231,
|
|
|
|
'moccasin': 230,
|
|
|
|
'navajowhite': 230,
|
|
|
|
'navy': 19,
|
|
|
|
'oldlace': 231,
|
|
|
|
'olive': 142,
|
|
|
|
'olivedrab': 106,
|
|
|
|
'orange': 214,
|
|
|
|
'orangered': 202,
|
|
|
|
'orchid': 213,
|
|
|
|
'palegoldenrod': 229,
|
|
|
|
'palegreen': 157,
|
|
|
|
'paleturquoise': 195,
|
|
|
|
'palevioletred': 211,
|
|
|
|
'papayawhip': 231,
|
|
|
|
'peachpuff': 230,
|
|
|
|
'peru': 179,
|
|
|
|
'pink': 224,
|
|
|
|
'plum': 219,
|
|
|
|
'powderblue': 195,
|
|
|
|
'purple': 127,
|
|
|
|
'red': 196,
|
|
|
|
'rosybrown': 181,
|
|
|
|
'royalblue': 69,
|
|
|
|
'saddlebrown': 130,
|
|
|
|
'salmon': 216,
|
|
|
|
'sandybrown': 216,
|
|
|
|
'seagreen': 72,
|
|
|
|
'seashell': 231,
|
|
|
|
'sienna': 131,
|
|
|
|
'silver': 250,
|
|
|
|
'skyblue': 153,
|
|
|
|
'slateblue': 104,
|
|
|
|
'slategray': 109,
|
|
|
|
'slategrey': 109,
|
|
|
|
'snow': 231,
|
|
|
|
'springgreen': 48,
|
|
|
|
'steelblue': 74,
|
|
|
|
'tan': 187,
|
|
|
|
'teal': 37,
|
|
|
|
'thistle': 225,
|
|
|
|
'tomato': 209,
|
|
|
|
'turquoise': 86,
|
|
|
|
'violet': 219,
|
|
|
|
'wheat': 230,
|
|
|
|
'white': 255,
|
|
|
|
'whitesmoke': 255,
|
|
|
|
'yellow': 226,
|
|
|
|
'yellowgreen': 149
|
|
|
|
}
|
2011-06-04 18:15:18 +00:00
|
|
|
|
2011-03-29 18:36:02 +00:00
|
|
|
log = logging.getLogger(__name__)
|
|
|
|
|
2011-09-23 22:10:16 +00:00
|
|
|
whitespace_re = re.compile(r'\s+')
|
2011-03-29 14:11:37 +00:00
|
|
|
|
2013-06-08 21:51:47 +00:00
|
|
|
xhtml_attr_re = re.compile(r'\x19-?\d[^}]*}|\x19[buaio]')
|
2011-10-20 19:55:24 +00:00
|
|
|
|
2011-11-06 18:21:59 +00:00
|
|
|
xhtml_simple_attr_re = re.compile(r'\x19\d')
|
|
|
|
|
2011-03-29 18:36:02 +00:00
|
|
|
def get_body_from_message_stanza(message):
|
|
|
|
"""
|
|
|
|
Returns a string with xhtml markups converted to
|
|
|
|
poezio colors if there's an xhtml_im element, or
|
|
|
|
the body (without any color) otherwise
|
|
|
|
"""
|
2011-04-10 14:44:26 +00:00
|
|
|
if config.get('enable_xhtml_im', 'true') == 'true':
|
2013-03-06 21:52:51 +00:00
|
|
|
xhtml = message['html'].xml
|
|
|
|
xhtml_body = xhtml.find('{http://www.w3.org/1999/xhtml}body')
|
2011-04-04 17:19:27 +00:00
|
|
|
if xhtml_body:
|
2012-02-07 20:47:40 +00:00
|
|
|
content = xhtml_to_poezio_colors(xhtml_body)
|
|
|
|
content = content if content else message['body']
|
|
|
|
return content or " "
|
2011-03-29 18:36:02 +00:00
|
|
|
return message['body']
|
|
|
|
|
2011-11-07 18:43:13 +00:00
|
|
|
def ncurses_color_to_html(color):
|
|
|
|
"""
|
|
|
|
Takes an int between 0 and 256 and returns
|
|
|
|
a string of the form #XXXXXX representing an
|
|
|
|
html color.
|
|
|
|
"""
|
|
|
|
if color <= 15:
|
2012-05-13 17:01:27 +00:00
|
|
|
try:
|
|
|
|
(r, g, b) = curses.color_content(color)
|
|
|
|
except: # fallback in faulty terminals (e.g. xterm)
|
|
|
|
(r, g, b) = curses.color_content(color%8)
|
2011-11-07 18:55:16 +00:00
|
|
|
r = r / 1000 * 6 - 0.01
|
|
|
|
g = g / 1000 * 6 - 0.01
|
|
|
|
b = b / 1000 * 6 - 0.01
|
2011-11-07 18:43:13 +00:00
|
|
|
elif color <= 231:
|
|
|
|
color = color - 16
|
|
|
|
r = color % 6
|
|
|
|
color = color / 6
|
|
|
|
g = color % 6
|
|
|
|
color = color / 6
|
|
|
|
b = color % 6
|
|
|
|
else:
|
|
|
|
color -= 232
|
|
|
|
r = g = b = color / 24 * 6
|
2011-11-07 18:55:16 +00:00
|
|
|
return '#%02X%02X%02X' % (r*256/6, g*256/6, b*256/6)
|
2011-11-07 18:43:13 +00:00
|
|
|
|
2013-01-17 16:37:06 +00:00
|
|
|
def xhtml_to_poezio_colors(xml):
|
2013-03-04 10:41:19 +00:00
|
|
|
if isinstance(xml, str):
|
2013-06-06 20:53:43 +00:00
|
|
|
try:
|
|
|
|
xml = ET.fromstring(xml)
|
2013-08-03 17:27:25 +00:00
|
|
|
except:
|
|
|
|
log.error("Error decoding XML: [%s]", repr(xml), exc_info=True)
|
2013-06-06 20:53:43 +00:00
|
|
|
return ""
|
2011-09-19 20:41:46 +00:00
|
|
|
def parse_css(css):
|
2011-09-22 22:09:01 +00:00
|
|
|
def get_color(value):
|
|
|
|
if value[0] == '#':
|
|
|
|
value = value[1:]
|
|
|
|
length = len(value)
|
|
|
|
if length != 3 and length != 6:
|
|
|
|
return -1
|
|
|
|
value = int(value, 16)
|
|
|
|
if length == 6:
|
|
|
|
r = int(value >> 16)
|
|
|
|
g = int((value >> 8) & 0xff)
|
|
|
|
b = int(value & 0xff)
|
|
|
|
if r == g == b:
|
|
|
|
return 232 + int(r/10.6251)
|
|
|
|
div = 42.51
|
|
|
|
else:
|
|
|
|
r = int(value >> 8)
|
|
|
|
g = int((value >> 4) & 0xf)
|
|
|
|
b = int(value & 0xf)
|
|
|
|
if r == g == b:
|
|
|
|
return 232 + int(1.54*r)
|
|
|
|
div = 2.51
|
|
|
|
return 6*6*int(r/div) + 6*int(g/div) + int(b/div) + 16
|
|
|
|
if value in colors:
|
|
|
|
return colors[value]
|
|
|
|
return -1
|
2011-09-19 20:41:46 +00:00
|
|
|
shell = ''
|
|
|
|
rules = css.split(';')
|
|
|
|
for rule in rules:
|
2011-09-23 21:47:25 +00:00
|
|
|
if ':' not in rule:
|
|
|
|
continue
|
2011-09-19 20:41:46 +00:00
|
|
|
key, value = rule.split(':', 1)
|
|
|
|
key = key.strip()
|
|
|
|
value = value.strip()
|
|
|
|
if key == 'background-color':
|
|
|
|
pass#shell += '\x191'
|
|
|
|
elif key == 'color':
|
2011-11-11 22:55:31 +00:00
|
|
|
color = get_color(value)
|
|
|
|
if color != -1:
|
|
|
|
shell += '\x19%d}' % color
|
2011-09-19 20:41:46 +00:00
|
|
|
elif key == 'font-style':
|
|
|
|
shell += '\x19i'
|
|
|
|
elif key == 'font-weight':
|
|
|
|
shell += '\x19b'
|
|
|
|
elif key == 'margin-left':
|
|
|
|
shell += ' '
|
|
|
|
elif key == 'text-align':
|
|
|
|
pass
|
|
|
|
elif key == 'text-decoration':
|
|
|
|
if value == 'underline':
|
|
|
|
shell += '\x19u'
|
|
|
|
elif value == 'blink':
|
|
|
|
shell += '\x19a'
|
|
|
|
return shell
|
|
|
|
|
2011-09-23 21:47:25 +00:00
|
|
|
def trim(string):
|
|
|
|
return re.sub(whitespace_re, ' ', string)
|
|
|
|
|
2013-04-18 17:54:13 +00:00
|
|
|
builder = []
|
|
|
|
|
2013-04-19 21:55:54 +00:00
|
|
|
if version_info[1] < 2: #deprecated
|
2011-09-29 21:19:21 +00:00
|
|
|
elems = xml.getiterator()
|
2013-04-19 21:55:54 +00:00
|
|
|
else:
|
|
|
|
elems = xml.iter()
|
2013-04-18 17:54:13 +00:00
|
|
|
|
2011-09-29 21:19:21 +00:00
|
|
|
for elem in elems:
|
2011-09-19 20:41:46 +00:00
|
|
|
if elem.tag == '{http://www.w3.org/1999/xhtml}a':
|
|
|
|
if 'href' in elem.attrib and elem.attrib['href'] != elem.text:
|
2013-04-18 17:54:13 +00:00
|
|
|
builder.append('\x19u%s\x19o (%s)' % (trim(elem.attrib['href']), trim(elem.text if elem.text else "")))
|
2011-09-19 20:41:46 +00:00
|
|
|
else:
|
2013-04-18 17:54:13 +00:00
|
|
|
builder.append('\x19u' + (elem.text if elem.text else "") + '\x19o')
|
2011-09-19 20:41:46 +00:00
|
|
|
elif elem.tag == '{http://www.w3.org/1999/xhtml}blockquote':
|
2013-04-18 17:54:13 +00:00
|
|
|
builder.append('“')
|
2011-09-19 20:41:46 +00:00
|
|
|
elif elem.tag == '{http://www.w3.org/1999/xhtml}body':
|
|
|
|
pass
|
|
|
|
elif elem.tag == '{http://www.w3.org/1999/xhtml}br':
|
2013-04-18 17:54:13 +00:00
|
|
|
builder.append('\n')
|
2011-09-19 20:41:46 +00:00
|
|
|
elif elem.tag == '{http://www.w3.org/1999/xhtml}cite':
|
2013-04-18 17:54:13 +00:00
|
|
|
builder.append('\x19u')
|
2011-09-19 20:41:46 +00:00
|
|
|
elif elem.tag == '{http://www.w3.org/1999/xhtml}em':
|
2013-04-18 17:54:13 +00:00
|
|
|
builder.append('\x19i')
|
2011-09-19 20:41:46 +00:00
|
|
|
elif elem.tag == '{http://www.w3.org/1999/xhtml}img' and 'src' in elem.attrib:
|
2011-09-22 22:09:01 +00:00
|
|
|
if 'alt' in elem.attrib:
|
2013-04-18 17:54:13 +00:00
|
|
|
builder.append('%s (%s)' % (trim(elem.attrib['src']), trim(elem.attrib['alt'])))
|
2011-09-19 20:41:46 +00:00
|
|
|
else:
|
2013-04-18 17:54:13 +00:00
|
|
|
builder.append(elem.attrib['src'])
|
2011-09-19 20:41:46 +00:00
|
|
|
elif elem.tag == '{http://www.w3.org/1999/xhtml}li':
|
|
|
|
pass
|
|
|
|
elif elem.tag == '{http://www.w3.org/1999/xhtml}ol':
|
|
|
|
pass
|
|
|
|
elif elem.tag == '{http://www.w3.org/1999/xhtml}p':
|
|
|
|
pass
|
|
|
|
elif elem.tag == '{http://www.w3.org/1999/xhtml}span':
|
|
|
|
pass
|
|
|
|
elif elem.tag == '{http://www.w3.org/1999/xhtml}strong':
|
2013-04-18 17:54:13 +00:00
|
|
|
builder.append('\x19b')
|
2011-09-19 20:41:46 +00:00
|
|
|
elif elem.tag == '{http://www.w3.org/1999/xhtml}ul':
|
|
|
|
pass
|
|
|
|
|
|
|
|
if ('style' in elem.attrib and elem.tag != '{http://www.w3.org/1999/xhtml}br'
|
|
|
|
and elem.tag != '{http://www.w3.org/1999/xhtml}em'
|
|
|
|
and elem.tag != '{http://www.w3.org/1999/xhtml}strong'):
|
2013-04-18 17:54:13 +00:00
|
|
|
builder.append(parse_css(elem.attrib['style']))
|
2011-09-19 20:41:46 +00:00
|
|
|
|
|
|
|
if (elem.text and elem.tag != '{http://www.w3.org/1999/xhtml}a'
|
|
|
|
and elem.tag != '{http://www.w3.org/1999/xhtml}br'
|
|
|
|
and elem.tag != '{http://www.w3.org/1999/xhtml}img'):
|
2013-04-18 17:54:13 +00:00
|
|
|
builder.append(trim(elem.text))
|
2011-09-19 20:41:46 +00:00
|
|
|
|
|
|
|
if ('style' in elem.attrib and elem.tag != '{http://www.w3.org/1999/xhtml}br'
|
|
|
|
and elem.tag != '{http://www.w3.org/1999/xhtml}em'
|
|
|
|
and elem.tag != '{http://www.w3.org/1999/xhtml}strong'):
|
2013-04-18 17:54:13 +00:00
|
|
|
builder.append('\x19o')
|
2011-09-19 20:41:46 +00:00
|
|
|
|
|
|
|
if elem.tag == '{http://www.w3.org/1999/xhtml}blockquote':
|
2013-04-18 17:54:13 +00:00
|
|
|
builder.append('”')
|
2011-09-19 20:41:46 +00:00
|
|
|
elif elem.tag == '{http://www.w3.org/1999/xhtml}cite':
|
2013-04-18 17:54:13 +00:00
|
|
|
builder.append('\x19o')
|
2011-09-19 20:41:46 +00:00
|
|
|
elif elem.tag == '{http://www.w3.org/1999/xhtml}em':
|
2013-04-18 17:54:13 +00:00
|
|
|
builder.append('\x19o')
|
2011-09-19 20:41:46 +00:00
|
|
|
elif elem.tag == '{http://www.w3.org/1999/xhtml}strong' or elem.tag == '{http://www.w3.org/1999/xhtml}b':
|
2013-04-18 17:54:13 +00:00
|
|
|
builder.append('\x19o')
|
2011-09-19 20:41:46 +00:00
|
|
|
elif elem.tag == '{http://www.w3.org/1999/xhtml}u':
|
2013-04-18 17:54:13 +00:00
|
|
|
builder.append('\x19o')
|
2011-09-19 20:41:46 +00:00
|
|
|
|
|
|
|
if 'title' in elem.attrib:
|
2013-04-18 17:54:13 +00:00
|
|
|
builder.append(' [' + elem.attrib['title'] + ']')
|
2011-09-19 20:41:46 +00:00
|
|
|
|
|
|
|
if elem.tail:
|
2013-04-18 17:54:13 +00:00
|
|
|
builder.append(trim(elem.tail))
|
|
|
|
return ''.join(builder)
|
2011-04-10 14:44:26 +00:00
|
|
|
|
2011-10-20 19:55:24 +00:00
|
|
|
def clean_text(s):
|
|
|
|
"""
|
|
|
|
Remove all xhtml-im attributes (\x19etc) from the string with the
|
|
|
|
complete color format, i.e \x19xxx}
|
|
|
|
"""
|
|
|
|
s = re.sub(xhtml_attr_re, "", s)
|
|
|
|
return s
|
|
|
|
|
|
|
|
def clean_text_simple(string):
|
2011-03-30 02:34:45 +00:00
|
|
|
"""
|
2011-10-20 19:55:24 +00:00
|
|
|
Remove all \x19 from the string formatted with simple colors:
|
|
|
|
\x198
|
2011-03-30 02:34:45 +00:00
|
|
|
"""
|
|
|
|
pos = string.find('\x19')
|
|
|
|
while pos != -1:
|
|
|
|
string = string[:pos] + string[pos+2:]
|
|
|
|
pos = string.find('\x19')
|
|
|
|
return string
|
|
|
|
|
2011-11-06 18:21:59 +00:00
|
|
|
def convert_simple_to_full_colors(text):
|
|
|
|
"""
|
|
|
|
takes a \x19n formatted string and returns
|
|
|
|
a \x19n} formatted one.
|
|
|
|
"""
|
Improve the input a lot
Noticeable changes:
- The input "view" is smarter, it always move to a decent position so we can
see enough text around the cursor.
- The cursor goes at the end of the input when pasting some long text
- The formatting chars (^C and o, b, a, 1, 2, 3 etc) are now visible in the
input. This makes it a lot easier to know where these special characters
are, to change them and efficiently edit our text (we just lose a little,
on the cosmetic side, but who cares)
- The code is actually a lot simpler in the functions to move the cursor,
insert/delete chars: we do not have to deal with special cases where the
formatting characters are actually composed of two chars.
fixes #2183
2013-10-20 21:42:13 +00:00
|
|
|
# TODO, have a single list of this. This is some sort of
|
|
|
|
# dusplicate from windows.format_chars
|
|
|
|
mapping = str.maketrans({'\x0E': '\x19b', '\x0F': '\x19o', '\x10': '\x19u',
|
|
|
|
'\x11': '\x191', '\x12': '\x192','\x13': '\x193',
|
|
|
|
'\x14': '\x194', '\x15': '\x195','\x16': '\x196',
|
|
|
|
'\x17': '\x197', '\x18': '\x198','\x19': '\x199'})
|
|
|
|
text = text.translate(mapping)
|
2011-11-06 18:21:59 +00:00
|
|
|
def add_curly_bracket(match):
|
|
|
|
return match.group(0) + '}'
|
|
|
|
return re.sub(xhtml_simple_attr_re, add_curly_bracket, text)
|
|
|
|
|
2011-03-29 18:56:11 +00:00
|
|
|
number_to_color_names = {
|
|
|
|
1: 'red',
|
|
|
|
2: 'green',
|
2011-03-30 02:34:45 +00:00
|
|
|
3: 'yellow',
|
|
|
|
4: 'blue',
|
|
|
|
5: 'violet',
|
|
|
|
6: 'turquoise',
|
|
|
|
7: 'white'
|
2011-03-29 18:56:11 +00:00
|
|
|
}
|
|
|
|
|
2013-05-14 20:29:45 +00:00
|
|
|
def format_inline_css(_dict):
|
|
|
|
return ''.join(('%s: %s;' % (key, value) for key, value in _dict.items()))
|
|
|
|
|
2011-03-29 18:56:11 +00:00
|
|
|
def poezio_colors_to_html(string):
|
|
|
|
"""
|
2013-05-14 20:29:45 +00:00
|
|
|
Convert poezio colors to html
|
|
|
|
(e.g. \x191}: <span style='color: red'>)
|
2011-03-29 18:56:11 +00:00
|
|
|
"""
|
2013-05-14 20:29:45 +00:00
|
|
|
# Maintain a list of the current css attributes used
|
|
|
|
# And check if a tag is open (by design, we only open
|
|
|
|
# spans tag, and they cannot be nested.
|
|
|
|
current_attrs = {}
|
|
|
|
tag_open = False
|
2011-03-29 18:56:11 +00:00
|
|
|
next_attr_char = string.find('\x19')
|
2013-05-14 20:29:45 +00:00
|
|
|
build = ["<body xmlns='http://www.w3.org/1999/xhtml'><p>"]
|
|
|
|
|
|
|
|
def check_property(key, value):
|
|
|
|
nonlocal tag_open
|
|
|
|
if current_attrs.get(key, None) == value:
|
|
|
|
return
|
|
|
|
current_attrs[key] = value
|
|
|
|
if tag_open:
|
|
|
|
tag_open = False
|
|
|
|
build.append('</span>')
|
|
|
|
|
2011-03-29 18:56:11 +00:00
|
|
|
while next_attr_char != -1:
|
|
|
|
attr_char = string[next_attr_char+1].lower()
|
2013-05-14 20:29:45 +00:00
|
|
|
|
|
|
|
if next_attr_char != 0 and string[:next_attr_char]:
|
|
|
|
if current_attrs and not tag_open:
|
|
|
|
build.append('<span style="%s">' % format_inline_css(current_attrs))
|
|
|
|
tag_open = True
|
|
|
|
build.append(xml.sax.saxutils.escape(string[:next_attr_char]))
|
|
|
|
|
2011-03-29 18:56:11 +00:00
|
|
|
if attr_char == 'o':
|
2013-05-14 20:29:45 +00:00
|
|
|
if tag_open:
|
|
|
|
build.append('</span>')
|
|
|
|
tag_open = False
|
|
|
|
current_attrs = {}
|
2011-03-29 18:56:11 +00:00
|
|
|
elif attr_char == 'b':
|
2013-05-14 20:29:45 +00:00
|
|
|
check_property('font-weight', 'bold')
|
2012-07-29 01:36:05 +00:00
|
|
|
elif attr_char == 'u':
|
2013-05-14 20:29:45 +00:00
|
|
|
check_property('text-decoration', 'underline')
|
|
|
|
|
2011-11-07 18:56:03 +00:00
|
|
|
if attr_char in digits:
|
2013-05-16 13:35:59 +00:00
|
|
|
number_str = string[next_attr_char+1:string.find('}', next_attr_char)]
|
|
|
|
number = int(number_str)
|
|
|
|
if number in number_to_color_names:
|
|
|
|
check_property('color', number_to_color_names.get(number, 'black'))
|
|
|
|
else:
|
|
|
|
check_property('color', ncurses_color_to_html(number))
|
|
|
|
string = string[next_attr_char+len(number_str)+2:]
|
2011-11-07 18:56:03 +00:00
|
|
|
else:
|
|
|
|
string = string[next_attr_char+2:]
|
2011-03-29 18:56:11 +00:00
|
|
|
next_attr_char = string.find('\x19')
|
2013-05-14 20:29:45 +00:00
|
|
|
|
|
|
|
if current_attrs and not tag_open and string:
|
|
|
|
build.append('<span style="%s">' % format_inline_css(current_attrs))
|
|
|
|
tag_open = True
|
|
|
|
build.append(xml.sax.saxutils.escape(string))
|
|
|
|
if tag_open:
|
|
|
|
build.append('</span>')
|
|
|
|
build.append("</p></body>")
|
|
|
|
text = ''.join(build)
|
|
|
|
return text.replace('\n', '<br />')
|