"""Filters for processing ANSI colors within Jinja templates. """ #----------------------------------------------------------------------------- # Copyright (c) 2013, the IPython Development Team. # # Distributed under the terms of the Modified BSD License. # # The full license is in the file COPYING.txt, distributed with this software. #----------------------------------------------------------------------------- #----------------------------------------------------------------------------- # Imports #----------------------------------------------------------------------------- import re from IPython.utils import coloransi #----------------------------------------------------------------------------- # Classes and functions #----------------------------------------------------------------------------- __all__ = [ 'strip_ansi', 'ansi2html', 'single_ansi2latex', 'ansi2latex' ] def strip_ansi(source): """ Remove ansi from text Parameters ---------- source : str Source to remove the ansi from """ return re.sub(r'\033\[(\d|;)+?m', '', source) ansi_colormap = { '30': 'ansiblack', '31': 'ansired', '32': 'ansigreen', '33': 'ansiyellow', '34': 'ansiblue', '35': 'ansipurple', '36': 'ansicyan', '37': 'ansigrey', '01': 'ansibold', } html_escapes = { '<': '<', '>': '>', "'": ''', '"': '"', '`': '`', } ansi_re = re.compile('\x1b' + r'\[([\dA-Fa-f;]*?)m') def ansi2html(text): """ Convert ansi colors to html colors. Parameters ---------- text : str Text containing ansi colors to convert to html """ # do ampersand first text = text.replace('&', '&') for c, escape in html_escapes.items(): text = text.replace(c, escape) m = ansi_re.search(text) opened = False cmds = [] opener = '' closer = '' while m: cmds = m.groups()[0].split(';') closer = '' if opened else '' # True if there is there more than one element in cmds, *or* # if there is only one but it is not equal to a string of zeroes. opened = len(cmds) > 1 or cmds[0] != '0' * len(cmds[0]) classes = [] for cmd in cmds: if cmd in ansi_colormap: classes.append(ansi_colormap[cmd]) if classes: opener = '' % (' '.join(classes)) else: opener = '' text = re.sub(ansi_re, closer + opener, text, 1) m = ansi_re.search(text) if opened: text += '' return text def single_ansi2latex(code): """Converts single ansi markup to latex format. Return latex code and number of open brackets. Accepts codes like '\x1b[1;32m' (bold, red) and the short form '\x1b[32m' (red) Colors are matched to those defined in coloransi, which defines colors using the 0, 1 (bold) and 5 (blinking) styles. Styles 1 and 5 are interpreted as bold. All other styles are mapped to 0. Note that in coloransi, a style of 1 does not just mean bold; for example, Brown is "0;33", but Yellow is "1;33". An empty string is returned for unrecognised codes and the "reset" code '\x1b[m'. """ components = code.split(';') if len(components) > 1: # Style is digits after '[' style = int(components[0].split('[')[-1]) color = components[1][:-1] else: style = 0 color = components[0][-3:-1] # If the style is not normal (0), bold (1) or blinking (5) then treat it as normal if style not in [0, 1, 5]: style = 0 for name, tcode in coloransi.color_templates: tstyle, tcolor = tcode.split(';') tstyle = int(tstyle) if tstyle == style and tcolor == color: break else: return '', 0 if style == 5: name = name[5:] # BlinkRed -> Red, etc name = name.lower() if style in [1, 5]: return r'\textbf{\color{'+name+'}', 1 else: return r'{\color{'+name+'}', 1 def ansi2latex(text): """Converts ansi formated text to latex version based on https://bitbucket.org/birkenfeld/sphinx-contrib/ansi.py """ color_pattern = re.compile('\x1b\\[([^m]*)m') last_end = 0 openbrack = 0 outstring = '' for match in color_pattern.finditer(text): head = text[last_end:match.start()] outstring += head if openbrack: outstring += '}'*openbrack openbrack = 0 code = match.group() if not (code == coloransi.TermColors.Normal or openbrack): texform, openbrack = single_ansi2latex(code) outstring += texform last_end = match.end() # Add the remainer of the string and THEN close any remaining color brackets. outstring += text[last_end:] if openbrack: outstring += '}'*openbrack return outstring.strip()