[Zope-Checkins] CVS: Zope/lib/python/docutils/writers -
__init__.py:1.5 docutils_xml.py:1.5 html4css1.py:1.5
latex2e.py:1.3 pep_html.py:1.5 pseudoxml.py:1.5
Andreas Jung
cvs-admin at zope.org
Sun Nov 30 10:06:10 EST 2003
Update of /cvs-repository/Zope/lib/python/docutils/writers
In directory cvs.zope.org:/tmp/cvs-serv30951/writers
Added Files:
__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.4 => 1.5 ===
--- /dev/null Sun Nov 30 10:06:10 2003
+++ Zope/lib/python/docutils/writers/__init__.py Sun Nov 30 10:06:09 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.4 => 1.5 ===
--- /dev/null Sun Nov 30 10:06:10 2003
+++ Zope/lib/python/docutils/writers/docutils_xml.py Sun Nov 30 10:06:09 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.4 => 1.5 ===
--- /dev/null Sun Nov 30 10:06:10 2003
+++ Zope/lib/python/docutils/writers/html4css1.py Sun Nov 30 10:06:09 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("&", "&")
+ text = text.replace("<", "<")
+ text = text.replace('"', """)
+ text = text.replace(">", ">")
+ text = text.replace("@", "@") # 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': ('—', ''),
+ '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(' ')
+ 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> </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(' ' * (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> </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.2 => 1.3 === (1162/1562 lines abridged)
--- /dev/null Sun Nov 30 10:06:10 2003
+++ Zope/lib/python/docutils/writers/latex2e.py Sun Nov 30 10:06:09 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.4 => 1.5 ===
--- /dev/null Sun Nov 30 10:06:10 2003
+++ Zope/lib/python/docutils/writers/pep_html.py Sun Nov 30 10:06:09 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.4 => 1.5 ===
--- /dev/null Sun Nov 30 10:06:10 2003
+++ Zope/lib/python/docutils/writers/pseudoxml.py Sun Nov 30 10:06:09 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