Back to index

moin  1.9.0~rc2
html.py
Go to the documentation of this file.
00001 # -*- coding: utf-8 -*-
00002 """
00003     pygments.formatters.html
00004     ~~~~~~~~~~~~~~~~~~~~~~~~
00005 
00006     Formatter for HTML output.
00007 
00008     :copyright: Copyright 2006-2009 by the Pygments team, see AUTHORS.
00009     :license: BSD, see LICENSE for details.
00010 """
00011 import sys, os
00012 import StringIO
00013 
00014 try:
00015     set
00016 except NameError:
00017     from sets import Set as set
00018 
00019 from pygments.formatter import Formatter
00020 from pygments.token import Token, Text, STANDARD_TYPES
00021 from pygments.util import get_bool_opt, get_int_opt, get_list_opt, bytes
00022 
00023 
00024 __all__ = ['HtmlFormatter']
00025 
00026 
00027 def escape_html(text):
00028     """Escape &, <, > as well as single and double quotes for HTML."""
00029     return text.replace('&', '&amp;').  \
00030                 replace('<', '&lt;').   \
00031                 replace('>', '&gt;').   \
00032                 replace('"', '&quot;'). \
00033                 replace("'", '&#39;')
00034 
00035 
00036 def get_random_id():
00037     """Return a random id for javascript fields."""
00038     from random import random
00039     from time import time
00040     try:
00041         from hashlib import sha1 as sha
00042     except ImportError:
00043         import sha
00044         sha = sha.new
00045     return sha('%s|%s' % (random(), time())).hexdigest()
00046 
00047 
00048 def _get_ttype_class(ttype):
00049     fname = STANDARD_TYPES.get(ttype)
00050     if fname:
00051         return fname
00052     aname = ''
00053     while fname is None:
00054         aname = '-' + ttype[-1] + aname
00055         ttype = ttype.parent
00056         fname = STANDARD_TYPES.get(ttype)
00057     return fname + aname
00058 
00059 
00060 CSSFILE_TEMPLATE = '''\
00061 td.linenos { background-color: #f0f0f0; padding-right: 10px; }
00062 span.lineno { background-color: #f0f0f0; padding: 0 5px 0 5px; }
00063 pre { line-height: 125%%; }
00064 %(styledefs)s
00065 '''
00066 
00067 DOC_HEADER = '''\
00068 <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
00069    "http://www.w3.org/TR/html4/strict.dtd">
00070 
00071 <html>
00072 <head>
00073   <title>%(title)s</title>
00074   <meta http-equiv="content-type" content="text/html; charset=%(encoding)s">
00075   <style type="text/css">
00076 ''' + CSSFILE_TEMPLATE + '''
00077   </style>
00078 </head>
00079 <body>
00080 <h2>%(title)s</h2>
00081 
00082 '''
00083 
00084 DOC_HEADER_EXTERNALCSS = '''\
00085 <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
00086    "http://www.w3.org/TR/html4/strict.dtd">
00087 
00088 <html>
00089 <head>
00090   <title>%(title)s</title>
00091   <meta http-equiv="content-type" content="text/html; charset=%(encoding)s">
00092   <link rel="stylesheet" href="%(cssfile)s" type="text/css">
00093 </head>
00094 <body>
00095 <h2>%(title)s</h2>
00096 
00097 '''
00098 
00099 DOC_FOOTER = '''\
00100 </body>
00101 </html>
00102 '''
00103 
00104 
00105 class HtmlFormatter(Formatter):
00106     r"""
00107     Format tokens as HTML 4 ``<span>`` tags within a ``<pre>`` tag, wrapped
00108     in a ``<div>`` tag. The ``<div>``'s CSS class can be set by the `cssclass`
00109     option.
00110 
00111     If the `linenos` option is set to ``"table"``, the ``<pre>`` is
00112     additionally wrapped inside a ``<table>`` which has one row and two
00113     cells: one containing the line numbers and one containing the code.
00114     Example:
00115 
00116     .. sourcecode:: html
00117 
00118         <div class="highlight" >
00119         <table><tr>
00120           <td class="linenos" title="click to toggle"
00121             onclick="with (this.firstChild.style)
00122                      { display = (display == '') ? 'none' : '' }">
00123             <pre>1
00124             2</pre>
00125           </td>
00126           <td class="code">
00127             <pre><span class="Ke">def </span><span class="NaFu">foo</span>(bar):
00128               <span class="Ke">pass</span>
00129             </pre>
00130           </td>
00131         </tr></table></div>
00132 
00133     (whitespace added to improve clarity).
00134 
00135     Wrapping can be disabled using the `nowrap` option.
00136 
00137     A list of lines can be specified using the `hl_lines` option to make these
00138     lines highlighted (as of Pygments 0.11).
00139 
00140     With the `full` option, a complete HTML 4 document is output, including
00141     the style definitions inside a ``<style>`` tag, or in a separate file if
00142     the `cssfile` option is given.
00143 
00144     The `get_style_defs(arg='')` method of a `HtmlFormatter` returns a string
00145     containing CSS rules for the CSS classes used by the formatter. The
00146     argument `arg` can be used to specify additional CSS selectors that
00147     are prepended to the classes. A call `fmter.get_style_defs('td .code')`
00148     would result in the following CSS classes:
00149 
00150     .. sourcecode:: css
00151 
00152         td .code .kw { font-weight: bold; color: #00FF00 }
00153         td .code .cm { color: #999999 }
00154         ...
00155 
00156     If you have Pygments 0.6 or higher, you can also pass a list or tuple to the
00157     `get_style_defs()` method to request multiple prefixes for the tokens:
00158 
00159     .. sourcecode:: python
00160 
00161         formatter.get_style_defs(['div.syntax pre', 'pre.syntax'])
00162 
00163     The output would then look like this:
00164 
00165     .. sourcecode:: css
00166 
00167         div.syntax pre .kw,
00168         pre.syntax .kw { font-weight: bold; color: #00FF00 }
00169         div.syntax pre .cm,
00170         pre.syntax .cm { color: #999999 }
00171         ...
00172 
00173     Additional options accepted:
00174 
00175     `nowrap`
00176         If set to ``True``, don't wrap the tokens at all, not even inside a ``<pre>``
00177         tag. This disables most other options (default: ``False``).
00178 
00179     `full`
00180         Tells the formatter to output a "full" document, i.e. a complete
00181         self-contained document (default: ``False``).
00182 
00183     `title`
00184         If `full` is true, the title that should be used to caption the
00185         document (default: ``''``).
00186 
00187     `style`
00188         The style to use, can be a string or a Style subclass (default:
00189         ``'default'``). This option has no effect if the `cssfile`
00190         and `noclobber_cssfile` option are given and the file specified in
00191         `cssfile` exists.
00192 
00193     `noclasses`
00194         If set to true, token ``<span>`` tags will not use CSS classes, but
00195         inline styles. This is not recommended for larger pieces of code since
00196         it increases output size by quite a bit (default: ``False``).
00197 
00198     `classprefix`
00199         Since the token types use relatively short class names, they may clash
00200         with some of your own class names. In this case you can use the
00201         `classprefix` option to give a string to prepend to all Pygments-generated
00202         CSS class names for token types.
00203         Note that this option also affects the output of `get_style_defs()`.
00204 
00205     `cssclass`
00206         CSS class for the wrapping ``<div>`` tag (default: ``'highlight'``).
00207         If you set this option, the default selector for `get_style_defs()`
00208         will be this class.
00209 
00210         *New in Pygments 0.9:* If you select the ``'table'`` line numbers, the
00211         wrapping table will have a CSS class of this string plus ``'table'``,
00212         the default is accordingly ``'highlighttable'``.
00213 
00214     `cssstyles`
00215         Inline CSS styles for the wrapping ``<div>`` tag (default: ``''``).
00216 
00217     `prestyles`
00218         Inline CSS styles for the ``<pre>`` tag (default: ``''``).  *New in
00219         Pygments 0.11.*
00220 
00221     `cssfile`
00222         If the `full` option is true and this option is given, it must be the
00223         name of an external file. If the filename does not include an absolute
00224         path, the file's path will be assumed to be relative to the main output
00225         file's path, if the latter can be found. The stylesheet is then written
00226         to this file instead of the HTML file. *New in Pygments 0.6.*
00227 
00228     `noclobber_cssfile`
00229         If `cssfile` is given and the specified file exists, the css file will
00230         not be overwritten. This allows the use of the `full` option in
00231         combination with a user specified css file. Default is ``False``.
00232         *New in Pygments 1.1.*
00233 
00234     `linenos`
00235         If set to ``'table'``, output line numbers as a table with two cells,
00236         one containing the line numbers, the other the whole code.  This is
00237         copy-and-paste-friendly, but may cause alignment problems with some
00238         browsers or fonts.  If set to ``'inline'``, the line numbers will be
00239         integrated in the ``<pre>`` tag that contains the code (that setting
00240         is *new in Pygments 0.8*).
00241 
00242         For compatibility with Pygments 0.7 and earlier, every true value
00243         except ``'inline'`` means the same as ``'table'`` (in particular, that
00244         means also ``True``).
00245 
00246         The default value is ``False``, which means no line numbers at all.
00247 
00248         **Note:** with the default ("table") line number mechanism, the line
00249         numbers and code can have different line heights in Internet Explorer
00250         unless you give the enclosing ``<pre>`` tags an explicit ``line-height``
00251         CSS property (you get the default line spacing with ``line-height:
00252         125%``).
00253 
00254     `hl_lines`
00255         Specify a list of lines to be highlighted.  *New in Pygments 0.11.*
00256 
00257     `linenostart`
00258         The line number for the first line (default: ``1``).
00259 
00260     `linenostep`
00261         If set to a number n > 1, only every nth line number is printed.
00262 
00263     `linenospecial`
00264         If set to a number n > 0, every nth line number is given the CSS
00265         class ``"special"`` (default: ``0``).
00266 
00267     `nobackground`
00268         If set to ``True``, the formatter won't output the background color
00269         for the wrapping element (this automatically defaults to ``False``
00270         when there is no wrapping element [eg: no argument for the
00271         `get_syntax_defs` method given]) (default: ``False``). *New in
00272         Pygments 0.6.*
00273 
00274     `lineseparator`
00275         This string is output between lines of code. It defaults to ``"\n"``,
00276         which is enough to break a line inside ``<pre>`` tags, but you can
00277         e.g. set it to ``"<br>"`` to get HTML line breaks. *New in Pygments
00278         0.7.*
00279 
00280     `lineanchors`
00281         If set to a nonempty string, e.g. ``foo``, the formatter will wrap each
00282         output line in an anchor tag with a ``name`` of ``foo-linenumber``.
00283         This allows easy linking to certain lines. *New in Pygments 0.9.*
00284 
00285     `anchorlinenos`
00286         If set to `True`, will wrap line numbers in <a> tags. Used in
00287         combination with `linenos` and `lineanchors`.
00288 
00289 
00290     **Subclassing the HTML formatter**
00291 
00292     *New in Pygments 0.7.*
00293 
00294     The HTML formatter is now built in a way that allows easy subclassing, thus
00295     customizing the output HTML code. The `format()` method calls
00296     `self._format_lines()` which returns a generator that yields tuples of ``(1,
00297     line)``, where the ``1`` indicates that the ``line`` is a line of the
00298     formatted source code.
00299 
00300     If the `nowrap` option is set, the generator is the iterated over and the
00301     resulting HTML is output.
00302 
00303     Otherwise, `format()` calls `self.wrap()`, which wraps the generator with
00304     other generators. These may add some HTML code to the one generated by
00305     `_format_lines()`, either by modifying the lines generated by the latter,
00306     then yielding them again with ``(1, line)``, and/or by yielding other HTML
00307     code before or after the lines, with ``(0, html)``. The distinction between
00308     source lines and other code makes it possible to wrap the generator multiple
00309     times.
00310 
00311     The default `wrap()` implementation adds a ``<div>`` and a ``<pre>`` tag.
00312 
00313     A custom `HtmlFormatter` subclass could look like this:
00314 
00315     .. sourcecode:: python
00316 
00317         class CodeHtmlFormatter(HtmlFormatter):
00318 
00319             def wrap(self, source, outfile):
00320                 return self._wrap_code(source)
00321 
00322             def _wrap_code(self, source):
00323                 yield 0, '<code>'
00324                 for i, t in source:
00325                     if i == 1:
00326                         # it's a line of formatted code
00327                         t += '<br>'
00328                     yield i, t
00329                 yield 0, '</code>'
00330 
00331     This results in wrapping the formatted lines with a ``<code>`` tag, where the
00332     source lines are broken using ``<br>`` tags.
00333 
00334     After calling `wrap()`, the `format()` method also adds the "line numbers"
00335     and/or "full document" wrappers if the respective options are set. Then, all
00336     HTML yielded by the wrapped generator is output.
00337     """
00338 
00339     name = 'HTML'
00340     aliases = ['html']
00341     filenames = ['*.html', '*.htm']
00342 
00343     def __init__(self, **options):
00344         Formatter.__init__(self, **options)
00345         self.title = self._decodeifneeded(self.title)
00346         self.nowrap = get_bool_opt(options, 'nowrap', False)
00347         self.noclasses = get_bool_opt(options, 'noclasses', False)
00348         self.classprefix = options.get('classprefix', '')
00349         self.cssclass = self._decodeifneeded(options.get('cssclass', 'highlight'))
00350         self.cssstyles = self._decodeifneeded(options.get('cssstyles', ''))
00351         self.prestyles = self._decodeifneeded(options.get('prestyles', ''))
00352         self.cssfile = self._decodeifneeded(options.get('cssfile', ''))
00353         self.noclobber_cssfile = get_bool_opt(options, 'noclobber_cssfile', False)
00354 
00355         linenos = options.get('linenos', False)
00356         if linenos == 'inline':
00357             self.linenos = 2
00358         elif linenos:
00359             # compatibility with <= 0.7
00360             self.linenos = 1
00361         else:
00362             self.linenos = 0
00363         self.linenostart = abs(get_int_opt(options, 'linenostart', 1))
00364         self.linenostep = abs(get_int_opt(options, 'linenostep', 1))
00365         self.linenospecial = abs(get_int_opt(options, 'linenospecial', 0))
00366         self.nobackground = get_bool_opt(options, 'nobackground', False)
00367         self.lineseparator = options.get('lineseparator', '\n')
00368         self.lineanchors = options.get('lineanchors', '')
00369         self.anchorlinenos = options.get('anchorlinenos', False)
00370         self.hl_lines = set()
00371         for lineno in get_list_opt(options, 'hl_lines', []):
00372             try:
00373                 self.hl_lines.add(int(lineno))
00374             except ValueError:
00375                 pass
00376 
00377         self._class_cache = {}
00378         self._create_stylesheet()
00379 
00380     def _get_css_class(self, ttype):
00381         """Return the css class of this token type prefixed with
00382         the classprefix option."""
00383         if ttype in self._class_cache:
00384             return self._class_cache[ttype]
00385         return self.classprefix + _get_ttype_class(ttype)
00386 
00387     def _create_stylesheet(self):
00388         t2c = self.ttype2class = {Token: ''}
00389         c2s = self.class2style = {}
00390         cp = self.classprefix
00391         for ttype, ndef in self.style:
00392             name = cp + _get_ttype_class(ttype)
00393             style = ''
00394             if ndef['color']:
00395                 style += 'color: #%s; ' % ndef['color']
00396             if ndef['bold']:
00397                 style += 'font-weight: bold; '
00398             if ndef['italic']:
00399                 style += 'font-style: italic; '
00400             if ndef['underline']:
00401                 style += 'text-decoration: underline; '
00402             if ndef['bgcolor']:
00403                 style += 'background-color: #%s; ' % ndef['bgcolor']
00404             if ndef['border']:
00405                 style += 'border: 1px solid #%s; ' % ndef['border']
00406             if style:
00407                 t2c[ttype] = name
00408                 # save len(ttype) to enable ordering the styles by
00409                 # hierarchy (necessary for CSS cascading rules!)
00410                 c2s[name] = (style[:-2], ttype, len(ttype))
00411 
00412     def get_style_defs(self, arg=None):
00413         """
00414         Return CSS style definitions for the classes produced by the current
00415         highlighting style. ``arg`` can be a string or list of selectors to
00416         insert before the token type classes.
00417         """
00418         if arg is None:
00419             arg = ('cssclass' in self.options and '.'+self.cssclass or '')
00420         if isinstance(arg, basestring):
00421             args = [arg]
00422         else:
00423             args = list(arg)
00424 
00425         def prefix(cls):
00426             if cls:
00427                 cls = '.' + cls
00428             tmp = []
00429             for arg in args:
00430                 tmp.append((arg and arg + ' ' or '') + cls)
00431             return ', '.join(tmp)
00432 
00433         styles = [(level, ttype, cls, style)
00434                   for cls, (style, ttype, level) in self.class2style.iteritems()
00435                   if cls and style]
00436         styles.sort()
00437         lines = ['%s { %s } /* %s */' % (prefix(cls), style, repr(ttype)[6:])
00438                  for (level, ttype, cls, style) in styles]
00439         if arg and not self.nobackground and \
00440            self.style.background_color is not None:
00441             text_style = ''
00442             if Text in self.ttype2class:
00443                 text_style = ' ' + self.class2style[self.ttype2class[Text]][0]
00444             lines.insert(0, '%s { background: %s;%s }' %
00445                          (prefix(''), self.style.background_color, text_style))
00446         if self.style.highlight_color is not None:
00447             lines.insert(0, '%s.hll { background-color: %s }' %
00448                          (prefix(''), self.style.highlight_color))
00449         return '\n'.join(lines)
00450 
00451     def _decodeifneeded(self, value):
00452         if isinstance(value, bytes):
00453             if self.encoding:
00454                 return value.decode(self.encoding)
00455             return value.decode()
00456         return value
00457 
00458     def _wrap_full(self, inner, outfile):
00459         if self.cssfile:
00460             if os.path.isabs(self.cssfile):
00461                 # it's an absolute filename
00462                 cssfilename = self.cssfile
00463             else:
00464                 try:
00465                     filename = outfile.name
00466                     if not filename or filename[0] == '<':
00467                         # pseudo files, e.g. name == '<fdopen>'
00468                         raise AttributeError
00469                     cssfilename = os.path.join(os.path.dirname(filename),
00470                                                self.cssfile)
00471                 except AttributeError:
00472                     print >>sys.stderr, 'Note: Cannot determine output file name, ' \
00473                           'using current directory as base for the CSS file name'
00474                     cssfilename = self.cssfile
00475             # write CSS file only if noclobber_cssfile isn't given as an option.
00476             try:
00477                 if not os.path.exists(cssfilename) or not self.noclobber_cssfile:
00478                     cf = open(cssfilename, "w")
00479                     cf.write(CSSFILE_TEMPLATE %
00480                             {'styledefs': self.get_style_defs('body')})
00481                     cf.close()
00482             except IOError, err:
00483                 err.strerror = 'Error writing CSS file: ' + err.strerror
00484                 raise
00485 
00486             yield 0, (DOC_HEADER_EXTERNALCSS %
00487                       dict(title     = self.title,
00488                            cssfile   = self.cssfile,
00489                            encoding  = self.encoding))
00490         else:
00491             yield 0, (DOC_HEADER %
00492                       dict(title     = self.title,
00493                            styledefs = self.get_style_defs('body'),
00494                            encoding  = self.encoding))
00495 
00496         for t, line in inner:
00497             yield t, line
00498         yield 0, DOC_FOOTER
00499 
00500     def _wrap_tablelinenos(self, inner):
00501         dummyoutfile = StringIO.StringIO()
00502         lncount = 0
00503         for t, line in inner:
00504             if t:
00505                 lncount += 1
00506             dummyoutfile.write(line)
00507 
00508         fl = self.linenostart
00509         mw = len(str(lncount + fl - 1))
00510         sp = self.linenospecial
00511         st = self.linenostep
00512         la = self.lineanchors
00513         aln = self.anchorlinenos
00514         if sp:
00515             lines = []
00516 
00517             for i in range(fl, fl+lncount):
00518                 if i % st == 0:
00519                     if i % sp == 0:
00520                         if aln:
00521                             lines.append('<a href="#%s-%d" class="special">%*d</a>' %
00522                                          (la, i, mw, i))
00523                         else:
00524                             lines.append('<span class="special">%*d</span>' % (mw, i))
00525                     else:
00526                         if aln:
00527                             lines.append('<a href="#%s-%d">%*d</a>' % (la, i, mw, i))
00528                         else:
00529                             lines.append('%*d' % (mw, i))
00530                 else:
00531                     lines.append('')
00532             ls = '\n'.join(lines)
00533         else:
00534             lines = []
00535             for i in range(fl, fl+lncount):
00536                 if i % st == 0:
00537                     if aln:
00538                         lines.append('<a href="#%s-%d">%*d</a>' % (la, i, mw, i))
00539                     else:
00540                         lines.append('%*d' % (mw, i))
00541                 else:
00542                     lines.append('')
00543             ls = '\n'.join(lines)
00544 
00545         # in case you wonder about the seemingly redundant <div> here: since the
00546         # content in the other cell also is wrapped in a div, some browsers in
00547         # some configurations seem to mess up the formatting...
00548         yield 0, ('<table class="%stable">' % self.cssclass +
00549                   '<tr><td class="linenos"><div class="linenodiv"><pre>' +
00550                   ls + '</pre></div></td><td class="code">')
00551         yield 0, dummyoutfile.getvalue()
00552         yield 0, '</td></tr></table>'
00553 
00554     def _wrap_inlinelinenos(self, inner):
00555         # need a list of lines since we need the width of a single number :(
00556         lines = list(inner)
00557         sp = self.linenospecial
00558         st = self.linenostep
00559         num = self.linenostart
00560         mw = len(str(len(lines) + num - 1))
00561 
00562         if sp:
00563             for t, line in lines:
00564                 yield 1, '<span class="lineno%s">%*s</span> ' % (
00565                     num%sp == 0 and ' special' or '', mw,
00566                     (num%st and ' ' or num)) + line
00567                 num += 1
00568         else:
00569             for t, line in lines:
00570                 yield 1, '<span class="lineno">%*s</span> ' % (
00571                     mw, (num%st and ' ' or num)) + line
00572                 num += 1
00573 
00574     def _wrap_lineanchors(self, inner):
00575         s = self.lineanchors
00576         i = 0
00577         for t, line in inner:
00578             if t:
00579                 i += 1
00580                 yield 1, '<a name="%s-%d"></a>' % (s, i) + line
00581             else:
00582                 yield 0, line
00583 
00584     def _wrap_div(self, inner):
00585         style = []
00586         if (self.noclasses and not self.nobackground and
00587             self.style.background_color is not None):
00588             style.append('background: %s' % (self.style.background_color,))
00589         if self.cssstyles:
00590             style.append(self.cssstyles)
00591         style = '; '.join(style)
00592 
00593         yield 0, ('<div' + (self.cssclass and ' class="%s"' % self.cssclass)
00594                   + (style and (' style="%s"' % style)) + '>')
00595         for tup in inner:
00596             yield tup
00597         yield 0, '</div>\n'
00598 
00599     def _wrap_pre(self, inner):
00600         style = []
00601         if self.prestyles:
00602             style.append(self.prestyles)
00603         if self.noclasses:
00604             style.append('line-height: 125%')
00605         style = '; '.join(style)
00606 
00607         yield 0, ('<pre' + (style and ' style="%s"' % style) + '>')
00608         for tup in inner:
00609             yield tup
00610         yield 0, '</pre>'
00611 
00612     def _format_lines(self, tokensource):
00613         """
00614         Just format the tokens, without any wrapping tags.
00615         Yield individual lines.
00616         """
00617         nocls = self.noclasses
00618         lsep = self.lineseparator
00619         # for <span style=""> lookup only
00620         getcls = self.ttype2class.get
00621         c2s = self.class2style
00622 
00623         lspan = ''
00624         line = ''
00625         for ttype, value in tokensource:
00626             if nocls:
00627                 cclass = getcls(ttype)
00628                 while cclass is None:
00629                     ttype = ttype.parent
00630                     cclass = getcls(ttype)
00631                 cspan = cclass and '<span style="%s">' % c2s[cclass][0] or ''
00632             else:
00633                 cls = self._get_css_class(ttype)
00634                 cspan = cls and '<span class="%s">' % cls or ''
00635 
00636             parts = escape_html(value).split('\n')
00637 
00638             # for all but the last line
00639             for part in parts[:-1]:
00640                 if line:
00641                     if lspan != cspan:
00642                         line += (lspan and '</span>') + cspan + part + \
00643                                 (cspan and '</span>') + lsep
00644                     else: # both are the same
00645                         line += part + (lspan and '</span>') + lsep
00646                     yield 1, line
00647                     line = ''
00648                 elif part:
00649                     yield 1, cspan + part + (cspan and '</span>') + lsep
00650                 else:
00651                     yield 1, lsep
00652             # for the last line
00653             if line and parts[-1]:
00654                 if lspan != cspan:
00655                     line += (lspan and '</span>') + cspan + parts[-1]
00656                     lspan = cspan
00657                 else:
00658                     line += parts[-1]
00659             elif parts[-1]:
00660                 line = cspan + parts[-1]
00661                 lspan = cspan
00662             # else we neither have to open a new span nor set lspan
00663 
00664         if line:
00665             yield 1, line + (lspan and '</span>') + lsep
00666 
00667     def _highlight_lines(self, tokensource):
00668         """
00669         Highlighted the lines specified in the `hl_lines` option by
00670         post-processing the token stream coming from `_format_lines`.
00671         """
00672         hls = self.hl_lines
00673 
00674         for i, (t, value) in enumerate(tokensource):
00675             if t != 1:
00676                 yield t, value
00677             if i + 1 in hls: # i + 1 because Python indexes start at 0
00678                 if self.noclasses:
00679                     style = ''
00680                     if self.style.highlight_color is not None:
00681                         style = (' style="background-color: %s"' %
00682                                  (self.style.highlight_color,))
00683                     yield 1, '<span%s>%s</span>' % (style, value)
00684                 else:
00685                     yield 1, '<span class="hll">%s</span>' % value
00686             else:
00687                 yield 1, value
00688 
00689     def wrap(self, source, outfile):
00690         """
00691         Wrap the ``source``, which is a generator yielding
00692         individual lines, in custom generators. See docstring
00693         for `format`. Can be overridden.
00694         """
00695         return self._wrap_div(self._wrap_pre(source))
00696 
00697     def format_unencoded(self, tokensource, outfile):
00698         """
00699         The formatting process uses several nested generators; which of
00700         them are used is determined by the user's options.
00701 
00702         Each generator should take at least one argument, ``inner``,
00703         and wrap the pieces of text generated by this.
00704 
00705         Always yield 2-tuples: (code, text). If "code" is 1, the text
00706         is part of the original tokensource being highlighted, if it's
00707         0, the text is some piece of wrapping. This makes it possible to
00708         use several different wrappers that process the original source
00709         linewise, e.g. line number generators.
00710         """
00711         source = self._format_lines(tokensource)
00712         if self.hl_lines:
00713             source = self._highlight_lines(source)
00714         if not self.nowrap:
00715             if self.linenos == 2:
00716                 source = self._wrap_inlinelinenos(source)
00717             if self.lineanchors:
00718                 source = self._wrap_lineanchors(source)
00719             source = self.wrap(source, outfile)
00720             if self.linenos == 1:
00721                 source = self._wrap_tablelinenos(source)
00722             if self.full:
00723                 source = self._wrap_full(source, outfile)
00724 
00725         for t, piece in source:
00726             outfile.write(piece)