📄 __init__.py
字号:
# Author: David Goodger# Contact: goodger@users.sourceforge.net# Revision: $Revision: 4219 $# Date: $Date: 2005-12-15 15:32:01 +0100 (Thu, 15 Dec 2005) $# Copyright: This module has been placed in the public domain."""Simple HyperText Markup Language document tree Writer.The output conforms to the XHTML version 1.0 Transitional DTD(*almost* strict). The output contains a minimum of formattinginformation. The cascading style sheet "html4css1.css" is requiredfor proper viewing with a modern graphical browser."""__docformat__ = 'reStructuredText'import sysimport osimport os.pathimport timeimport refrom types import ListTypetry: import Image # check for the Python Imaging Libraryexcept ImportError: Image = Noneimport docutilsfrom docutils import frontend, nodes, utils, writers, languagesclass Writer(writers.Writer): supported = ('html', 'html4css1', 'xhtml') """Formats this writer supports.""" default_stylesheet = 'html4css1.css' default_stylesheet_path = utils.relative_path( os.path.join(os.getcwd(), 'dummy'), os.path.join(os.path.dirname(__file__), default_stylesheet)) settings_spec = ( 'HTML-Specific Options', None, (('Specify a stylesheet URL, used verbatim. Overrides ' '--stylesheet-path.', ['--stylesheet'], {'metavar': '<URL>', 'overrides': 'stylesheet_path'}), ('Specify a stylesheet file, relative to the current working ' 'directory. The path is adjusted relative to the output HTML ' 'file. Overrides --stylesheet. Default: "%s"' % default_stylesheet_path, ['--stylesheet-path'], {'metavar': '<file>', 'overrides': 'stylesheet', 'default': default_stylesheet_path}), ('Embed the stylesheet in the output HTML file. The stylesheet ' 'file must be accessible during processing (--stylesheet-path is ' 'recommended). This is the default.', ['--embed-stylesheet'], {'default': 1, 'action': 'store_true', 'validator': frontend.validate_boolean}), ('Link to the stylesheet in the output HTML file. Default: ' 'embed the stylesheet, do not link to it.', ['--link-stylesheet'], {'dest': 'embed_stylesheet', 'action': 'store_false', 'validator': frontend.validate_boolean}), ('Specify the initial header level. Default is 1 for "<h1>". ' 'Does not affect document title & subtitle (see --no-doc-title).', ['--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'], {'choices': ['superscript', 'brackets'], 'default': 'brackets', 'metavar': '<format>', 'overrides': 'trim_footnote_reference_space'}), ('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 "simple" bullet ' 'lists and enumerated lists. 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'}), ('Remove extra vertical whitespace between items of simple field ' 'lists. Default: enabled.', ['--compact-field-lists'], {'default': 1, 'action': 'store_true', 'validator': frontend.validate_boolean}), ('Disable compact simple field lists.', ['--no-compact-field-lists'], {'dest': 'compact_field_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}), ('Obfuscate email addresses to confuse harvesters while still ' 'keeping email links usable with standards-compliant browsers.', ['--cloak-email-addresses'], {'action': 'store_true', 'validator': frontend.validate_boolean}),)) settings_defaults = {'output_encoding_error_handler': 'xmlcharrefreplace'} relative_path_settings = ('stylesheet_path',) config_section = 'html4css1 writer' config_section_dependencies = ('writers',) def __init__(self): writers.Writer.__init__(self) self.translator_class = HTMLTranslator def translate(self): self.visitor = visitor = self.translator_class(self.document) self.document.walkabout(visitor) self.output = visitor.astext() for attr in ('head_prefix', 'stylesheet', 'head', 'body_prefix', 'body_pre_docinfo', 'docinfo', 'body', 'fragment', 'body_suffix'): setattr(self, attr, getattr(visitor, attr)) def assemble_parts(self): writers.Writer.assemble_parts(self) for part in ('title', 'subtitle', 'docinfo', 'body', 'header', 'footer', 'meta', 'stylesheet', 'fragment', 'html_prolog', 'html_head', 'html_title', 'html_subtitle', 'html_body'): self.parts[part] = ''.join(getattr(self.visitor, part))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 respectively) 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') 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', 'applet', 'form', 'frame', 'iframe', 'img', 'map'] 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.meta = [self.content_type % settings.output_encoding, self.generator % docutils.__version__] self.head_prefix = [] self.html_prolog = [] if settings.xml_declaration: self.head_prefix.append(self.xml_declaration % settings.output_encoding) # 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[:] stylesheet = utils.get_stylesheet_reference(settings) self.stylesheet = [] if stylesheet: if settings.embed_stylesheet: stylesheet = utils.get_stylesheet_reference( settings, os.path.join(os.getcwd(), 'dummy')) settings.record_dependencies.add(stylesheet) stylesheet_text = open(stylesheet).read() self.stylesheet = [self.embedded_stylesheet % stylesheet_text] else: self.stylesheet = [self.stylesheet_link % self.encode(stylesheet)] self.body_prefix = ['</head>\n<body>\n'] # document title, subtitle display self.body_pre_docinfo = [] # author, date, etc. self.docinfo = [] self.body = [] self.fragment = [] self.body_suffix = ['</body>\n</html>\n'] self.section_level = 0 self.initial_header_level = int(settings.initial_header_level) # A heterogenous stack used in conjunction with the tree traversal. # Make sure that the pops correspond to the pushes: self.context = [] self.topic_classes = [] self.colspecs = [] self.compact_p = 1 self.compact_simple = None self.compact_field_list = None self.in_docinfo = None self.in_sidebar = None self.title = [] 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 self.author_in_authors = None 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 # Replace the non-breaking space character with the HTML entity: text = text.replace(u'\u00a0', " ") return text def cloak_mailto(self, uri): """Try to hide a mailto: URL from harvesters.""" # Encode "@" using a URL octet reference (see RFC 1738). # Further cloaking with HTML entities will be done in the # `attval` function. return uri.replace('@', '%40') def cloak_email(self, addr): """Try to hide the link text of a email link from harversters.""" # Surround at-signs and periods with <span> tags. ("@" has # already been encoded to "@" by the `encode` method.) addr = addr.replace('@', '<span>@</span>') addr = addr.replace('.', '<span>.</span>') return addr def attval(self, text, whitespace=re.compile('[\n\r\t\v\f]')): """Cleanse, HTML encode, and return attribute value text.""" encoded = self.encode(whitespace.sub(' ', text)) if self.in_mailto and self.settings.cloak_email_addresses: # Cloak at-signs ("%40") and periods with HTML entities. encoded = encoded.replace('%40', '%40') encoded = encoded.replace('.', '.') return encoded def starttag(self, node, tagname, suffix='\n', empty=0, **attributes): """ Construct and return a start tag given a node (id & class attributes are extracted), tag name, and optional attributes. """ tagname = tagname.lower() prefix = [] atts = {} ids = [] for (name, value) in attributes.items(): atts[name.lower()] = value 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') ids.extend(node.get('ids', [])) if atts.has_key('ids'): ids.extend(atts['ids']) del atts['ids'] if ids: atts['id'] = ids[0] for id in ids[1:]: # Add empty "span" elements for additional IDs. Note # that we cannot use empty "a" elements because there # may be targets inside of references, but nested "a" # elements aren't allowed in XHTML (even if they do # not all have a "href" attribute). if empty: # Empty tag. Insert target right in front of element. prefix.append('<span id="%s"></span>' % id) else: # Non-empty tag. Place the auxiliary <span> tag # *inside* the element, as the first child. suffix += '<span id="%s"></span>' % id # !!! next 2 lines to be removed in Docutils 0.5: 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() parts = [tagname] for name, value in attlist: # value=None was used for boolean attributes without # value, but this isn't supported by XHTML. assert value is not None if isinstance(value, ListType): values = [unicode(v) for v in value] parts.append('%s="%s"' % (name.lower(), self.attval(' '.join(values)))) else: try: uval = unicode(value) except TypeError: # for Python 2.1 compatibility: uval = unicode(str(value)) parts.append('%s="%s"' % (name.lower(), self.attval(uval))) if empty: infix = ' /' else: infix = '' 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, empty=1, **attributes) # !!! to be removed in Docutils 0.5 (change calls to use "starttag"): def start_tag_with_title(self, node, tagname, **atts): """ID and NAME attributes will be handled in the title.""" node = {'classes': node.get('classes', [])} return self.starttag(node, tagname, **atts) def set_class_on_child(self, node, class_, index=0): """ Set class `class_` on the visible child no. index of `node`. Do nothing if node has fewer children than `index`. """ children = [n for n in node if not isinstance(n, nodes.Invisible)] try: child = children[index] except IndexError: return child['classes'].append(class_) def set_first_last(self, node):
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -