2 # :Author: Günter Milde <milde@users.berlios.de>
3 # :Revision: $Revision$
4 # :Date: $Date: 2005-06-28$
5 # :Copyright: © 2005, 2009 Günter Milde.
6 # :License: Released under the terms of the `2-Clause BSD license`_, in short:
8 # Copying and distribution of this file, with or without modification,
9 # are permitted in any medium without royalty provided the copyright
10 # notice and this notice are preserved.
11 # This file is offered as-is, without any warranty.
13 # .. _2-Clause BSD license: http://www.spdx.org/licenses/BSD-2-Clause
16 Strict eXtensible HyperText Markup Language (XHTML) document Writer.
18 This is a variant of Docutils' `html-base` writer.
19 The output conforms to the XHTML version 1.1 DTD.
22 __docformat__
= 'reStructuredText'
29 from docutils
import frontend
, nodes
, utils
, writers
, languages
30 from docutils
.writers
import html_base
32 class Writer(html_base
.Writer
):
34 supported
= ('html', 'html4', 'html4strict', 'html4css2',
35 'xhtml', 'xhtml1', 'xhtml1strict', 'xhtml11')
36 """Formats this writer supports."""
38 default_stylesheets
= ['html-base.css', 'xhtml11.css']
39 default_stylesheet_dirs
= ['.',
40 os
.path
.abspath(os
.path
.dirname(__file__
)),
41 os
.path
.abspath(os
.path
.join(
42 os
.path
.dirname(os
.path
.dirname(__file__
)), 'html_base'))
45 config_section
= 'xhtml11 writer'
46 config_section_dependencies
= ('writers', 'html writer')
48 settings_spec
= frontend
.filter_settings_spec(
49 html_base
.Writer
.settings_spec
,
51 'Comma separated list of stylesheet paths. '
52 'Relative paths are expanded if a matching file is found in '
53 'the --stylesheet-dirs. With --link-stylesheet, '
54 'the path is rewritten relative to the output HTML file. '
55 'Default: "%s"' % ','.join(default_stylesheets
),
56 ['--stylesheet-path'],
57 {'metavar': '<file[,file,...]>', 'overrides': 'stylesheet',
58 'validator': frontend
.validate_comma_separated_list
,
59 'default': default_stylesheets
}),
61 'Comma-separated list of directories where stylesheets are found. '
62 'Used by --stylesheet-path when expanding relative path arguments. '
63 'Default: "%s"' % default_stylesheet_dirs
,
64 ['--stylesheet-dirs'],
65 {'metavar': '<dir[,dir,...]>',
66 'validator': frontend
.validate_comma_separated_list
,
67 'default': default_stylesheet_dirs
}),
68 math_output
= ('Math output format, one of "MathML", "HTML", '
69 '"MathJax" or "LaTeX". Default: "MathML"',
71 {'default': 'MathML'}),
72 xml_declaration
= ('Prepend an XML declaration. '
74 ['--xml-declaration'],
75 {'default': True, 'action': 'store_true',
76 'validator': frontend
.validate_boolean
}))
79 writers
.Writer
.__init
__(self
)
80 self
.translator_class
= HTMLTranslator
83 class HTMLTranslator(html_base
.HTMLTranslator
):
85 This writer generates XHTML 1.1
86 without formatting that interferes with a CSS stylesheet.
88 doctype
= ('<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" '
89 '"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">\n')
91 '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1 plus MathML 2.0//EN" '
92 '"http://www.w3.org/Math/DTD/mathml2/xhtml-math11-f.dtd">\n')
94 # there is no attribute "lang" in XHTML 1.1
95 lang_attribute
= 'xml:lang' # changed from 'lang' in XHTML 1.0
96 head_prefix_template
= ('<html xmlns="http://www.w3.org/1999/xhtml"'
97 ' xml:lang="%(lang)s">\n<head>\n')
102 # The 'start' attribute does not conform to HTML4/XHTML1 Strict
103 # (resurfaced in HTML5)
105 def visit_enumerated_list(self
, node
):
108 atts
['style'] = 'counter-reset: item %d;' % (node
['start'] - 1)
109 classes
= node
.setdefault('classes', [])
110 if 'enumtype' in node
:
111 classes
.append(node
['enumtype'])
112 if self
.is_compactable(node
):
113 classes
.append('simple')
114 self
.body
.append(self
.starttag(node
, 'ol', **atts
))
117 # <sup> and <sub> tags (possible with parsed-literal) are not allowed
118 # in <pre> --- use <span> ::
120 def visit_subscript(self
, node
):
121 if isinstance(node
.parent
, nodes
.literal_block
):
122 self
.body
.append(self
.starttag(node
, 'span', '',
125 self
.body
.append(self
.starttag(node
, 'sub', ''))
127 def depart_subscript(self
, node
):
128 if isinstance(node
.parent
, nodes
.literal_block
):
129 self
.body
.append('</span>')
131 self
.body
.append('</sub>')
134 def visit_superscript(self
, node
):
135 # <sup> not allowed in <pre>
136 if isinstance(node
.parent
, nodes
.literal_block
):
137 self
.body
.append(self
.starttag(node
, 'span', '',
138 CLASS
='superscript'))
140 self
.body
.append(self
.starttag(node
, 'sup', ''))
142 def depart_superscript(self
, node
):
143 if isinstance(node
.parent
, nodes
.literal_block
):
144 self
.body
.append('</span>')
146 self
.body
.append('</sup>')
148 # Meta tags: 'lang' attribute replaced by 'xml:lang' in XHTML 1.1
149 # HTML5/polyglott recommends using both
151 def visit_meta(self
, node
):
152 if node
.hasattr('lang'):
153 node
['xml:lang'] = node
['lang']
155 meta
= self
.emptytag(node
, 'meta', **node
.non_default_attributes())