##// END OF EJS Templates
tiny refactor to nbconvert ansi filter...
Paul Ivanov -
Show More
@@ -1,177 +1,178 b''
1 1 """Filters for processing ANSI colors within Jinja templates.
2 2 """
3 3 #-----------------------------------------------------------------------------
4 4 # Copyright (c) 2013, the IPython Development Team.
5 5 #
6 6 # Distributed under the terms of the Modified BSD License.
7 7 #
8 8 # The full license is in the file COPYING.txt, distributed with this software.
9 9 #-----------------------------------------------------------------------------
10 10
11 11 #-----------------------------------------------------------------------------
12 12 # Imports
13 13 #-----------------------------------------------------------------------------
14 14
15 15 import re
16 16 from IPython.utils import coloransi
17 17
18 18 #-----------------------------------------------------------------------------
19 19 # Classes and functions
20 20 #-----------------------------------------------------------------------------
21 21
22 22 __all__ = [
23 23 'strip_ansi',
24 24 'ansi2html',
25 25 'single_ansi2latex',
26 26 'ansi2latex'
27 27 ]
28 28
29 29 def strip_ansi(source):
30 30 """
31 31 Remove ansi from text
32 32
33 33 Parameters
34 34 ----------
35 35 source : str
36 36 Source to remove the ansi from
37 37 """
38 38
39 39 return re.sub(r'\033\[(\d|;)+?m', '', source)
40 40
41 41
42 ansi_colormap = {
43 '30': 'ansiblack',
44 '31': 'ansired',
45 '32': 'ansigreen',
46 '33': 'ansiyellow',
47 '34': 'ansiblue',
48 '35': 'ansipurple',
49 '36': 'ansicyan',
50 '37': 'ansigrey',
51 '01': 'ansibold',
52 }
53
54 html_escapes = {
55 '<': '&lt;',
56 '>': '&gt;',
57 "'": '&apos;',
58 '"': '&quot;',
59 '`': '&#96;',
60 }
61 ansi_re = re.compile('\x1b' + r'\[([\dA-Fa-f;]*?)m')
62
42 63 def ansi2html(text):
43 64 """
44 Conver ansi colors to html colors.
65 Convert ansi colors to html colors.
45 66
46 67 Parameters
47 68 ----------
48 69 text : str
49 70 Text containing ansi colors to convert to html
50 71 """
51
52 ansi_colormap = {
53 '30': 'ansiblack',
54 '31': 'ansired',
55 '32': 'ansigreen',
56 '33': 'ansiyellow',
57 '34': 'ansiblue',
58 '35': 'ansipurple',
59 '36': 'ansicyan',
60 '37': 'ansigrey',
61 '01': 'ansibold',
62 }
63 72
64 73 # do ampersand first
65 74 text = text.replace('&', '&amp;')
66 html_escapes = {
67 '<': '&lt;',
68 '>': '&gt;',
69 "'": '&apos;',
70 '"': '&quot;',
71 '`': '&#96;',
72 }
73 75
74 76 for c, escape in html_escapes.items():
75 77 text = text.replace(c, escape)
76 78
77 ansi_re = re.compile('\x1b' + r'\[([\dA-Fa-f;]*?)m')
78 79 m = ansi_re.search(text)
79 80 opened = False
80 81 cmds = []
81 82 opener = ''
82 83 closer = ''
83 84 while m:
84 85 cmds = m.groups()[0].split(';')
85 86 closer = '</span>' if opened else ''
86 87
87 88 # True if there is there more than one element in cmds, *or*
88 89 # if there is only one but it is not equal to a string of zeroes.
89 90 opened = len(cmds) > 1 or cmds[0] != '0' * len(cmds[0])
90 91 classes = []
91 92 for cmd in cmds:
92 93 if cmd in ansi_colormap:
93 classes.append(ansi_colormap.get(cmd))
94 classes.append(ansi_colormap[cmd])
94 95
95 96 if classes:
96 97 opener = '<span class="%s">' % (' '.join(classes))
97 98 else:
98 99 opener = ''
99 100 text = re.sub(ansi_re, closer + opener, text, 1)
100 101
101 102 m = ansi_re.search(text)
102 103
103 104 if opened:
104 105 text += '</span>'
105 106 return text
106 107
107 108
108 109 def single_ansi2latex(code):
109 110 """Converts single ansi markup to latex format.
110 111
111 112 Return latex code and number of open brackets.
112 113
113 114 Accepts codes like '\x1b[1;32m' (bold, red) and the short form '\x1b[32m' (red)
114 115
115 116 Colors are matched to those defined in coloransi, which defines colors
116 117 using the 0, 1 (bold) and 5 (blinking) styles. Styles 1 and 5 are
117 118 interpreted as bold. All other styles are mapped to 0. Note that in
118 119 coloransi, a style of 1 does not just mean bold; for example, Brown is
119 120 "0;33", but Yellow is "1;33". An empty string is returned for unrecognised
120 121 codes and the "reset" code '\x1b[m'.
121 122 """
122 123 components = code.split(';')
123 124 if len(components) > 1:
124 125 # Style is digits after '['
125 126 style = int(components[0].split('[')[-1])
126 127 color = components[1][:-1]
127 128 else:
128 129 style = 0
129 130 color = components[0][-3:-1]
130 131
131 132 # If the style is not normal (0), bold (1) or blinking (5) then treat it as normal
132 133 if style not in [0, 1, 5]:
133 134 style = 0
134 135
135 136 for name, tcode in coloransi.color_templates:
136 137 tstyle, tcolor = tcode.split(';')
137 138 tstyle = int(tstyle)
138 139 if tstyle == style and tcolor == color:
139 140 break
140 141 else:
141 142 return '', 0
142 143
143 144 if style == 5:
144 145 name = name[5:] # BlinkRed -> Red, etc
145 146 name = name.lower()
146 147
147 148 if style in [1, 5]:
148 149 return r'\textbf{\color{'+name+'}', 1
149 150 else:
150 151 return r'{\color{'+name+'}', 1
151 152
152 153 def ansi2latex(text):
153 154 """Converts ansi formated text to latex version
154 155
155 156 based on https://bitbucket.org/birkenfeld/sphinx-contrib/ansi.py
156 157 """
157 158 color_pattern = re.compile('\x1b\\[([^m]*)m')
158 159 last_end = 0
159 160 openbrack = 0
160 161 outstring = ''
161 162 for match in color_pattern.finditer(text):
162 163 head = text[last_end:match.start()]
163 164 outstring += head
164 165 if openbrack:
165 166 outstring += '}'*openbrack
166 167 openbrack = 0
167 168 code = match.group()
168 169 if not (code == coloransi.TermColors.Normal or openbrack):
169 170 texform, openbrack = single_ansi2latex(code)
170 171 outstring += texform
171 172 last_end = match.end()
172 173
173 174 # Add the remainer of the string and THEN close any remaining color brackets.
174 175 outstring += text[last_end:]
175 176 if openbrack:
176 177 outstring += '}'*openbrack
177 178 return outstring.strip()
General Comments 0
You need to be logged in to leave comments. Login now