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

Andreas Jung andreas at andreas-jung.com
Sun Oct 9 10:43:47 EDT 2005


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

Modified Files:
      Tag: Zope-2_7-branch
	__init__.py docutils_xml.py html4css1.py latex2e.py 
	pep_html.py pseudoxml.py 
Log Message:
upgrade to docutils 0.3.9


=== Zope/lib/python/docutils/writers/__init__.py 1.2.10.7 => 1.2.10.8 ===


=== Zope/lib/python/docutils/writers/docutils_xml.py 1.2.10.7 => 1.2.10.8 ===


=== Zope/lib/python/docutils/writers/html4css1.py 1.2.10.7 => 1.2.10.8 ===
--- Zope/lib/python/docutils/writers/html4css1.py:1.2.10.7	Fri Jan  7 08:26:06 2005
+++ Zope/lib/python/docutils/writers/html4css1.py	Sun Oct  9 10:43:46 2005
@@ -63,6 +63,20 @@
           ['--initial-header-level'],
           {'choices': '1 2 3 4 5 6'.split(), 'default': '1',
            'metavar': '<level>'}),
+         ('Specify the maximum width (in characters) for one-column field '
+          'names.  Longer field names will span an entire row of the table '
+          'used to render the field list.  Default is 14 characters.  '
+          'Use 0 for "no limit".',
+          ['--field-name-limit'],
+          {'default': 14, 'metavar': '<level>',
+           'validator': frontend.validate_nonnegative_int}),
+         ('Specify the maximum width (in characters) for options in option '
+          'lists.  Longer options will span an entire row of the table used '
+          'to render the option list.  Default is 14 characters.  '
+          'Use 0 for "no limit".',
+          ['--option-limit'],
+          {'default': 14, 'metavar': '<level>',
+           'validator': frontend.validate_nonnegative_int}),
          ('Format for footnote references: one of "superscript" or '
           '"brackets".  Default is "brackets".',
           ['--footnote-references'],
@@ -87,7 +101,12 @@
          ('Omit the XML declaration.  Use with caution.',
           ['--no-xml-declaration'],
           {'dest': 'xml_declaration', 'default': 1, 'action': 'store_false',
-           'validator': frontend.validate_boolean}),))
+           'validator': frontend.validate_boolean}),
+         ('Scramble email addresses to confuse harvesters.  '
+          'For example, "abc at example.org" will become '
+          '``<a href="mailto:%61%62%63%40...">abc at example dot org</a>``.',
+          ['--cloak-email-addresses'],
+          {'action': 'store_true', 'validator': frontend.validate_boolean}),))
 
     relative_path_settings = ('stylesheet_path',)
 
@@ -99,10 +118,9 @@
         self.translator_class = HTMLTranslator
 
     def translate(self):
-        visitor = self.translator_class(self.document)
+        self.visitor = visitor = self.translator_class(self.document)
         self.document.walkabout(visitor)
         self.output = visitor.astext()
-        self.visitor = visitor
         for attr in ('head_prefix', 'stylesheet', 'head', 'body_prefix',
                      'body_pre_docinfo', 'docinfo', 'body', 'fragment',
                      'body_suffix'):
@@ -111,7 +129,9 @@
     def assemble_parts(self):
         writers.Writer.assemble_parts(self)
         for part in ('title', 'subtitle', 'docinfo', 'body', 'header',
-                     'footer', 'meta', 'stylesheet', 'fragment'):
+                     'footer', 'meta', 'stylesheet', 'fragment',
+                     'html_prolog', 'html_head', 'html_title', 'html_subtitle',
+                     'html_body'):
             self.parts[part] = ''.join(getattr(self.visitor, part))
 
 
@@ -163,16 +183,15 @@
                ' 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')
+    head_prefix_template = ('<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}
+    named_tags = ['a', 'applet', 'form', 'frame', 'iframe', 'img', 'map']
     words_and_spaces = re.compile(r'\S+| +|\n')
 
     def __init__(self, document):
@@ -182,14 +201,17 @@
         self.language = languages.get_language(lcode)
         self.meta = [self.content_type % settings.output_encoding,
                      self.generator % docutils.__version__]
-        self.head_prefix = [
-              self.doctype,
-              self.html_head % (lcode, lcode)]
-        self.head_prefix.extend(self.meta)
+        self.head_prefix = []
+        self.html_prolog = []
         if settings.xml_declaration:
-            self.head_prefix.insert(0, self.xml_declaration
+            self.head_prefix.append(self.xml_declaration
                                     % settings.output_encoding)
-        self.head = []
+            # encoding not interpolated:
+            self.html_prolog.append(self.xml_declaration)
+        self.head_prefix.extend([self.doctype,
+                                 self.head_prefix_template % (lcode, lcode)])
+        self.html_prolog.append(self.doctype)
+        self.head = self.meta[:]
         if settings.embed_stylesheet:
             stylesheet = utils.get_stylesheet_reference(settings,
                 os.path.join(os.getcwd(), 'dummy'))
@@ -199,7 +221,8 @@
         else:
             stylesheet = utils.get_stylesheet_reference(settings)
             if stylesheet:
-                self.stylesheet = [self.stylesheet_link % stylesheet]
+                self.stylesheet = [self.stylesheet_link
+                                   % self.encode(stylesheet)]
             else:
                 self.stylesheet = []
         self.body_prefix = ['</head>\n<body>\n']
@@ -215,7 +238,7 @@
         # A heterogenous stack used in conjunction with the tree traversal.
         # Make sure that the pops correspond to the pushes:
         self.context = []
-        self.topic_class = ''
+        self.topic_classes = []
         self.colspecs = []
         self.compact_p = 1
         self.compact_simple = None
@@ -225,7 +248,12 @@
         self.subtitle = []
         self.header = []
         self.footer = []
+        self.html_head = [self.content_type] # charset not interpolated
+        self.html_title = []
+        self.html_subtitle = []
+        self.html_body = []
         self.in_document_title = 0
+        self.in_mailto = 0
 
     def astext(self):
         return ''.join(self.head_prefix + self.head
@@ -245,6 +273,20 @@
         text = text.replace(u'\u00a0', "&nbsp;")
         return text
 
+    def cloak_mailto(self, uri):
+        """Try to hide a mailto: URL from harvesters."""
+        addr = uri.split(':', 1)[1]
+        if '?' in addr:
+            addr, query = addr.split('?', 1)
+            query = '?' + query
+        else:
+            query = ''
+        escaped = ['%%%02X' % ord(c) for c in addr]
+        return 'mailto:%s%s' % (''.join(escaped), query)
+
+    def cloak_email(self, addr):
+        return addr.replace('@', ' at ').replace('.', ' dot ')
+
     def attval(self, text,
                whitespace=re.compile('[\n\r\t\v\f]')):
         """Cleanse, HTML encode, and return attribute value text."""
@@ -256,17 +298,21 @@
         are extracted), tag name, and optional attributes.
         """
         tagname = tagname.lower()
+        prefix = []
         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):
+        classes = node.get('classes', [])
+        if atts.has_key('class'):
+            classes.append(atts['class'])
+        if classes:
+            atts['class'] = ' '.join(classes)
+        assert not atts.has_key('id')
+        if node.get('ids'):
+            atts['id'] = node['ids'][0]
+            for id in node['ids'][1:]:
+                prefix.append('<span id="%s"></span>' % id)
+        if atts.has_key('id') and tagname in self.named_tags:
             atts['name'] = atts['id']   # for compatibility with old browsers
         attlist = atts.items()
         attlist.sort()
@@ -285,19 +331,23 @@
                 except TypeError:       # for Python 2.1 compatibility:
                     uval = unicode(str(value))
                 parts.append('%s="%s"' % (name.lower(), self.attval(uval)))
-        return '<%s%s>%s' % (' '.join(parts), infix, suffix)
+        return ''.join(prefix) + '<%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 set_first_last(self, node):
-        if len(node):
-            node[0].set_class('first')
-            node[-1].set_class('last')
+        children = [n for n in node if not isinstance(n, nodes.Invisible)]
+        if children:
+            children[0]['classes'].append('first')
+            children[-1]['classes'].append('last')
 
     def visit_Text(self, node):
-        self.body.append(self.encode(node.astext()))
+        text = node.astext()
+        if self.in_mailto and self.settings.cloak_email_addresses:
+            text = self.cloak_email(text)
+        self.body.append(self.encode(text))
 
     def depart_Text(self, node):
         pass
@@ -389,7 +439,7 @@
         self.compact_p = None
         self.compact_simple = (self.settings.compact_lists and
                                (self.compact_simple
-                                or self.topic_class == 'contents'
+                                or self.topic_classes == ['contents']
                                 or self.check_simple_list(node)))
         if self.compact_simple and not old_compact_simple:
             atts['class'] = 'simple'
@@ -425,14 +475,9 @@
                          '</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', '[',
-                                       CLASS='citation-reference',
-                                       **(href and {'href': href} or {})))
+        href = '#' + node['refid']
+        self.body.append(self.starttag(
+            node, 'a', '[', CLASS='citation-reference', href=href))
 
     def depart_citation_reference(self, node):
         self.body.append(']</a>')
@@ -446,6 +491,8 @@
 
     def visit_colspec(self, node):
         self.colspecs.append(node)
+        # "stubs" list is an attribute of the tgroup element:
+        node.parent.stubs.append(node.attributes.get('stub'))
 
     def depart_colspec(self, node):
         pass
@@ -470,10 +517,10 @@
     def visit_compound(self, node):
         self.body.append(self.starttag(node, 'div', CLASS='compound'))
         if len(node) > 1:
-            node[0].set_class('compound-first')
-            node[-1].set_class('compound-last')
+            node[0]['classes'].append('compound-first')
+            node[-1]['classes'].append('compound-last')
             for child in node[1:-1]:
-                child.set_class('compound-middle')
+                child['classes'].append('compound-middle')
 
     def depart_compound(self, node):
         self.body.append('</div>\n')
@@ -562,9 +609,9 @@
                          % self.language.labels[name])
         if len(node):
             if isinstance(node[0], nodes.Element):
-                node[0].set_class('first')
+                node[0]['classes'].append('first')
             if isinstance(node[-1], nodes.Element):
-                node[-1].set_class('last')
+                node[-1]['classes'].append('last')
 
     def depart_docinfo_item(self):
         self.body.append('</td></tr>\n')
@@ -579,12 +626,17 @@
         # empty or untitled document?
         if not len(node) or not isinstance(node[0], nodes.title):
             # for XHTML conformance, modulo IE6 appeasement:
-            self.head.insert(0, '<title></title>\n')
+            self.head.append('<title></title>\n')
 
     def depart_document(self, node):
         self.fragment.extend(self.body)
         self.body_prefix.append(self.starttag(node, 'div', CLASS='document'))
         self.body_suffix.insert(0, '</div>\n')
+        # skip content-type meta tag with interpolated charset value:
+        self.html_head.extend(self.head[1:])
+        self.html_body.extend(self.body_prefix[1:] + self.body_pre_docinfo
+                              + self.docinfo + self.body
+                              + self.body_suffix[:-1])
 
     def visit_emphasis(self, node):
         self.body.append('<em>')
@@ -593,15 +645,24 @@
         self.body.append('</em>')
 
     def visit_entry(self, node):
+        atts = {'class': []}
         if isinstance(node.parent.parent, nodes.thead):
+            atts['class'].append('head')
+        if node.parent.parent.parent.stubs[node.parent.column]:
+            # "stubs" list is an attribute of the tgroup element
+            atts['class'].append('stub')
+        if atts['class']:
             tagname = 'th'
+            atts['class'] = ' '.join(atts['class'])
         else:
             tagname = 'td'
-        atts = {}
+            del atts['class']
+        node.parent.column += 1
         if node.has_key('morerows'):
             atts['rowspan'] = node['morerows'] + 1
         if node.has_key('morecols'):
             atts['colspan'] = node['morecols'] + 1
+            node.parent.column += node['morecols']
         self.body.append(self.starttag(node, tagname, '', **atts))
         self.context.append('</%s>\n' % tagname.lower())
         if len(node) == 0:              # empty cell
@@ -629,7 +690,7 @@
         self.compact_p = None
         self.compact_simple = (self.settings.compact_lists and
                                (self.compact_simple
-                                or self.topic_class == 'contents'
+                                or self.topic_classes == ['contents']
                                 or self.check_simple_list(node)))
         if self.compact_simple and not old_compact_simple:
             atts['class'] = (atts.get('class', '') + ' simple').strip()
@@ -675,7 +736,8 @@
             atts['class'] = 'docinfo-name'
         else:
             atts['class'] = 'field-name'
-        if len(node.astext()) > 14:
+        if ( self.settings.field_name_limit
+             and len(node.astext()) > self.settings.field_name_limit):
             atts['colspan'] = 2
             self.context.append('</tr>\n<tr><td>&nbsp;</td>')
         else:
@@ -690,6 +752,8 @@
         atts = {'class': 'figure'}
         if node.get('width'):
             atts['style'] = 'width: %spx' % node['width']
+        if node.get('align'):
+            atts['align'] = node['align']
         self.body.append(self.starttag(node, 'div', **atts))
 
     def depart_figure(self, node):
@@ -700,9 +764,10 @@
 
     def depart_footer(self, node):
         start = self.context.pop()
-        footer = (['<hr class="docutils footer" />\n',
-                   self.starttag(node, 'div', CLASS='footer')]
-                  + self.body[start:] + ['</div>\n'])
+        footer = [self.starttag(node, 'div', CLASS='footer'),
+                  '<hr class="footer" />\n']
+        footer.extend(self.body[start:])
+        footer.append('\n</div>\n')
         self.footer.extend(footer)
         self.body_suffix[:0] = footer
         del self.body[start:]
@@ -718,12 +783,13 @@
 
     def footnote_backrefs(self, node):
         backlinks = []
-        if self.settings.footnote_backlinks and node.hasattr('backrefs'):
-            backrefs = node['backrefs']
+        backrefs = node['backrefs']
+        if self.settings.footnote_backlinks and backrefs:
             if len(backrefs) == 1:
                 self.context.append('')
-                self.context.append('<a class="fn-backref" href="#%s" '
-                                    'name="%s">' % (backrefs[0], node['id']))
+                self.context.append(
+                    '<a class="fn-backref" href="#%s" name="%s">'
+                    % (backrefs[0], node['ids'][0]))
             else:
                 i = 1
                 for backref in backrefs:
@@ -731,41 +797,34 @@
                                      % (backref, i))
                     i += 1
                 self.context.append('<em>(%s)</em> ' % ', '.join(backlinks))
-                self.context.append('<a name="%s">' % node['id'])
+                self.context.append('<a name="%s">' % node['ids'][0])
         else:
             self.context.append('')
-            self.context.append('<a name="%s">' % node['id'])
+            self.context.append('<a name="%s">' % node['ids'][0])
         # If the node does not only consist of a label.
         if len(node) > 1:
             # If there are preceding backlinks, we do not set class
             # 'first', because we need to retain the top-margin.
             if not backlinks:
-                node[1].set_class('first')
-            node[-1].set_class('last')
+                node[1]['classes'].append('first')
+            node[-1]['classes'].append('last')
 
     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']]
+        href = '#' + node['refid']
         format = self.settings.footnote_references
         if format == 'brackets':
             suffix = '['
             self.context.append(']')
-        elif format == 'superscript':
+        else:
+            assert format == 'superscript'
             suffix = '<sup>'
             self.context.append('</sup>')
-        else:                           # shouldn't happen
-            suffix = '???'
-            self.content.append('???')
         self.body.append(self.starttag(node, 'a', suffix,
-                                       CLASS='footnote-reference',
-                                       **(href and {'href': href} or {})))
+                                       CLASS='footnote-reference', href=href))
 
     def depart_footnote_reference(self, node):
         self.body.append(self.context.pop() + '</a>')
@@ -783,9 +842,9 @@
         start = self.context.pop()
         header = [self.starttag(node, 'div', CLASS='header')]
         header.extend(self.body[start:])
-        header.append('<hr class="docutils header"/>\n</div>\n')
+        header.append('\n<hr class="header"/>\n</div>\n')
         self.body_prefix.extend(header)
-        self.header = header
+        self.header.extend(header)
         del self.body[start:]
 
     def visit_hint(self, node):
@@ -795,9 +854,9 @@
         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 = node.non_default_attributes()
+        if atts.has_key('classes'):
+            del atts['classes']         # prevent duplication with node attrs
         atts['src'] = atts['uri']
         del atts['uri']
         if atts.has_key('scale'):
@@ -832,9 +891,8 @@
         self.body.append(self.emptytag(node, 'img', '', **atts))
 
     def image_div_atts(self, image_node):
-        div_atts = {'class': 'image'}
-        if image_node.attributes.has_key('class'):
-            div_atts['class'] += ' ' + image_node.attributes['class']
+        div_atts = {}
+        div_atts['class'] = ' '.join(['image'] + image_node['classes'])
         if image_node.attributes.has_key('align'):
             div_atts['align'] = self.attval(image_node.attributes['align'])
             div_atts['class'] += ' align-%s' % div_atts['align']
@@ -885,14 +943,15 @@
     def visit_list_item(self, node):
         self.body.append(self.starttag(node, 'li', ''))
         if len(node):
-            node[0].set_class('first')
+            node[0]['classes'].append('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='docutils literal'))
+        self.body.append(
+            self.starttag(node, 'tt', '', CLASS='docutils literal'))
         text = node.astext()
         for token in self.words_and_spaces.findall(text):
             if token.strip():
@@ -916,7 +975,7 @@
         self.body.append('\n</pre>\n')
 
     def visit_meta(self, node):
-        meta = self.emptytag(node, 'meta', **node.attributes)
+        meta = self.emptytag(node, 'meta', **node.non_default_attributes())
         self.add_meta(meta)
 
     def depart_meta(self, node):
@@ -935,8 +994,10 @@
     def visit_option(self, node):
         if self.context[-1]:
             self.body.append(', ')
+        self.body.append(self.starttag(node, 'span', '', CLASS='option'))
 
     def depart_option(self, node):
+        self.body.append('</span>')
         self.context[-1] += 1
 
     def visit_option_argument(self, node):
@@ -948,12 +1009,14 @@
 
     def visit_option_group(self, node):
         atts = {}
-        if len(node.astext()) > 14:
+        if ( self.settings.option_limit
+             and len(node.astext()) > self.settings.option_limit):
             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(
+            self.starttag(node, 'td', CLASS='option-group', **atts))
         self.body.append('<kbd>')
         self.context.append(0)          # count number of options
 
@@ -980,10 +1043,10 @@
         self.body.append('</tr>\n')
 
     def visit_option_string(self, node):
-        self.body.append(self.starttag(node, 'span', '', CLASS='option'))
+        pass
 
     def depart_option_string(self, node):
-        self.body.append('</span>')
+        pass
 
     def visit_organization(self, node):
         self.visit_docinfo_item(node, 'organization')
@@ -999,12 +1062,16 @@
             isinstance(node.parent, nodes.compound)):
             # Never compact paragraphs in document or compound.
             return 0
-        if ((node.attributes in ({}, {'class': 'first'}, {'class': 'last'},
-                                 {'class': 'first last'})) and
-            (self.compact_simple or
-             self.compact_p and (len(node.parent) == 1 or
-                                 len(node.parent) == 2 and
-                                 isinstance(node.parent[0], nodes.label)))):
+        for key, value in node.attlist():
+            if (node.is_not_default(key) and
+                not (key == 'classes' and value in
+                     ([], ['first'], ['last'], ['first', 'last']))):
+                # Attribute which needs to survive.
+                return 0
+        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))):
             return 1
         return 0
 
@@ -1021,7 +1088,7 @@
     def visit_problematic(self, node):
         if node.hasattr('refid'):
             self.body.append('<a href="#%s" name="%s">' % (node['refid'],
-                                                           node['id']))
+                                                           node['ids'][0]))
             self.context.append('</a>')
         else:
             self.context.append('')
@@ -1033,12 +1100,11 @@
 
     def visit_raw(self, node):
         if 'html' in node.get('format', '').split():
-            add_class = node.attributes.get('class') is not None
             t = isinstance(node.parent, nodes.TextElement) and 'span' or 'div'
-            if add_class:
+            if node['classes']:
                 self.body.append(self.starttag(node, t, suffix=''))
             self.body.append(node.astext())
-            if add_class:
+            if node['classes']:
                 self.body.append('</%s>' % t)
         # Keep non-HTML raw text out of output:
         raise nodes.SkipNode
@@ -1052,19 +1118,23 @@
             div_atts['class'] += ' image-reference'
             self.body.append(self.starttag({}, 'div', '', **div_atts))
             self.context.append('</div>\n')
-        href = ''
         if node.has_key('refuri'):
             href = node['refuri']
-        elif node.has_key('refid'):
+            if ( self.settings.cloak_email_addresses
+                 and href.startswith('mailto:')):
+                href = self.cloak_mailto(href)
+                self.in_mailto = 1
+        else:
+            assert node.has_key('refid'), \
+                   'References must have "refuri" or "refid" attribute.'
             href = '#' + node['refid']
-        elif node.has_key('refname'):
-            href = '#' + self.document.nameids[node['refname']]
         self.body.append(self.starttag(node, 'a', '', CLASS='reference',
-                                       **(href and {'href': href} or {})))
+                                       href=href))
 
     def depart_reference(self, node):
         self.body.append('</a>')
         self.body.append(self.context.pop())
+        self.in_mailto = 0
 
     def visit_revision(self, node):
         self.visit_docinfo_item(node, 'revision', meta=None)
@@ -1074,6 +1144,7 @@
 
     def visit_row(self, node):
         self.body.append(self.starttag(node, 'tr', ''))
+        node.column = 0
 
     def depart_row(self, node):
         self.body.append('</tr>\n')
@@ -1135,6 +1206,12 @@
             self.body.append(self.starttag(node, 'h2', '', CLASS='subtitle'))
             self.context.append('</h2>\n')
             self.in_document_title = len(self.body)
+        elif isinstance(node.parent, nodes.section):
+            tag = 'h%s' % (self.section_level + self.initial_header_level - 1)
+            self.body.append(
+                self.starttag(node, tag, '', CLASS='section-subtitle') +
+                self.starttag({}, 'span', '', CLASS='section-subtitle'))
+            self.context.append('</span></%s>\n' % tag)
 
     def depart_subtitle(self, node):
         self.body.append(self.context.pop())
@@ -1142,6 +1219,7 @@
             self.subtitle = self.body[self.in_document_title:-1]
             self.in_document_title = 0
             self.body_pre_docinfo.extend(self.body)
+            self.html_subtitle.extend(self.body)
             del self.body[:]
 
     def visit_superscript(self, node):
@@ -1151,16 +1229,16 @@
         self.body.append('</sup>')
 
     def visit_system_message(self, node):
-        if node['level'] < self.document.reporter['writer'].report_level:
+        if node['level'] < self.document.reporter.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'):
+        if node['ids']:
+            attr['name'] = node['ids'][0]
+        if len(node['backrefs']):
             backrefs = node['backrefs']
             if len(backrefs) == 1:
                 backref_text = ('; <em><a href="#%s">backlink</a></em>'
@@ -1200,8 +1278,8 @@
     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>')
+            self.body.append(self.starttag(node, 'span', '', CLASS='target'))
+            self.context.append('</span>')
         else:
             self.context.append('')
 
@@ -1231,6 +1309,7 @@
         self.body.append(self.starttag(node, 'colgroup'))
         # Appended by thead or tbody:
         self.context.append('</colgroup>\n')
+        node.stubs = []
 
     def depart_tgroup(self, node):
         pass
@@ -1273,6 +1352,7 @@
             check_id = 1
             close_tag = '</caption>\n'
         elif self.section_level == 0:
+            assert node.parent is self.document
             # document title
             self.head.append('<title>%s</title>\n'
                              % self.encode(node.astext()))
@@ -1280,21 +1360,26 @@
             self.context.append('</h1>\n')
             self.in_document_title = len(self.body)
         else:
+            assert isinstance(node.parent, nodes.section)
             h_level = self.section_level + self.initial_header_level - 1
+            atts = {}
+            if (len(node.parent) >= 2 and
+                isinstance(node.parent[1], nodes.subtitle)):
+                atts['CLASS'] = 'with-subtitle'
             self.body.append(
-                  self.starttag(node, 'h%s' % h_level, ''))
+                  self.starttag(node, 'h%s' % h_level, '', **atts))
             atts = {}
-            if node.parent.hasattr('id'):
-                atts['name'] = node.parent['id']
+            if node.parent['ids']:
+                atts['name'] = node.parent['ids'][0]
             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' % (h_level))
         if check_id:
-            if node.parent.hasattr('id'):
+            if node.parent['ids']:
                 self.body.append(
-                    self.starttag({}, 'a', '', name=node.parent['id']))
+                    self.starttag({}, 'a', '', name=node.parent['ids'][0]))
                 self.context.append('</a>' + close_tag)
             else:
                 self.context.append(close_tag)
@@ -1305,6 +1390,7 @@
             self.title = self.body[self.in_document_title:-1]
             self.in_document_title = 0
             self.body_pre_docinfo.extend(self.body)
+            self.html_title.extend(self.body)
             del self.body[:]
 
     def visit_title_reference(self, node):
@@ -1315,11 +1401,11 @@
 
     def visit_topic(self, node):
         self.body.append(self.starttag(node, 'div', CLASS='topic'))
-        self.topic_class = node.get('class')
+        self.topic_classes = node['classes']
 
     def depart_topic(self, node):
         self.body.append('</div>\n')
-        self.topic_class = ''
+        self.topic_classes = []
 
     def visit_transition(self, node):
         self.body.append(self.emptytag(node, 'hr', CLASS='docutils'))
@@ -1364,7 +1450,7 @@
 
     def visit_list_item(self, node):
         children = []
-        for child in node.get_children():
+        for child in node.children:
             if not isinstance(child, nodes.Invisible):
                 children.append(child)
         if (children and isinstance(children[0], nodes.paragraph)


=== Zope/lib/python/docutils/writers/latex2e.py 1.1.2.7 => 1.1.2.8 ===
--- Zope/lib/python/docutils/writers/latex2e.py:1.1.2.7	Fri Jan  7 08:26:06 2005
+++ Zope/lib/python/docutils/writers/latex2e.py	Sun Oct  9 10:43:46 2005
@@ -349,7 +349,7 @@
             return self._deepest_section
 
 class Table:
-    """ Manage a table while traversing. 
+    """ Manage a table while traversing.
         Maybe change to a mixin defining the visit/departs, but then
         class Table internal variables are in the Translator.
     """
@@ -381,7 +381,7 @@
         return ''
     def get_latex_type(self):
         return self._latex_type
-    
+
     def set(self,attr,value):
         self._attrs[attr] = value
     def get(self,attr):
@@ -442,7 +442,7 @@
         return latex_table_spec+bar
 
     def get_column_width(self):
-        """ return columnwidth for current cell (not multicell) 
+        """ return columnwidth for current cell (not multicell)
         """
         return "%.2f\\locallinewidth" % self._col_width[self._cell_in_row-1]
 
@@ -471,7 +471,7 @@
         for i in range(len(self._rowspan)):
             if (self._rowspan[i]>0):
                 self._rowspan[i] -= 1
-        
+
         if self._table_style == 'standard':
             rowspans = []
             for i in range(len(self._rowspan)):
@@ -507,7 +507,7 @@
     def visit_entry(self):
         self._cell_in_row += 1
 
-        
+
 class LaTeXTranslator(nodes.NodeVisitor):
 
     # When options are given to the documentclass, latex will pass them
@@ -664,21 +664,24 @@
         # NOTE: Latex wants a date and an author, rst puts this into
         #   docinfo, so normally we donot want latex author/date handling.
         # latex article has its own handling of date and author, deactivate.
+        # So we always emit \title{...} \author{...} \date{...}, even if the
+        # "..." are empty strings.
         self.head = [ ]
-        if not self.use_latex_docinfo:
-            self.head.extend( [ '\\author{}\n', '\\date{}\n' ] )
-        self.body_prefix = ['\\raggedbottom\n']
         # separate title, so we can appen subtitle.
-        self.title = ""
+        self.title = ''
+        # if use_latex_docinfo: collects lists of author/organization/contact/address lines
+        self.author_stack = []
+        self.date = ''
+
+        self.body_prefix = ['\\raggedbottom\n']
         self.body = []
         self.body_suffix = ['\n']
         self.section_level = 0
         self.context = []
-        self.topic_class = ''
+        self.topic_classes = []
         # column specification for tables
         self.table_caption = None
-        # do we have one or more authors
-        self.author_stack = None
+        
         # Flags to encode
         # ---------------
         # verbatim: to tell encode not to encode.
@@ -878,15 +881,19 @@
         return self.encode(whitespace.sub(' ', text))
 
     def astext(self):
-        if self.pdfinfo:
+        if self.pdfinfo is not None:
             if self.pdfauthor:
                 self.pdfinfo.append('pdfauthor={%s}' % self.pdfauthor)
+        if self.pdfinfo:
             pdfinfo = '\\hypersetup{\n' + ',\n'.join(self.pdfinfo) + '\n}\n'
         else:
             pdfinfo = ''
-        title = '\\title{%s}\n' % self.title
-        return ''.join(self.head_prefix + [title]
-                        + self.head + [pdfinfo]
+        head = '\\title{%s}\n\\author{%s}\n\\date{%s}\n' % \
+               (self.title,
+                ' \\and\n'.join(['~\\\\\n'.join(author_lines)
+                                 for author_lines in self.author_stack]),
+                self.date)
+        return ''.join(self.head_prefix + [head] + self.head + [pdfinfo]
                         + self.body_prefix  + self.body + self.body_suffix)
 
     def visit_Text(self, node):
@@ -927,14 +934,10 @@
 
     def visit_authors(self, node):
         # not used: visit_author is called anyway for each author.
-        if self.use_latex_docinfo:
-            self.author_stack = []
+        pass
 
     def depart_authors(self, node):
-        if self.use_latex_docinfo:
-            self.head.append('\\author{%s}\n' % \
-                ' \\and '.join(self.author_stack) )
-            self.author_stack = None
+        pass
 
     def visit_block_quote(self, node):
         self.body.append( '\\begin{quote}\n')
@@ -943,14 +946,14 @@
         self.body.append( '\\end{quote}\n')
 
     def visit_bullet_list(self, node):
-        if self.topic_class == 'contents':
+        if self.topic_classes == ['contents']:
             if not self.use_latex_toc:
                 self.body.append( '\\begin{list}{}{}\n' )
         else:
             self.body.append( '\\begin{itemize}\n' )
 
     def depart_bullet_list(self, node):
-        if self.topic_class == 'contents':
+        if self.topic_classes == ['contents']:
             if not self.use_latex_toc:
                 self.body.append( '\\end{list}\n' )
         else:
@@ -998,7 +1001,8 @@
             self.context.append(len(self.body))
         else:
             self.body.append('\\begin{figure}[b]')
-            self.body.append('\\hypertarget{%s}' % node['id'])
+            for id in node['ids']:
+                self.body.append('\\hypertarget{%s}' % id)
 
     def depart_citation(self, node):
         if self._use_latex_citations:
@@ -1128,15 +1132,23 @@
                     self.pdfauthor = self.attval(node.astext())
                 else:
                     self.pdfauthor += self.author_separator + self.attval(node.astext())
-            if self.use_latex_docinfo:
-                if self.author_stack == None:
-                    self.head.append('\\author{%s}\n' % self.attval(node.astext()))
+        if self.use_latex_docinfo:
+            if name in ('author', 'organization', 'contact', 'address'):
+                # We attach these to the last author.  If any of them precedes
+                # the first author, put them in a separate "author" group (for
+                # no better semantics).
+                if name == 'author' or not self.author_stack:
+                    self.author_stack.append([])
+                if name == 'address':   # newlines are meaningful
+                    self.insert_newline = 1
+                    text = self.encode(node.astext())
+                    self.insert_newline = 0
                 else:
-                    self.author_stack.append( self.attval(node.astext()) )
+                    text = self.attval(node.astext())
+                self.author_stack[-1].append(text)
                 raise nodes.SkipNode
-        elif name == 'date':
-            if self.use_latex_docinfo:
-                self.head.append('\\date{%s}\n' % self.attval(node.astext()))
+            elif name == 'date':
+                self.date = self.attval(node.astext())
                 raise nodes.SkipNode
         self.docinfo.append('\\textbf{%s}: &\n\t' % self.language_label(name))
         if name == 'address':
@@ -1169,7 +1181,7 @@
     def visit_document(self, node):
         self.body_prefix.append('\\begin{document}\n')
         # titled document?
-        if len(node) and isinstance(node[0], nodes.title):
+        if self.use_latex_docinfo or len(node) and isinstance(node[0], nodes.title):
             self.body_prefix.append('\\maketitle\n\n')
             # alternative use titlepage environment.
             # \begin{titlepage}
@@ -1186,7 +1198,7 @@
             for bi in self._bibitems:
                 self.body.append('\\bibitem[%s]{%s}{%s}\n' % (bi[0], bi[0], bi[1]))
             self.body.append('\\end{thebibliography}\n')
-            
+
         self.body_suffix.append('\\end{document}\n')
 
     def visit_emphasis(self, node):
@@ -1204,7 +1216,10 @@
             # if the firstrow is a multirow, this actually is the second row.
             # this gets hairy if rowspans follow each other.
             if self.active_table.get_rowspan(0):
-                self.body.append(' & ')
+                count = 0
+                while self.active_table.get_rowspan(count):
+                    count += 1
+                    self.body.append(' & ')
                 self.active_table.visit_entry() # increment cell count
         else:
             self.body.append(' & ')
@@ -1384,7 +1399,8 @@
             self.body.append('{')
         else:
             self.body.append('\\begin{figure}[b]')
-            self.body.append('\\hypertarget{%s}' % node['id'])
+            for id in node['ids']:
+                self.body.append('\\hypertarget{%s}' % id)
 
     def depart_footnote(self, node):
         if self.use_latex_footnotes:
@@ -1692,7 +1708,7 @@
 
     def visit_paragraph(self, node):
         index = node.parent.index(node)
-        if not (self.topic_class == 'contents' or
+        if not (self.topic_classes == ['contents'] or
                 (isinstance(node.parent, nodes.compound) and
                  index > 0 and
                  not isinstance(node.parent[index - 1], nodes.paragraph) and
@@ -1799,17 +1815,19 @@
         if isinstance(node.parent, nodes.sidebar):
             self.body.append('~\\\\\n\\textbf{')
             self.context.append('}\n\\smallskip\n')
-        else:
+        elif isinstance(node.parent, nodes.document):
             self.title = self.title + \
                 '\\\\\n\\large{%s}\n' % self.encode(node.astext())
             raise nodes.SkipNode
+        elif isinstance(node.parent, nodes.section):
+            self.body.append('\\textbf{')
+            self.context.append('}\\vspace{0.2cm}\n\n\\noindent ')
 
     def depart_subtitle(self, node):
-        if isinstance(node.parent, nodes.sidebar):
-            self.body.append(self.context.pop())
+        self.body.append(self.context.pop())
 
     def visit_system_message(self, node):
-        if node['level'] < self.document.reporter['writer'].report_level:
+        if node['level'] < self.document.reporter.report_level:
             raise nodes.SkipNode
 
     def depart_system_message(self, node):
@@ -1830,8 +1848,9 @@
         # 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('}')
+            for id in node['ids']:
+                self.body.append('\\hypertarget{%s}{' % id)
+            self.context.append('}' * len(node['ids']))
         else:
             self.context.append('')
 
@@ -1849,11 +1868,11 @@
         pass
 
     def visit_term(self, node):
-        self.body.append('\\item[')
+        self.body.append('\\item[{')
 
     def depart_term(self, node):
         # definition list term.
-        self.body.append('] ')
+        self.body.append('}] ')
 
     def visit_tgroup(self, node):
         #self.body.append(self.starttag(node, 'colgroup'))
@@ -1895,8 +1914,9 @@
     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 node.parent['ids']:
+            for id in node.parent['ids']:
+                self.body.append('\\hypertarget{%s}{}\n' % 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.
@@ -1907,8 +1927,9 @@
                     l = l-1
                 # pdftex does not like "_" subscripts in titles
                 text = self.encode(node.astext())
-                self.body.append('\\pdfbookmark[%d]{%s}{%s}\n' % \
-                        (l,text,node.parent['id']))
+                for id in node.parent['ids']:
+                    self.body.append('\\pdfbookmark[%d]{%s}{%s}\n' % \
+                                     (l, text, id))
 
     def visit_title(self, node):
         """Only 3 section levels are supported by LaTeX article (AFAIR)."""
@@ -1957,10 +1978,10 @@
         self.body.append(self.context.pop())
 
     def visit_topic(self, node):
-        self.topic_class = node.get('class')
-        if self.use_latex_toc:
+        self.topic_classes = node['classes']
+        if 'contents' in node['classes'] and self.use_latex_toc:
             self.body.append('\\tableofcontents\n\n\\bigskip\n')
-            self.topic_class = ''
+            self.topic_classes = []
             raise nodes.SkipNode
 
     def visit_inline(self, node): # titlereference
@@ -1970,7 +1991,7 @@
         self.body.append( '}' )
 
     def depart_topic(self, node):
-        self.topic_class = ''
+        self.topic_classes = []
         self.body.append('\n')
 
     def visit_rubric(self, node):


=== Zope/lib/python/docutils/writers/pep_html.py 1.2.10.6 => 1.2.10.7 ===
--- Zope/lib/python/docutils/writers/pep_html.py:1.2.10.6	Fri Jan  7 08:26:06 2005
+++ Zope/lib/python/docutils/writers/pep_html.py	Sun Oct  9 10:43:47 2005
@@ -11,7 +11,6 @@
 __docformat__ = 'reStructuredText'
 
 
-import random
 import sys
 import docutils
 from docutils import frontend, nodes, utils
@@ -22,8 +21,7 @@
 
     settings_spec = html4css1.Writer.settings_spec + (
         'PEP/HTML-Specific Options',
-        """The HTML --footnote-references option's default is set to """
-        '"brackets".',
+        None,
         (('Specify a template file.  Default is "pep-html-template".',
           ['--template'],
           {'default': 'pep-html-template', 'metavar': '<file>'}),
@@ -32,9 +30,11 @@
           {'default': '..', 'metavar': '<URL>'}),
          ('Home URL prefix for PEPs.  Default is "." (current directory).',
           ['--pep-home'],
-          {'default': '.', 'metavar': '<URL>'}),))
-
-    settings_default_overrides = {'footnote_references': 'brackets'}
+          {'default': '.', 'metavar': '<URL>'}),
+         # For testing.
+         (frontend.SUPPRESS_HELP,
+          ['--no-random'],
+          {'action': 'store_true', 'validator': frontend.validate_boolean}),))
 
     relative_path_settings = (html4css1.Writer.relative_path_settings
                               + ('template',))
@@ -66,7 +66,11 @@
         header = self.document[index]
         pepnum = header[0][1].astext()
         subs['pep'] = pepnum
-        subs['banner'] = random.randrange(64)
+        if settings.no_random:
+            subs['banner'] = 0
+        else:
+            import random
+            subs['banner'] = random.randrange(64)
         try:
             subs['pepnum'] = '%04i' % int(pepnum)
         except ValueError:
@@ -82,5 +86,5 @@
 
     def depart_field_list(self, node):
         html4css1.HTMLTranslator.depart_field_list(self, node)
-        if node.get('class') == 'rfc2822':
+        if 'rfc2822' in node['classes']:
              self.body.append('<hr />\n')


=== Zope/lib/python/docutils/writers/pseudoxml.py 1.2.10.6 => 1.2.10.7 ===



More information about the Zope-Checkins mailing list