[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', " ")
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> </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> </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