6
6
Formatter for HTML output.
8
:copyright: 2006-2008 by Georg Brandl, Armin Ronacher.
9
:license: BSD, see LICENSE for more details.
8
:copyright: Copyright 2006-2010 by the Pygments team, see AUTHORS.
9
:license: BSD, see LICENSE for details.
17
from sets import Set as set
19
16
from pygments.formatter import Formatter
20
17
from pygments.token import Token, Text, STANDARD_TYPES
21
from pygments.util import get_bool_opt, get_int_opt, get_list_opt
18
from pygments.util import get_bool_opt, get_int_opt, get_list_opt, bytes
24
21
__all__ = ['HtmlFormatter']
27
def escape_html(text):
24
_escape_html_table = {
32
def escape_html(text, table=_escape_html_table):
28
33
"""Escape &, <, > as well as single and double quotes for HTML."""
29
return text.replace('&', '&'). \
30
replace('<', '<'). \
31
replace('>', '>'). \
32
replace('"', '"'). \
34
return text.translate(table)
36
36
def get_random_id():
37
37
"""Return a random id for javascript fields."""
188
188
The style to use, can be a string or a Style subclass (default:
189
``'default'``). This option has no effect if the `cssfile`
190
and `noclobber_cssfile` option are given and the file specified in
192
194
If set to true, token ``<span>`` tags will not use CSS classes, but
223
225
file's path, if the latter can be found. The stylesheet is then written
224
226
to this file instead of the HTML file. *New in Pygments 0.6.*
229
If `cssfile` is given and the specified file exists, the css file will
230
not be overwritten. This allows the use of the `full` option in
231
combination with a user specified css file. Default is ``False``.
232
*New in Pygments 1.1.*
227
235
If set to ``'table'``, output line numbers as a table with two cells,
228
236
one containing the line numbers, the other the whole code. This is
274
282
output line in an anchor tag with a ``name`` of ``foo-linenumber``.
275
283
This allows easy linking to certain lines. *New in Pygments 0.9.*
286
If set to `True`, will wrap line numbers in <a> tags. Used in
287
combination with `linenos` and `lineanchors`.
278
290
**Subclassing the HTML formatter**
331
343
def __init__(self, **options):
332
344
Formatter.__init__(self, **options)
333
self.title = self._encodeifneeded(self.title)
345
self.title = self._decodeifneeded(self.title)
334
346
self.nowrap = get_bool_opt(options, 'nowrap', False)
335
347
self.noclasses = get_bool_opt(options, 'noclasses', False)
336
348
self.classprefix = options.get('classprefix', '')
337
self.cssclass = self._encodeifneeded(options.get('cssclass', 'highlight'))
338
self.cssstyles = self._encodeifneeded(options.get('cssstyles', ''))
339
self.prestyles = self._encodeifneeded(options.get('prestyles', ''))
340
self.cssfile = self._encodeifneeded(options.get('cssfile', ''))
349
self.cssclass = self._decodeifneeded(options.get('cssclass', 'highlight'))
350
self.cssstyles = self._decodeifneeded(options.get('cssstyles', ''))
351
self.prestyles = self._decodeifneeded(options.get('prestyles', ''))
352
self.cssfile = self._decodeifneeded(options.get('cssfile', ''))
353
self.noclobber_cssfile = get_bool_opt(options, 'noclobber_cssfile', False)
341
355
linenos = options.get('linenos', False)
342
356
if linenos == 'inline':
352
366
self.nobackground = get_bool_opt(options, 'nobackground', False)
353
367
self.lineseparator = options.get('lineseparator', '\n')
354
368
self.lineanchors = options.get('lineanchors', '')
369
self.anchorlinenos = options.get('anchorlinenos', False)
355
370
self.hl_lines = set()
356
371
for lineno in get_list_opt(options, 'hl_lines', []):
359
374
except ValueError:
362
self._class_cache = {}
363
377
self._create_stylesheet()
365
379
def _get_css_class(self, ttype):
366
380
"""Return the css class of this token type prefixed with
367
381
the classprefix option."""
368
if ttype in self._class_cache:
369
return self._class_cache[ttype]
370
return self.classprefix + _get_ttype_class(ttype)
382
ttypeclass = _get_ttype_class(ttype)
384
return self.classprefix + ttypeclass
372
387
def _create_stylesheet(self):
373
388
t2c = self.ttype2class = {Token: ''}
374
389
c2s = self.class2style = {}
375
cp = self.classprefix
376
390
for ttype, ndef in self.style:
377
name = cp + _get_ttype_class(ttype)
391
name = self._get_css_class(ttype)
379
393
if ndef['color']:
380
394
style += 'color: #%s; ' % ndef['color']
433
447
(prefix(''), self.style.highlight_color))
434
448
return '\n'.join(lines)
436
def _encodeifneeded(self, value):
437
if not self.encoding or isinstance(value, str):
439
return value.encode(self.encoding)
450
def _decodeifneeded(self, value):
451
if isinstance(value, bytes):
453
return value.decode(self.encoding)
454
return value.decode()
441
457
def _wrap_full(self, inner, outfile):
449
465
if not filename or filename[0] == '<':
450
466
# pseudo files, e.g. name == '<fdopen>'
451
467
raise AttributeError
452
cssfilename = os.path.join(os.path.dirname(filename), self.cssfile)
468
cssfilename = os.path.join(os.path.dirname(filename),
453
470
except AttributeError:
454
471
print >>sys.stderr, 'Note: Cannot determine output file name, ' \
455
472
'using current directory as base for the CSS file name'
456
473
cssfilename = self.cssfile
474
# write CSS file only if noclobber_cssfile isn't given as an option.
459
cf = open(cssfilename, "w")
460
cf.write(CSSFILE_TEMPLATE %
461
{'styledefs': self.get_style_defs('body')})
476
if not os.path.exists(cssfilename) or not self.noclobber_cssfile:
477
cf = open(cssfilename, "w")
478
cf.write(CSSFILE_TEMPLATE %
479
{'styledefs': self.get_style_defs('body')})
463
481
except IOError, err:
464
482
err.strerror = 'Error writing CSS file: ' + err.strerror
490
508
mw = len(str(lncount + fl - 1))
491
509
sp = self.linenospecial
492
510
st = self.linenostep
511
la = self.lineanchors
512
aln = self.anchorlinenos
513
nocls = self.noclasses
494
ls = '\n'.join([(i%st == 0 and
495
(i%sp == 0 and '<span class="special">%*d</span>'
498
for i in range(fl, fl + lncount)])
500
ls = '\n'.join([(i%st == 0 and ('%*d' % (mw, i)) or '')
501
for i in range(fl, fl + lncount)])
503
yield 0, ('<table class="%stable">' % self.cssclass +
504
'<tr><td class="linenos"><pre>' +
505
ls + '</pre></td><td class="code">')
517
for i in range(fl, fl+lncount):
521
lines.append('<a href="#%s-%d" class="special">%*d</a>' %
524
lines.append('<span class="special">%*d</span>' % (mw, i))
527
lines.append('<a href="#%s-%d">%*d</a>' % (la, i, mw, i))
529
lines.append('%*d' % (mw, i))
532
ls = '\n'.join(lines)
535
for i in range(fl, fl+lncount):
538
lines.append('<a href="#%s-%d">%*d</a>' % (la, i, mw, i))
540
lines.append('%*d' % (mw, i))
543
ls = '\n'.join(lines)
545
# in case you wonder about the seemingly redundant <div> here: since the
546
# content in the other cell also is wrapped in a div, some browsers in
547
# some configurations seem to mess up the formatting...
549
yield 0, ('<table class="%stable">' % self.cssclass +
550
'<tr><td><div class="linenodiv" '
551
'style="background-color: #f0f0f0; padding-right: 10px">'
552
'<pre style="line-height: 125%">' +
553
ls + '</pre></div></td><td class="code">')
555
yield 0, ('<table class="%stable">' % self.cssclass +
556
'<tr><td class="linenos"><div class="linenodiv"><pre>' +
557
ls + '</pre></div></td><td class="code">')
506
558
yield 0, dummyoutfile.getvalue()
507
559
yield 0, '</td></tr></table>'
514
566
num = self.linenostart
515
567
mw = len(str(len(lines) + num - 1))
571
for t, line in lines:
573
style = 'background-color: #ffffc0; padding: 0 5px 0 5px'
575
style = 'background-color: #f0f0f0; padding: 0 5px 0 5px'
576
yield 1, '<span style="%s">%*s</span> ' % (
577
style, mw, (num%st and ' ' or num)) + line
580
for t, line in lines:
581
yield 1, ('<span style="background-color: #f0f0f0; '
582
'padding: 0 5px 0 5px">%*s</span> ' % (
583
mw, (num%st and ' ' or num)) + line)
518
586
for t, line in lines:
519
587
yield 1, '<span class="lineno%s">%*s</span> ' % (
520
num%sp == 0 and ' special' or '', mw, (num%st and ' ' or num)) + line
588
num%sp == 0 and ' special' or '', mw,
589
(num%st and ' ' or num)) + line
523
592
for t, line in lines:
538
607
def _wrap_div(self, inner):
609
if (self.noclasses and not self.nobackground and
610
self.style.background_color is not None):
611
style.append('background: %s' % (self.style.background_color,))
613
style.append(self.cssstyles)
614
style = '; '.join(style)
539
616
yield 0, ('<div' + (self.cssclass and ' class="%s"' % self.cssclass)
540
+ (self.cssstyles and ' style="%s"' % self.cssstyles) + '>')
617
+ (style and (' style="%s"' % style)) + '>')
541
618
for tup in inner:
543
620
yield 0, '</div>\n'
545
622
def _wrap_pre(self, inner):
547
+ (self.prestyles and ' style="%s"' % self.prestyles) + '>')
625
style.append(self.prestyles)
627
style.append('line-height: 125%')
628
style = '; '.join(style)
630
yield 0, ('<pre' + (style and ' style="%s"' % style) + '>')
548
631
for tup in inner:
550
633
yield 0, '</pre>'
574
657
cls = self._get_css_class(ttype)
575
658
cspan = cls and '<span class="%s">' % cls or ''
578
value = value.encode(enc)
580
parts = escape_html(value).split('\n')
660
parts = value.translate(escape_table).split('\n')
582
662
# for all but the last line
583
663
for part in parts[:-1]:
621
701
if i + 1 in hls: # i + 1 because Python indexes start at 0
622
yield 1, '<span class="hll">%s</span>' % value
704
if self.style.highlight_color is not None:
705
style = (' style="background-color: %s"' %
706
(self.style.highlight_color,))
707
yield 1, '<span%s>%s</span>' % (style, value)
709
yield 1, '<span class="hll">%s</span>' % value
632
719
return self._wrap_div(self._wrap_pre(source))
634
def format(self, tokensource, outfile):
721
def format_unencoded(self, tokensource, outfile):
636
723
The formatting process uses several nested generators; which of
637
724
them are used is determined by the user's options.