Home | History | Annotate | Download | only in misc
      1 #!/usr/bin/env python
      2 
      3 import math, os, sys
      4 
      5 webcolors = {
      6 "indianred": "#cd5c5c",
      7 "lightcoral": "#f08080",
      8 "salmon": "#fa8072",
      9 "darksalmon": "#e9967a",
     10 "lightsalmon": "#ffa07a",
     11 "red": "#ff0000",
     12 "crimson": "#dc143c",
     13 "firebrick": "#b22222",
     14 "darkred": "#8b0000",
     15 "pink": "#ffc0cb",
     16 "lightpink": "#ffb6c1",
     17 "hotpink": "#ff69b4",
     18 "deeppink": "#ff1493",
     19 "mediumvioletred": "#c71585",
     20 "palevioletred": "#db7093",
     21 "lightsalmon": "#ffa07a",
     22 "coral": "#ff7f50",
     23 "tomato": "#ff6347",
     24 "orangered": "#ff4500",
     25 "darkorange": "#ff8c00",
     26 "orange": "#ffa500",
     27 "gold": "#ffd700",
     28 "yellow": "#ffff00",
     29 "lightyellow": "#ffffe0",
     30 "lemonchiffon": "#fffacd",
     31 "lightgoldenrodyellow": "#fafad2",
     32 "papayawhip": "#ffefd5",
     33 "moccasin": "#ffe4b5",
     34 "peachpuff": "#ffdab9",
     35 "palegoldenrod": "#eee8aa",
     36 "khaki": "#f0e68c",
     37 "darkkhaki": "#bdb76b",
     38 "lavender": "#e6e6fa",
     39 "thistle": "#d8bfd8",
     40 "plum": "#dda0dd",
     41 "violet": "#ee82ee",
     42 "orchid": "#da70d6",
     43 "fuchsia": "#ff00ff",
     44 "magenta": "#ff00ff",
     45 "mediumorchid": "#ba55d3",
     46 "mediumpurple": "#9370db",
     47 "blueviolet": "#8a2be2",
     48 "darkviolet": "#9400d3",
     49 "darkorchid": "#9932cc",
     50 "darkmagenta": "#8b008b",
     51 "purple": "#800080",
     52 "indigo": "#4b0082",
     53 "darkslateblue": "#483d8b",
     54 "slateblue": "#6a5acd",
     55 "mediumslateblue": "#7b68ee",
     56 "greenyellow": "#adff2f",
     57 "chartreuse": "#7fff00",
     58 "lawngreen": "#7cfc00",
     59 "lime": "#00ff00",
     60 "limegreen": "#32cd32",
     61 "palegreen": "#98fb98",
     62 "lightgreen": "#90ee90",
     63 "mediumspringgreen": "#00fa9a",
     64 "springgreen": "#00ff7f",
     65 "mediumseagreen": "#3cb371",
     66 "seagreen": "#2e8b57",
     67 "forestgreen": "#228b22",
     68 "green": "#008000",
     69 "darkgreen": "#006400",
     70 "yellowgreen": "#9acd32",
     71 "olivedrab": "#6b8e23",
     72 "olive": "#808000",
     73 "darkolivegreen": "#556b2f",
     74 "mediumaquamarine": "#66cdaa",
     75 "darkseagreen": "#8fbc8f",
     76 "lightseagreen": "#20b2aa",
     77 "darkcyan": "#008b8b",
     78 "teal": "#008080",
     79 "aqua": "#00ffff",
     80 "cyan": "#00ffff",
     81 "lightcyan": "#e0ffff",
     82 "paleturquoise": "#afeeee",
     83 "aquamarine": "#7fffd4",
     84 "turquoise": "#40e0d0",
     85 "mediumturquoise": "#48d1cc",
     86 "darkturquoise": "#00ced1",
     87 "cadetblue": "#5f9ea0",
     88 "steelblue": "#4682b4",
     89 "lightsteelblue": "#b0c4de",
     90 "powderblue": "#b0e0e6",
     91 "lightblue": "#add8e6",
     92 "skyblue": "#87ceeb",
     93 "lightskyblue": "#87cefa",
     94 "deepskyblue": "#00bfff",
     95 "dodgerblue": "#1e90ff",
     96 "cornflowerblue": "#6495ed",
     97 "royalblue": "#4169e1",
     98 "blue": "#0000ff",
     99 "mediumblue": "#0000cd",
    100 "darkblue": "#00008b",
    101 "navy": "#000080",
    102 "midnightblue": "#191970",
    103 "cornsilk": "#fff8dc",
    104 "blanchedalmond": "#ffebcd",
    105 "bisque": "#ffe4c4",
    106 "navajowhite": "#ffdead",
    107 "wheat": "#f5deb3",
    108 "burlywood": "#deb887",
    109 "tan": "#d2b48c",
    110 "rosybrown": "#bc8f8f",
    111 "sandybrown": "#f4a460",
    112 "goldenrod": "#daa520",
    113 "darkgoldenrod": "#b8860b",
    114 "peru": "#cd853f",
    115 "chocolate": "#d2691e",
    116 "saddlebrown": "#8b4513",
    117 "sienna": "#a0522d",
    118 "brown": "#a52a2a",
    119 "maroon": "#800000",
    120 "white": "#ffffff",
    121 "snow": "#fffafa",
    122 "honeydew": "#f0fff0",
    123 "mintcream": "#f5fffa",
    124 "azure": "#f0ffff",
    125 "aliceblue": "#f0f8ff",
    126 "ghostwhite": "#f8f8ff",
    127 "whitesmoke": "#f5f5f5",
    128 "seashell": "#fff5ee",
    129 "beige": "#f5f5dc",
    130 "oldlace": "#fdf5e6",
    131 "floralwhite": "#fffaf0",
    132 "ivory": "#fffff0",
    133 "antiquewhite": "#faebd7",
    134 "linen": "#faf0e6",
    135 "lavenderblush": "#fff0f5",
    136 "mistyrose": "#ffe4e1",
    137 "gainsboro": "#dcdcdc",
    138 "lightgrey": "#d3d3d3",
    139 "silver": "#c0c0c0",
    140 "darkgray": "#a9a9a9",
    141 "gray": "#808080",
    142 "dimgray": "#696969",
    143 "lightslategray": "#778899",
    144 "slategray": "#708090",
    145 "darkslategray": "#2f4f4f",
    146 "black": "#000000",
    147 }
    148 
    149 if os.name == "nt":
    150     consoleColors = [
    151     "#000000",  #{   0,   0,   0 },//0 - black
    152     "#000080",  #{   0,   0, 128 },//1 - navy
    153     "#008000",  #{   0, 128,   0 },//2 - green
    154     "#008080",  #{   0, 128, 128 },//3 - teal
    155     "#800000",  #{ 128,   0,   0 },//4 - maroon
    156     "#800080",  #{ 128,   0, 128 },//5 - purple
    157     "#808000",  #{ 128, 128,   0 },//6 - olive
    158     "#C0C0C0",  #{ 192, 192, 192 },//7 - silver
    159     "#808080",  #{ 128, 128, 128 },//8 - gray
    160     "#0000FF",  #{   0,   0, 255 },//9 - blue
    161     "#00FF00",  #{   0, 255,   0 },//a - lime
    162     "#00FFFF",  #{   0, 255, 255 },//b - cyan
    163     "#FF0000",  #{ 255,   0,   0 },//c - red
    164     "#FF00FF",  #{ 255,   0, 255 },//d - magenta
    165     "#FFFF00",  #{ 255, 255,   0 },//e - yellow
    166     "#FFFFFF",  #{ 255, 255, 255 } //f - white
    167     ]
    168 else:
    169     consoleColors = [
    170     "#2e3436",
    171     "#cc0000",
    172     "#4e9a06",
    173     "#c4a000",
    174     "#3465a4",
    175     "#75507b",
    176     "#06989a",
    177     "#d3d7cf",
    178     "#ffffff",
    179 
    180     "#555753",
    181     "#ef2929",
    182     "#8ae234",
    183     "#fce94f",
    184     "#729fcf",
    185     "#ad7fa8",
    186     "#34e2e2",
    187     "#eeeeec",
    188     ]
    189 
    190 def RGB2LAB(r,g,b):
    191     if max(r,g,b):
    192         r /= 255.
    193         g /= 255.
    194         b /= 255.
    195 
    196     X = (0.412453 * r + 0.357580 * g + 0.180423 * b) / 0.950456
    197     Y = (0.212671 * r + 0.715160 * g + 0.072169 * b)
    198     Z = (0.019334 * r + 0.119193 * g + 0.950227 * b) / 1.088754
    199 
    200     #[X * 0.950456]   [0.412453 0.357580 0.180423]   [R]
    201     #[Y           ] = [0.212671 0.715160 0.072169] * [G]
    202     #[Z * 1.088754]   [0.019334 0.119193 0.950227]   [B]
    203 
    204     T = 0.008856 #threshold
    205 
    206     if X > T:
    207         fX = math.pow(X, 1./3.)
    208     else:
    209         fX = 7.787 * X + 16./116.
    210 
    211     # Compute L
    212     if Y > T:
    213         Y3 = math.pow(Y, 1./3.)
    214         fY = Y3
    215         L  = 116. * Y3 - 16.0
    216     else:
    217         fY = 7.787 * Y + 16./116.
    218         L  = 903.3 * Y
    219 
    220     if Z > T:
    221         fZ = math.pow(Z, 1./3.)
    222     else:
    223         fZ = 7.787 * Z + 16./116.
    224 
    225     # Compute a and b
    226     a = 500. * (fX - fY)
    227     b = 200. * (fY - fZ)
    228 
    229     return (L,a,b)
    230 
    231 def colorDistance(r1,g1,b1 = None, r2 = None, g2 = None,b2 = None):
    232     if type(r1) == tuple and type(g1) == tuple and b1 is None and r2 is None and g2 is None and b2 is None:
    233         (l1,a1,b1) = RGB2LAB(*r1)
    234         (l2,a2,b2) = RGB2LAB(*g1)
    235     else:
    236         (l1,a1,b1) = RGB2LAB(r1,g1,b1)
    237         (l2,a2,b2) = RGB2LAB(r2,g2,b2)
    238     #CIE94
    239     dl = l1-l2
    240     C1 = math.sqrt(a1*a1 + b1*b1)
    241     C2 = math.sqrt(a2*a2 + b2*b2)
    242     dC = C1 - C2
    243     da = a1-a2
    244     db = b1-b2
    245     dH = math.sqrt(max(0, da*da + db*db - dC*dC))
    246     Kl = 1
    247     K1 = 0.045
    248     K2 = 0.015
    249 
    250     s1 = dl/Kl
    251     s2 = dC/(1. + K1 * C1)
    252     s3 = dH/(1. + K2 * C1)
    253     return math.sqrt(s1*s1 + s2*s2 + s3*s3)
    254 
    255 def parseHexColor(col):
    256     if len(col) != 4 and len(col) != 7 and not col.startswith("#"):
    257         return (0,0,0)
    258     if len(col) == 4:
    259         r = col[1]*2
    260         g = col[2]*2
    261         b = col[3]*2
    262     else:
    263         r = col[1:3]
    264         g = col[3:5]
    265         b = col[5:7]
    266     return (int(r,16), int(g,16), int(b,16))
    267 
    268 def getColor(col):
    269     if isinstance(col, str):
    270         if col.lower() in webcolors:
    271             return parseHexColor(webcolors[col.lower()])
    272         else:
    273             return parseHexColor(col)
    274     else:
    275         return col
    276 
    277 def getNearestConsoleColor(col):
    278     color = getColor(col)
    279     minidx = 0
    280     mindist = colorDistance(color, getColor(consoleColors[0]))
    281     for i in range(len(consoleColors)):
    282         dist = colorDistance(color, getColor(consoleColors[i]))
    283         if dist < mindist:
    284             mindist = dist
    285             minidx = i
    286     return minidx
    287 
    288 if os.name == 'nt':
    289     import msvcrt
    290     from ctypes import windll, Structure, c_short, c_ushort, byref
    291     SHORT = c_short
    292     WORD = c_ushort
    293 
    294     class COORD(Structure):
    295         _fields_ = [
    296             ("X", SHORT),
    297             ("Y", SHORT)]
    298 
    299     class SMALL_RECT(Structure):
    300         _fields_ = [
    301             ("Left", SHORT),
    302             ("Top", SHORT),
    303             ("Right", SHORT),
    304             ("Bottom", SHORT)]
    305 
    306     class CONSOLE_SCREEN_BUFFER_INFO(Structure):
    307         _fields_ = [
    308             ("dwSize", COORD),
    309             ("dwCursorPosition", COORD),
    310             ("wAttributes", WORD),
    311             ("srWindow", SMALL_RECT),
    312             ("dwMaximumWindowSize", COORD)]
    313 
    314     class winConsoleColorizer(object):
    315         def __init__(self, stream):
    316             self.handle = msvcrt.get_osfhandle(stream.fileno())
    317             self.default_attrs = 7#self.get_text_attr()
    318             self.stream = stream
    319 
    320         def get_text_attr(self):
    321             csbi = CONSOLE_SCREEN_BUFFER_INFO()
    322             windll.kernel32.GetConsoleScreenBufferInfo(self.handle, byref(csbi))
    323             return csbi.wAttributes
    324 
    325         def set_text_attr(self, color):
    326             windll.kernel32.SetConsoleTextAttribute(self.handle, color)
    327 
    328         def write(self, *text, **attrs):
    329             if not text:
    330                 return
    331             color = attrs.get("color", None)
    332             if color:
    333                 col = getNearestConsoleColor(color)
    334                 self.stream.flush()
    335                 self.set_text_attr(col)
    336             self.stream.write(" ".join([str(t) for t in text]))
    337             if color:
    338                 self.stream.flush()
    339                 self.set_text_attr(self.default_attrs)
    340 
    341 class dummyColorizer(object):
    342     def __init__(self, stream):
    343         self.stream = stream
    344 
    345     def write(self, *text, **attrs):
    346         if text:
    347             self.stream.write(" ".join([str(t) for t in text]))
    348 
    349 class asciiSeqColorizer(object):
    350     RESET_SEQ = "\033[0m"
    351     #BOLD_SEQ = "\033[1m"
    352     ITALIC_SEQ = "\033[3m"
    353     UNDERLINE_SEQ = "\033[4m"
    354     STRIKEOUT_SEQ = "\033[9m"
    355     COLOR_SEQ0 = "\033[00;%dm" #dark
    356     COLOR_SEQ1 = "\033[01;%dm" #bold and light
    357 
    358     def __init__(self, stream):
    359         self.stream = stream
    360 
    361     def get_seq(self, code):
    362         if code > 8:
    363             return self.__class__.COLOR_SEQ1 % (30 + code - 9)
    364         else:
    365             return self.__class__.COLOR_SEQ0 % (30 + code)
    366 
    367     def write(self, *text, **attrs):
    368         if not text:
    369             return
    370         color = attrs.get("color", None)
    371         if color:
    372             col = getNearestConsoleColor(color)
    373             self.stream.write(self.get_seq(col))
    374         self.stream.write(" ".join([str(t) for t in text]))
    375         if color:
    376             self.stream.write(self.__class__.RESET_SEQ)
    377 
    378 
    379 def getColorizer(stream):
    380     if stream.isatty():
    381         if os.name == "nt":
    382             return winConsoleColorizer(stream)
    383         else:
    384             return asciiSeqColorizer(stream)
    385     else:
    386         return dummyColorizer(stream)
    387