|
1 # Author: David Goodger |
|
2 # Contact: goodger@users.sourceforge.net |
|
3 # Revision: $Revision: 1.2 $ |
|
4 # Date: $Date: 2005-07-04 16:36:50 $ |
|
5 # Copyright: This module has been placed in the public domain. |
|
6 |
|
7 """ |
|
8 Simple HyperText Markup Language document tree Writer. |
|
9 |
|
10 The output conforms to the HTML 4.01 Transitional DTD and to the Extensible |
|
11 HTML version 1.0 Transitional DTD (*almost* strict). The output contains a |
|
12 minimum of formatting information. A cascading style sheet ("default.css" by |
|
13 default) is required for proper viewing with a modern graphical browser. |
|
14 |
|
15 http://cvs.zope.org/Zope/lib/python/docutils/writers/Attic/html4zope.py?rev=1.1.2.2&only_with_tag=ajung-restructuredtext-integration-branch&content-type=text/vnd.viewcvs-markup |
|
16 """ |
|
17 |
|
18 __docformat__ = 'reStructuredText' |
|
19 |
|
20 from logilab.mtconverter import html_escape |
|
21 |
|
22 from docutils import nodes |
|
23 from docutils.writers.html4css1 import Writer as CSS1Writer |
|
24 from docutils.writers.html4css1 import HTMLTranslator as CSS1HTMLTranslator |
|
25 import os |
|
26 |
|
27 default_level = int(os.environ.get('STX_DEFAULT_LEVEL', 3)) |
|
28 |
|
29 class Writer(CSS1Writer): |
|
30 """css writer using our html translator""" |
|
31 def __init__(self, base_url): |
|
32 CSS1Writer.__init__(self) |
|
33 self.translator_class = URLBinder(base_url, HTMLTranslator) |
|
34 |
|
35 def apply_template(self): |
|
36 """overriding this is necessary with docutils >= 0.5""" |
|
37 return self.visitor.astext() |
|
38 |
|
39 class URLBinder: |
|
40 def __init__(self, url, klass): |
|
41 self.base_url = url |
|
42 self.translator_class = HTMLTranslator |
|
43 |
|
44 def __call__(self, document): |
|
45 translator = self.translator_class(document) |
|
46 translator.base_url = self.base_url |
|
47 return translator |
|
48 |
|
49 class HTMLTranslator(CSS1HTMLTranslator): |
|
50 """ReST tree to html translator""" |
|
51 |
|
52 def astext(self): |
|
53 """return the extracted html""" |
|
54 return ''.join(self.body) |
|
55 |
|
56 def visit_title(self, node): |
|
57 """Only 6 section levels are supported by HTML.""" |
|
58 if isinstance(node.parent, nodes.topic): |
|
59 self.body.append( |
|
60 self.starttag(node, 'p', '', CLASS='topic-title')) |
|
61 if node.parent.hasattr('id'): |
|
62 self.body.append( |
|
63 self.starttag({}, 'a', '', name=node.parent['id'])) |
|
64 self.context.append('</a></p>\n') |
|
65 else: |
|
66 self.context.append('</p>\n') |
|
67 elif self.section_level == 0: |
|
68 # document title |
|
69 self.head.append('<title>%s</title>\n' |
|
70 % self.encode(node.astext())) |
|
71 self.body.append(self.starttag(node, 'h%d' % default_level, '', |
|
72 CLASS='title')) |
|
73 self.context.append('</h%d>\n' % default_level) |
|
74 else: |
|
75 self.body.append( |
|
76 self.starttag(node, 'h%s' % ( |
|
77 default_level+self.section_level-1), '')) |
|
78 atts = {} |
|
79 if node.hasattr('refid'): |
|
80 atts['class'] = 'toc-backref' |
|
81 atts['href'] = '%s#%s' % (self.base_url, node['refid']) |
|
82 self.body.append(self.starttag({}, 'a', '', **atts)) |
|
83 self.context.append('</a></h%s>\n' % ( |
|
84 default_level+self.section_level-1)) |
|
85 |
|
86 def visit_subtitle(self, node): |
|
87 """format a subtitle""" |
|
88 if isinstance(node.parent, nodes.sidebar): |
|
89 self.body.append(self.starttag(node, 'p', '', |
|
90 CLASS='sidebar-subtitle')) |
|
91 self.context.append('</p>\n') |
|
92 else: |
|
93 self.body.append( |
|
94 self.starttag(node, 'h%s' % (default_level+1), '', |
|
95 CLASS='subtitle')) |
|
96 self.context.append('</h%s>\n' % (default_level+1)) |
|
97 |
|
98 def visit_document(self, node): |
|
99 """syt: i don't want the enclosing <div class="document">""" |
|
100 def depart_document(self, node): |
|
101 """syt: i don't want the enclosing <div class="document">""" |
|
102 |
|
103 def visit_reference(self, node): |
|
104 """syt: i want absolute urls""" |
|
105 if node.has_key('refuri'): |
|
106 href = node['refuri'] |
|
107 if ( self.settings.cloak_email_addresses |
|
108 and href.startswith('mailto:')): |
|
109 href = self.cloak_mailto(href) |
|
110 self.in_mailto = 1 |
|
111 else: |
|
112 assert node.has_key('refid'), \ |
|
113 'References must have "refuri" or "refid" attribute.' |
|
114 href = '%s#%s' % (self.base_url, node['refid']) |
|
115 atts = {'href': href, 'class': 'reference'} |
|
116 if not isinstance(node.parent, nodes.TextElement): |
|
117 assert len(node) == 1 and isinstance(node[0], nodes.image) |
|
118 atts['class'] += ' image-reference' |
|
119 self.body.append(self.starttag(node, 'a', '', **atts)) |
|
120 |
|
121 ## override error messages to avoid XHTML problems ######################## |
|
122 def visit_problematic(self, node): |
|
123 pass |
|
124 |
|
125 def depart_problematic(self, node): |
|
126 pass |
|
127 |
|
128 def visit_system_message(self, node): |
|
129 backref_text = '' |
|
130 if len(node['backrefs']): |
|
131 backrefs = node['backrefs'] |
|
132 if len(backrefs) == 1: |
|
133 backref_text = '; <em>backlink</em>' |
|
134 else: |
|
135 i = 1 |
|
136 backlinks = [] |
|
137 for backref in backrefs: |
|
138 backlinks.append(str(i)) |
|
139 i += 1 |
|
140 backref_text = ('; <em>backlinks: %s</em>' |
|
141 % ', '.join(backlinks)) |
|
142 if node.hasattr('line'): |
|
143 line = ', line %s' % node['line'] |
|
144 else: |
|
145 line = '' |
|
146 a_start = a_end = '' |
|
147 error = u'System Message: %s%s/%s%s (%s %s)%s</p>\n' % ( |
|
148 a_start, node['type'], node['level'], a_end, |
|
149 self.encode(node['source']), line, backref_text) |
|
150 self.body.append(u'<div class="system-message"><b>ReST / HTML errors:</b>%s</div>' % html_escape(error)) |
|
151 |
|
152 def depart_system_message(self, node): |
|
153 pass |