1 | """
|
---|
2 | Mostly stolen from www.djangoproject.com, with a slight change that involves putting
|
---|
3 | <a>'s on headings back into the mix - TOC didn't want to work without them for me.
|
---|
4 | """
|
---|
5 |
|
---|
6 | from docutils import nodes, utils
|
---|
7 | from docutils.core import publish_parts
|
---|
8 | from docutils.writers import html4css1
|
---|
9 |
|
---|
10 |
|
---|
11 | def build_document(input_string, source_path=None, destination_path=None,
|
---|
12 | input_encoding='unicode', doctitle=1, initial_header_level=1):
|
---|
13 | overrides = {'input_encoding': input_encoding,
|
---|
14 | 'doctitle_xform': doctitle,
|
---|
15 | 'initial_header_level': initial_header_level}
|
---|
16 | writer = DjangoHTMLWriter()
|
---|
17 | parts = publish_parts(
|
---|
18 | input_string,
|
---|
19 | writer=writer,
|
---|
20 | settings_overrides=overrides,
|
---|
21 | )
|
---|
22 | return {'html_body' : parts['html_body'], 'toc' : parts['toc']}
|
---|
23 |
|
---|
24 |
|
---|
25 |
|
---|
26 | class DjangoHTMLWriter(html4css1.Writer):
|
---|
27 | def __init__(self):
|
---|
28 | html4css1.Writer.__init__(self)
|
---|
29 | self.translator_class = DjangoHTMLTranslator
|
---|
30 |
|
---|
31 | def translate(self):
|
---|
32 | # build the document
|
---|
33 | html4css1.Writer.translate(self)
|
---|
34 |
|
---|
35 | # build the contents
|
---|
36 | contents = self.build_contents(self.document)
|
---|
37 | contents_doc = self.document.copy()
|
---|
38 | contents_doc.children = contents
|
---|
39 | contents_visitor = self.translator_class(contents_doc)
|
---|
40 | contents_doc.walkabout(contents_visitor)
|
---|
41 | self.parts['toc'] = "<ul class='toc'>%s</ul>" % ''.join(contents_visitor.fragment)
|
---|
42 |
|
---|
43 | def build_contents(self, node, level=0):
|
---|
44 | level += 1
|
---|
45 | sections = []
|
---|
46 | i = len(node) - 1
|
---|
47 | while i >= 0 and isinstance(node[i], nodes.section):
|
---|
48 | sections.append(node[i])
|
---|
49 | i -= 1
|
---|
50 | sections.reverse()
|
---|
51 | entries = []
|
---|
52 | autonum = 0
|
---|
53 | depth = 4 # XXX FIXME
|
---|
54 | for section in sections:
|
---|
55 | title = section[0]
|
---|
56 | entrytext = title
|
---|
57 | try:
|
---|
58 | reference = nodes.reference('', '', refid=section['ids'][0], *entrytext)
|
---|
59 | except IndexError:
|
---|
60 | continue
|
---|
61 | ref_id = self.document.set_id(reference)
|
---|
62 | entry = nodes.paragraph('', '', reference)
|
---|
63 | item = nodes.list_item('', entry)
|
---|
64 | if level < depth:
|
---|
65 | subsects = self.build_contents(section, level)
|
---|
66 | item += subsects
|
---|
67 | entries.append(item)
|
---|
68 | if entries:
|
---|
69 | contents = nodes.bullet_list('', *entries)
|
---|
70 | return contents
|
---|
71 | else:
|
---|
72 | return []
|
---|
73 |
|
---|
74 | class DjangoHTMLTranslator(html4css1.HTMLTranslator):
|
---|
75 | def visit_table(self, node):
|
---|
76 | """Remove the damn border=1 from the standard HTML writer"""
|
---|
77 | self.body.append(self.starttag(node, 'table', CLASS='docutils'))
|
---|
78 |
|
---|
79 | def visit_title(self, node, move_ids=1):
|
---|
80 | """Only 6 section levels are supported by HTML."""
|
---|
81 | check_id = 0
|
---|
82 | close_tag = '</p>\n'
|
---|
83 | if isinstance(node.parent, nodes.topic):
|
---|
84 | self.body.append(
|
---|
85 | self.starttag(node, 'p', '', CLASS='topic-title first'))
|
---|
86 | check_id = 1
|
---|
87 | elif isinstance(node.parent, nodes.sidebar):
|
---|
88 | self.body.append(
|
---|
89 | self.starttag(node, 'p', '', CLASS='sidebar-title'))
|
---|
90 | check_id = 1
|
---|
91 | elif isinstance(node.parent, nodes.Admonition):
|
---|
92 | self.body.append(
|
---|
93 | self.starttag(node, 'p', '', CLASS='admonition-title'))
|
---|
94 | check_id = 1
|
---|
95 | elif isinstance(node.parent, nodes.table):
|
---|
96 | self.body.append(
|
---|
97 | self.starttag(node, 'caption', ''))
|
---|
98 | check_id = 1
|
---|
99 | close_tag = '</caption>\n'
|
---|
100 | elif isinstance(node.parent, nodes.document):
|
---|
101 | self.body.append(self.starttag(node, 'h1', '', CLASS='title'))
|
---|
102 | self.context.append('</h1>\n')
|
---|
103 | self.in_document_title = len(self.body)
|
---|
104 | else:
|
---|
105 | assert isinstance(node.parent, nodes.section)
|
---|
106 | h_level = self.section_level + self.initial_header_level - 1
|
---|
107 | atts = {}
|
---|
108 | if (len(node.parent) >= 2 and
|
---|
109 | isinstance(node.parent[1], nodes.subtitle)):
|
---|
110 | atts['CLASS'] = 'with-subtitle'
|
---|
111 | self.body.append(
|
---|
112 | self.starttag(node, 'h%s' % h_level, '', **atts))
|
---|
113 | atts = {}
|
---|
114 | # !!! conditional to be removed in Docutils 0.5:
|
---|
115 | if move_ids:
|
---|
116 | if node.parent['ids']:
|
---|
117 | atts['ids'] = node.parent['ids']
|
---|
118 | if node.hasattr('refid'):
|
---|
119 | atts['class'] = 'toc-backref'
|
---|
120 | atts['href'] = '#' + node['refid']
|
---|
121 | if atts:
|
---|
122 | self.body.append(self.starttag({}, 'a', '', **atts))
|
---|
123 | self.context.append('</a></h%s>\n' % (h_level))
|
---|
124 | else:
|
---|
125 | self.context.append('</h%s>\n' % (h_level))
|
---|
126 | # !!! conditional to be removed in Docutils 0.5:
|
---|
127 | if check_id:
|
---|
128 | if node.parent['ids']:
|
---|
129 | atts={'ids': node.parent['ids']}
|
---|
130 | self.body.append(
|
---|
131 | self.starttag({}, 'a', '', **atts))
|
---|
132 | self.context.append('</a>' + close_tag)
|
---|
133 | else:
|
---|
134 | self.context.append(close_tag)
|
---|
135 |
|
---|