1
# $Id: __init__.py 7534 2012-10-25 11:48:32Z milde $
1
# $Id: __init__.py 7661 2013-05-07 10:52:59Z milde $
2
2
# Author: David Goodger
3
3
# Maintainer: docutils-develop@lists.sourceforge.net
4
4
# Copyright: This module has been placed in the public domain.
34
34
from docutils import frontend, nodes, utils, writers, languages, io
35
35
from docutils.utils.error_reporting import SafeString
36
36
from docutils.transforms import writer_aux
37
from docutils.utils.math import unichar2tex, pick_math_environment
37
from docutils.utils.math import unichar2tex, pick_math_environment, math2html
38
38
from docutils.utils.math.latex2mathml import parse_latex_math
39
from docutils.utils.math.math2html import math2html
41
40
class Writer(writers.Writer):
44
43
"""Formats this writer supports."""
46
45
default_stylesheet = 'html4css1.css'
48
default_stylesheet_path = utils.relative_path(
49
os.path.join(os.getcwd(), 'dummy'),
50
os.path.join(os.path.dirname(__file__), default_stylesheet))
46
default_stylesheet_dirs = ['.', utils.relative_path(
47
os.path.join(os.getcwd(), 'dummy'), os.path.dirname(__file__))]
52
49
default_template = 'template.txt'
62
59
% default_template_path,
64
61
{'default': default_template_path, 'metavar': '<file>'}),
65
('Specify comma separated list of stylesheet URLs. '
62
('Comma separated list of stylesheet URLs. '
66
63
'Overrides previous --stylesheet and --stylesheet-path settings.',
68
{'metavar': '<URL>', 'overrides': 'stylesheet_path',
65
{'metavar': '<URL[,URL,...]>', 'overrides': 'stylesheet_path',
69
66
'validator': frontend.validate_comma_separated_list}),
70
('Specify comma separated list of stylesheet paths. '
71
'With --link-stylesheet, '
67
('Comma separated list of stylesheet paths. '
68
'Relative paths are expanded if a matching file is found in '
69
'the --stylesheet-dirs. With --link-stylesheet, '
72
70
'the path is rewritten relative to the output HTML file. '
73
'Default: "%s"' % default_stylesheet_path,
71
'Default: "%s"' % default_stylesheet,
74
72
['--stylesheet-path'],
75
{'metavar': '<file>', 'overrides': 'stylesheet',
73
{'metavar': '<file[,file,...]>', 'overrides': 'stylesheet',
76
74
'validator': frontend.validate_comma_separated_list,
77
'default': [default_stylesheet_path]}),
75
'default': [default_stylesheet]}),
78
76
('Embed the stylesheet(s) in the output HTML file. The stylesheet '
79
77
'files must be accessible during processing. This is the default.',
80
78
['--embed-stylesheet'],
84
82
'Default: embed stylesheets.',
85
83
['--link-stylesheet'],
86
84
{'dest': 'embed_stylesheet', 'action': 'store_false'}),
85
('Comma-separated list of directories where stylesheets are found. '
86
'Used by --stylesheet-path when expanding relative path arguments. '
87
'Default: "%s"' % default_stylesheet_dirs,
88
['--stylesheet-dirs'],
89
{'metavar': '<dir[,dir,...]>',
90
'validator': frontend.validate_comma_separated_list,
91
'default': default_stylesheet_dirs}),
87
92
('Specify the initial header level. Default is 1 for "<h1>". '
88
93
'Does not affect document title & subtitle (see --no-doc-title).',
89
94
['--initial-header-level'],
135
140
['--table-style'],
136
141
{'default': ''}),
137
142
('Math output format, one of "MathML", "HTML", "MathJax" '
138
'or "LaTeX". Default: "HTML"',
143
'or "LaTeX". Default: "HTML math.css"',
139
144
['--math-output'],
140
{'default': 'HTML'}),
145
{'default': 'HTML math.css'}),
141
146
('Omit the XML declaration. Use with caution.',
142
147
['--no-xml-declaration'],
143
148
{'dest': 'xml_declaration', 'default': 1, 'action': 'store_false',
150
155
settings_defaults = {'output_encoding_error_handler': 'xmlcharrefreplace'}
152
relative_path_settings = ('stylesheet_path',)
154
157
config_section = 'html4css1 writer'
155
158
config_section_dependencies = ('writers',)
400
403
for (name, value) in attributes.items():
401
404
atts[name.lower()] = value
402
classes = node.get('classes', [])
404
classes.append(atts.pop('class'))
405
# move language specification to 'lang' attribute
406
languages = [cls for cls in classes
407
if cls.startswith('language-')]
407
# unify class arguments and move language specification
408
for cls in node.get('classes', []) + atts.pop('class', '').split() :
409
if cls.startswith('language-'):
410
languages.append(cls[9:])
411
elif cls.strip() and cls not in classes:
409
414
# attribute name is 'lang' in XHTML 1.0 but 'xml:lang' in 1.1
410
atts[self.lang_attribute] = languages[0][9:]
411
classes.pop(classes.index(languages[0]))
412
classes = ' '.join(classes).strip()
415
atts[self.lang_attribute] = languages[0]
414
atts['class'] = classes
417
atts['class'] = ' '.join(classes)
415
418
assert 'id' not in atts
416
419
ids.extend(node.get('ids', []))
417
420
if 'ids' in atts:
768
771
self.meta.insert(0, self.content_type % self.settings.output_encoding)
769
772
self.head.insert(0, self.content_type % self.settings.output_encoding)
770
773
if self.math_header:
771
self.head.append(self.math_header)
774
if self.math_output == 'mathjax':
775
self.head.extend(self.math_header)
777
self.stylesheet.extend(self.math_header)
772
778
# skip content-type meta tag with interpolated charset value:
773
779
self.html_head.extend(self.head[1:])
774
780
self.body_prefix.append(self.starttag(node, 'div', CLASS='document'))
900
906
and len(node.astext()) > self.settings.field_name_limit):
901
907
atts['colspan'] = 2
902
908
self.context.append('</tr>\n'
903
+ self.starttag(node.parent, 'tr', '')
909
+ self.starttag(node.parent, 'tr', '',
904
911
+ '<td> </td>')
906
913
self.context.append('')
1205
1212
# settings and conversion
1206
1213
if self.math_output in ('latex', 'mathjax'):
1207
1214
math_code = self.encode(math_code)
1208
if self.math_output == 'mathjax':
1215
if self.math_output == 'mathjax' and not self.math_header:
1209
1216
if self.math_output_options:
1210
1217
self.mathjax_url = self.math_output_options[0]
1211
self.math_header = self.mathjax_script % self.mathjax_url
1218
self.math_header = [self.mathjax_script % self.mathjax_url]
1212
1219
elif self.math_output == 'html':
1213
math_code = math2html(math_code)
1220
if self.math_output_options and not self.math_header:
1221
self.math_header = [self.stylesheet_call(
1222
utils.find_file_in_dirs(s, self.settings.stylesheet_dirs))
1223
for s in self.math_output_options[0].split(',')]
1224
# TODO: fix display mode in matrices and fractions
1225
math2html.DocumentParameters.displaymode = (math_env != '')
1226
math_code = math2html.math2html(math_code)
1214
1227
elif self.math_output == 'mathml':
1215
1228
self.doctype = self.doctype_mathml
1216
1229
self.content_type = self.content_type_mathml
1231
1244
raise nodes.SkipNode
1232
1245
# append to document body
1234
self.body.append(self.starttag(node, tag,
1247
self.body.append(self.starttag(node, tag,
1235
1248
suffix='\n'*bool(math_env),
1237
1250
self.body.append(math_code)
1334
1347
if (isinstance(node.parent, nodes.document) or
1335
1348
isinstance(node.parent, nodes.compound)):
1336
1349
# Never compact paragraphs in document or compound.
1338
1351
for key, value in node.attlist():
1339
1352
if (node.is_not_default(key) and
1340
1353
not (key == 'classes' and value in
1341
1354
([], ['first'], ['last'], ['first', 'last']))):
1342
1355
# Attribute which needs to survive.
1344
1357
first = isinstance(node.parent[0], nodes.label) # skip label
1345
1358
for child in node.parent.children[first:]:
1346
1359
# only first paragraph can be compact
1349
1362
if child is node:
1352
1365
parent_length = len([n for n in node.parent if not isinstance(
1353
1366
n, (nodes.Invisible, nodes.label))])
1354
1367
if ( self.compact_simple
1355
1368
or self.compact_field_list
1356
1369
or self.compact_p and parent_length == 1):
1360
1373
def visit_paragraph(self, node):
1361
1374
if self.should_be_compact_paragraph(node):
1539
1552
self.body.append('</div>\n')
1541
1554
def visit_table(self, node):
1555
self.context.append(self.compact_p)
1556
self.compact_p = True
1542
1557
classes = ' '.join(['docutils', self.settings.table_style]).strip()
1543
1558
self.body.append(
1544
1559
self.starttag(node, 'table', CLASS=classes, border="1"))
1546
1561
def depart_table(self, node):
1562
self.compact_p = self.context.pop()
1547
1563
self.body.append('</table>\n')
1549
1565
def visit_target(self, node):