Implement to and from html color parsing.
This commit is contained in:
parent
3351e3b4dd
commit
918489fb1f
1 changed files with 417 additions and 10 deletions
|
@ -10,6 +10,7 @@ import datetime
|
||||||
import pprint
|
import pprint
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
|
import webcolors
|
||||||
|
|
||||||
# pylint: disable=redefined-builtin
|
# pylint: disable=redefined-builtin
|
||||||
from builtins import bytes, str
|
from builtins import bytes, str
|
||||||
|
@ -588,7 +589,7 @@ Default_format_attributes = {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def line_color_to_sgr(color_string):
|
def color_line_to_weechat(color_string):
|
||||||
# type: (str) -> str
|
# type: (str) -> str
|
||||||
line_colors = {
|
line_colors = {
|
||||||
"0": "white",
|
"0": "white",
|
||||||
|
@ -698,12 +699,396 @@ def line_color_to_sgr(color_string):
|
||||||
return line_colors[color_string]
|
return line_colors[color_string]
|
||||||
|
|
||||||
|
|
||||||
def html_color_to_sgr(color):
|
# The functions colour_dist_sq(), colour_to_6cube(), and colour_find_rgb
|
||||||
pass
|
# are python ports of the same named functions from the tmux
|
||||||
|
# source, they are under the copyright of Nicholas Marriott, and Avi Halachmi
|
||||||
|
# under the ISC license.
|
||||||
|
# More info: https://github.com/tmux/tmux/blob/master/colour.c
|
||||||
|
|
||||||
|
def colour_dist_sq(R, G, B, r, g, b):
|
||||||
|
# pylint: disable=invalid-name,too-many-arguments
|
||||||
|
# type: (int, int, int, int, int, int) -> int
|
||||||
|
return (R - r) * (R - r) + (G - g) * (G - g) + (B - b) * (B - b)
|
||||||
|
|
||||||
|
|
||||||
def sgr_to_rgb(color):
|
def colour_to_6cube(v):
|
||||||
pass
|
# pylint: disable=invalid-name
|
||||||
|
# type: (int) -> int
|
||||||
|
if v < 48:
|
||||||
|
return 0
|
||||||
|
if v < 114:
|
||||||
|
return 1
|
||||||
|
return (v - 35) // 40
|
||||||
|
|
||||||
|
|
||||||
|
def colour_find_rgb(r, g, b):
|
||||||
|
"""Convert an RGB triplet to the xterm(1) 256 colour palette.
|
||||||
|
|
||||||
|
xterm provides a 6x6x6 colour cube (16 - 231) and 24 greys (232 - 255).
|
||||||
|
We map our RGB colour to the closest in the cube, also work out the
|
||||||
|
closest grey, and use the nearest of the two.
|
||||||
|
|
||||||
|
Note that the xterm has much lower resolution for darker colours (they
|
||||||
|
are not evenly spread out), so our 6 levels are not evenly spread: 0x0,
|
||||||
|
0x5f (95), 0x87 (135), 0xaf (175), 0xd7 (215) and 0xff (255). Greys are
|
||||||
|
more evenly spread (8, 18, 28 ... 238).
|
||||||
|
"""
|
||||||
|
# pylint: disable=invalid-name
|
||||||
|
# type: (int, int, int) -> int
|
||||||
|
q2c = [0x00, 0x5f, 0x87, 0xaf, 0xd7, 0xff]
|
||||||
|
|
||||||
|
# Map RGB to 6x6x6 cube.
|
||||||
|
qr = colour_to_6cube(r)
|
||||||
|
qg = colour_to_6cube(g)
|
||||||
|
qb = colour_to_6cube(b)
|
||||||
|
|
||||||
|
cr = q2c[qr]
|
||||||
|
cg = q2c[qg]
|
||||||
|
cb = q2c[qb]
|
||||||
|
|
||||||
|
# If we have hit the colour exactly, return early.
|
||||||
|
if (cr == r and cg == g and cb == b):
|
||||||
|
return 16 + (36 * qr) + (6 * qg) + qb
|
||||||
|
|
||||||
|
# Work out the closest grey (average of RGB).
|
||||||
|
grey_avg = (r + g + b) // 3
|
||||||
|
|
||||||
|
if grey_avg > 238:
|
||||||
|
grey_idx = 23
|
||||||
|
else:
|
||||||
|
grey_idx = (grey_avg - 3) // 10
|
||||||
|
|
||||||
|
grey = 8 + (10 * grey_idx)
|
||||||
|
|
||||||
|
# Is grey or 6x6x6 colour closest?
|
||||||
|
d = colour_dist_sq(cr, cg, cb, r, g, b)
|
||||||
|
|
||||||
|
if colour_dist_sq(grey, grey, grey, r, g, b) < d:
|
||||||
|
idx = 232 + grey_idx
|
||||||
|
else:
|
||||||
|
idx = 16 + (36 * qr) + (6 * qg) + qb
|
||||||
|
|
||||||
|
return idx
|
||||||
|
|
||||||
|
|
||||||
|
def color_html_to_weechat(color):
|
||||||
|
# type: (str) -> str
|
||||||
|
first_16 = {
|
||||||
|
(0, 0, 0): "black", # 0
|
||||||
|
(128, 0, 0): "red", # 1
|
||||||
|
(0, 128, 0): "green", # 2
|
||||||
|
(128, 128, 0): "brown", # 3
|
||||||
|
(0, 0, 128): "blue", # 4
|
||||||
|
(128, 0, 128): "magenta", # 5
|
||||||
|
(0, 128, 128): "cyan", # 6
|
||||||
|
(192, 192, 192): "default", # 7
|
||||||
|
(128, 128, 128): "gray", # 8
|
||||||
|
(255, 0, 0): "lightred", # 9
|
||||||
|
(0, 255, 0): "lightgreen", # 11
|
||||||
|
(255, 255, 0): "yellow", # 12
|
||||||
|
(0, 0, 255): "lightblue", # 13
|
||||||
|
(255, 0, 255): "lightmagenta", # 14
|
||||||
|
(0, 255, 255): "lightcyan", # 15
|
||||||
|
}
|
||||||
|
|
||||||
|
try:
|
||||||
|
rgb_color = webcolors.html5_parse_legacy_color(color)
|
||||||
|
except ValueError:
|
||||||
|
return None
|
||||||
|
|
||||||
|
if rgb_color in first_16:
|
||||||
|
return first_16[rgb_color]
|
||||||
|
|
||||||
|
return str(colour_find_rgb(*rgb_color))
|
||||||
|
|
||||||
|
|
||||||
|
def color_weechat_to_html(color):
|
||||||
|
first_16 = {
|
||||||
|
"black": "black", # 0
|
||||||
|
"red": "maroon", # 1
|
||||||
|
"green": "green", # 2
|
||||||
|
"brown": "olive", # 3
|
||||||
|
"blue": "navy", # 4
|
||||||
|
"magenta": "purple", # 5
|
||||||
|
"cyan": "teal", # 6
|
||||||
|
"default": "silver", # 7
|
||||||
|
"gray": "grey", # 8
|
||||||
|
"lightred": "red", # 9
|
||||||
|
"lightgreen": "lime", # 11
|
||||||
|
"yellow": "yellow", # 12
|
||||||
|
"lightblue": "fuchsia", # 13
|
||||||
|
"lightmagenta": "aqua", # 14
|
||||||
|
"lightcyan": "white", # 15
|
||||||
|
}
|
||||||
|
|
||||||
|
hex_colors = {
|
||||||
|
"0": "#000000",
|
||||||
|
"1": "#800000",
|
||||||
|
"2": "#008000",
|
||||||
|
"3": "#808000",
|
||||||
|
"4": "#000080",
|
||||||
|
"5": "#800080",
|
||||||
|
"6": "#008080",
|
||||||
|
"7": "#c0c0c0",
|
||||||
|
"8": "#808080",
|
||||||
|
"9": "#ff0000",
|
||||||
|
"10": "#00ff00",
|
||||||
|
"11": "#ffff00",
|
||||||
|
"12": "#0000ff",
|
||||||
|
"13": "#ff00ff",
|
||||||
|
"14": "#00ffff",
|
||||||
|
"15": "#ffffff",
|
||||||
|
"16": "#000000",
|
||||||
|
"17": "#00005f",
|
||||||
|
"18": "#000087",
|
||||||
|
"19": "#0000af",
|
||||||
|
"20": "#0000d7",
|
||||||
|
"21": "#0000ff",
|
||||||
|
"22": "#005f00",
|
||||||
|
"23": "#005f5f",
|
||||||
|
"24": "#005f87",
|
||||||
|
"25": "#005faf",
|
||||||
|
"26": "#005fd7",
|
||||||
|
"27": "#005fff",
|
||||||
|
"28": "#008700",
|
||||||
|
"29": "#00875f",
|
||||||
|
"30": "#008787",
|
||||||
|
"31": "#0087af",
|
||||||
|
"32": "#0087d7",
|
||||||
|
"33": "#0087ff",
|
||||||
|
"34": "#00af00",
|
||||||
|
"35": "#00af5f",
|
||||||
|
"36": "#00af87",
|
||||||
|
"37": "#00afaf",
|
||||||
|
"38": "#00afd7",
|
||||||
|
"39": "#00afff",
|
||||||
|
"40": "#00d700",
|
||||||
|
"41": "#00d75f",
|
||||||
|
"42": "#00d787",
|
||||||
|
"43": "#00d7af",
|
||||||
|
"44": "#00d7d7",
|
||||||
|
"45": "#00d7ff",
|
||||||
|
"46": "#00ff00",
|
||||||
|
"47": "#00ff5f",
|
||||||
|
"48": "#00ff87",
|
||||||
|
"49": "#00ffaf",
|
||||||
|
"50": "#00ffd7",
|
||||||
|
"51": "#00ffff",
|
||||||
|
"52": "#5f0000",
|
||||||
|
"53": "#5f005f",
|
||||||
|
"54": "#5f0087",
|
||||||
|
"55": "#5f00af",
|
||||||
|
"56": "#5f00d7",
|
||||||
|
"57": "#5f00ff",
|
||||||
|
"58": "#5f5f00",
|
||||||
|
"59": "#5f5f5f",
|
||||||
|
"60": "#5f5f87",
|
||||||
|
"61": "#5f5faf",
|
||||||
|
"62": "#5f5fd7",
|
||||||
|
"63": "#5f5fff",
|
||||||
|
"64": "#5f8700",
|
||||||
|
"65": "#5f875f",
|
||||||
|
"66": "#5f8787",
|
||||||
|
"67": "#5f87af",
|
||||||
|
"68": "#5f87d7",
|
||||||
|
"69": "#5f87ff",
|
||||||
|
"70": "#5faf00",
|
||||||
|
"71": "#5faf5f",
|
||||||
|
"72": "#5faf87",
|
||||||
|
"73": "#5fafaf",
|
||||||
|
"74": "#5fafd7",
|
||||||
|
"75": "#5fafff",
|
||||||
|
"76": "#5fd700",
|
||||||
|
"77": "#5fd75f",
|
||||||
|
"78": "#5fd787",
|
||||||
|
"79": "#5fd7af",
|
||||||
|
"80": "#5fd7d7",
|
||||||
|
"81": "#5fd7ff",
|
||||||
|
"82": "#5fff00",
|
||||||
|
"83": "#5fff5f",
|
||||||
|
"84": "#5fff87",
|
||||||
|
"85": "#5fffaf",
|
||||||
|
"86": "#5fffd7",
|
||||||
|
"87": "#5fffff",
|
||||||
|
"88": "#870000",
|
||||||
|
"89": "#87005f",
|
||||||
|
"90": "#870087",
|
||||||
|
"91": "#8700af",
|
||||||
|
"92": "#8700d7",
|
||||||
|
"93": "#8700ff",
|
||||||
|
"94": "#875f00",
|
||||||
|
"95": "#875f5f",
|
||||||
|
"96": "#875f87",
|
||||||
|
"97": "#875faf",
|
||||||
|
"98": "#875fd7",
|
||||||
|
"99": "#875fff",
|
||||||
|
"100": "#878700",
|
||||||
|
"101": "#87875f",
|
||||||
|
"102": "#878787",
|
||||||
|
"103": "#8787af",
|
||||||
|
"104": "#8787d7",
|
||||||
|
"105": "#8787ff",
|
||||||
|
"106": "#87af00",
|
||||||
|
"107": "#87af5f",
|
||||||
|
"108": "#87af87",
|
||||||
|
"109": "#87afaf",
|
||||||
|
"110": "#87afd7",
|
||||||
|
"111": "#87afff",
|
||||||
|
"112": "#87d700",
|
||||||
|
"113": "#87d75f",
|
||||||
|
"114": "#87d787",
|
||||||
|
"115": "#87d7af",
|
||||||
|
"116": "#87d7d7",
|
||||||
|
"117": "#87d7ff",
|
||||||
|
"118": "#87ff00",
|
||||||
|
"119": "#87ff5f",
|
||||||
|
"120": "#87ff87",
|
||||||
|
"121": "#87ffaf",
|
||||||
|
"122": "#87ffd7",
|
||||||
|
"123": "#87ffff",
|
||||||
|
"124": "#af0000",
|
||||||
|
"125": "#af005f",
|
||||||
|
"126": "#af0087",
|
||||||
|
"127": "#af00af",
|
||||||
|
"128": "#af00d7",
|
||||||
|
"129": "#af00ff",
|
||||||
|
"130": "#af5f00",
|
||||||
|
"131": "#af5f5f",
|
||||||
|
"132": "#af5f87",
|
||||||
|
"133": "#af5faf",
|
||||||
|
"134": "#af5fd7",
|
||||||
|
"135": "#af5fff",
|
||||||
|
"136": "#af8700",
|
||||||
|
"137": "#af875f",
|
||||||
|
"138": "#af8787",
|
||||||
|
"139": "#af87af",
|
||||||
|
"140": "#af87d7",
|
||||||
|
"141": "#af87ff",
|
||||||
|
"142": "#afaf00",
|
||||||
|
"143": "#afaf5f",
|
||||||
|
"144": "#afaf87",
|
||||||
|
"145": "#afafaf",
|
||||||
|
"146": "#afafd7",
|
||||||
|
"147": "#afafff",
|
||||||
|
"148": "#afd700",
|
||||||
|
"149": "#afd75f",
|
||||||
|
"150": "#afd787",
|
||||||
|
"151": "#afd7af",
|
||||||
|
"152": "#afd7d7",
|
||||||
|
"153": "#afd7ff",
|
||||||
|
"154": "#afff00",
|
||||||
|
"155": "#afff5f",
|
||||||
|
"156": "#afff87",
|
||||||
|
"157": "#afffaf",
|
||||||
|
"158": "#afffd7",
|
||||||
|
"159": "#afffff",
|
||||||
|
"160": "#d70000",
|
||||||
|
"161": "#d7005f",
|
||||||
|
"162": "#d70087",
|
||||||
|
"163": "#d700af",
|
||||||
|
"164": "#d700d7",
|
||||||
|
"165": "#d700ff",
|
||||||
|
"166": "#d75f00",
|
||||||
|
"167": "#d75f5f",
|
||||||
|
"168": "#d75f87",
|
||||||
|
"169": "#d75faf",
|
||||||
|
"170": "#d75fd7",
|
||||||
|
"171": "#d75fff",
|
||||||
|
"172": "#d78700",
|
||||||
|
"173": "#d7875f",
|
||||||
|
"174": "#d78787",
|
||||||
|
"175": "#d787af",
|
||||||
|
"176": "#d787d7",
|
||||||
|
"177": "#d787ff",
|
||||||
|
"178": "#d7af00",
|
||||||
|
"179": "#d7af5f",
|
||||||
|
"180": "#d7af87",
|
||||||
|
"181": "#d7afaf",
|
||||||
|
"182": "#d7afd7",
|
||||||
|
"183": "#d7afff",
|
||||||
|
"184": "#d7d700",
|
||||||
|
"185": "#d7d75f",
|
||||||
|
"186": "#d7d787",
|
||||||
|
"187": "#d7d7af",
|
||||||
|
"188": "#d7d7d7",
|
||||||
|
"189": "#d7d7ff",
|
||||||
|
"190": "#d7ff00",
|
||||||
|
"191": "#d7ff5f",
|
||||||
|
"192": "#d7ff87",
|
||||||
|
"193": "#d7ffaf",
|
||||||
|
"194": "#d7ffd7",
|
||||||
|
"195": "#d7ffff",
|
||||||
|
"196": "#ff0000",
|
||||||
|
"197": "#ff005f",
|
||||||
|
"198": "#ff0087",
|
||||||
|
"199": "#ff00af",
|
||||||
|
"200": "#ff00d7",
|
||||||
|
"201": "#ff00ff",
|
||||||
|
"202": "#ff5f00",
|
||||||
|
"203": "#ff5f5f",
|
||||||
|
"204": "#ff5f87",
|
||||||
|
"205": "#ff5faf",
|
||||||
|
"206": "#ff5fd7",
|
||||||
|
"207": "#ff5fff",
|
||||||
|
"208": "#ff8700",
|
||||||
|
"209": "#ff875f",
|
||||||
|
"210": "#ff8787",
|
||||||
|
"211": "#ff87af",
|
||||||
|
"212": "#ff87d7",
|
||||||
|
"213": "#ff87ff",
|
||||||
|
"214": "#ffaf00",
|
||||||
|
"215": "#ffaf5f",
|
||||||
|
"216": "#ffaf87",
|
||||||
|
"217": "#ffafaf",
|
||||||
|
"218": "#ffafd7",
|
||||||
|
"219": "#ffafff",
|
||||||
|
"220": "#ffd700",
|
||||||
|
"221": "#ffd75f",
|
||||||
|
"222": "#ffd787",
|
||||||
|
"223": "#ffd7af",
|
||||||
|
"224": "#ffd7d7",
|
||||||
|
"225": "#ffd7ff",
|
||||||
|
"226": "#ffff00",
|
||||||
|
"227": "#ffff5f",
|
||||||
|
"228": "#ffff87",
|
||||||
|
"229": "#ffffaf",
|
||||||
|
"230": "#ffffd7",
|
||||||
|
"231": "#ffffff",
|
||||||
|
"232": "#080808",
|
||||||
|
"233": "#121212",
|
||||||
|
"234": "#1c1c1c",
|
||||||
|
"235": "#262626",
|
||||||
|
"236": "#303030",
|
||||||
|
"237": "#3a3a3a",
|
||||||
|
"238": "#444444",
|
||||||
|
"239": "#4e4e4e",
|
||||||
|
"240": "#585858",
|
||||||
|
"241": "#626262",
|
||||||
|
"242": "#6c6c6c",
|
||||||
|
"243": "#767676",
|
||||||
|
"244": "#808080",
|
||||||
|
"245": "#8a8a8a",
|
||||||
|
"246": "#949494",
|
||||||
|
"247": "#9e9e9e",
|
||||||
|
"248": "#a8a8a8",
|
||||||
|
"249": "#b2b2b2",
|
||||||
|
"250": "#bcbcbc",
|
||||||
|
"251": "#c6c6c6",
|
||||||
|
"252": "#d0d0d0",
|
||||||
|
"253": "#dadada",
|
||||||
|
"254": "#e4e4e4",
|
||||||
|
"255": "#eeeeee"
|
||||||
|
}
|
||||||
|
|
||||||
|
if color in first_16:
|
||||||
|
return first_16[color]
|
||||||
|
|
||||||
|
hex_color = hex_colors[color]
|
||||||
|
|
||||||
|
try:
|
||||||
|
return webcolors.hex_to_name(hex_color)
|
||||||
|
except ValueError:
|
||||||
|
return hex_color
|
||||||
|
|
||||||
|
|
||||||
# TODO reverse video
|
# TODO reverse video
|
||||||
|
@ -745,7 +1130,7 @@ def parse_input_line(line):
|
||||||
color_string = color_string + line[i]
|
color_string = color_string + line[i]
|
||||||
i = i + 1
|
i = i + 1
|
||||||
|
|
||||||
attributes["fgcolor"] = line_color_to_sgr(color_string)
|
attributes["fgcolor"] = color_line_to_weechat(color_string)
|
||||||
else:
|
else:
|
||||||
attributes["fgcolor"] = None
|
attributes["fgcolor"] = None
|
||||||
|
|
||||||
|
@ -761,7 +1146,7 @@ def parse_input_line(line):
|
||||||
color_string = color_string + line[i]
|
color_string = color_string + line[i]
|
||||||
i = i + 1
|
i = i + 1
|
||||||
|
|
||||||
attributes["bgcolor"] = line_color_to_sgr(color_string)
|
attributes["bgcolor"] = color_line_to_weechat(color_string)
|
||||||
else:
|
else:
|
||||||
attributes["bgcolor"] = None
|
attributes["bgcolor"] = None
|
||||||
# Reset
|
# Reset
|
||||||
|
@ -861,7 +1246,6 @@ def formatted_to_weechat(strings):
|
||||||
def formatted_to_html(strings):
|
def formatted_to_html(strings):
|
||||||
# type: (List[FormattedString]) -> str
|
# type: (List[FormattedString]) -> str
|
||||||
# TODO BG COLOR
|
# TODO BG COLOR
|
||||||
# TODO color conversion SGR -> HTML
|
|
||||||
def add_attribute(string, name, value):
|
def add_attribute(string, name, value):
|
||||||
if name == "bold" and value:
|
if name == "bold" and value:
|
||||||
return "{bold_on}{text}{bold_off}".format(
|
return "{bold_on}{text}{bold_off}".format(
|
||||||
|
@ -890,7 +1274,9 @@ def formatted_to_html(strings):
|
||||||
quote_off="</blockquote>")
|
quote_off="</blockquote>")
|
||||||
elif name == "fgcolor" and value:
|
elif name == "fgcolor" and value:
|
||||||
return "{color_on}{text}{color_off}".format(
|
return "{color_on}{text}{color_off}".format(
|
||||||
color_on="<font color={color}>".format(color=value),
|
color_on="<font color={color}>".format(
|
||||||
|
color=color_weechat_to_html(value)
|
||||||
|
),
|
||||||
text=string,
|
text=string,
|
||||||
color_off="</font>")
|
color_off="</font>")
|
||||||
|
|
||||||
|
@ -928,7 +1314,7 @@ def formatted_to_plain(strings):
|
||||||
|
|
||||||
|
|
||||||
class MatrixHtmlParser(HTMLParser):
|
class MatrixHtmlParser(HTMLParser):
|
||||||
# TODO colors
|
# TODO bg color
|
||||||
# TODO bullets
|
# TODO bullets
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
HTMLParser.__init__(self)
|
HTMLParser.__init__(self)
|
||||||
|
@ -957,6 +1343,20 @@ class MatrixHtmlParser(HTMLParser):
|
||||||
self._toggle_attribute("quote")
|
self._toggle_attribute("quote")
|
||||||
elif tag == "blockquote":
|
elif tag == "blockquote":
|
||||||
self._toggle_attribute("quote")
|
self._toggle_attribute("quote")
|
||||||
|
elif tag == "font":
|
||||||
|
for key, value in attrs:
|
||||||
|
if key == "color":
|
||||||
|
color = color_html_to_weechat(value)
|
||||||
|
|
||||||
|
if not color:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if self.text:
|
||||||
|
self.substrings.append(
|
||||||
|
FormattedString(self.text, self.attributes.copy())
|
||||||
|
)
|
||||||
|
self.text = ""
|
||||||
|
self.attributes["fgcolor"] = color
|
||||||
else:
|
else:
|
||||||
W.prnt("", "Unhandled tag {t}".format(t=tag))
|
W.prnt("", "Unhandled tag {t}".format(t=tag))
|
||||||
|
|
||||||
|
@ -971,6 +1371,13 @@ class MatrixHtmlParser(HTMLParser):
|
||||||
self._toggle_attribute("strikethrough")
|
self._toggle_attribute("strikethrough")
|
||||||
elif tag == "blockquote":
|
elif tag == "blockquote":
|
||||||
self._toggle_attribute("quote")
|
self._toggle_attribute("quote")
|
||||||
|
elif tag == "font":
|
||||||
|
if self.text:
|
||||||
|
self.substrings.append(
|
||||||
|
FormattedString(self.text, self.attributes.copy())
|
||||||
|
)
|
||||||
|
self.text = ""
|
||||||
|
self.attributes["fgcolor"] = None
|
||||||
else:
|
else:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue