[Zope-Checkins] CVS: Zope/lib/python/docutils/writers - __init__.py:1.2.10.3 docutils_xml.py:1.2.10.3 html4css1.py:1.2.10.3 latex2e.py:1.1.2.3 pep_html.py:1.2.10.3 pseudoxml.py:1.2.10.3

Andreas Jung cvs-admin at zope.org
Sun Nov 30 11:05:27 EST 2003


Update of /cvs-repository/Zope/lib/python/docutils/writers
In directory cvs.zope.org:/tmp/cvs-serv9303/writers

Added Files:
      Tag: Zope-2_7-branch
	__init__.py docutils_xml.py html4css1.py latex2e.py 
	pep_html.py pseudoxml.py 
Log Message:
updated



=== Zope/lib/python/docutils/writers/__init__.py 1.2.10.2 => 1.2.10.3 ===
--- /dev/null	Sun Nov 30 11:05:27 2003
+++ Zope/lib/python/docutils/writers/__init__.py	Sun Nov 30 11:05:26 2003
@@ -0,0 +1,84 @@
+# Authors: David Goodger
+# Contact: goodger at users.sourceforge.net
+# Revision: $Revision$
+# Date: $Date$
+# Copyright: This module has been placed in the public domain.
+
+"""
+This package contains Docutils Writer modules.
+"""
+
+__docformat__ = 'reStructuredText'
+
+
+import sys
+import docutils
+from docutils import languages, Component
+from docutils.transforms import universal
+
+
+class Writer(Component):
+
+    """
+    Abstract base class for docutils Writers.
+
+    Each writer module or package must export a subclass also called 'Writer'.
+    Each writer must support all standard node types listed in
+    `docutils.nodes.node_class_names`.
+
+    Call `write()` to process a document.
+    """
+
+    component_type = 'writer'
+    config_section = 'writers'
+
+    document = None
+    """The document to write."""
+
+    language = None
+    """Language module for the document."""
+
+    destination = None
+    """`docutils.io` IO object; where to write the document."""
+
+    def __init__(self):
+        """Initialize the Writer instance."""
+
+    def write(self, document, destination):
+        self.document = document
+        self.language = languages.get_language(
+            document.settings.language_code)
+        self.destination = destination
+        self.translate()
+        output = self.destination.write(self.output)
+        return output
+
+    def translate(self):
+        """
+        Override to do final document tree translation.
+
+        This is usually done with a `docutils.nodes.NodeVisitor` subclass, in
+        combination with a call to `docutils.nodes.Node.walk()` or
+        `docutils.nodes.Node.walkabout()`.  The ``NodeVisitor`` subclass must
+        support all standard elements (listed in
+        `docutils.nodes.node_class_names`) and possibly non-standard elements
+        used by the current Reader as well.
+        """
+        raise NotImplementedError('subclass must override this method')
+
+
+_writer_aliases = {
+      'html': 'html4css1',
+      'latex': 'latex2e',
+      'pprint': 'pseudoxml',
+      'pformat': 'pseudoxml',
+      'pdf': 'rlpdf',
+      'xml': 'docutils_xml',}
+
+def get_writer_class(writer_name):
+    """Return the Writer class from the `writer_name` module."""
+    writer_name = writer_name.lower()
+    if _writer_aliases.has_key(writer_name):
+        writer_name = _writer_aliases[writer_name]
+    module = __import__(writer_name, globals(), locals())
+    return module.Writer


=== Zope/lib/python/docutils/writers/docutils_xml.py 1.2.10.2 => 1.2.10.3 ===
--- /dev/null	Sun Nov 30 11:05:27 2003
+++ Zope/lib/python/docutils/writers/docutils_xml.py	Sun Nov 30 11:05:26 2003
@@ -0,0 +1,73 @@
+# Authors: David Goodger
+# Contact: goodger at users.sourceforge.net
+# Revision: $Revision$
+# Date: $Date$
+# Copyright: This module has been placed in the public domain.
+
+"""
+Simple internal document tree Writer, writes Docutils XML.
+"""
+
+__docformat__ = 'reStructuredText'
+
+
+import docutils
+from docutils import frontend, writers
+
+
+class Writer(writers.Writer):
+
+    supported = ('xml',)
+    """Formats this writer supports."""
+
+    settings_spec = (
+        '"Docutils XML" Writer Options',
+        'Warning: the --newlines and --indents options may adversely affect '
+        'whitespace; use them only for reading convenience.',
+        (('Generate XML with newlines before and after tags.',
+          ['--newlines'],
+          {'action': 'store_true', 'validator': frontend.validate_boolean}),
+         ('Generate XML with indents and newlines.',
+          ['--indents'],
+          {'action': 'store_true', 'validator': frontend.validate_boolean}),
+         ('Omit the XML declaration.  Use with caution.',
+          ['--no-xml-declaration'],
+          {'dest': 'xml_declaration', 'default': 1, 'action': 'store_false',
+           'validator': frontend.validate_boolean}),
+         ('Omit the DOCTYPE declaration.',
+          ['--no-doctype'],
+          {'dest': 'doctype_declaration', 'default': 1,
+           'action': 'store_false', 'validator': frontend.validate_boolean}),))
+
+    config_section = 'docutils_xml writer'
+    config_section_dependencies = ('writers',)
+
+    output = None
+    """Final translated form of `document`."""
+
+    xml_declaration = '<?xml version="1.0" encoding="%s"?>\n'
+    #xml_stylesheet = '<?xml-stylesheet type="text/xsl" href="%s"?>\n'
+    doctype = (
+        '<!DOCTYPE document PUBLIC'
+        ' "+//IDN docutils.sourceforge.net//DTD Docutils Generic//EN//XML"'
+        ' "http://docutils.sourceforge.net/spec/docutils.dtd">\n')
+    generator = '<!-- Generated by Docutils %s -->\n'
+
+    def translate(self):
+        settings = self.document.settings
+        indent = newline = ''
+        if settings.newlines:
+            newline = '\n'
+        if settings.indents:
+            newline = '\n'
+            indent = '    '
+        output_prefix = []
+        if settings.xml_declaration:
+            output_prefix.append(
+                self.xml_declaration % settings.output_encoding)
+        if settings.doctype_declaration:
+            output_prefix.append(self.doctype)
+        output_prefix.append(self.generator % docutils.__version__)
+        docnode = self.document.asdom().childNodes[0]
+        self.output = (''.join(output_prefix)
+                       + docnode.toprettyxml(indent, newline))


=== Zope/lib/python/docutils/writers/html4css1.py 1.2.10.2 => 1.2.10.3 ===
--- /dev/null	Sun Nov 30 11:05:27 2003
+++ Zope/lib/python/docutils/writers/html4css1.py	Sun Nov 30 11:05:26 2003
@@ -0,0 +1,1257 @@
+# Author: David Goodger
+# Contact: goodger at users.sourceforge.net
+# Revision: $Revision$
+# Date: $Date$
+# Copyright: This module has been placed in the public domain.
+
+"""
+Simple HyperText Markup Language document tree Writer.
+
+The output conforms to the HTML 4.01 Transitional DTD and to the Extensible
+HTML version 1.0 Transitional DTD (*almost* strict).  The output contains a
+minimum of formatting information.  A cascading style sheet ("default.css" by
+default) is required for proper viewing with a modern graphical browser.
+"""
+
+__docformat__ = 'reStructuredText'
+
+
+import sys
+import os
+import os.path
+import time
+import re
+from types import ListType
+import docutils
+from docutils import frontend, nodes, utils, writers, languages
+
+
+class Writer(writers.Writer):
+
+    supported = ('html', 'html4css1', 'xhtml')
+    """Formats this writer supports."""
+
+    settings_spec = (
+        'HTML-Specific Options',
+        None,
+        (('Specify a stylesheet URL, used verbatim.  Default is '
+          '"default.css".  Overridden by --stylesheet-path.',
+          ['--stylesheet'],
+          {'default': 'default.css', 'metavar': '<URL>'}),
+         ('Specify a stylesheet file, relative to the current working '
+          'directory.  The path is adjusted relative to the output HTML '
+          'file.  Overrides --stylesheet.',
+          ['--stylesheet-path'],
+          {'metavar': '<file>'}),
+         ('Link to the stylesheet in the output HTML file.  This is the '
+          'default.',
+          ['--link-stylesheet'],
+          {'dest': 'embed_stylesheet', 'action': 'store_false',
+           'validator': frontend.validate_boolean}),
+         ('Embed the stylesheet in the output HTML file.  The stylesheet '
+          'file must be accessible during processing (--stylesheet-path is '
+          'recommended).  The stylesheet is embedded inside a comment, so it '
+          'must not contain the text "--" (two hyphens).  Default: link the '
+          'stylesheet, do not embed it.',
+          ['--embed-stylesheet'],
+          {'action': 'store_true', 'validator': frontend.validate_boolean}),
+         ('Format for footnote references: one of "superscript" or '
+          '"brackets".  Default is "superscript".',
+          ['--footnote-references'],
+          {'choices': ['superscript', 'brackets'], 'default': 'superscript',
+           'metavar': '<format>'}),
+         ('Format for block quote attributions: one of "dash" (em-dash '
+          'prefix), "parentheses"/"parens", or "none".  Default is "dash".',
+          ['--attribution'],
+          {'choices': ['dash', 'parentheses', 'parens', 'none'],
+           'default': 'dash', 'metavar': '<format>'}),
+         ('Remove extra vertical whitespace between items of bullet lists '
+          'and enumerated lists, when list items are "simple" (i.e., all '
+          'items each contain one paragraph and/or one "simple" sublist '
+          'only).  Default: enabled.',
+          ['--compact-lists'],
+          {'default': 1, 'action': 'store_true',
+           'validator': frontend.validate_boolean}),
+         ('Disable compact simple bullet and enumerated lists.',
+          ['--no-compact-lists'],
+          {'dest': 'compact_lists', 'action': 'store_false'}),
+         ('Omit the XML declaration.  Use with caution.',
+          ['--no-xml-declaration'],
+          {'dest': 'xml_declaration', 'default': 1, 'action': 'store_false',
+           'validator': frontend.validate_boolean}),))
+
+    relative_path_settings = ('stylesheet_path',)
+
+    config_section = 'html4css1 writer'
+    config_section_dependencies = ('writers',)
+
+    output = None
+    """Final translated form of `document`."""
+
+    def __init__(self):
+        writers.Writer.__init__(self)
+        self.translator_class = HTMLTranslator
+
+    def translate(self):
+        visitor = self.translator_class(self.document)
+        self.document.walkabout(visitor)
+        self.output = visitor.astext()
+        self.head_prefix = visitor.head_prefix
+        self.stylesheet = visitor.stylesheet
+        self.head = visitor.head
+        self.body_prefix = visitor.body_prefix
+        self.body_pre_docinfo = visitor.body_pre_docinfo
+        self.docinfo = visitor.docinfo
+        self.body = visitor.body
+        self.body_suffix = visitor.body_suffix
+
+
+class HTMLTranslator(nodes.NodeVisitor):
+
+    """
+    This HTML writer has been optimized to produce visually compact
+    lists (less vertical whitespace).  HTML's mixed content models
+    allow list items to contain "<li><p>body elements</p></li>" or
+    "<li>just text</li>" or even "<li>text<p>and body
+    elements</p>combined</li>", each with different effects.  It would
+    be best to stick with strict body elements in list items, but they
+    affect vertical spacing in browsers (although they really
+    shouldn't).
+
+    Here is an outline of the optimization:
+
+    - Check for and omit <p> tags in "simple" lists: list items
+      contain either a single paragraph, a nested simple list, or a
+      paragraph followed by a nested simple list.  This means that
+      this list can be compact:
+
+          - Item 1.
+          - Item 2.
+
+      But this list cannot be compact:
+
+          - Item 1.
+
+            This second paragraph forces space between list items.
+
+          - Item 2.
+
+    - In non-list contexts, omit <p> tags on a paragraph if that
+      paragraph is the only child of its parent (footnotes & citations
+      are allowed a label first).
+
+    - Regardless of the above, in definitions, table cells, field bodies,
+      option descriptions, and list items, mark the first child with
+      'class="first"' and the last child with 'class="last"'.  The stylesheet
+      sets the margins (top & bottom respecively) to 0 for these elements.
+
+    The ``no_compact_lists`` setting (``--no-compact-lists`` command-line
+    option) disables list whitespace optimization.
+    """
+
+    xml_declaration = '<?xml version="1.0" encoding="%s" ?>\n'
+    doctype = ('<!DOCTYPE html' 
+               ' PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"'
+               ' "http://www.w3.org/TR/xhtml1/DTD/'
+               'xhtml1-transitional.dtd">\n')
+    html_head = ('<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="%s" '
+                 'lang="%s">\n<head>\n')
+    content_type = ('<meta http-equiv="Content-Type" content="text/html; '
+                    'charset=%s" />\n')
+    generator = ('<meta name="generator" content="Docutils %s: '
+                 'http://docutils.sourceforge.net/" />\n')
+    stylesheet_link = '<link rel="stylesheet" href="%s" type="text/css" />\n'
+    embedded_stylesheet = '<style type="text/css"><!--\n\n%s\n--></style>\n'
+    named_tags = {'a': 1, 'applet': 1, 'form': 1, 'frame': 1, 'iframe': 1,
+                  'img': 1, 'map': 1}
+    words_and_spaces = re.compile(r'\S+| +|\n')
+
+    def __init__(self, document):
+        nodes.NodeVisitor.__init__(self, document)
+        self.settings = settings = document.settings
+        lcode = settings.language_code
+        self.language = languages.get_language(lcode)
+        self.head_prefix = [
+              self.doctype,
+              self.html_head % (lcode, lcode),
+              self.content_type % settings.output_encoding,
+              self.generator % docutils.__version__]
+        if settings.xml_declaration:
+            self.head_prefix.insert(0, self.xml_declaration
+                                    % settings.output_encoding)
+        self.head = []
+        if settings.embed_stylesheet:
+            stylesheet = self.get_stylesheet_reference(
+                os.path.join(os.getcwd(), 'dummy'))
+            stylesheet_text = open(stylesheet).read()
+            self.stylesheet = [self.embedded_stylesheet % stylesheet_text]
+        else:
+            stylesheet = self.get_stylesheet_reference()
+            if stylesheet:
+                self.stylesheet = [self.stylesheet_link % stylesheet]
+            else:
+                self.stylesheet = []
+        self.body_prefix = ['</head>\n<body>\n']
+        self.body_pre_docinfo = []
+        self.docinfo = []
+        self.body = []
+        self.body_suffix = ['</body>\n</html>\n']
+        self.section_level = 0
+        self.context = []
+        self.topic_class = ''
+        self.colspecs = []
+        self.compact_p = 1
+        self.compact_simple = None
+        self.in_docinfo = None
+        self.in_sidebar = None
+
+    def get_stylesheet_reference(self, relative_to=None):
+        settings = self.settings
+        if settings.stylesheet_path:
+            if relative_to == None:
+                relative_to = settings._destination
+            return utils.relative_path(relative_to, settings.stylesheet_path)
+        else:
+            return settings.stylesheet
+
+    def astext(self):
+        return ''.join(self.head_prefix + self.head
+                       + self.stylesheet + self.body_prefix
+                       + self.body_pre_docinfo + self.docinfo
+                       + self.body + self.body_suffix)
+
+    def encode(self, text):
+        """Encode special characters in `text` & return."""
+        # @@@ A codec to do these and all other HTML entities would be nice.
+        text = text.replace("&", "&amp;")
+        text = text.replace("<", "&lt;")
+        text = text.replace('"', "&quot;")
+        text = text.replace(">", "&gt;")
+        text = text.replace("@", "&#64;") # may thwart some address harvesters
+        return text
+
+    def attval(self, text,
+               whitespace=re.compile('[\n\r\t\v\f]')):
+        """Cleanse, HTML encode, and return attribute value text."""
+        return self.encode(whitespace.sub(' ', text))
+
+    def starttag(self, node, tagname, suffix='\n', infix='', **attributes):
+        """
+        Construct and return a start tag given a node (id & class attributes
+        are extracted), tag name, and optional attributes.
+        """
+        tagname = tagname.lower()
+        atts = {}
+        for (name, value) in attributes.items():
+            atts[name.lower()] = value
+        for att in ('class',):          # append to node attribute
+            if node.has_key(att) or atts.has_key(att):
+                atts[att] = \
+                      (node.get(att, '') + ' ' + atts.get(att, '')).strip()
+        for att in ('id',):             # node attribute overrides
+            if node.has_key(att):
+                atts[att] = node[att]
+        if atts.has_key('id') and self.named_tags.has_key(tagname):
+            atts['name'] = atts['id']   # for compatibility with old browsers
+        attlist = atts.items()
+        attlist.sort()
+        parts = [tagname]
+        for name, value in attlist:
+            if value is None:           # boolean attribute
+                # According to the HTML spec, ``<element boolean>`` is good,
+                # ``<element boolean="boolean">`` is bad.
+                # (But the XHTML (XML) spec says the opposite.  <sigh>)
+                parts.append(name.lower())
+            elif isinstance(value, ListType):
+                values = [unicode(v) for v in value]
+                parts.append('%s="%s"' % (name.lower(),
+                                          self.attval(' '.join(values))))
+            else:
+                parts.append('%s="%s"' % (name.lower(),
+                                          self.attval(unicode(value))))
+        return '<%s%s>%s' % (' '.join(parts), infix, suffix)
+
+    def emptytag(self, node, tagname, suffix='\n', **attributes):
+        """Construct and return an XML-compatible empty tag."""
+        return self.starttag(node, tagname, suffix, infix=' /', **attributes)
+
+    def visit_Text(self, node):
+        self.body.append(self.encode(node.astext()))
+
+    def depart_Text(self, node):
+        pass
+
+    def visit_abbreviation(self, node):
+        # @@@ implementation incomplete ("title" attribute)
+        self.body.append(self.starttag(node, 'abbr', ''))
+
+    def depart_abbreviation(self, node):
+        self.body.append('</abbr>')
+
+    def visit_acronym(self, node):
+        # @@@ implementation incomplete ("title" attribute)
+        self.body.append(self.starttag(node, 'acronym', ''))
+
+    def depart_acronym(self, node):
+        self.body.append('</acronym>')
+
+    def visit_address(self, node):
+        self.visit_docinfo_item(node, 'address', meta=None)
+        self.body.append(self.starttag(node, 'pre', CLASS='address'))
+
+    def depart_address(self, node):
+        self.body.append('\n</pre>\n')
+        self.depart_docinfo_item()
+
+    def visit_admonition(self, node, name=''):
+        self.body.append(self.starttag(node, 'div',
+                                        CLASS=(name or 'admonition')))
+        if name:
+            self.body.append('<p class="admonition-title">'
+                             + self.language.labels[name] + '</p>\n')
+
+    def depart_admonition(self, node=None):
+        self.body.append('</div>\n')
+
+    def visit_attention(self, node):
+        self.visit_admonition(node, 'attention')
+
+    def depart_attention(self, node):
+        self.depart_admonition()
+
+    attribution_formats = {'dash': ('&mdash;', ''),
+                           'parentheses': ('(', ')'),
+                           'parens': ('(', ')'),
+                           'none': ('', '')}
+
+    def visit_attribution(self, node):
+        prefix, suffix = self.attribution_formats[self.settings.attribution]
+        self.context.append(suffix)
+        self.body.append(
+            self.starttag(node, 'p', prefix, CLASS='attribution'))
+
+    def depart_attribution(self, node):
+        self.body.append(self.context.pop() + '</p>\n')
+
+    def visit_author(self, node):
+        self.visit_docinfo_item(node, 'author')
+
+    def depart_author(self, node):
+        self.depart_docinfo_item()
+
+    def visit_authors(self, node):
+        pass
+
+    def depart_authors(self, node):
+        pass
+
+    def visit_block_quote(self, node):
+        self.body.append(self.starttag(node, 'blockquote'))
+
+    def depart_block_quote(self, node):
+        self.body.append('</blockquote>\n')
+
+    def check_simple_list(self, node):
+        """Check for a simple list that can be rendered compactly."""
+        visitor = SimpleListChecker(self.document)
+        try:
+            node.walk(visitor)
+        except nodes.NodeFound:
+            return None
+        else:
+            return 1
+
+    def visit_bullet_list(self, node):
+        atts = {}
+        old_compact_simple = self.compact_simple
+        self.context.append((self.compact_simple, self.compact_p))
+        self.compact_p = None
+        self.compact_simple = (self.settings.compact_lists and
+                               (self.compact_simple
+                                or self.topic_class == 'contents'
+                                or self.check_simple_list(node)))
+        if self.compact_simple and not old_compact_simple:
+            atts['class'] = 'simple'
+        self.body.append(self.starttag(node, 'ul', **atts))
+
+    def depart_bullet_list(self, node):
+        self.compact_simple, self.compact_p = self.context.pop()
+        self.body.append('</ul>\n')
+
+    def visit_caption(self, node):
+        self.body.append(self.starttag(node, 'p', '', CLASS='caption'))
+
+    def depart_caption(self, node):
+        self.body.append('</p>\n')
+
+    def visit_caution(self, node):
+        self.visit_admonition(node, 'caution')
+
+    def depart_caution(self, node):
+        self.depart_admonition()
+
+    def visit_citation(self, node):
+        self.body.append(self.starttag(node, 'table', CLASS='citation',
+                                       frame="void", rules="none"))
+        self.body.append('<colgroup><col class="label" /><col /></colgroup>\n'
+                         '<col />\n'
+                         '<tbody valign="top">\n'
+                         '<tr>')
+        self.footnote_backrefs(node)
+
+    def depart_citation(self, node):
+        self.body.append('</td></tr>\n'
+                         '</tbody>\n</table>\n')
+
+    def visit_citation_reference(self, node):
+        href = ''
+        if node.has_key('refid'):
+            href = '#' + node['refid']
+        elif node.has_key('refname'):
+            href = '#' + self.document.nameids[node['refname']]
+        self.body.append(self.starttag(node, 'a', '[', href=href,
+                                       CLASS='citation-reference'))
+
+    def depart_citation_reference(self, node):
+        self.body.append(']</a>')
+
+    def visit_classifier(self, node):
+        self.body.append(' <span class="classifier-delimiter">:</span> ')
+        self.body.append(self.starttag(node, 'span', '', CLASS='classifier'))
+
+    def depart_classifier(self, node):
+        self.body.append('</span>')
+
+    def visit_colspec(self, node):
+        self.colspecs.append(node)
+
+    def depart_colspec(self, node):
+        pass
+
+    def write_colspecs(self):
+        width = 0
+        for node in self.colspecs:
+            width += node['colwidth']
+        for node in self.colspecs:
+            colwidth = int(node['colwidth'] * 100.0 / width + 0.5)
+            self.body.append(self.emptytag(node, 'col',
+                                           width='%i%%' % colwidth))
+        self.colspecs = []
+
+    def visit_comment(self, node,
+                      sub=re.compile('-(?=-)').sub):
+        """Escape double-dashes in comment text."""
+        self.body.append('<!-- %s -->\n' % sub('- ', node.astext()))
+        # Content already processed:
+        raise nodes.SkipNode
+
+    def visit_contact(self, node):
+        self.visit_docinfo_item(node, 'contact', meta=None)
+
+    def depart_contact(self, node):
+        self.depart_docinfo_item()
+
+    def visit_copyright(self, node):
+        self.visit_docinfo_item(node, 'copyright')
+
+    def depart_copyright(self, node):
+        self.depart_docinfo_item()
+
+    def visit_danger(self, node):
+        self.visit_admonition(node, 'danger')
+
+    def depart_danger(self, node):
+        self.depart_admonition()
+
+    def visit_date(self, node):
+        self.visit_docinfo_item(node, 'date')
+
+    def depart_date(self, node):
+        self.depart_docinfo_item()
+
+    def visit_decoration(self, node):
+        pass
+
+    def depart_decoration(self, node):
+        pass
+
+    def visit_definition(self, node):
+        self.body.append('</dt>\n')
+        self.body.append(self.starttag(node, 'dd', ''))
+        if len(node):
+            node[0].set_class('first')
+            node[-1].set_class('last')
+
+    def depart_definition(self, node):
+        self.body.append('</dd>\n')
+
+    def visit_definition_list(self, node):
+        self.body.append(self.starttag(node, 'dl'))
+
+    def depart_definition_list(self, node):
+        self.body.append('</dl>\n')
+
+    def visit_definition_list_item(self, node):
+        pass
+
+    def depart_definition_list_item(self, node):
+        pass
+
+    def visit_description(self, node):
+        self.body.append(self.starttag(node, 'td', ''))
+        if len(node):
+            node[0].set_class('first')
+            node[-1].set_class('last')
+
+    def depart_description(self, node):
+        self.body.append('</td>')
+
+    def visit_docinfo(self, node):
+        self.context.append(len(self.body))
+        self.body.append(self.starttag(node, 'table', CLASS='docinfo',
+                                       frame="void", rules="none"))
+        self.body.append('<col class="docinfo-name" />\n'
+                         '<col class="docinfo-content" />\n'
+                         '<tbody valign="top">\n')
+        self.in_docinfo = 1
+
+    def depart_docinfo(self, node):
+        self.body.append('</tbody>\n</table>\n')
+        self.in_docinfo = None
+        start = self.context.pop()
+        self.body_pre_docinfo = self.body[:start]
+        self.docinfo = self.body[start:]
+        self.body = []
+
+    def visit_docinfo_item(self, node, name, meta=1):
+        if meta:
+            self.head.append('<meta name="%s" content="%s" />\n'
+                             % (name, self.attval(node.astext())))
+        self.body.append(self.starttag(node, 'tr', ''))
+        self.body.append('<th class="docinfo-name">%s:</th>\n<td>'
+                         % self.language.labels[name])
+        if len(node):
+            if isinstance(node[0], nodes.Element):
+                node[0].set_class('first')
+            if isinstance(node[-1], nodes.Element):
+                node[-1].set_class('last')
+
+    def depart_docinfo_item(self):
+        self.body.append('</td></tr>\n')
+
+    def visit_doctest_block(self, node):
+        self.body.append(self.starttag(node, 'pre', CLASS='doctest-block'))
+
+    def depart_doctest_block(self, node):
+        self.body.append('\n</pre>\n')
+
+    def visit_document(self, node):
+        self.body.append(self.starttag(node, 'div', CLASS='document'))
+
+    def depart_document(self, node):
+        self.body.append('</div>\n')
+
+    def visit_emphasis(self, node):
+        self.body.append('<em>')
+
+    def depart_emphasis(self, node):
+        self.body.append('</em>')
+
+    def visit_entry(self, node):
+        if isinstance(node.parent.parent, nodes.thead):
+            tagname = 'th'
+        else:
+            tagname = 'td'
+        atts = {}
+        if node.has_key('morerows'):
+            atts['rowspan'] = node['morerows'] + 1
+        if node.has_key('morecols'):
+            atts['colspan'] = node['morecols'] + 1
+        self.body.append(self.starttag(node, tagname, '', **atts))
+        self.context.append('</%s>\n' % tagname.lower())
+        if len(node) == 0:              # empty cell
+            self.body.append('&nbsp;')
+        else:
+            node[0].set_class('first')
+            node[-1].set_class('last')
+
+    def depart_entry(self, node):
+        self.body.append(self.context.pop())
+
+    def visit_enumerated_list(self, node):
+        """
+        The 'start' attribute does not conform to HTML 4.01's strict.dtd, but
+        CSS1 doesn't help. CSS2 isn't widely enough supported yet to be
+        usable.
+        """
+        atts = {}
+        if node.has_key('start'):
+            atts['start'] = node['start']
+        if node.has_key('enumtype'):
+            atts['class'] = node['enumtype']
+        # @@@ To do: prefix, suffix. How? Change prefix/suffix to a
+        # single "format" attribute? Use CSS2?
+        old_compact_simple = self.compact_simple
+        self.context.append((self.compact_simple, self.compact_p))
+        self.compact_p = None
+        self.compact_simple = (self.settings.compact_lists and
+                               (self.compact_simple
+                                or self.topic_class == 'contents'
+                                or self.check_simple_list(node)))
+        if self.compact_simple and not old_compact_simple:
+            atts['class'] = (atts.get('class', '') + ' simple').strip()
+        self.body.append(self.starttag(node, 'ol', **atts))
+
+    def depart_enumerated_list(self, node):
+        self.compact_simple, self.compact_p = self.context.pop()
+        self.body.append('</ol>\n')
+
+    def visit_error(self, node):
+        self.visit_admonition(node, 'error')
+
+    def depart_error(self, node):
+        self.depart_admonition()
+
+    def visit_field(self, node):
+        self.body.append(self.starttag(node, 'tr', '', CLASS='field'))
+
+    def depart_field(self, node):
+        self.body.append('</tr>\n')
+
+    def visit_field_body(self, node):
+        self.body.append(self.starttag(node, 'td', '', CLASS='field-body'))
+        if len(node):
+            node[0].set_class('first')
+            node[-1].set_class('last')
+
+    def depart_field_body(self, node):
+        self.body.append('</td>\n')
+
+    def visit_field_list(self, node):
+        self.body.append(self.starttag(node, 'table', frame='void',
+                                       rules='none', CLASS='field-list'))
+        self.body.append('<col class="field-name" />\n'
+                         '<col class="field-body" />\n'
+                         '<tbody valign="top">\n')
+
+    def depart_field_list(self, node):
+        self.body.append('</tbody>\n</table>\n')
+
+    def visit_field_name(self, node):
+        atts = {}
+        if self.in_docinfo:
+            atts['class'] = 'docinfo-name'
+        else:
+            atts['class'] = 'field-name'
+        if len(node.astext()) > 14:
+            atts['colspan'] = 2
+            self.context.append('</tr>\n<tr><td>&nbsp;</td>')
+        else:
+            self.context.append('')
+        self.body.append(self.starttag(node, 'th', '', **atts))
+
+    def depart_field_name(self, node):
+        self.body.append(':</th>')
+        self.body.append(self.context.pop())
+
+    def visit_figure(self, node):
+        atts = {'class': 'figure'}
+        if node.get('width'):
+            atts['style'] = 'width: %spx' % node['width']
+        self.body.append(self.starttag(node, 'div', **atts))
+
+    def depart_figure(self, node):
+        self.body.append('</div>\n')
+
+    def visit_footer(self, node):
+        self.context.append(len(self.body))
+
+    def depart_footer(self, node):
+        start = self.context.pop()
+        footer = (['<hr class="footer" />\n',
+                   self.starttag(node, 'div', CLASS='footer')]
+                  + self.body[start:] + ['</div>\n'])
+        self.body_suffix[:0] = footer
+        del self.body[start:]
+
+    def visit_footnote(self, node):
+        self.body.append(self.starttag(node, 'table', CLASS='footnote',
+                                       frame="void", rules="none"))
+        self.body.append('<colgroup><col class="label" /><col /></colgroup>\n'
+                         '<tbody valign="top">\n'
+                         '<tr>')
+        self.footnote_backrefs(node)
+
+    def footnote_backrefs(self, node):
+        if self.settings.footnote_backlinks and node.hasattr('backrefs'):
+            backrefs = node['backrefs']
+            if len(backrefs) == 1:
+                self.context.append('')
+                self.context.append('<a class="fn-backref" href="#%s" '
+                                    'name="%s">' % (backrefs[0], node['id']))
+            else:
+                i = 1
+                backlinks = []
+                for backref in backrefs:
+                    backlinks.append('<a class="fn-backref" href="#%s">%s</a>'
+                                     % (backref, i))
+                    i += 1
+                self.context.append('<em>(%s)</em> ' % ', '.join(backlinks))
+                self.context.append('<a name="%s">' % node['id'])
+        else:
+            self.context.append('')
+            self.context.append('<a name="%s">' % node['id'])
+
+    def depart_footnote(self, node):
+        self.body.append('</td></tr>\n'
+                         '</tbody>\n</table>\n')
+
+    def visit_footnote_reference(self, node):
+        href = ''
+        if node.has_key('refid'):
+            href = '#' + node['refid']
+        elif node.has_key('refname'):
+            href = '#' + self.document.nameids[node['refname']]
+        format = self.settings.footnote_references
+        if format == 'brackets':
+            suffix = '['
+            self.context.append(']')
+        elif format == 'superscript':
+            suffix = '<sup>'
+            self.context.append('</sup>')
+        else:                           # shouldn't happen
+            suffix = '???'
+            self.content.append('???')
+        self.body.append(self.starttag(node, 'a', suffix, href=href,
+                                       CLASS='footnote-reference'))
+
+    def depart_footnote_reference(self, node):
+        self.body.append(self.context.pop() + '</a>')
+
+    def visit_generated(self, node):
+        pass
+
+    def depart_generated(self, node):
+        pass
+
+    def visit_header(self, node):
+        self.context.append(len(self.body))
+
+    def depart_header(self, node):
+        start = self.context.pop()
+        self.body_prefix.append(self.starttag(node, 'div', CLASS='header'))
+        self.body_prefix.extend(self.body[start:])
+        self.body_prefix.append('<hr />\n</div>\n')
+        del self.body[start:]
+
+    def visit_hint(self, node):
+        self.visit_admonition(node, 'hint')
+
+    def depart_hint(self, node):
+        self.depart_admonition()
+
+    def visit_image(self, node):
+        atts = node.attributes.copy()
+        if atts.has_key('class'):
+            del atts['class']           # prevent duplication with node attrs
+        atts['src'] = atts['uri']
+        del atts['uri']
+        if not atts.has_key('alt'):
+            atts['alt'] = atts['src']
+        if isinstance(node.parent, nodes.TextElement):
+            self.context.append('')
+        else:
+            if atts.has_key('align'):
+                self.body.append('<p align="%s">' %
+                                 (self.attval(atts['align'],)))
+            else:
+                self.body.append('<p>')
+            self.context.append('</p>\n')
+        self.body.append(self.emptytag(node, 'img', '', **atts))
+
+    def depart_image(self, node):
+        self.body.append(self.context.pop())
+
+    def visit_important(self, node):
+        self.visit_admonition(node, 'important')
+
+    def depart_important(self, node):
+        self.depart_admonition()
+
+    def visit_inline(self, node):
+        self.body.append(self.starttag(node, 'span', ''))
+
+    def depart_inline(self, node):
+        self.body.append('</span>')
+
+    def visit_label(self, node):
+        self.body.append(self.starttag(node, 'td', '%s[' % self.context.pop(),
+                                       CLASS='label'))
+
+    def depart_label(self, node):
+        self.body.append(']</a></td><td>%s' % self.context.pop())
+
+    def visit_legend(self, node):
+        self.body.append(self.starttag(node, 'div', CLASS='legend'))
+
+    def depart_legend(self, node):
+        self.body.append('</div>\n')
+
+    def visit_line_block(self, node):
+        self.body.append(self.starttag(node, 'pre', CLASS='line-block'))
+
+    def depart_line_block(self, node):
+        self.body.append('\n</pre>\n')
+
+    def visit_list_item(self, node):
+        self.body.append(self.starttag(node, 'li', ''))
+        if len(node):
+            node[0].set_class('first')
+
+    def depart_list_item(self, node):
+        self.body.append('</li>\n')
+
+    def visit_literal(self, node):
+        """Process text to prevent tokens from wrapping."""
+        self.body.append(self.starttag(node, 'tt', '', CLASS='literal'))
+        text = node.astext()
+        for token in self.words_and_spaces.findall(text):
+            if token.strip():
+                # Protect text like "--an-option" from bad line wrapping:
+                self.body.append('<span class="pre">%s</span>'
+                                 % self.encode(token))
+            elif token in ('\n', ' '):
+                # Allow breaks at whitespace:
+                self.body.append(token)
+            else:
+                # Protect runs of multiple spaces; the last space can wrap:
+                self.body.append('&nbsp;' * (len(token) - 1) + ' ')
+        self.body.append('</tt>')
+        # Content already processed:
+        raise nodes.SkipNode
+
+    def visit_literal_block(self, node):
+        self.body.append(self.starttag(node, 'pre', CLASS='literal-block'))
+
+    def depart_literal_block(self, node):
+        self.body.append('\n</pre>\n')
+
+    def visit_meta(self, node):
+        self.head.append(self.emptytag(node, 'meta', **node.attributes))
+
+    def depart_meta(self, node):
+        pass
+
+    def visit_note(self, node):
+        self.visit_admonition(node, 'note')
+
+    def depart_note(self, node):
+        self.depart_admonition()
+
+    def visit_option(self, node):
+        if self.context[-1]:
+            self.body.append(', ')
+
+    def depart_option(self, node):
+        self.context[-1] += 1
+
+    def visit_option_argument(self, node):
+        self.body.append(node.get('delimiter', ' '))
+        self.body.append(self.starttag(node, 'var', ''))
+
+    def depart_option_argument(self, node):
+        self.body.append('</var>')
+
+    def visit_option_group(self, node):
+        atts = {}
+        if len(node.astext()) > 14:
+            atts['colspan'] = 2
+            self.context.append('</tr>\n<tr><td>&nbsp;</td>')
+        else:
+            self.context.append('')
+        self.body.append(self.starttag(node, 'td', **atts))
+        self.body.append('<kbd>')
+        self.context.append(0)          # count number of options
+
+    def depart_option_group(self, node):
+        self.context.pop()
+        self.body.append('</kbd></td>\n')
+        self.body.append(self.context.pop())
+
+    def visit_option_list(self, node):
+        self.body.append(
+              self.starttag(node, 'table', CLASS='option-list',
+                            frame="void", rules="none"))
+        self.body.append('<col class="option" />\n'
+                         '<col class="description" />\n'
+                         '<tbody valign="top">\n')
+
+    def depart_option_list(self, node):
+        self.body.append('</tbody>\n</table>\n')
+
+    def visit_option_list_item(self, node):
+        self.body.append(self.starttag(node, 'tr', ''))
+
+    def depart_option_list_item(self, node):
+        self.body.append('</tr>\n')
+
+    def visit_option_string(self, node):
+        self.body.append(self.starttag(node, 'span', '', CLASS='option'))
+
+    def depart_option_string(self, node):
+        self.body.append('</span>')
+
+    def visit_organization(self, node):
+        self.visit_docinfo_item(node, 'organization')
+
+    def depart_organization(self, node):
+        self.depart_docinfo_item()
+
+    def visit_paragraph(self, node):
+        # Omit <p> tags if this is an only child and optimizable.
+        if (self.compact_simple or
+            self.compact_p and (len(node.parent) == 1 or
+                                len(node.parent) == 2 and
+                                isinstance(node.parent[0], nodes.label))):
+            self.context.append('')
+        else:
+            self.body.append(self.starttag(node, 'p', ''))
+            self.context.append('</p>\n')
+
+    def depart_paragraph(self, node):
+        self.body.append(self.context.pop())
+
+    def visit_problematic(self, node):
+        if node.hasattr('refid'):
+            self.body.append('<a href="#%s" name="%s">' % (node['refid'],
+                                                           node['id']))
+            self.context.append('</a>')
+        else:
+            self.context.append('')
+        self.body.append(self.starttag(node, 'span', '', CLASS='problematic'))
+
+    def depart_problematic(self, node):
+        self.body.append('</span>')
+        self.body.append(self.context.pop())
+
+    def visit_raw(self, node):
+        if node.get('format') == 'html':
+            self.body.append(node.astext())
+        # Keep non-HTML raw text out of output:
+        raise nodes.SkipNode
+
+    def visit_reference(self, node):
+        if node.has_key('refuri'):
+            href = node['refuri']
+        elif node.has_key('refid'):
+            href = '#' + node['refid']
+        elif node.has_key('refname'):
+            href = '#' + self.document.nameids[node['refname']]
+        self.body.append(self.starttag(node, 'a', '', href=href,
+                                       CLASS='reference'))
+
+    def depart_reference(self, node):
+        self.body.append('</a>')
+
+    def visit_revision(self, node):
+        self.visit_docinfo_item(node, 'revision', meta=None)
+
+    def depart_revision(self, node):
+        self.depart_docinfo_item()
+
+    def visit_row(self, node):
+        self.body.append(self.starttag(node, 'tr', ''))
+
+    def depart_row(self, node):
+        self.body.append('</tr>\n')
+
+    def visit_rubric(self, node):
+        self.body.append(self.starttag(node, 'p', '', CLASS='rubric'))
+
+    def depart_rubric(self, node):
+        self.body.append('</p>\n')
+
+    def visit_section(self, node):
+        self.section_level += 1
+        self.body.append(self.starttag(node, 'div', CLASS='section'))
+
+    def depart_section(self, node):
+        self.section_level -= 1
+        self.body.append('</div>\n')
+
+    def visit_sidebar(self, node):
+        self.body.append(self.starttag(node, 'div', CLASS='sidebar'))
+        self.in_sidebar = 1
+
+    def depart_sidebar(self, node):
+        self.body.append('</div>\n')
+        self.in_sidebar = None
+
+    def visit_status(self, node):
+        self.visit_docinfo_item(node, 'status', meta=None)
+
+    def depart_status(self, node):
+        self.depart_docinfo_item()
+
+    def visit_strong(self, node):
+        self.body.append('<strong>')
+
+    def depart_strong(self, node):
+        self.body.append('</strong>')
+
+    def visit_subscript(self, node):
+        self.body.append(self.starttag(node, 'sub', ''))
+
+    def depart_subscript(self, node):
+        self.body.append('</sub>')
+
+    def visit_substitution_definition(self, node):
+        """Internal only."""
+        raise nodes.SkipNode
+
+    def visit_substitution_reference(self, node):
+        self.unimplemented_visit(node)
+
+    def visit_subtitle(self, node):
+        if isinstance(node.parent, nodes.sidebar):
+            self.body.append(self.starttag(node, 'p', '',
+                                           CLASS='sidebar-subtitle'))
+            self.context.append('</p>\n')
+        else:
+            self.body.append(self.starttag(node, 'h2', '', CLASS='subtitle'))
+            self.context.append('</h2>\n')
+
+    def depart_subtitle(self, node):
+        self.body.append(self.context.pop())
+
+    def visit_superscript(self, node):
+        self.body.append(self.starttag(node, 'sup', ''))
+
+    def depart_superscript(self, node):
+        self.body.append('</sup>')
+
+    def visit_system_message(self, node):
+        if node['level'] < self.document.reporter['writer'].report_level:
+            # Level is too low to display:
+            raise nodes.SkipNode
+        self.body.append(self.starttag(node, 'div', CLASS='system-message'))
+        self.body.append('<p class="system-message-title">')
+        attr = {}
+        backref_text = ''
+        if node.hasattr('id'):
+            attr['name'] = node['id']
+        if node.hasattr('backrefs'):
+            backrefs = node['backrefs']
+            if len(backrefs) == 1:
+                backref_text = ('; <em><a href="#%s">backlink</a></em>'
+                                % backrefs[0])
+            else:
+                i = 1
+                backlinks = []
+                for backref in backrefs:
+                    backlinks.append('<a href="#%s">%s</a>' % (backref, i))
+                    i += 1
+                backref_text = ('; <em>backlinks: %s</em>'
+                                % ', '.join(backlinks))
+        if node.hasattr('line'):
+            line = ', line %s' % node['line']
+        else:
+            line = ''
+        if attr:
+            a_start = self.starttag({}, 'a', '', **attr)
+            a_end = '</a>'
+        else:
+            a_start = a_end = ''
+        self.body.append('System Message: %s%s/%s%s (<tt>%s</tt>%s)%s</p>\n'
+                         % (a_start, node['type'], node['level'], a_end,
+                            self.encode(node['source']), line, backref_text))
+
+    def depart_system_message(self, node):
+        self.body.append('</div>\n')
+
+    def visit_table(self, node):
+        self.body.append(
+              # "border=None" is a boolean attribute;
+              # it means "standard border", not "no border":
+              self.starttag(node, 'table', CLASS="table", border=None))
+
+    def depart_table(self, node):
+        self.body.append('</table>\n')
+
+    def visit_target(self, node):
+        if not (node.has_key('refuri') or node.has_key('refid')
+                or node.has_key('refname')):
+            self.body.append(self.starttag(node, 'a', '', CLASS='target'))
+            self.context.append('</a>')
+        else:
+            self.context.append('')
+
+    def depart_target(self, node):
+        self.body.append(self.context.pop())
+
+    def visit_tbody(self, node):
+        self.write_colspecs()
+        self.body.append(self.context.pop()) # '</colgroup>\n' or ''
+        self.body.append(self.starttag(node, 'tbody', valign='top'))
+
+    def depart_tbody(self, node):
+        self.body.append('</tbody>\n')
+
+    def visit_term(self, node):
+        self.body.append(self.starttag(node, 'dt', ''))
+
+    def depart_term(self, node):
+        """
+        Leave the end tag to `self.visit_definition()`, in case there's a
+        classifier.
+        """
+        pass
+
+    def visit_tgroup(self, node):
+        # Mozilla needs <colgroup>:
+        self.body.append(self.starttag(node, 'colgroup'))
+        # Appended by thead or tbody:
+        self.context.append('</colgroup>\n')
+
+    def depart_tgroup(self, node):
+        pass
+
+    def visit_thead(self, node):
+        self.write_colspecs()
+        self.body.append(self.context.pop()) # '</colgroup>\n'
+        # There may or may not be a <thead>; this is for <tbody> to use:
+        self.context.append('')
+        self.body.append(self.starttag(node, 'thead', valign='bottom'))
+
+    def depart_thead(self, node):
+        self.body.append('</thead>\n')
+
+    def visit_tip(self, node):
+        self.visit_admonition(node, 'tip')
+
+    def depart_tip(self, node):
+        self.depart_admonition()
+
+    def visit_title(self, node):
+        """Only 6 section levels are supported by HTML."""
+        check_id = 0
+        if isinstance(node.parent, nodes.topic):
+            self.body.append(
+                  self.starttag(node, 'p', '', CLASS='topic-title'))
+            check_id = 1
+        elif isinstance(node.parent, nodes.sidebar):
+            self.body.append(
+                  self.starttag(node, 'p', '', CLASS='sidebar-title'))
+            check_id = 1
+        elif isinstance(node.parent, nodes.admonition):
+            self.body.append(
+                  self.starttag(node, 'p', '', CLASS='admonition-title'))
+            check_id = 1
+        elif self.section_level == 0:
+            # document title
+            self.head.append('<title>%s</title>\n'
+                             % self.encode(node.astext()))
+            self.body.append(self.starttag(node, 'h1', '', CLASS='title'))
+            self.context.append('</h1>\n')
+        else:
+            self.body.append(
+                  self.starttag(node, 'h%s' % self.section_level, ''))
+            atts = {}
+            if node.parent.hasattr('id'):
+                atts['name'] = node.parent['id']
+            if node.hasattr('refid'):
+                atts['class'] = 'toc-backref'
+                atts['href'] = '#' + node['refid']
+            self.body.append(self.starttag({}, 'a', '', **atts))
+            self.context.append('</a></h%s>\n' % (self.section_level))
+        if check_id:
+            if node.parent.hasattr('id'):
+                self.body.append(
+                    self.starttag({}, 'a', '', name=node.parent['id']))
+                self.context.append('</a></p>\n')
+            else:
+                self.context.append('</p>\n')
+
+    def depart_title(self, node):
+        self.body.append(self.context.pop())
+
+    def visit_title_reference(self, node):
+        self.body.append(self.starttag(node, 'cite', ''))
+
+    def depart_title_reference(self, node):
+        self.body.append('</cite>')
+
+    def visit_topic(self, node):
+        self.body.append(self.starttag(node, 'div', CLASS='topic'))
+        self.topic_class = node.get('class')
+
+    def depart_topic(self, node):
+        self.body.append('</div>\n')
+        self.topic_class = ''
+
+    def visit_transition(self, node):
+        self.body.append(self.emptytag(node, 'hr'))
+
+    def depart_transition(self, node):
+        pass
+
+    def visit_version(self, node):
+        self.visit_docinfo_item(node, 'version', meta=None)
+
+    def depart_version(self, node):
+        self.depart_docinfo_item()
+
+    def visit_warning(self, node):
+        self.visit_admonition(node, 'warning')
+
+    def depart_warning(self, node):
+        self.depart_admonition()
+
+    def unimplemented_visit(self, node):
+        raise NotImplementedError('visiting unimplemented node type: %s'
+                                  % node.__class__.__name__)
+
+
+class SimpleListChecker(nodes.GenericNodeVisitor):
+
+    """
+    Raise `nodes.SkipNode` if non-simple list item is encountered.
+
+    Here "simple" means a list item containing nothing other than a single
+    paragraph, a simple list, or a paragraph followed by a simple list.
+    """
+
+    def default_visit(self, node):
+        raise nodes.NodeFound
+
+    def visit_bullet_list(self, node):
+        pass
+
+    def visit_enumerated_list(self, node):
+        pass
+
+    def visit_list_item(self, node):
+        children = []
+        for child in node.get_children():
+            if not isinstance(child, nodes.Invisible):
+                children.append(child)
+        if (children and isinstance(children[0], nodes.paragraph)
+            and (isinstance(children[-1], nodes.bullet_list)
+                 or isinstance(children[-1], nodes.enumerated_list))):
+            children.pop()
+        if len(children) <= 1:
+            return
+        else:
+            raise nodes.NodeFound
+
+    def visit_paragraph(self, node):
+        raise nodes.SkipNode
+
+    def invisible_visit(self, node):
+        """Invisible nodes should be ignored."""
+        pass
+
+    visit_comment = invisible_visit
+    visit_substitution_definition = invisible_visit
+    visit_target = invisible_visit
+    visit_pending = invisible_visit


=== Zope/lib/python/docutils/writers/latex2e.py 1.1.2.2 => 1.1.2.3 === (1162/1562 lines abridged)
--- /dev/null	Sun Nov 30 11:05:27 2003
+++ Zope/lib/python/docutils/writers/latex2e.py	Sun Nov 30 11:05:26 2003
@@ -0,0 +1,1559 @@
+"""
+:Author: Engelbert Gruber
+:Contact: grubert at users.sourceforge.net
+:Revision: $Revision$
+:Date: $Date$
+:Copyright: This module has been placed in the public domain.
+
+LaTeX2e document tree Writer.
+"""
+
+__docformat__ = 'reStructuredText'
+
+# code contributions from several people included, thanks too all.
+# some named: David Abrahams, Julien Letessier, who is missing.
+#
+# convention deactivate code by two # e.g. ##.
+
+import sys
+import time
+import re
+import string
+from types import ListType
+from docutils import frontend, nodes, languages, writers
+
+class Writer(writers.Writer):
+
+    supported = ('latex','latex2e')
+    """Formats this writer supports."""
+
+    settings_spec = (
+        'LaTeX-Specific Options',
+        'The LaTeX "--output-encoding" default is "latin-1:strict".',
+        (('Specify documentclass.  Default is "article".',
+          ['--documentclass'],
+          {'default': 'article', }),
+         ('Specify document options.  Multiple options can be given, '
+          'separated by commas.  Default is "10pt".',
+          ['--documentoptions'],
+          {'default': '10pt', }),
+         ('Use LaTeX footnotes. '
+          'Default: no, uses figures.',
+          ['--use-latex-footnotes'],
+          {'default': 0, 'action': 'store_true',
+           'validator': frontend.validate_boolean}),
+         ('Format for footnote references: one of "superscript" or '
+          '"brackets".  Default is "brackets".',
+          ['--footnote-references'],
+          {'choices': ['superscript', 'brackets'], 'default': 'brackets',
+           'metavar': '<format>'}),
+         ('Format for block quote attributions: one of "dash" (em-dash '
+          'prefix), "parentheses"/"parens", or "none".  Default is "dash".',
+          ['--attribution'],
+          {'choices': ['dash', 'parentheses', 'parens', 'none'],
+           'default': 'dash', 'metavar': '<format>'}),
+         ('Specify a stylesheet file. The file will be "input" by latex in '
+          'the document header.  Default is no stylesheet ("").  '
+          'Overridden by --stylesheet-path.',
+          ['--stylesheet'],
+          {'default': '', 'metavar': '<file>'}),
+         ('Specify a stylesheet file, relative to the current working '
+          'directory.  Overrides --stylesheet.',
+          ['--stylesheet-path'],
+          {'metavar': '<file>'}),
+         ('Link to the stylesheet in the output LaTeX file.  This is the '
+          'default.',
+          ['--link-stylesheet'],
+          {'dest': 'embed_stylesheet', 'action': 'store_false',
+           'validator': frontend.validate_boolean}),
+         ('Embed the stylesheet in the output LaTeX file.  The stylesheet '
+          'file must be accessible during processing (--stylesheet-path is '
+          'recommended).',
+          ['--embed-stylesheet'], {'action': 'store_true'}),
+         ('Table of contents by docutils (default) or latex. Latex (writer) '
+          'supports only one ToC per document, but docutils does not write '
+          'pagenumbers.',
+          ['--use-latex-toc'],
+          {'default': 0, 'action': 'store_true',
+           'validator': frontend.validate_boolean}),
+         ('Let LaTeX print author and date, donot show it in docutils document info.',
+          ['--use-latex-docinfo'],
+          {'default': 0, 'action': 'store_true',
+           'validator': frontend.validate_boolean}),
+         ('Color of any hyperlinks embedded in text '
+          '(default: "blue", "0" to disable).',
+          ['--hyperlink-color'], {'default': 'blue'}),))
+
+    settings_defaults = {'output_encoding': 'latin-1'}
+
+    config_section = 'latex2e writer'
+    config_section_dependencies = ('writers',)
+
+    output = None
+    """Final translated form of `document`."""
+
+    def translate(self):
+        visitor = LaTeXTranslator(self.document)
+        self.document.walkabout(visitor)
+        self.output = visitor.astext()
+        self.head_prefix = visitor.head_prefix
+        self.head = visitor.head
+        self.body_prefix = visitor.body_prefix
+        self.body = visitor.body
+        self.body_suffix = visitor.body_suffix
+
+"""
+Notes on LaTeX
+--------------
+
+* latex does not support multiple tocs in one document.
+  (might be no limitation except for docutils documentation)
+
+* width
+
+  * linewidth - width of a line in the local environment
+  * textwidth - the width of text on the page
+
+  Maybe always use linewidth ?
+
+  *Bug* inside a minipage a (e.g. Sidebar) the linewidth is
+        not changed, needs fix in docutils so that tables
+        are not too wide.
+
+        So we add locallinewidth set it initially and
+        on entering sidebar and reset on exit.
+"""
+
+class Babel:
+    """Language specifics for LaTeX."""
+    # country code by a.schlock.
+    # partly manually converted from iso and babel stuff, dialects and some
+    _ISO639_TO_BABEL = {
+        'no': 'norsk',     #XXX added by hand ( forget about nynorsk?)
+        'gd': 'scottish',  #XXX added by hand
+        'hu': 'magyar',    #XXX added by hand
+        'pt': 'portuguese',#XXX added by hand
+        'sl': 'slovenian',
+        'af': 'afrikaans',
+        'bg': 'bulgarian',
+        'br': 'breton',
+        'ca': 'catalan',
+        'cs': 'czech',
+        'cy': 'welsh',
+        'da': 'danish',
+        'fr': 'french',
+        # french, francais, canadien, acadian
+        'de': 'ngerman',  #XXX rather than german
+        # ngerman, naustrian, german, germanb, austrian
+        'el': 'greek',
+        'en': 'english',
+        # english, USenglish, american, UKenglish, british, canadian
+        'eo': 'esperanto',
+        'es': 'spanish',
+        'et': 'estonian',
+        'eu': 'basque',
+        'fi': 'finnish',
+        'ga': 'irish',
+        'gl': 'galician',
+        'he': 'hebrew',
+        'hr': 'croatian',
+        'hu': 'hungarian',
+        'is': 'icelandic',
+        'it': 'italian',
+        'la': 'latin',
+        'nl': 'dutch',
+        'pl': 'polish',
+        'pt': 'portuguese',
+        'ro': 'romanian',
+        'ru': 'russian',
+        'sk': 'slovak',
+        'sr': 'serbian',
+        'sv': 'swedish',
+        'tr': 'turkish',
+        'uk': 'ukrainian'
+    }
+
+    def __init__(self,lang):
+        self.language = lang
+        # pdflatex does not produce double quotes for ngerman in tt.
+        self.double_quote_replacment = None
+        if re.search('^de',self.language):
+            # maybe use: {\glqq} {\grqq}.
+            self.quotes = ("\"`", "\"'")
+            self.double_quote_replacment = "{\\dq}"
+        else:
+            self.quotes = ("``", "''")
+        self.quote_index = 0
+
+    def next_quote(self):
+        q = self.quotes[self.quote_index]
+        self.quote_index = (self.quote_index+1)%2
+        return q
+
+    def quote_quotes(self,text):
+        t = None
+        for part in text.split('"'):
+            if t == None:
+                t = part

[-=- -=- -=- 1162 lines omitted -=- -=- -=-]

+        if self.use_longtable:
+            self.body.append('\\end{longtable}\n')
+        else:
+            self.body.append('\\end{tabularx}\n')
+            sentinel = self.context.pop()
+            if sentinel != 'table_sentinel':
+                print 'context:', self.context + [sentinel]
+                raise AssertionError
+
+    def table_preamble(self):
+        if self.use_longtable:
+            self.body.append('{%s}\n' % self.get_colspecs())
+        else:
+            if self.context[-1] != 'table_sentinel':
+                self.body.append('{%s}' % ('|X' * self.context.pop() + '|'))
+                self.body.append('\n\\hline')
+
+    def visit_target(self, node):
+        # BUG: why not (refuri or refid or refname) means not footnote ?
+        if not (node.has_key('refuri') or node.has_key('refid')
+                or node.has_key('refname')):
+            self.body.append('\\hypertarget{%s}{' % node['id'])
+            self.context.append('}')
+        else:
+            self.context.append('')
+
+    def depart_target(self, node):
+        self.body.append(self.context.pop())
+
+    def visit_tbody(self, node):
+        # BUG write preamble if not yet done (colspecs not [])
+        # for tables without heads.
+        if self.colspecs:
+            self.visit_thead(None)
+            self.depart_thead(None)
+        self.body.append('%[visit_tbody]\n')
+
+    def depart_tbody(self, node):
+        self.body.append('%[depart_tbody]\n')
+
+    def visit_term(self, node):
+        self.body.append('\\item[')
+
+    def depart_term(self, node):
+        # definition list term.
+        self.body.append(']\n')
+
+    def visit_tgroup(self, node):
+        #self.body.append(self.starttag(node, 'colgroup'))
+        #self.context.append('</colgroup>\n')
+        pass
+
+    def depart_tgroup(self, node):
+        pass
+
+    def visit_thead(self, node):
+        # number_of_columns will be zero after get_colspecs.
+        # BUG ! push onto context for depart to pop it.
+        number_of_columns = len(self.colspecs)
+        self.table_preamble()
+        #BUG longtable needs firstpage and lastfooter too.
+        self.body.append('\\hline\n')
+
+    def depart_thead(self, node):
+        if self.use_longtable:
+            # the table header written should be on every page
+            # => \endhead
+            self.body.append('\\endhead\n')
+            # and the firsthead => \endfirsthead
+            # BUG i want a "continued from previous page" on every not
+            # firsthead, but then we need the header twice.
+            #
+            # there is a \endfoot and \endlastfoot too.
+            # but we need the number of columns to
+            # self.body.append('\\multicolumn{%d}{c}{"..."}\n' % number_of_columns)
+            # self.body.append('\\hline\n\\endfoot\n')
+            # self.body.append('\\hline\n')
+            # self.body.append('\\endlastfoot\n')
+
+
+    def visit_tip(self, node):
+        self.visit_admonition(node, 'tip')
+
+    def depart_tip(self, node):
+        self.depart_admonition()
+
+    def bookmark(self, node):
+        """Append latex href and pdfbookmarks for titles.
+        """
+        if node.parent.hasattr('id'):
+            self.body.append('\\hypertarget{%s}{}\n' % node.parent['id'])
+            if not self.use_latex_toc:
+                # BUG level depends on style. pdflatex allows level 0 to 3
+                # ToC would be the only on level 0 so i choose to decrement the rest.
+                # "Table of contents" bookmark to see the ToC. To avoid this
+                # we set all zeroes to one.
+                l = self.section_level
+                if l>0:
+                    l = l-1
+                # pdftex does not like "_" subscripts in titles
+                text = node.astext().replace("_","\\_")
+                self.body.append('\\pdfbookmark[%d]{%s}{%s}\n' % \
+                        (l,text,node.parent['id']))
+
+    def visit_title(self, node):
+        """Only 3 section levels are supported by LaTeX article (AFAIR)."""
+
+
+        if isinstance(node.parent, nodes.topic):
+            # section titles before the table of contents.
+            self.bookmark(node)
+            # BUG: latex chokes on center environment with "perhaps a missing item".
+            # so we use hfill.
+            self.body.append('\\subsection*{~\\hfill ')
+            # the closing brace for subsection.
+            self.context.append('\\hfill ~}\n')
+        elif isinstance(node.parent, nodes.sidebar):
+            self.body.append('\\textbf{\\large ')
+            self.context.append('}\n\\smallskip\n')
+        elif self.section_level == 0:
+            # document title
+            self.title = self.encode(node.astext())
+            if not self.pdfinfo == None:
+                self.pdfinfo.append( 'pdftitle={%s}' % self.encode(node.astext()) )
+            raise nodes.SkipNode
+        else:
+            self.body.append('\n\n')
+            self.body.append('%' + '_' * 75)
+            self.body.append('\n\n')
+            self.bookmark(node)
+            # section_level 0 is title and handled above.
+            # BUG: latex has no deeper sections (actually paragrah is no section either).
+            if self.use_latex_toc:
+                section_star = ""
+            else:
+                section_star = "*"
+            if (self.section_level<=3):  # 1,2,3
+                self.body.append('\\%ssection%s{' % ('sub'*(self.section_level-1),section_star))
+            elif (self.section_level==4):
+                #self.body.append('\\paragraph*{')
+                self.body.append('\\subsubsection%s{' % (section_star))
+            else:
+                #self.body.append('\\subparagraph*{')
+                self.body.append('\\subsubsection%s{' % (section_star))
+            # BUG: self.body.append( '\\label{%s}\n' % name)
+            self.context.append('}\n')
+
+    def depart_title(self, node):
+        self.body.append(self.context.pop())
+
+    def visit_topic(self, node):
+        self.topic_class = node.get('class')
+        if self.use_latex_toc:
+            self.body.append('\\tableofcontents\n\n\\bigskip\n')
+            self.topic_class = ''
+            raise nodes.SkipNode
+
+    def depart_topic(self, node):
+        self.topic_class = ''
+        self.body.append('\n')
+
+    def visit_rubric(self, node):
+#        self.body.append('\\hfill {\\color{red}\\bfseries{}')
+#        self.context.append('} \\hfill ~\n')
+        self.body.append('\\rubric{')
+        self.context.append('}\n')
+
+    def depart_rubric(self, node):
+        self.body.append(self.context.pop())
+
+    def visit_transition(self, node):
+        self.body.append('\n\n')
+        self.body.append('%' + '_' * 75)
+        self.body.append('\n\\hspace*{\\fill}\\hrulefill\\hspace*{\\fill}')
+        self.body.append('\n\n')
+
+    def depart_transition(self, node):
+        #self.body.append('[depart_transition]')
+        pass
+
+    def visit_version(self, node):
+        self.visit_docinfo_item(node, 'version')
+
+    def depart_version(self, node):
+        self.depart_docinfo_item(node)
+
+    def visit_warning(self, node):
+        self.visit_admonition(node, 'warning')
+
+    def depart_warning(self, node):
+        self.depart_admonition()
+
+    def unimplemented_visit(self, node):
+        raise NotImplementedError('visiting unimplemented node type: %s'
+                                  % node.__class__.__name__)
+
+#    def unknown_visit(self, node):
+#    def default_visit(self, node):
+
+# vim: set ts=4 et ai :


=== Zope/lib/python/docutils/writers/pep_html.py 1.2.10.2 => 1.2.10.3 ===
--- /dev/null	Sun Nov 30 11:05:27 2003
+++ Zope/lib/python/docutils/writers/pep_html.py	Sun Nov 30 11:05:26 2003
@@ -0,0 +1,103 @@
+# Author: David Goodger
+# Contact: goodger at users.sourceforge.net
+# Revision: $Revision$
+# Date: $Date$
+# Copyright: This module has been placed in the public domain.
+
+"""
+PEP HTML Writer.
+"""
+
+__docformat__ = 'reStructuredText'
+
+
+import sys
+import docutils
+from docutils import frontend, nodes, utils
+from docutils.writers import html4css1
+
+
+class Writer(html4css1.Writer):
+
+    settings_spec = html4css1.Writer.settings_spec + (
+        'PEP/HTML-Specific Options',
+        """The HTML --footnote-references option's default is set to """
+        '"brackets".',
+        (('Specify a template file.  Default is "pep-html-template".',
+          ['--template'],
+          {'default': 'pep-html-template', 'metavar': '<file>'}),
+         ('Python\'s home URL.  Default is ".." (parent directory).',
+          ['--python-home'],
+          {'default': '..', 'metavar': '<URL>'}),
+         ('Home URL prefix for PEPs.  Default is "." (current directory).',
+          ['--pep-home'],
+          {'default': '.', 'metavar': '<URL>'}),
+         # Workaround for SourceForge's broken Python
+         # (``import random`` causes a segfault).
+         (frontend.SUPPRESS_HELP,
+          ['--no-random'],
+          {'action': 'store_true', 'validator': frontend.validate_boolean}),))
+
+    settings_default_overrides = {'footnote_references': 'brackets'}
+
+    relative_path_settings = (html4css1.Writer.relative_path_settings
+                              + ('template',))
+
+    config_section = 'pep_html writer'
+    config_section_dependencies = ('writers', 'html4css1 writer')
+
+    def __init__(self):
+        html4css1.Writer.__init__(self)
+        self.translator_class = HTMLTranslator
+
+    def translate(self):
+        html4css1.Writer.translate(self)
+        settings = self.document.settings
+        template = open(settings.template).read()
+        # Substitutions dict for template:
+        subs = {}
+        subs['encoding'] = settings.output_encoding
+        subs['version'] = docutils.__version__
+        subs['stylesheet'] = ''.join(self.stylesheet)
+        pyhome = settings.python_home
+        subs['pyhome'] = pyhome
+        subs['pephome'] = settings.pep_home
+        if pyhome == '..':
+            subs['pepindex'] = '.'
+        else:
+            subs['pepindex'] = pyhome + '/peps/'
+        index = self.document.first_child_matching_class(nodes.field_list)
+        header = self.document[index]
+        pepnum = header[0][1].astext()
+        subs['pep'] = pepnum
+        if settings.no_random:
+            subs['banner'] = 0
+        else:
+            import random
+            subs['banner'] = random.randrange(64)
+        try:
+            subs['pepnum'] = '%04i' % int(pepnum)
+        except:
+            subs['pepnum'] = pepnum
+        subs['title'] = header[1][1].astext()
+        subs['body'] = ''.join(
+            self.body_pre_docinfo + self.docinfo + self.body)
+        subs['body_suffix'] = ''.join(self.body_suffix)
+        self.output = template % subs
+
+
+class HTMLTranslator(html4css1.HTMLTranslator):
+
+    def get_stylesheet_reference(self, relative_to=None):
+        settings = self.settings
+        if relative_to == None:
+            relative_to = settings._destination
+        if settings.stylesheet_path:
+            return utils.relative_path(relative_to, settings.stylesheet_path)
+        else:
+            return settings.stylesheet
+
+    def depart_field_list(self, node):
+        html4css1.HTMLTranslator.depart_field_list(self, node)
+        if node.get('class') == 'rfc2822':
+             self.body.append('<hr />\n')


=== Zope/lib/python/docutils/writers/pseudoxml.py 1.2.10.2 => 1.2.10.3 ===
--- /dev/null	Sun Nov 30 11:05:27 2003
+++ Zope/lib/python/docutils/writers/pseudoxml.py	Sun Nov 30 11:05:26 2003
@@ -0,0 +1,33 @@
+# Authors: David Goodger
+# Contact: goodger at users.sourceforge.net
+# Revision: $Revision$
+# Date: $Date$
+# Copyright: This module has been placed in the public domain.
+
+"""
+Simple internal document tree Writer, writes indented pseudo-XML.
+"""
+
+__docformat__ = 'reStructuredText'
+
+
+from docutils import writers
+
+
+class Writer(writers.Writer):
+
+    supported = ('pprint', 'pformat', 'pseudoxml')
+    """Formats this writer supports."""
+
+    config_section = 'pseudoxml writer'
+    config_section_dependencies = ('writers',)
+
+    output = None
+    """Final translated form of `document`."""
+
+    def translate(self):
+        self.output = self.document.pformat()
+
+    def supports(self, format):
+        """This writer supports all format-specific elements."""
+        return 1




More information about the Zope-Checkins mailing list