removed obsolete issues (many of them fixed with AE)
[docutils.git] / sandbox / bbum / DocArticle / DocArticle / __init__.py
blob9a6b421e2a5a33db688f258835aeba8638f56654
1 # Author: Bill Bumgarner
2 # Contact: bbum@mac.com
3 # Copyright: 2002 - Bill Bumgarner - All Rights Reserved
4 # License: The MIT License -- see LICENSE.txt
6 """
7 This is the DocArticle package.
9 This package provides a writer for DocUtils that spews HTML compliant
10 with O'Reilly's Dev Center article submission guidelines.
11 """
13 try:
14 x = True
15 except NameError:
16 True = 1
17 False = 0
19 __docformat__ = 'reStructuredText'
21 import sys
22 from warnings import warn
23 import re
24 from types import *
26 import docutils
27 from docutils import nodes, utils, writers, languages
29 from DocArticle import DocArticleText
31 class DocArticleWriter(writers.Writer):
32 supported = ('html',)
33 """Formats this writer supports."""
35 output = None
36 """Final translated form of `document`."""
38 def __init__(self):
39 writers.Writer.__init__(self)
40 self.translator_class = HTMLDocArticleTranslator
42 def translate(self):
43 visitor = self.translator_class(self.document)
44 self.document.walkabout(visitor)
45 self.output = visitor.astext()
47 SpewNothing = 0
48 SpewParagraph = 1
49 SpewBreak = 2
50 SpewBreakBreak = 3
51 SpewNothingThenPara = 4
53 olTypeTranslator = {
54 'arabic' : '1',
55 'upperalpha' : 'A',
56 'loweralpha' : 'a',
57 'upperroman' : 'I',
58 'lowerroman' : 'i'
61 class HTMLDocArticleTranslator(nodes.NodeVisitor):
62 named_tags = {'a': 1,
63 'applet': 1,
64 'form': 1,
65 'frame': 1,
66 'iframe': 1,
67 'img': 1,
68 'map': 1}
70 words_and_spaces = re.compile(r'\S+| +|\n')
72 def __init__(self, document):
73 nodes.NodeVisitor.__init__(self, document)
74 self.section_level = 0
75 self.headerContent = []
76 self.bodyContent = []
77 self.bodySuffix = []
78 self.metaContent = []
79 self.context = []
80 self.spewTextContext = [True]
81 self.spewParaTag = [SpewParagraph]
82 self.paraFormat = [(None,None)]
83 self.colspecs = []
85 self.body_pre_docinfo = []
86 self.docinfo = []
87 self.compact_simple = None
88 self.compact_p = 1
90 self.firstFootnoteVisited = False
92 # lcode = settings.language_code
93 lcode = 'en'
94 self.language = languages.get_language(lcode)
96 def astext(self):
97 return ''.join([DocArticleText.contentStart, DocArticleText.headerStart] +
98 self.headerContent +
99 [DocArticleText.headerEnd, DocArticleText.bodyStart] +
100 self.body_pre_docinfo + self.docinfo + self.bodyContent + self.bodySuffix +
101 [DocArticleText.bodyEnd, DocArticleText.contentEnd])
103 def encode(self, text):
104 """Encode special characters in `text` & return."""
105 # @@@ A codec to do these and all other HTML entities would be nice.
106 text = text.replace('&', '&')
107 text = text.replace('<', '&lt;')
108 text = text.replace('"', '&quot;')
109 text = text.replace('>', '&gt;')
110 return text
112 def popAndAppend(self, node):
113 possiblePoppedContent = self.context.pop()
114 if possiblePoppedContent:
115 self.bodyContent.append(possiblePoppedContent)
117 def attval(self, text,
118 whitespace=re.compile('[\n\r\t\v\f]')):
119 """Cleanse, HTML encode, and return attribute value text."""
120 return self.encode(whitespace.sub(' ', text))
122 def emptytag(self, node, tagname, suffix='\n', **attributes):
123 """Construct and return an XML-compatible empty tag."""
124 return self.starttag(node, tagname, suffix, infix=' /', **attributes)
126 def starttag(self, node, tagname, suffix='\n', infix='', **attributes):
127 tagname = tagname.lower()
128 atts = {}
129 for (name, value) in attributes.items():
130 atts[name.lower()] = value
131 for att in ('id',): # node attribute overrides
132 if node.has_key(att):
133 atts[att] = node[att]
134 if atts.has_key('id') and self.named_tags.has_key(tagname):
135 atts['name'] = atts['id'] # for compatibility with old browsers
136 attlist = atts.items()
137 attlist.sort()
138 parts = [tagname]
139 for name, value in attlist:
140 if value is None: # boolean attribute
141 # According to the HTML spec, ``<element boolean>`` is good,
142 # ``<element boolean="boolean">`` is bad.
143 # (But the XHTML (XML) spec says the opposite. <sigh>)
144 parts.append(name.lower())
145 elif isinstance(value, ListType):
146 values = [str(v) for v in value]
147 parts.append('%s="%s"' % (name.lower(),
148 self.attval(' '.join(values))))
149 else:
150 parts.append('%s="%s"' % (name.lower(),
151 self.attval(str(value))))
152 return '<%s%s>%s' % (' '.join(parts), infix, suffix)
154 def visit_Text(self, node):
155 if self.spewTextContext[-1]:
156 self.bodyContent.append(self.encode(node.astext()))
158 def depart_Text(self, node):
159 pass
161 def visit_address(self, node):
162 self.visit_docinfo_item(node, 'address', meta=None)
163 self.bodyContent.append(self.starttag(node, 'pre'))
165 def depart_address(self, node):
166 self.bodyContent.append('\n</pre>\n')
167 self.depart_docinfo_item(node)
169 def visit_admonition(self, node, name='', admonitionCellAtts={}):
170 baseAdmonitionCellAtts = {"width" : "15%"}
171 baseAdmonitionCellAtts.update(admonitionCellAtts)
172 self.bodyContent.append('<table width="90%" border="1" align="center">\n'
173 '<tbody><tr><td><table width="100%"><tbody><tr>\n')
174 self.bodyContent.append(self.starttag(node, 'td', **baseAdmonitionCellAtts))
175 if name:
176 self.bodyContent.append(self.language.labels[name.lower()])
177 self.bodyContent.append('</td><td>')
180 def depart_admonition(self, node):
181 self.bodyContent.append('</td></tr></tbody></table></td></tr></tbody></table>')
183 attribution_formats = {'dash': ('&mdash;', ''),
184 'parentheses': ('(', ')'),
185 'parens': ('(', ')'),
186 'none': ('', '')}
188 def visit_attribution(self, node):
189 # prefix, suffix = self.attribution_formats[self.settings.attribution]
190 prefix, suffix = ('(', ')')
191 self.context.append(suffix)
192 self.bodyContent.append(
193 self.starttag(node, 'p', prefix))
195 def depart_attribution(self, node):
196 self.bodyContent.append(self.context.pop() + '</p>\n')
199 def visit_author(self, node):
200 self.visit_docinfo_item(node, 'Author')
202 def depart_author(self, node):
203 self.depart_docinfo_item(node)
205 def visit_authors(self, node):
206 pass
208 def depart_authors(self, node):
209 pass
211 def visit_attention(self, node):
212 self.visit_admonition(node, 'attention', admonitionCellAtts={"bgcolor":"#ffffcc"})
214 def depart_attention(self, node):
215 self.depart_admonition(node)
217 def visit_block_quote(self, node):
218 self.bodyContent.append(self.starttag(node, 'blockquote'))
220 def depart_block_quote(self, node):
221 self.bodyContent.append('</blockquote>\n')
223 def visit_line_block(self, node):
224 self.bodyContent.append(self.starttag(node, 'pre'))
226 def depart_line_block(self, node):
227 self.bodyContent.append('\n</pre>\n')
229 def visit_bullet_list(self, node):
230 self.bodyContent.append(self.starttag(node, 'ul'))
231 self.spewParaTag.append(SpewNothing)
233 def depart_bullet_list(self, node):
234 self.bodyContent.append('</ul>\n')
235 self.spewParaTag.pop()
237 def visit_caption(self, node):
238 self.bodyContent.append(self.starttag(node, 'p', ''))
240 def depart_caption(self, node):
241 self.bodyContent.append('</p>\n')
243 def visit_caution(self, node):
244 self.visit_admonition(node, 'caution', admonitionCellAtts={"bgcolor":"#ffff99"})
246 def depart_caution(self, node):
247 self.depart_admonition(node)
249 def visit_citation(self, node):
250 ##! verify col configuration
251 self.bodyContent.append(self.starttag(node, 'table'))
252 self.bodyContent.append('<colgroup><col /><col /></colgroup>\n'
253 '<col />\n'
254 '<tbody valign="top">\n'
255 '<tr>')
256 self.footnote_backrefs(node)
258 def depart_citation(self, node):
259 self.bodyContent.append('</td></tr>\n'
260 '</tbody>\n</table>\n')
262 def visit_citation_reference(self, node):
263 href = ''
264 if node.has_key('refid'):
265 href = '#' + node['refid']
266 elif node.has_key('refname'):
267 href = '#' + self.document.nameids[node['refname']]
268 self.bodyContent.append(self.starttag(node, 'a', '[', href=href))
270 def depart_citation_reference(self, node):
271 self.bodyContent.append(']</a>')
273 def visit_classifier(self, node):
274 pass
276 def depart_classifier(self, node):
277 pass
279 def visit_colspec(self, node):
280 self.colspecs.append(node)
282 def depart_colspec(self, node):
283 pass
285 def write_colspecs(self):
286 ##! verify
287 width = 0
288 for node in self.colspecs:
289 width += node['colwidth']
290 for node in self.colspecs:
291 colwidth = int(node['colwidth'] * 100.0 / width + 0.5)
292 self.bodyContent.append(self.emptytag(node, 'col', width='%i%%' % colwidth))
293 self.colspecs = []
295 def visit_comment(self, node, sub=re.compile('-(?=-)').sub):
296 self.bodyContent.append('<!-- %s -->\n' % sub('- ', node.astext()))
297 raise nodes.SkipNode
299 def visit_contact(self, node):
300 self.visit_docinfo_item(node, 'Contact')
302 def depart_contact(self, node):
303 self.depart_docinfo_item(node)
305 def visit_copyright(self, node):
306 self.visit_docinfo_item(node, 'copyright')
308 def depart_copyright(self, node):
309 self.depart_docinfo_item(node)
311 def visit_danger(self, node):
312 self.visit_admonition(node, 'danger', admonitionCellAtts={"bgcolor":"#ff6666"})
314 def depart_danger(self, node):
315 self.depart_admonition(node)
317 def visit_date(self, node):
318 self.visit_docinfo_item(node, 'date')
320 def depart_date(self, node):
321 self.depart_docinfo_item(node)
323 def visit_decoration(self, node):
324 pass
326 def depart_decoration(self, node):
327 pass
329 def visit_definition(self, node):
330 self.bodyContent.append('</dt>\n')
331 self.bodyContent.append(self.starttag(node, 'dd', ''))
333 def depart_definition(self, node):
334 self.bodyContent.append('</dd>\n')
336 def visit_definition_list(self, node):
337 self.bodyContent.append(self.starttag(node, 'dl'))
339 def depart_definition_list(self, node):
340 self.bodyContent.append('</dl>\n')
342 def visit_definition_list_item(self, node):
343 pass
345 def depart_definition_list_item(self, node):
346 pass
348 def visit_description(self, node):
349 self.bodyContent.append(self.starttag(node, 'td', ''))
351 def depart_description(self, node):
352 self.bodyContent.append('</td>')
354 def visit_docinfo(self, node):
355 self.context.append(len(self.bodyContent))
356 self.bodyContent.append(self.starttag(node, 'table'))
357 self.bodyContent.append('<tbody valign="top">\n')
358 self.in_docinfo = 1
360 def depart_docinfo(self, node):
361 self.bodyContent.append('</tbody>\n</table>\n')
362 self.in_docinfo = None
363 start = self.context.pop()
364 self.body_pre_docinfo = self.bodyContent[:start]
365 self.docinfo = self.bodyContent[start:]
366 self.bodyContent = []
368 def visit_docinfo_item(self, node, name, meta=1):
369 if meta:
370 self.headerContent.append('<meta name="%s" content="%s" />\n' %
371 (name, self.attval(node.astext())))
372 self.bodyContent.append(self.starttag(node, 'tr', ''))
373 self.bodyContent.append('<th >%s:</th>\n<td>' % self.language.labels[name.lower()])
375 def depart_docinfo_item(self, node):
376 self.bodyContent.append('</td></tr>\n')
378 def visit_doctest_block(self, node):
379 self.bodyContent.append(self.starttag(node, 'pre'))
381 def depart_doctest_block(self, node):
382 self.bodyContent.append('\n</pre>\n')
384 def visit_document(self, node):
385 pass
387 def depart_document(self, node):
388 pass
390 def visit_emphasis(self, node):
391 self.bodyContent.append('<i>')
393 def depart_emphasis(self, node):
394 self.bodyContent.append('</i>')
396 def visit_entry(self, node):
397 if isinstance(node.parent.parent, nodes.thead):
398 tagname = 'th'
399 else:
400 tagname = 'td'
401 atts = {}
402 if node.has_key('morerows'):
403 atts['rowspan'] = node['morerows'] + 1
404 if node.has_key('morecols'):
405 atts['colspan'] = node['morecols'] + 1
406 self.bodyContent.append(self.starttag(node, tagname, '', **atts))
407 self.context.append('</%s>\n' % tagname.lower())
408 if len(node) == 0: # empty cell
409 self.bodyContent.append('&nbsp;')
411 def depart_entry(self, node):
412 self.bodyContent.append(self.context.pop())
414 def visit_enumerated_list(self, node):
415 atts = {}
416 if node.has_key('start'):
417 atts['start'] = node['start']
418 if node.has_key('enumtype'):
419 atts['type'] = olTypeTranslator[node['enumtype']]
420 self.bodyContent.append(self.starttag(node, 'ol', **atts))
422 def depart_enumerated_list(self, node):
423 self.bodyContent.append('</ol>\n')
425 def visit_error(self, node):
426 self.visit_admonition(node, 'error', admonitionCellAtts={"bgcolor":"#ff6666"})
428 def depart_error(self, node):
429 self.depart_admonition(node)
431 def visit_field(self, node):
432 self.bodyContent.append(self.starttag(node, 'tr', ''))
434 def depart_field(self, node):
435 self.bodyContent.append('</tr>\n')
437 def visit_field_body(self, node):
438 self.bodyContent.append(self.starttag(node, 'td', ''))
439 self.spewParaTag.append(SpewBreak)
441 def depart_field_body(self, node):
442 self.bodyContent.append('</td>\n')
443 self.spewParaTag.pop()
445 def visit_field_list(self, node):
446 self.bodyContent.append(self.starttag(node, 'table'))
447 self.bodyContent.append('<tbody valign="top">\n')
449 def depart_field_list(self, node):
450 self.bodyContent.append('</tbody>\n</table>\n')
452 def visit_field_name(self, node):
453 atts = {}
454 if len(node.astext()) > 14:
455 atts['colspan'] = 2
456 self.context.append('</tr>\n<tr><td>&nbsp;</td>')
457 else:
458 self.context.append('')
459 self.bodyContent.append(self.starttag(node, 'th', '', **atts))
461 def depart_field_name(self, node):
462 self.bodyContent.append(':</th>')
463 self.bodyContent.append(self.context.pop())
465 def visit_figure(self, node):
466 self.bodyContent.append(self.starttag(node, 'div'))
468 def depart_figure(self, node):
469 self.bodyContent.append('</div>\n')
471 def visit_footer(self, node):
472 self.context.append(len(self.body))
474 def depart_footer(self, node):
475 start = self.context.pop()
476 footer = (['<hr/>\n',
477 self.starttag(node, 'div')]
478 + self.bodyContent[start:] + ['</div>\n'])
479 self.bodySuffix[:0] = footer
480 del self.bodyContent[start:]
482 def check_simple_list(self, node):
483 """Check for a simple list that can be rendered compactly."""
484 visitor = SimpleListChecker(self.document)
485 try:
486 node.walk(visitor)
487 except nodes.NodeFound:
488 return None
489 else:
490 return 1
492 def visit_footnote(self, node):
493 if not self.firstFootnoteVisited:
494 self.bodyContent.append('<hr />')
495 self.firstFootnoteVisited = True
496 self.bodyContent.append(self.starttag(node, 'table'))
497 self.bodyContent.append('<tbody valign="top">\n<tr>')
498 self.spewParaTag.append(SpewBreak)
499 self.footnote_backrefs(node)
501 def footnote_backrefs(self, node):
502 # if self.settings.footnote_backlinks and node.hasattr('backrefs'):
503 if node.hasattr('backrefs'):
504 backrefs = node['backrefs']
505 if len(backrefs) == 1:
506 self.context.append('')
507 self.context.append('<a href="#%s" name="%s">' % (backrefs[0], node['id']))
508 else:
509 i = 1
510 backlinks = []
511 for backref in backrefs:
512 backlinks.append('<a href="#%s">%s</a>' % (backref, i))
513 i += 1
514 self.context.append('<em>(%s)</em> ' % ', '.join(backlinks))
515 self.context.append('<a name="%s">' % node['id'])
516 else:
517 self.context.append('')
518 self.context.append('<a name="%s">' % node['id'])
520 def depart_footnote(self, node):
521 self.spewParaTag.pop()
522 self.paraFormat.pop()
523 self.bodyContent.append('</td></tr>\n</tbody>\n</table>\n')
525 def visit_footnote_reference(self, node):
526 href = ''
527 if node.has_key('refid'):
528 href = '#' + node['refid']
529 elif node.has_key('refname'):
530 href = '#' + self.document.nameids[node['refname']]
531 # format = self.settings.footnote_references
532 format = 'superscript'
533 if format == 'brackets':
534 suffix = '['
535 self.context.append(']')
536 elif format == 'superscript':
537 suffix = '<sup>'
538 self.context.append('</sup>')
539 else: # shouldn't happen
540 suffix = '???'
541 self.content.append('???')
542 self.bodyContent.append('<b>')
543 self.bodyContent.append(suffix)
544 self.bodyContent.append(self.starttag(node, 'a', '', href=href))
546 def depart_footnote_reference(self, node):
547 self.bodyContent.append('</a>')
548 self.bodyContent.append(self.context.pop())
549 self.bodyContent.append('</b>')
551 def visit_rubric(self, node):
552 self.bodyContent.append(self.starttag(node, 'p', ''))
554 def depart_rubric(self, node):
555 self.bodyContent.append('</p>\n')
557 def visit_generated(self, node):
558 pass
560 def depart_generated(self, node):
561 pass
563 def visit_hint(self, node):
564 self.visit_admonition(node, 'hint', admonitionCellAtts={"bgcolor":"#99ff99"})
566 def depart_hint(self, node):
567 self.depart_admonition(node)
569 def visit_image(self, node):
570 atts = node.attributes.copy()
571 atts['src'] = atts['uri']
572 del atts['uri']
573 if not atts.has_key('alt'):
574 atts['alt'] = atts['src']
575 if isinstance(node.parent, nodes.TextElement):
576 self.context.append(None)
577 else:
578 self.bodyContent.append('<p>')
579 self.context.append('</p>\n')
580 self.bodyContent.append(self.emptytag(node, 'img', '', **atts))
582 depart_image = popAndAppend
584 def visit_important(self, node):
585 self.visit_admonition(node, 'important', admonitionCellAtts={"bgcolor":"#ffcccc"})
587 def depart_important(self, node):
588 self.depart_admonition(node)
590 def visit_interpreted(self, node):
591 ###! no idea what to do here
592 pass
594 def depart_interpreted(self, node):
595 pass
597 def visit_label(self, node):
598 self.bodyContent.append(self.starttag(node, 'td', '<b>[%s' % self.context.pop()))
600 def depart_label(self, node):
601 self.paraFormat.append(('<font size=-1><i>', '</i></font>'))
602 self.bodyContent.append('</a>]</b></td><td>%s' % self.context.pop())
604 def visit_legend(self, node):
605 self.bodyContent.append(self.starttag(node, 'div'))
607 def depart_legend(self, node):
608 self.bodyContent.append('</div>\n')
610 def visit_list_item(self, node):
611 self.bodyContent.append(self.starttag(node, 'li', ''))
612 self.spewParaTag.append(SpewNothingThenPara)
614 def depart_list_item(self, node):
615 self.bodyContent.append('</li>\n')
616 self.spewParaTag.pop()
618 def visit_literal(self, node):
619 self.bodyContent.append(self.starttag(node, 'code', ''))
620 text = node.astext()
621 for token in self.words_and_spaces.findall(text):
622 if token.strip():
623 # Protect text like "--an-option" from bad line wrapping:
624 self.bodyContent.append('<span>%s</span>' % self.encode(token))
625 elif token in ('\n', ' '):
626 # Allow breaks at whitespace:
627 self.bodyContent.append(token)
628 else:
629 # Protect runs of multiple spaces; the last space can wrap:
630 self.bodyContent.append('&nbsp;' * (len(token) - 1) + ' ')
631 self.bodyContent.append('</code>')
632 # Content already processed:
633 raise nodes.SkipNode
635 def visit_literal_block(self, node):
636 self.bodyContent.append(self.starttag(node, 'pre'))
638 def depart_literal_block(self, node):
639 self.bodyContent.append('\n</pre>\n')
641 def visit_meta(self, node):
642 self.headerContent.append(self.emptytag(node, 'meta', **node.attributes))
644 def depart_meta(self, node):
645 pass
647 def visit_note(self, node):
648 self.visit_admonition(node, 'note')
650 def depart_note(self, node):
651 self.depart_admonition(node)
653 def visit_option(self, node):
654 if self.context[-1]:
655 self.bodyContent.append(', ')
657 def depart_option(self, node):
658 self.context[-1] += 1
660 def visit_option_argument(self, node):
661 self.bodyContent.append(node.get('delimiter', ' '))
662 self.bodyContent.append(self.starttag(node, 'var', ''))
664 def depart_option_argument(self, node):
665 self.bodyContent.append('</var>')
667 def visit_option_group(self, node):
668 atts = {}
669 if len(node.astext()) > 14:
670 atts['colspan'] = 2
671 self.context.append('</tr>\n<tr><td>&nbsp;</td>')
672 else:
673 self.context.append('')
674 self.bodyContent.append(self.starttag(node, 'td', **atts))
675 self.bodyContent.append('<kbd>') ###! What tag is this?
676 self.context.append(0) # count number of options
678 def depart_option_group(self, node):
679 self.context.pop()
680 self.bodyContent.append('</kbd></td>\n')
681 self.bodyContent.append(self.context.pop())
683 def visit_option_list(self, node):
684 self.bodyContent.append(self.starttag(node, 'table'))
685 self.bodyContent.append('<tbody valign="top">\n')
687 def depart_option_list(self, node):
688 self.bodyContent.append('</tbody>\n</table>\n')
690 def visit_option_list_item(self, node):
691 self.bodyContent.append(self.starttag(node, 'tr', ''))
693 def depart_option_list_item(self, node):
694 self.bodyContent.append('</tr>\n')
696 def visit_option_string(self, node):
697 self.bodyContent.append(self.starttag(node, 'span', ''))
699 def depart_option_string(self, node):
700 self.bodyContent.append('</span>')
702 def visit_organization(self, node):
703 self.visit_docinfo_item(node, 'organization')
705 def depart_organization(self, node):
706 self.depart_docinfo_item(node)
708 def visit_paragraph(self, node):
709 currentSpewParaTag = self.spewParaTag[-1]
710 if currentSpewParaTag == SpewParagraph:
711 self.bodyContent.append(self.starttag(node, 'p', ''))
712 self.context.append('</p>\n')
713 elif currentSpewParaTag == SpewBreak:
714 self.context.append('<br />\n')
715 elif currentSpewParaTag == SpewBreakBreak:
716 self.context.append('<br /><br />\n')
717 elif currentSpewParaTag == SpewNothingThenPara:
718 self.context.append(None)
719 self.spewParaTag[-1] = SpewParagraph
720 else:
721 self.context.append(None)
723 start, end = self.paraFormat[-1]
724 if start:
725 self.bodyContent.append(start)
726 if end:
727 self.context.append(end)
728 else:
729 self.context.append(None)
731 def depart_paragraph(self, node):
732 self.popAndAppend(node) # pop end formatting tag, if any
733 self.popAndAppend(node) # pop end paragraph tag, if any
735 def visit_problematic(self, node):
736 if node.hasattr('refid'):
737 self.bodyContent.append('<a href="#%s" name="%s">' % (node['refid'], node['id']))
738 self.context.append('</a>')
739 else:
740 self.context.append('')
741 self.bodyContent.append(self.starttag(node, 'span', ''))
743 def depart_problematic(self, node):
744 self.bodyContent.append('</span>')
745 self.bodyContent.append(self.context.pop())
747 def visit_reference(self, node):
748 if node.has_key('refuri'):
749 href = node['refuri']
750 elif node.has_key('refid'):
751 href = '#' + node['refid']
752 elif node.has_key('refname'):
753 href = '#' + self.document.nameids[node['refname']]
754 self.bodyContent.append(self.starttag(node, 'a', '', href=href))
755 self.context.append('</a>')
757 depart_reference = popAndAppend
759 def visit_revision(self, node):
760 self.visit_docinfo_item(node, 'revision')
762 def depart_revision(self, node):
763 self.depart_docinfo_item(node)
765 def visit_row(self, node):
766 self.bodyContent.append(self.starttag(node, 'tr', ''))
768 def depart_row(self, node):
769 self.bodyContent.append('</tr>\n')
771 def visit_section(self, node):
772 self.section_level += 1
773 #hTag = 'h%s'% self.section_level
774 #self.bodyContent.append(self.starttag(node, hTag))
775 #self.bodyContent.append('</%s>\n' % hTag)
777 def depart_section(self, node):
778 self.section_level -= 1
780 def visit_sidebar(self, node):
781 self.bodyContent.append('<hr width="80%" align="center"/>')
782 self.bodyContent.append('<table border="0" width="80%" align="center"><tbody><tr><td>')
784 def depart_sidebar(self, node):
785 self.bodyContent.append('</td></tr></tbody></table>\n')
786 self.bodyContent.append('<hr width="80%" align="center"/>')
788 def visit_status(self, node):
789 self.visit_docinfo_item(node, 'status', meta=None)
791 def depart_status(self, node):
792 self.depart_docinfo_item(node)
794 def visit_strong(self, node):
795 self.bodyContent.append('<strong>')
797 def depart_strong(self, node):
798 self.bodyContent.append('</strong>')
800 def visit_subscript(self, node):
801 self.bodyContent.append(self.starttag(node, 'sub', ''))
803 def depart_subscript(self, node):
804 self.bodyContent.append('</sub>')
806 def visit_substitution_definition(self, node):
807 raise nodes.SkipNode # internal
809 def visit_substitution_reference(self, node):
810 pass
812 def visit_subtitle(self, node):
813 self.bodyContent.append(self.starttag(node, 'h3', ''))
815 def depart_subtitle(self, node):
816 self.bodyContent.append('</h3>\n')
818 def visit_superscript(self, node):
819 self.bodyContent.append(self.starttag(node, 'sup', ''))
821 def depart_superscript(self, node):
822 self.bodyContent.append('</sup>')
824 def visit_table(self, node):
825 self.bodyContent.append(self.starttag(node, 'table'))
827 def depart_table(self, node):
828 self.bodyContent.append('</table>\n')
830 def visit_target(self, node):
831 if not (node.has_key('refuri') or node.has_key('refid') or node.has_key('refname')):
832 self.bodyContent.append(self.starttag(node, 'a', ''))
833 self.context.append('</a>')
834 else:
835 self.context.append(None)
837 depart_target = popAndAppend
839 def visit_tbody(self, node):
840 self.write_colspecs()
841 self.bodyContent.append(self.context.pop()) # '</colgroup>\n' or ''
842 self.bodyContent.append(self.starttag(node, 'tbody', valign='top'))
844 def depart_tbody(self, node):
845 self.bodyContent.append('</tbody>\n')
847 def visit_term(self, node):
848 self.bodyContent.append(self.starttag(node, 'dt', ''))
850 def depart_term(self, node):
851 pass
853 def visit_tgroup(self, node):
854 ###! verify
855 # Mozilla needs <colgroup>:
856 self.bodyContent.append(self.starttag(node, 'colgroup'))
857 # Appended by thead or tbody:
858 self.context.append('</colgroup>\n')
860 def depart_tgroup(self, node):
861 pass
863 def visit_thead(self, node):
864 self.write_colspecs()
865 self.bodyContent.append(self.context.pop()) # '</colgroup>\n'
866 # There may or may not be a <thead>; this is for <tbody> to use:
867 self.context.append('')
868 self.bodyContent.append(self.starttag(node, 'thead', valign='bottom'))
870 def depart_thead(self, node):
871 self.bodyContent.append('</thead>\n')
873 def visit_tip(self, node):
874 self.visit_admonition(node, 'tip')
876 def depart_tip(self, node):
877 self.depart_admonition(node)
879 def visit_title(self, node):
880 if isinstance(node.parent, nodes.topic) and (node.parent.attributes.get('name', None) != 'contents'):
881 self.bodyContent.append('<i><center>')
882 if node.parent.hasattr('id'):
883 self.bodyContent.append(self.starttag({},'a','',name=node.parent['id']))
884 self.context.append('</a></center></i><br />\n')
885 else:
886 self.context.append('</center></i><br />\n')
887 elif self.section_level == 0:
888 self.headerContent.append(DocArticleText.titleStart)
889 self.headerContent.append(self.encode(node.astext()))
890 self.headerContent.append(DocArticleText.titleEnd)
891 self.bodyContent.append(self.starttag(node, 'h2', ''))
892 self.context.append('</h2>\n')
893 else:
894 ### O'Reilly uses h2 to denote title and h3 for all sections. In theory,
895 ### nothing should hang below h3. In practice, we leave it up to the
896 ### author.
897 level = self.section_level + 1
898 self.bodyContent.append(self.starttag(node, 'h%s' % level, ''))
899 atts = {}
900 if node.parent.hasattr('id'):
901 atts['name'] = node.parent['id']
902 if node.hasattr('refid'):
903 atts['href'] = '#' + node['refid']
904 self.bodyContent.append(self.starttag({}, 'a', '', **atts))
905 self.context.append('</a></h%s>\n' % level)
907 def depart_title(self, node):
908 self.popAndAppend(node)
910 def visit_title_reference(self, node):
911 self.bodyContent.append(self.starttag(node, 'cite', ''))
913 def depart_title_reference(self, node):
914 self.bodyContent.append('</cite>')
916 def visit_topic(self, node):
917 if node.attributes.get('name', None) != 'contents':
918 self.spewParaTag.append(SpewNothingThenPara)
919 self.bodyContent.append('<table border="1" width="80%" align="center"><tbody><tr><td>')
921 def depart_topic(self, node):
922 if node.attributes.get('name', None) != 'contents':
923 self.bodyContent.append('</td></tr></tbody></table>\n')
924 self.spewParaTag.pop()
926 def visit_transition(self, node):
927 self.bodyContent.append(self.emptytag(node, 'hr'))
929 def depart_transition(self, node):
930 pass
932 def visit_version(self, node):
933 self.visit_docinfo_item(node, 'version')
935 def depart_version(self, node):
936 self.depart_docinfo_item(node)
938 def visit_warning(self, node):
939 self.visit_admonition(node, 'warning', admonitionCellAtts={"bgcolor":"#ffff33"})
941 def depart_warning(self, node):
942 self.depart_admonition(node)
944 def unknown_visit(self, node):
945 print 'Node: %s' % node.__class__.__name__
946 print "Failure processing at line (%s) of node:\n %s" % (node.line, node.pformat())
947 raise NotImplementedError('visiting unknown node type: %s'
948 % node.__class__.__name__)
950 def visit_system_message(self, node):
951 if node['level'] < self.document.reporter['writer'].report_level:
952 # Level is too low to display:
953 raise nodes.SkipNode
954 self.bodyContent.append(self.starttag(node, 'div'))
955 self.bodyContent.append('<p>')
956 attr = {}
957 backref_text = ''
958 if node.hasattr('id'):
959 attr['name'] = node['id']
960 if node.hasattr('backrefs'):
961 backrefs = node['backrefs']
962 if len(backrefs) == 1:
963 backref_text = ('; <em><a href="#%s">backlink</a></em>'
964 % backrefs[0])
965 else:
966 i = 1
967 backlinks = []
968 for backref in backrefs:
969 backlinks.append('<a href="#%s">%s</a>' % (backref, i))
970 i += 1
971 backref_text = ('; <em>backlinks: %s</em>'
972 % ', '.join(backlinks))
973 if node.hasattr('line'):
974 line = ', line %s' % node['line']
975 else:
976 line = ''
977 if attr:
978 a_start = self.starttag({}, 'a', '', **attr)
979 a_end = '</a>'
980 else:
981 a_start = a_end = ''
982 self.bodyContent.append('System Message: %s%s/%s%s (<tt>%s</tt>%s)%s</p>\n'
983 % (a_start, node['type'], node['level'], a_end,
984 node['source'], line, backref_text))
986 def depart_system_message(self, node):
987 self.bodyContent.append('</div>\n')