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 re
|
||||
import sys
|
||||
import webcolors
|
||||
|
||||
# pylint: disable=redefined-builtin
|
||||
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
|
||||
line_colors = {
|
||||
"0": "white",
|
||||
|
@ -698,12 +699,396 @@ def line_color_to_sgr(color_string):
|
|||
return line_colors[color_string]
|
||||
|
||||
|
||||
def html_color_to_sgr(color):
|
||||
pass
|
||||
# The functions colour_dist_sq(), colour_to_6cube(), and colour_find_rgb
|
||||
# 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):
|
||||
pass
|
||||
def colour_to_6cube(v):
|
||||
# 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
|
||||
|
@ -745,7 +1130,7 @@ def parse_input_line(line):
|
|||
color_string = color_string + line[i]
|
||||
i = i + 1
|
||||
|
||||
attributes["fgcolor"] = line_color_to_sgr(color_string)
|
||||
attributes["fgcolor"] = color_line_to_weechat(color_string)
|
||||
else:
|
||||
attributes["fgcolor"] = None
|
||||
|
||||
|
@ -761,7 +1146,7 @@ def parse_input_line(line):
|
|||
color_string = color_string + line[i]
|
||||
i = i + 1
|
||||
|
||||
attributes["bgcolor"] = line_color_to_sgr(color_string)
|
||||
attributes["bgcolor"] = color_line_to_weechat(color_string)
|
||||
else:
|
||||
attributes["bgcolor"] = None
|
||||
# Reset
|
||||
|
@ -861,7 +1246,6 @@ def formatted_to_weechat(strings):
|
|||
def formatted_to_html(strings):
|
||||
# type: (List[FormattedString]) -> str
|
||||
# TODO BG COLOR
|
||||
# TODO color conversion SGR -> HTML
|
||||
def add_attribute(string, name, value):
|
||||
if name == "bold" and value:
|
||||
return "{bold_on}{text}{bold_off}".format(
|
||||
|
@ -890,7 +1274,9 @@ def formatted_to_html(strings):
|
|||
quote_off="</blockquote>")
|
||||
elif name == "fgcolor" and value:
|
||||
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,
|
||||
color_off="</font>")
|
||||
|
||||
|
@ -928,7 +1314,7 @@ def formatted_to_plain(strings):
|
|||
|
||||
|
||||
class MatrixHtmlParser(HTMLParser):
|
||||
# TODO colors
|
||||
# TODO bg color
|
||||
# TODO bullets
|
||||
def __init__(self):
|
||||
HTMLParser.__init__(self)
|
||||
|
@ -957,6 +1343,20 @@ class MatrixHtmlParser(HTMLParser):
|
|||
self._toggle_attribute("quote")
|
||||
elif tag == "blockquote":
|
||||
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:
|
||||
W.prnt("", "Unhandled tag {t}".format(t=tag))
|
||||
|
||||
|
@ -971,6 +1371,13 @@ class MatrixHtmlParser(HTMLParser):
|
|||
self._toggle_attribute("strikethrough")
|
||||
elif tag == "blockquote":
|
||||
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:
|
||||
pass
|
||||
|
||||
|
|
Loading…
Reference in a new issue