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 | |
---|