Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/nbconvert/filters/ansi.py: 7%
169 statements
« prev ^ index » next coverage.py v7.2.7, created at 2023-07-01 06:54 +0000
« prev ^ index » next coverage.py v7.2.7, created at 2023-07-01 06:54 +0000
1"""Filters for processing ANSI colors within Jinja templates."""
3# Copyright (c) IPython Development Team.
4# Distributed under the terms of the Modified BSD License.
6import re
8import markupsafe
10__all__ = ["strip_ansi", "ansi2html", "ansi2latex"]
12_ANSI_RE = re.compile("\x1b\\[(.*?)([@-~])")
14_ANSI_COLORS = (
15 "ansi-black",
16 "ansi-red",
17 "ansi-green",
18 "ansi-yellow",
19 "ansi-blue",
20 "ansi-magenta",
21 "ansi-cyan",
22 "ansi-white",
23 "ansi-black-intense",
24 "ansi-red-intense",
25 "ansi-green-intense",
26 "ansi-yellow-intense",
27 "ansi-blue-intense",
28 "ansi-magenta-intense",
29 "ansi-cyan-intense",
30 "ansi-white-intense",
31)
34def strip_ansi(source):
35 """
36 Remove ANSI escape codes from text.
38 Parameters
39 ----------
40 source : str
41 Source to remove the ANSI from
43 """
44 return _ANSI_RE.sub("", source)
47def ansi2html(text):
48 """
49 Convert ANSI colors to HTML colors.
51 Parameters
52 ----------
53 text : unicode
54 Text containing ANSI colors to convert to HTML
56 """
57 text = markupsafe.escape(text)
58 return _ansi2anything(text, _htmlconverter)
61def ansi2latex(text):
62 """
63 Convert ANSI colors to LaTeX colors.
65 Parameters
66 ----------
67 text : unicode
68 Text containing ANSI colors to convert to LaTeX
70 """
71 return _ansi2anything(text, _latexconverter)
74def _htmlconverter(fg, bg, bold, underline, inverse): # noqa
75 """
76 Return start and end tags for given foreground/background/bold/underline.
78 """
79 if (fg, bg, bold, underline, inverse) == (None, None, False, False, False):
80 return "", ""
82 classes = []
83 styles = []
85 if inverse:
86 fg, bg = bg, fg
88 if isinstance(fg, int):
89 classes.append(_ANSI_COLORS[fg] + "-fg")
90 elif fg:
91 styles.append("color: rgb({},{},{})".format(*fg))
92 elif inverse:
93 classes.append("ansi-default-inverse-fg")
95 if isinstance(bg, int):
96 classes.append(_ANSI_COLORS[bg] + "-bg")
97 elif bg:
98 styles.append("background-color: rgb({},{},{})".format(*bg))
99 elif inverse:
100 classes.append("ansi-default-inverse-bg")
102 if bold:
103 classes.append("ansi-bold")
105 if underline:
106 classes.append("ansi-underline")
108 starttag = "<span"
109 if classes:
110 starttag += ' class="' + " ".join(classes) + '"'
111 if styles:
112 starttag += ' style="' + "; ".join(styles) + '"'
113 starttag += ">"
114 return starttag, "</span>"
117def _latexconverter(fg, bg, bold, underline, inverse): # noqa
118 """
119 Return start and end markup given foreground/background/bold/underline.
121 """
122 if (fg, bg, bold, underline, inverse) == (None, None, False, False, False):
123 return "", ""
125 starttag, endtag = "", ""
127 if inverse:
128 fg, bg = bg, fg
130 if isinstance(fg, int):
131 starttag += r"\textcolor{" + _ANSI_COLORS[fg] + "}{"
132 endtag = "}" + endtag
133 elif fg:
134 # See http://tex.stackexchange.com/a/291102/13684
135 starttag += r"\def\tcRGB{\textcolor[RGB]}\expandafter"
136 starttag += r"\tcRGB\expandafter{{\detokenize{{{},{},{}}}}}{{".format(*fg)
137 endtag = "}" + endtag
138 elif inverse:
139 starttag += r"\textcolor{ansi-default-inverse-fg}{"
140 endtag = "}" + endtag
142 if isinstance(bg, int):
143 starttag += r"\setlength{\fboxsep}{0pt}"
144 starttag += r"\colorbox{" + _ANSI_COLORS[bg] + "}{"
145 endtag = r"\strut}" + endtag
146 elif bg:
147 starttag += r"\setlength{\fboxsep}{0pt}"
148 # See http://tex.stackexchange.com/a/291102/13684
149 starttag += r"\def\cbRGB{\colorbox[RGB]}\expandafter"
150 starttag += r"\cbRGB\expandafter{{\detokenize{{{},{},{}}}}}{{".format(*bg)
151 endtag = r"\strut}" + endtag
152 elif inverse:
153 starttag += r"\setlength{\fboxsep}{0pt}"
154 starttag += r"\colorbox{ansi-default-inverse-bg}{"
155 endtag = r"\strut}" + endtag
157 if bold:
158 starttag += r"\textbf{"
159 endtag = "}" + endtag
161 if underline:
162 starttag += r"\underline{"
163 endtag = "}" + endtag
165 return starttag, endtag
168def _ansi2anything(text, converter): # noqa
169 r"""
170 Convert ANSI colors to HTML or LaTeX.
172 See https://en.wikipedia.org/wiki/ANSI_escape_code
174 Accepts codes like '\x1b[32m' (red) and '\x1b[1;32m' (bold, red).
176 Non-color escape sequences (not ending with 'm') are filtered out.
178 Ideally, this should have the same behavior as the function
179 fixConsole() in notebook/notebook/static/base/js/utils.js.
181 """
182 fg, bg = None, None
183 bold = False
184 underline = False
185 inverse = False
186 numbers = []
187 out = []
189 while text:
190 m = _ANSI_RE.search(text)
191 if m:
192 if m.group(2) == "m":
193 try:
194 # Empty code is same as code 0
195 numbers = [int(n) if n else 0 for n in m.group(1).split(";")]
196 except ValueError:
197 pass # Invalid color specification
198 else:
199 pass # Not a color code
200 chunk, text = text[: m.start()], text[m.end() :]
201 else:
202 chunk, text = text, ""
204 if chunk:
205 starttag, endtag = converter(
206 fg + 8 if bold and fg in range(8) else fg, # type:ignore
207 bg,
208 bold,
209 underline,
210 inverse,
211 )
212 out.append(starttag)
213 out.append(chunk)
214 out.append(endtag)
216 while numbers:
217 n = numbers.pop(0)
218 if n == 0:
219 # Code 0 (same as empty code): reset everything
220 fg = bg = None
221 bold = underline = inverse = False
222 elif n == 1:
223 bold = True
224 elif n == 4:
225 underline = True
226 elif n == 5:
227 # Code 5: blinking
228 bold = True
229 elif n == 7:
230 inverse = True
231 elif n in (21, 22):
232 bold = False
233 elif n == 24:
234 underline = False
235 elif n == 27:
236 inverse = False
237 elif 30 <= n <= 37:
238 fg = n - 30
239 elif n == 38:
240 try:
241 fg = _get_extended_color(numbers)
242 except ValueError:
243 numbers.clear()
244 elif n == 39:
245 fg = None
246 elif 40 <= n <= 47:
247 bg = n - 40
248 elif n == 48:
249 try:
250 bg = _get_extended_color(numbers)
251 except ValueError:
252 numbers.clear()
253 elif n == 49:
254 bg = None
255 elif 90 <= n <= 97:
256 fg = n - 90 + 8
257 elif 100 <= n <= 107:
258 bg = n - 100 + 8
259 else:
260 pass # Unknown codes are ignored
261 return "".join(out)
264def _get_extended_color(numbers):
265 n = numbers.pop(0)
266 if n == 2 and len(numbers) >= 3:
267 # 24-bit RGB
268 r = numbers.pop(0)
269 g = numbers.pop(0)
270 b = numbers.pop(0)
271 if not all(0 <= c <= 255 for c in (r, g, b)):
272 raise ValueError()
273 elif n == 5 and len(numbers) >= 1:
274 # 256 colors
275 idx = numbers.pop(0)
276 if idx < 0:
277 raise ValueError()
278 elif idx < 16:
279 # 16 default terminal colors
280 return idx
281 elif idx < 232:
282 # 6x6x6 color cube, see http://stackoverflow.com/a/27165165/500098
283 r = (idx - 16) // 36
284 r = 55 + r * 40 if r > 0 else 0
285 g = ((idx - 16) % 36) // 6
286 g = 55 + g * 40 if g > 0 else 0
287 b = (idx - 16) % 6
288 b = 55 + b * 40 if b > 0 else 0
289 elif idx < 256:
290 # grayscale, see http://stackoverflow.com/a/27165165/500098
291 r = g = b = (idx - 232) * 10 + 8
292 else:
293 raise ValueError()
294 else:
295 raise ValueError()
296 return r, g, b