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
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.
19 __docformat__
= 'reStructuredText'
22 from warnings
import warn
27 from docutils
import nodes
, utils
, writers
, languages
29 from DocArticle
import DocArticleText
31 class DocArticleWriter(writers
.Writer
):
33 """Formats this writer supports."""
36 """Final translated form of `document`."""
39 writers
.Writer
.__init
__(self
)
40 self
.translator_class
= HTMLDocArticleTranslator
43 visitor
= self
.translator_class(self
.document
)
44 self
.document
.walkabout(visitor
)
45 self
.output
= visitor
.astext()
51 SpewNothingThenPara
= 4
61 class HTMLDocArticleTranslator(nodes
.NodeVisitor
):
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
= []
80 self
.spewTextContext
= [True]
81 self
.spewParaTag
= [SpewParagraph
]
82 self
.paraFormat
= [(None,None)]
85 self
.body_pre_docinfo
= []
87 self
.compact_simple
= None
90 self
.firstFootnoteVisited
= False
92 # lcode = settings.language_code
94 self
.language
= languages
.get_language(lcode
)
97 return ''.join([DocArticleText
.contentStart
, DocArticleText
.headerStart
] +
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('<', '<')
108 text
= text
.replace('"', '"')
109 text
= text
.replace('>', '>')
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()
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()
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
))))
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
):
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
))
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': ('—', ''),
184 'parentheses': ('(', ')'),
185 'parens': ('(', ')'),
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
):
208 def depart_authors(self
, node
):
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'
254 '<tbody valign="top">\n'
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
):
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
):
276 def depart_classifier(self
, node
):
279 def visit_colspec(self
, node
):
280 self
.colspecs
.append(node
)
282 def depart_colspec(self
, node
):
285 def write_colspecs(self
):
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
))
295 def visit_comment(self
, node
, sub
=re
.compile('-(?=-)').sub
):
296 self
.bodyContent
.append('<!-- %s -->\n' % sub('- ', node
.astext()))
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
):
326 def depart_decoration(self
, node
):
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
):
345 def depart_definition_list_item(self
, node
):
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')
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):
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
):
387 def depart_document(self
, node
):
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
):
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(' ')
411 def depart_entry(self
, node
):
412 self
.bodyContent
.append(self
.context
.pop())
414 def visit_enumerated_list(self
, node
):
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
):
454 if len(node
.astext()) > 14:
456 self
.context
.append('</tr>\n<tr><td> </td>')
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
)
487 except nodes
.NodeFound
:
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']))
511 for backref
in backrefs
:
512 backlinks
.append('<a href="#%s">%s</a>' % (backref
, i
))
514 self
.context
.append('<em>(%s)</em> ' % ', '.join(backlinks
))
515 self
.context
.append('<a name="%s">' % node
['id'])
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
):
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':
535 self
.context
.append(']')
536 elif format
== 'superscript':
538 self
.context
.append('</sup>')
539 else: # shouldn't happen
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
):
560 def depart_generated(self
, node
):
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']
573 if not atts
.has_key('alt'):
574 atts
['alt'] = atts
['src']
575 if isinstance(node
.parent
, nodes
.TextElement
):
576 self
.context
.append(None)
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
594 def depart_interpreted(self
, node
):
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', ''))
621 for token
in self
.words_and_spaces
.findall(text
):
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
)
629 # Protect runs of multiple spaces; the last space can wrap:
630 self
.bodyContent
.append(' ' * (len(token
) - 1) + ' ')
631 self
.bodyContent
.append('</code>')
632 # Content already processed:
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
):
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
):
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
):
669 if len(node
.astext()) > 14:
671 self
.context
.append('</tr>\n<tr><td> </td>')
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
):
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
721 self
.context
.append(None)
723 start
, end
= self
.paraFormat
[-1]
725 self
.bodyContent
.append(start
)
727 self
.context
.append(end
)
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>')
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
):
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>')
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
):
853 def visit_tgroup(self
, node
):
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
):
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')
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')
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
897 level
= self
.section_level
+ 1
898 self
.bodyContent
.append(self
.starttag(node
, 'h%s' % level
, ''))
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
):
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:
954 self
.bodyContent
.append(self
.starttag(node
, 'div'))
955 self
.bodyContent
.append('<p>')
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>'
968 for backref
in backrefs
:
969 backlinks
.append('<a href="#%s">%s</a>' % (backref
, i
))
971 backref_text
= ('; <em>backlinks: %s</em>'
972 % ', '.join(backlinks
))
973 if node
.hasattr('line'):
974 line
= ', line %s' % node
['line']
978 a_start
= self
.starttag({}, 'a', '', **attr
)
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')