# This is from https://github.com/broadinstitute/xtermcolor # Copyright (C) 2012 The Broad Institute # Permission is hereby granted, free of charge, to any person obtaining a copy of # this software and associated documentation files (the "Software"), to deal in # the Software without restriction, including without limitation the rights to # use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies # of the Software, and to permit persons to whom the Software is furnished to do # so, subject to the following conditions: # The above copyright notice and this permission notice shall be included in all # copies or substantial portions of the Software. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. class TerminalColorMapException(Exception): pass def _rgb(color): return ((color >> 16) & 0xff, (color >> 8) & 0xff, color & 0xff) def _diff(color1, color2): (r1, g1, b1) = _rgb(color1) (r2, g2, b2) = _rgb(color2) return abs(r1 - r2) + abs(g1 - g2) + abs(b1 - b2) class TerminalColorMap: def getColors(self, order='rgb'): return self.colors def convert(self, hexcolor): diffs = {} for xterm, rgb in self.colors.items(): diffs[_diff(rgb, hexcolor)] = xterm minDiffAnsi = diffs[min(diffs.keys())] return (minDiffAnsi, self.colors[minDiffAnsi]) def colorize(self, string, rgb=None, ansi=None, bg=None, ansi_bg=None): '''Returns the colored string''' if not isinstance(string, str): string = str(string) if rgb is None and ansi is None: raise TerminalColorMapException( 'colorize: must specify one named parameter: rgb or ansi') if rgb is not None and ansi is not None: raise TerminalColorMapException( 'colorize: must specify only one named parameter: rgb or ansi') if bg is not None and ansi_bg is not None: raise TerminalColorMapException( 'colorize: must specify only one named parameter: bg or ansi_bg') if rgb is not None: (closestAnsi, closestRgb) = self.convert(rgb) elif ansi is not None: (closestAnsi, closestRgb) = (ansi, self.colors[ansi]) if bg is None and ansi_bg is None: return "\033[38;5;{ansiCode:d}m{string:s}\033[0m".format(ansiCode=closestAnsi, string=string) if bg is not None: (closestBgAnsi, unused) = self.convert(bg) elif ansi_bg is not None: (closestBgAnsi, unused) = (ansi_bg, self.colors[ansi_bg]) return "\033[38;5;{ansiCode:d}m\033[48;5;{bf:d}m{string:s}\033[0m".format(ansiCode=closestAnsi, bf=closestBgAnsi, string=string) class VT100ColorMap(TerminalColorMap): primary = [ 0x000000, 0x800000, 0x008000, 0x808000, 0x000080, 0x800080, 0x008080, 0xc0c0c0 ] bright = [ 0x808080, 0xff0000, 0x00ff00, 0xffff00, 0x0000ff, 0xff00ff, 0x00ffff, 0xffffff ] def __init__(self): self.colors = dict() self._compute() def _compute(self): for index, color in enumerate(self.primary + self.bright): self.colors[index] = color class XTermColorMap(VT100ColorMap): grayscale_start = 0x08 grayscale_end = 0xf8 grayscale_step = 10 intensities = [ 0x00, 0x5F, 0x87, 0xAF, 0xD7, 0xFF ] def _compute(self): for index, color in enumerate(self.primary + self.bright): self.colors[index] = color c = 16 for i in self.intensities: color = i << 16 for j in self.intensities: color &= ~(0xff << 8) color |= j << 8 for k in self.intensities: color &= ~0xff color |= k self.colors[c] = color c += 1 c = 232 for hex in list(range(self.grayscale_start, self.grayscale_end, self.grayscale_step)): color = (hex << 16) | (hex << 8) | hex self.colors[c] = color c += 1