3 pygments.formatters.latex |
3 pygments.formatters.latex |
4 ~~~~~~~~~~~~~~~~~~~~~~~~~ |
4 ~~~~~~~~~~~~~~~~~~~~~~~~~ |
5 |
5 |
6 Formatter for LaTeX fancyvrb output. |
6 Formatter for LaTeX fancyvrb output. |
7 |
7 |
8 :copyright: Copyright 2006-2013 by the Pygments team, see AUTHORS. |
8 :copyright: Copyright 2006-2014 by the Pygments team, see AUTHORS. |
9 :license: BSD, see LICENSE for details. |
9 :license: BSD, see LICENSE for details. |
10 """ |
10 """ |
11 |
11 |
|
12 from __future__ import division |
|
13 |
12 from pygments.formatter import Formatter |
14 from pygments.formatter import Formatter |
|
15 from pygments.lexer import Lexer |
13 from pygments.token import Token, STANDARD_TYPES |
16 from pygments.token import Token, STANDARD_TYPES |
14 from pygments.util import get_bool_opt, get_int_opt, StringIO |
17 from pygments.util import get_bool_opt, get_int_opt, StringIO, xrange, \ |
|
18 iteritems |
15 |
19 |
16 |
20 |
17 __all__ = ['LatexFormatter'] |
21 __all__ = ['LatexFormatter'] |
18 |
22 |
19 |
23 |
150 Without the `full` option, code is formatted as one ``Verbatim`` |
154 Without the `full` option, code is formatted as one ``Verbatim`` |
151 environment, like this: |
155 environment, like this: |
152 |
156 |
153 .. sourcecode:: latex |
157 .. sourcecode:: latex |
154 |
158 |
155 \begin{Verbatim}[commandchars=\\{\}] |
159 \begin{Verbatim}[commandchars=\\\{\}] |
156 \PY{k}{def }\PY{n+nf}{foo}(\PY{n}{bar}): |
160 \PY{k}{def }\PY{n+nf}{foo}(\PY{n}{bar}): |
157 \PY{k}{pass} |
161 \PY{k}{pass} |
158 \end{Verbatim} |
162 \end{Verbatim} |
159 |
163 |
160 The special command used here (``\PY``) and all the other macros it needs |
164 The special command used here (``\PY``) and all the other macros it needs |
203 docs for possible values) (default: ``''``). |
207 docs for possible values) (default: ``''``). |
204 |
208 |
205 `commandprefix` |
209 `commandprefix` |
206 The LaTeX commands used to produce colored output are constructed |
210 The LaTeX commands used to produce colored output are constructed |
207 using this prefix and some letters (default: ``'PY'``). |
211 using this prefix and some letters (default: ``'PY'``). |
208 *New in Pygments 0.7.* |
212 |
209 |
213 .. versionadded:: 0.7 |
210 *New in Pygments 0.10:* the default is now ``'PY'`` instead of ``'C'``. |
214 .. versionchanged:: 0.10 |
|
215 The default is now ``'PY'`` instead of ``'C'``. |
211 |
216 |
212 `texcomments` |
217 `texcomments` |
213 If set to ``True``, enables LaTeX comment lines. That is, LaTex markup |
218 If set to ``True``, enables LaTeX comment lines. That is, LaTex markup |
214 in comment tokens is not escaped so that LaTeX can render it (default: |
219 in comment tokens is not escaped so that LaTeX can render it (default: |
215 ``False``). *New in Pygments 1.2.* |
220 ``False``). |
|
221 |
|
222 .. versionadded:: 1.2 |
216 |
223 |
217 `mathescape` |
224 `mathescape` |
218 If set to ``True``, enables LaTeX math mode escape in comments. That |
225 If set to ``True``, enables LaTeX math mode escape in comments. That |
219 is, ``'$...$'`` inside a comment will trigger math mode (default: |
226 is, ``'$...$'`` inside a comment will trigger math mode (default: |
220 ``False``). *New in Pygments 1.2.* |
227 ``False``). |
|
228 |
|
229 .. versionadded:: 1.2 |
|
230 |
|
231 `escapeinside` |
|
232 If set to a string of length 2, enables escaping to LaTeX. Text |
|
233 delimited by these 2 characters is read as LaTeX code and |
|
234 typeset accordingly. It has no effect in string literals. It has |
|
235 no effect in comments if `texcomments` or `mathescape` is |
|
236 set. (default: ``''``). |
|
237 |
|
238 .. versionadded:: 2.0 |
|
239 |
|
240 `envname` |
|
241 Allows you to pick an alternative environment name replacing Verbatim. |
|
242 The alternate environment still has to support Verbatim's option syntax. |
|
243 (default: ``'Verbatim'``). |
|
244 |
|
245 .. versionadded:: 2.0 |
221 """ |
246 """ |
222 name = 'LaTeX' |
247 name = 'LaTeX' |
223 aliases = ['latex', 'tex'] |
248 aliases = ['latex', 'tex'] |
224 filenames = ['*.tex'] |
249 filenames = ['*.tex'] |
225 |
250 |
233 self.verboptions = options.get('verboptions', '') |
258 self.verboptions = options.get('verboptions', '') |
234 self.nobackground = get_bool_opt(options, 'nobackground', False) |
259 self.nobackground = get_bool_opt(options, 'nobackground', False) |
235 self.commandprefix = options.get('commandprefix', 'PY') |
260 self.commandprefix = options.get('commandprefix', 'PY') |
236 self.texcomments = get_bool_opt(options, 'texcomments', False) |
261 self.texcomments = get_bool_opt(options, 'texcomments', False) |
237 self.mathescape = get_bool_opt(options, 'mathescape', False) |
262 self.mathescape = get_bool_opt(options, 'mathescape', False) |
|
263 self.escapeinside = options.get('escapeinside', '') |
|
264 if len(self.escapeinside) == 2: |
|
265 self.left = self.escapeinside[0] |
|
266 self.right = self.escapeinside[1] |
|
267 else: |
|
268 self.escapeinside = '' |
|
269 self.envname = options.get('envname', u'Verbatim') |
238 |
270 |
239 self._create_stylesheet() |
271 self._create_stylesheet() |
240 |
|
241 |
272 |
242 def _create_stylesheet(self): |
273 def _create_stylesheet(self): |
243 t2n = self.ttype2name = {Token: ''} |
274 t2n = self.ttype2name = {Token: ''} |
244 c2d = self.cmd2def = {} |
275 c2d = self.cmd2def = {} |
245 cp = self.commandprefix |
276 cp = self.commandprefix |
246 |
277 |
247 def rgbcolor(col): |
278 def rgbcolor(col): |
248 if col: |
279 if col: |
249 return ','.join(['%.2f' %(int(col[i] + col[i + 1], 16) / 255.0) |
280 return ','.join(['%.2f' % (int(col[i] + col[i + 1], 16) / 255.0) |
250 for i in (0, 2, 4)]) |
281 for i in (0, 2, 4)]) |
251 else: |
282 else: |
252 return '1,1,1' |
283 return '1,1,1' |
253 |
284 |
254 for ttype, ndef in self.style: |
285 for ttype, ndef in self.style: |
289 Return the command sequences needed to define the commands |
320 Return the command sequences needed to define the commands |
290 used to format text in the verbatim environment. ``arg`` is ignored. |
321 used to format text in the verbatim environment. ``arg`` is ignored. |
291 """ |
322 """ |
292 cp = self.commandprefix |
323 cp = self.commandprefix |
293 styles = [] |
324 styles = [] |
294 for name, definition in self.cmd2def.items(): |
325 for name, definition in iteritems(self.cmd2def): |
295 styles.append(r'\expandafter\def\csname %s@tok@%s\endcsname{%s}' % |
326 styles.append(r'\expandafter\def\csname %s@tok@%s\endcsname{%s}' % |
296 (cp, name, definition)) |
327 (cp, name, definition)) |
297 return STYLE_TEMPLATE % {'cp': self.commandprefix, |
328 return STYLE_TEMPLATE % {'cp': self.commandprefix, |
298 'styles': '\n'.join(styles)} |
329 'styles': '\n'.join(styles)} |
299 |
330 |
304 |
335 |
305 if self.full: |
336 if self.full: |
306 realoutfile = outfile |
337 realoutfile = outfile |
307 outfile = StringIO() |
338 outfile = StringIO() |
308 |
339 |
309 outfile.write(r'\begin{Verbatim}[commandchars=\\\{\}') |
340 outfile.write(u'\\begin{' + self.envname + u'}[commandchars=\\\\\\{\\}') |
310 if self.linenos: |
341 if self.linenos: |
311 start, step = self.linenostart, self.linenostep |
342 start, step = self.linenostart, self.linenostep |
312 outfile.write(',numbers=left' + |
343 outfile.write(u',numbers=left' + |
313 (start and ',firstnumber=%d' % start or '') + |
344 (start and u',firstnumber=%d' % start or u'') + |
314 (step and ',stepnumber=%d' % step or '')) |
345 (step and u',stepnumber=%d' % step or u'')) |
315 if self.mathescape or self.texcomments: |
346 if self.mathescape or self.texcomments or self.escapeinside: |
316 outfile.write(r',codes={\catcode`\$=3\catcode`\^=7\catcode`\_=8}') |
347 outfile.write(u',codes={\\catcode`\\$=3\\catcode`\\^=7\\catcode`\\_=8}') |
317 if self.verboptions: |
348 if self.verboptions: |
318 outfile.write(',' + self.verboptions) |
349 outfile.write(u',' + self.verboptions) |
319 outfile.write(']\n') |
350 outfile.write(u']\n') |
320 |
351 |
321 for ttype, value in tokensource: |
352 for ttype, value in tokensource: |
322 if ttype in Token.Comment: |
353 if ttype in Token.Comment: |
323 if self.texcomments: |
354 if self.texcomments: |
324 # Try to guess comment starting lexeme and escape it ... |
355 # Try to guess comment starting lexeme and escape it ... |
325 start = value[0:1] |
356 start = value[0:1] |
326 for i in range(1, len(value)): |
357 for i in xrange(1, len(value)): |
327 if start[0] != value[i]: |
358 if start[0] != value[i]: |
328 break |
359 break |
329 start += value[i] |
360 start += value[i] |
330 |
361 |
331 value = value[len(start):] |
362 value = value[len(start):] |
340 for i, part in enumerate(parts): |
371 for i, part in enumerate(parts): |
341 if not in_math: |
372 if not in_math: |
342 parts[i] = escape_tex(part, self.commandprefix) |
373 parts[i] = escape_tex(part, self.commandprefix) |
343 in_math = not in_math |
374 in_math = not in_math |
344 value = '$'.join(parts) |
375 value = '$'.join(parts) |
|
376 elif self.escapeinside: |
|
377 text = value |
|
378 value = '' |
|
379 while len(text) > 0: |
|
380 a, sep1, text = text.partition(self.left) |
|
381 if len(sep1) > 0: |
|
382 b, sep2, text = text.partition(self.right) |
|
383 if len(sep2) > 0: |
|
384 value += escape_tex(a, self.commandprefix) + b |
|
385 else: |
|
386 value += escape_tex(a + sep1 + b, self.commandprefix) |
|
387 else: |
|
388 value = value + escape_tex(a, self.commandprefix) |
345 else: |
389 else: |
346 value = escape_tex(value, self.commandprefix) |
390 value = escape_tex(value, self.commandprefix) |
347 else: |
391 elif ttype not in Token.Escape: |
348 value = escape_tex(value, self.commandprefix) |
392 value = escape_tex(value, self.commandprefix) |
349 styles = [] |
393 styles = [] |
350 while ttype is not Token: |
394 while ttype is not Token: |
351 try: |
395 try: |
352 styles.append(t2n[ttype]) |
396 styles.append(t2n[ttype]) |
364 if spl[-1]: |
408 if spl[-1]: |
365 outfile.write("\\%s{%s}{%s}" % (cp, styleval, spl[-1])) |
409 outfile.write("\\%s{%s}{%s}" % (cp, styleval, spl[-1])) |
366 else: |
410 else: |
367 outfile.write(value) |
411 outfile.write(value) |
368 |
412 |
369 outfile.write('\\end{Verbatim}\n') |
413 outfile.write(u'\\end{' + self.envname + u'}\n') |
370 |
414 |
371 if self.full: |
415 if self.full: |
372 realoutfile.write(DOC_TEMPLATE % |
416 realoutfile.write(DOC_TEMPLATE % |
373 dict(docclass = self.docclass, |
417 dict(docclass = self.docclass, |
374 preamble = self.preamble, |
418 preamble = self.preamble, |
375 title = self.title, |
419 title = self.title, |
376 encoding = self.encoding or 'latin1', |
420 encoding = self.encoding or 'utf8', |
377 styledefs = self.get_style_defs(), |
421 styledefs = self.get_style_defs(), |
378 code = outfile.getvalue())) |
422 code = outfile.getvalue())) |
|
423 |
|
424 |
|
425 class LatexEmbeddedLexer(Lexer): |
|
426 r""" |
|
427 |
|
428 This lexer takes one lexer as argument, the lexer for the language |
|
429 being formatted, and the left and right delimiters for escaped text. |
|
430 |
|
431 First everything is scanned using the language lexer to obtain |
|
432 strings and comments. All other consecutive tokens are merged and |
|
433 the resulting text is scanned for escaped segments, which are given |
|
434 the Token.Escape type. Finally text that is not escaped is scanned |
|
435 again with the language lexer. |
|
436 """ |
|
437 def __init__(self, left, right, lang, **options): |
|
438 self.left = left |
|
439 self.right = right |
|
440 self.lang = lang |
|
441 Lexer.__init__(self, **options) |
|
442 |
|
443 def get_tokens_unprocessed(self, text): |
|
444 buf = '' |
|
445 idx = 0 |
|
446 for i, t, v in self.lang.get_tokens_unprocessed(text): |
|
447 if t in Token.Comment or t in Token.String: |
|
448 if buf: |
|
449 for x in self.get_tokens_aux(idx, buf): |
|
450 yield x |
|
451 buf = '' |
|
452 yield i, t, v |
|
453 else: |
|
454 if not buf: |
|
455 idx = i |
|
456 buf += v |
|
457 if buf: |
|
458 for x in self.get_tokens_aux(idx, buf): |
|
459 yield x |
|
460 |
|
461 def get_tokens_aux(self, index, text): |
|
462 while text: |
|
463 a, sep1, text = text.partition(self.left) |
|
464 if a: |
|
465 for i, t, v in self.lang.get_tokens_unprocessed(a): |
|
466 yield index + i, t, v |
|
467 index += len(a) |
|
468 if sep1: |
|
469 b, sep2, text = text.partition(self.right) |
|
470 if sep2: |
|
471 yield index + len(sep1), Token.Escape, b |
|
472 index += len(sep1) + len(b) + len(sep2) |
|
473 else: |
|
474 yield index, Token.Error, sep1 |
|
475 index += len(sep1) |
|
476 text = b |