ansi.py
177 lines
| 5.0 KiB
| text/x-python
|
PythonLexer
Jonathan Frederic
|
r10676 | """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 | ||||
#----------------------------------------------------------------------------- | ||||
Jonathan Frederic
|
r10485 | import re | ||
jakobgager
|
r10946 | from IPython.utils import coloransi | ||
Jonathan Frederic
|
r10485 | |||
Jonathan Frederic
|
r10676 | #----------------------------------------------------------------------------- | ||
# Classes and functions | ||||
#----------------------------------------------------------------------------- | ||||
Jonathan Frederic
|
r10485 | |||
Brian E. Granger
|
r11088 | __all__ = [ | ||
Jonathan Frederic
|
r11685 | 'strip_ansi', | ||
Brian E. Granger
|
r11088 | 'ansi2html', | ||
'single_ansi2latex', | ||||
'ansi2latex' | ||||
] | ||||
Jonathan Frederic
|
r11685 | def strip_ansi(source): | ||
Jonathan Frederic
|
r10676 | """ | ||
Remove ansi from text | ||||
Parameters | ||||
---------- | ||||
source : str | ||||
Source to remove the ansi from | ||||
""" | ||||
Jonathan Frederic
|
r11435 | return re.sub(r'\033\[(\d|;)+?m', '', source) | ||
Jonathan Frederic
|
r10485 | |||
Jonathan Frederic
|
r10676 | |||
def ansi2html(text): | ||||
""" | ||||
Conver ansi colors to html colors. | ||||
Parameters | ||||
---------- | ||||
text : str | ||||
Text containing ansi colors to convert to html | ||||
""" | ||||
Jonathan Frederic
|
r10485 | ansi_colormap = { | ||
'30': 'ansiblack', | ||||
'31': 'ansired', | ||||
'32': 'ansigreen', | ||||
'33': 'ansiyellow', | ||||
'34': 'ansiblue', | ||||
'35': 'ansipurple', | ||||
'36': 'ansicyan', | ||||
'37': 'ansigrey', | ||||
'01': 'ansibold', | ||||
} | ||||
# do ampersand first | ||||
Jonathan Frederic
|
r10676 | text = text.replace('&', '&') | ||
Jonathan Frederic
|
r10485 | html_escapes = { | ||
'<': '<', | ||||
'>': '>', | ||||
"'": ''', | ||||
'"': '"', | ||||
'`': '`', | ||||
} | ||||
Jonathan Frederic
|
r10624 | |||
Thomas Kluyver
|
r13361 | for c, escape in html_escapes.items(): | ||
Jonathan Frederic
|
r10676 | text = text.replace(c, escape) | ||
Jonathan Frederic
|
r10485 | |||
ansi_re = re.compile('\x1b' + r'\[([\dA-Fa-f;]*?)m') | ||||
Jonathan Frederic
|
r10676 | m = ansi_re.search(text) | ||
Jonathan Frederic
|
r10485 | opened = False | ||
cmds = [] | ||||
opener = '' | ||||
closer = '' | ||||
while m: | ||||
cmds = m.groups()[0].split(';') | ||||
closer = '</span>' if opened else '' | ||||
Jonathan Frederic
|
r10624 | |||
Jonathan Frederic
|
r10485 | # 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.get(cmd)) | ||||
if classes: | ||||
opener = '<span class="%s">' % (' '.join(classes)) | ||||
else: | ||||
opener = '' | ||||
Jonathan Frederic
|
r10676 | text = re.sub(ansi_re, closer + opener, text, 1) | ||
Jonathan Frederic
|
r10485 | |||
Jonathan Frederic
|
r10676 | m = ansi_re.search(text) | ||
Jonathan Frederic
|
r10485 | |||
if opened: | ||||
Jonathan Frederic
|
r10676 | text += '</span>' | ||
return text | ||||
jakobgager
|
r10943 | |||
def single_ansi2latex(code): | ||||
Richard Everson
|
r14215 | """Converts single ansi markup to latex format. | ||
jakobgager
|
r10943 | |||
Return latex code and number of open brackets. | ||||
Richard Everson
|
r14215 | |||
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'. | ||||
jakobgager
|
r10943 | """ | ||
Richard Everson
|
r14215 | components = code.split(';') | ||
if len(components) > 1: | ||||
Richard Everson
|
r14783 | # Style is digits after '[' | ||
style = int(components[0].split('[')[-1]) | ||||
Richard Everson
|
r14215 | color = components[1][:-1] | ||
else: | ||||
Richard Everson
|
r14783 | style = 0 | ||
Richard Everson
|
r14215 | color = components[0][-3:-1] | ||
# If the style is not normal (0), bold (1) or blinking (5) then treat it as normal | ||||
Richard Everson
|
r14783 | if style not in [0, 1, 5]: | ||
style = 0 | ||||
Richard Everson
|
r14215 | |||
for name, tcode in coloransi.color_templates: | ||||
tstyle, tcolor = tcode.split(';') | ||||
Richard Everson
|
r14783 | tstyle = int(tstyle) | ||
Richard Everson
|
r14215 | if tstyle == style and tcolor == color: | ||
break | ||||
else: | ||||
return '', 0 | ||||
Richard Everson
|
r14783 | if style == 5: | ||
Richard Everson
|
r14215 | name = name[5:] # BlinkRed -> Red, etc | ||
name = name.lower() | ||||
Richard Everson
|
r14783 | if style in [1, 5]: | ||
Richard Everson
|
r14215 | return r'\textbf{\color{'+name+'}', 1 | ||
else: | ||||
return r'{\color{'+name+'}', 1 | ||||
jakobgager
|
r10943 | |||
def ansi2latex(text): | ||||
"""Converts ansi formated text to latex version | ||||
based on https://bitbucket.org/birkenfeld/sphinx-contrib/ansi.py | ||||
""" | ||||
Richard Everson
|
r14215 | color_pattern = re.compile('\x1b\\[([^m]*)m') | ||
jakobgager
|
r10943 | 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 | ||||
Richard Everson
|
r14215 | code = match.group() | ||
if not (code == coloransi.TermColors.Normal or openbrack): | ||||
texform, openbrack = single_ansi2latex(code) | ||||
jakobgager
|
r10943 | outstring += texform | ||
last_end = match.end() | ||||
MinRK
|
r13441 | |||
# Add the remainer of the string and THEN close any remaining color brackets. | ||||
Jonathan Frederic
|
r11496 | outstring += text[last_end:] | ||
jakobgager
|
r10943 | if openbrack: | ||
outstring += '}'*openbrack | ||||
return outstring.strip() | ||||