5 :Contact: dkuhlman@rexx.com
8 :Copyright: This module has been placed in the public domain.
10 LaTeX2e document tree Writer.
13 __docformat__
= 'reStructuredText'
15 # convention deactivate code by two # e.g. ##.
21 import urllib
, urlparse
22 from docutils
import writers
, nodes
, languages
24 ## from IPython.Shell import IPShellEmbed
26 ## ipshell = IPShellEmbed(args,
27 ## banner = 'Dropping into IPython',
28 ## exit_msg = 'Leaving Interpreter, back to program.')
31 # To turn off debug printing, set to 0.
34 # Constants and globals:
36 COMMENT_RE_SUB
= re
.compile('\n').sub
37 PROGRAMOPT_RE
= re
.compile('(--[_\-a-zA-Z0-9]+)')
38 EMAILADDR_RE
= re
.compile(r
'[_\-a-zA-Z0-9.]+@[_\-a-zA-Z0-9.]+')
39 WEBADDR_RE
= re
.compile(r
'http://[_\-a-zA-Z0-9./~]+')
48 self
.mode
= TABLE_MODE_NONE
49 def getColumnCount(self
): return self
.columnCount
50 def setColumnCount(self
, columnCount
): self
.columnCount
= columnCount
51 def getMode(self
): return self
.mode
52 def setMode(self
, mode
): self
.mode
= mode
55 class Writer(writers
.Writer
):
57 supported
= ('latex','latex2e')
58 """Formats this writer supports."""
61 'Documenting Python LaTeX Specific Options',
62 'The LaTeX "--output-encoding" default is "latin-1:strict".',
63 (('Specify documentclass (one of howto, manual, module). Default is "howto".',
64 # (('Specify documentclass (one of howto, manual). Default is "howto".',
66 {'default': 'howto', }),
69 #settings_defaults = {}
70 settings_defaults
= {'output_encoding': 'latin-1'}
73 """Final translated form of `document`."""
76 visitor
= DocPyTranslator(self
.document
)
77 self
.document
.walkabout(visitor
)
79 #pdb.run('self.document.walkabout(visitor)', globals(), locals())
80 self
.output
= visitor
.astext()
83 class DocPyTranslator(nodes
.NodeVisitor
):
84 latex_head
= '\\documentclass{%s}\n'
85 # add a generated on day , machine by user using docutils version.
86 generator
= '\n%% generator -- Docutils: http://docutils.sourceforge.net/\n' \
87 '%% writer -- documenting_python\n' \
88 '%% generated on -- %s\n\n' % time
.ctime()
90 def __init__(self
, document
):
91 nodes
.NodeVisitor
.__init
__(self
, document
)
93 self
.settings
= settings
= document
.settings
94 if self
.settings
.documentclass
== 'module':
95 raise NotImplementedError, 'Error: module document type not yet implemented.'
97 self
.latex_head
% (self
.settings
.documentclass
, ),
99 #self.linking % (self.colorlinks, self.hyperlink_color, self.hyperlink_color),
100 # geometry and fonts might go into style.tex.
101 #self.geometry % (self.d_paper, self.d_margins),
104 '\\usepackage{html}\n',
107 # NOTE: Latex wants a date and an author, rst puts this into
108 # docinfo, so normally we donot want latex author/date handling.
109 # latex article has its own handling of date and author, deactivate.
110 self
.latex_docinfo
= 0
112 self
.body_prefix
= []
113 # separate title, so we can appen subtitle.
116 self
.body_suffix
= ['\n']
117 self
.section_level
= 0
119 self
.topic_class
= ''
122 # verbatim: to tell encode not to encode.
124 # insert_newline: to tell encode to replace blanks by "~".
125 self
.insert_none_breaking_blanks
= 0
126 # insert_newline: to tell encode to add latex newline.
127 self
.insert_newline
= 0
128 # mbox_newline: to tell encode to add mbox and newline.
129 self
.mbox_newline
= 0
131 # inside literal block: no quote mangling.
134 self
.title_before_section
= 0
135 self
.seen_section
= 0
137 def pdebug(self
, msg
):
139 self
.body
.append(msg
)
141 def encode(self
, text
):
143 Encode special characters in `text` & return.
145 Escaping with a backslash does not help with backslashes, ~ and ^.
147 < > are only available in math-mode (really ?)
154 # compile the regexps once. do it here so one can see them.
157 if not self
.__dict
__.has_key('encode_re_braces'):
158 self
.encode_re_braces
= re
.compile(r
'([{}])')
159 text
= self
.encode_re_braces
.sub(r
'{\\\1}',text
)
160 if not self
.__dict
__.has_key('encode_re_bslash'):
161 # find backslash: except in the form '{\{}' or '{\}}'.
162 self
.encode_re_bslash
= re
.compile(r
'(?<!{)(\\)(?![{}]})')
163 # then the backslash: except in the form from line above:
164 # either '{\{}' or '{\}}'.
165 text
= self
.encode_re_bslash
.sub(r
'{\\textbackslash}', text
)
168 text
= text
.replace("$", '{\\$}')
169 # then all that needs math mode
170 text
= text
.replace("<", '{$<$}')
171 text
= text
.replace(">", '{$>$}')
173 text
= text
.replace("&", '{\\&}')
174 text
= text
.replace("_", '{\\_}')
176 # * verb|^| does not work in mbox.
177 # * mathmode has wedge. hat{~} would also work.
178 text
= text
.replace("^", '{\\ensuremath{^\\wedge}}')
179 text
= text
.replace("%", '{\\%}')
180 text
= text
.replace("#", '{\\#}')
181 text
= text
.replace("~", '{\\~{}}')
182 if self
.insert_newline
:
183 # HACK: insert a blank before the newline, to avoid
184 # ! LaTeX Error: There's no line here to end.
185 text
= text
.replace("\n", '~\\\\\n')
186 elif self
.mbox_newline
:
187 text
= text
.replace("\n", '}\\\\\n\\mbox{')
188 if self
.insert_none_breaking_blanks
:
189 text
= text
.replace(' ', '~')
191 text
= text
.replace(u
'\u2020', '{$\\dagger$}')
194 def attval(self
, text
,
195 whitespace
=re
.compile('[\n\r\t\v\f]')):
196 """Cleanse, encode, and return attribute value text."""
197 return self
.encode(whitespace
.sub(' ', text
))
199 def replace_email_addr(self
, mo
):
201 outtext
= '\\ulink{%s}{mailto:%s}' % (addr
, addr
)
204 def replace_web_addr(self
, mo
):
206 outtext
= '\\ulink{%s}{%s}' % (addr
, addr
)
209 def linkify(self
, intext
):
210 # If it looks like an email address, convert it to a "mailto" URL.
211 text1
= EMAILADDR_RE
.sub(self
.replace_email_addr
, intext
)
212 # If it looks like a URL, convert it to a ulink.
213 text2
= WEBADDR_RE
.sub(self
.replace_web_addr
, text1
)
217 title
= '\\title{%s}\n' % self
.title
218 if self
.docinfo
.has_key('revision'):
219 self
.head
.append('\\release{%s}\n' % self
.docinfo
['revision'])
220 if self
.docinfo
.has_key('date'):
221 self
.head
.append('\\date{%s}\n' % self
.docinfo
['date'])
222 if self
.docinfo
.has_key('author'):
223 self
.head
.append('\\author{%s}\n' % self
.docinfo
['author'])
224 if self
.docinfo
.has_key('address'):
225 self
.pdebug('%% [(astext) text: %s]\n' % self
.docinfo
['address'])
226 self
.head
.append('\\authoraddress{%s}\n' % \
227 #self.linkify(self.cleanHref(self.docinfo['address'])))
228 self
.cleanHref(self
.linkify(
229 self
.docinfo
['address'])).replace('\n', '\\\\\n'))
230 self
.body_prefix
.append('\\maketitle\n')
231 self
.body_prefix
.append('\\ifhtml\n')
232 self
.body_prefix
.append('\\chapter*{Front Matter\\label{front}}\n')
233 self
.body_prefix
.append('\\fi\n')
234 if self
.docinfo
.has_key('copyright'):
235 self
.body_prefix
.append('\n%s\n' % self
.docinfo
['copyright'])
236 if self
.docinfo
.has_key('abstract'):
237 self
.body_prefix
.append('\\begin{abstract}\n\\noindent\n')
238 self
.body_prefix
.append('%s\n' % self
.docinfo
['abstract'])
239 self
.body_prefix
.append('\\end{abstract}\n')
240 self
.body_prefix
.append('\\tableofcontents\n')
250 def visit_Text(self
, node
):
251 self
.body
.append(self
.encode(node
.astext()))
253 def depart_Text(self
, node
):
256 def visit_address(self
, node
):
257 self
.visit_docinfo_item(node
, 'address')
260 def depart_address(self
, node
):
261 self
.depart_docinfo_item(node
)
263 def visit_admonition(self
, node
, name
):
264 self
.body
.append('\\begin{center}\\begin{sffamily}\n')
265 self
.body
.append('\\fbox{\\parbox{\\admonitionwidth}{\n')
266 self
.body
.append('\\textbf{\\large '+ self
.language
.labels
[name
] + '}\n');
267 self
.body
.append('\\vspace{2mm}\n')
270 def depart_admonition(self
):
271 self
.body
.append('}}\n') # end parbox fbox
272 self
.body
.append('\\end{sffamily}\n\\end{center}\n');
274 def visit_attention(self
, node
):
275 self
.visit_admonition(node
, 'attention')
277 def depart_attention(self
, node
):
278 self
.depart_admonition()
280 def visit_author(self
, node
):
281 #self.pdebug('%% [(visit_author) node: %s]\n' % str(node))
282 self
.visit_docinfo_item(node
, 'author')
285 def depart_author(self
, node
):
286 self
.depart_docinfo_item(node
)
288 def visit_authors(self
, node
):
289 # ignore. visit_author is called for each one
290 # self.visit_docinfo_item(node, 'author')
293 def depart_authors(self
, node
):
294 # self.depart_docinfo_item(node)
297 def visit_block_quote(self
, node
):
298 # If the block quote contains a single object and that object
299 # is a list, then generate a list not a block quote.
300 # This lets us indent lists.
302 if len(node
.children
) == 1:
303 child
= node
.children
[0]
304 if isinstance(child
, nodes
.bullet_list
) or \
305 isinstance(child
, nodes
.enumerated_list
):
308 self
.body
.append('\\begin{quote}\n')
310 def depart_block_quote(self
, node
):
312 if len(node
.children
) == 1:
313 child
= node
.children
[0]
314 if isinstance(child
, nodes
.bullet_list
) or \
315 isinstance(child
, nodes
.enumerated_list
):
318 self
.body
.append('\\end{quote}\n')
320 def visit_bullet_list(self
, node
):
321 self
.body
.append('\\begin{itemize}\n' )
323 def depart_bullet_list(self
, node
):
324 self
.body
.append('\\end{itemize}\n' )
326 def visit_enumerated_list(self
, node
):
327 self
.body
.append('\\begin{enumerate}\n' )
329 def depart_enumerated_list(self
, node
):
330 self
.body
.append('\\end{enumerate}\n')
332 def visit_caption(self
, node
):
333 self
.body
.append('\\caption{' )
335 def depart_caption(self
, node
):
336 self
.body
.append('}')
338 def visit_caution(self
, node
):
339 self
.visit_admonition(node
, 'caution')
341 def depart_caution(self
, node
):
342 self
.depart_admonition()
344 def visit_citation(self
, node
):
345 self
.visit_footnote(node
)
347 def depart_citation(self
, node
):
348 self
.depart_footnote(node
)
350 def visit_title_reference(self
, node
):
351 # BUG title-references are what?
354 def depart_title_reference(self
, node
):
357 def visit_citation_reference(self
, node
):
360 def depart_citation_reference(self
, node
):
363 def visit_classifier(self
, node
):
366 def depart_classifier(self
, node
):
369 def visit_colspec(self
, node
):
372 def depart_colspec(self
, node
):
375 def visit_comment(self
, node
):
376 """Escape end of line by a ne comment start in comment text."""
377 self
.body
.append('\n%% %s \n' % COMMENT_RE_SUB('\n% ', node
.astext()))
380 def visit_contact(self
, node
):
381 self
.visit_docinfo_item(node
, 'contact')
383 def depart_contact(self
, node
):
384 self
.depart_docinfo_item(node
)
386 def visit_copyright(self
, node
):
387 self
.visit_docinfo_item(node
, 'copyright')
390 def depart_copyright(self
, node
):
391 self
.depart_docinfo_item(node
)
393 def visit_danger(self
, node
):
394 self
.visit_admonition(node
, 'danger')
396 def depart_danger(self
, node
):
397 self
.depart_admonition()
399 def visit_date(self
, node
):
400 ## self.pdebug('%% [(visit_date) node: %s]\n' % str(node))
401 self
.visit_docinfo_item(node
, 'date')
404 def depart_date(self
, node
):
405 self
.depart_docinfo_item(node
)
407 def visit_decoration(self
, node
):
410 def depart_decoration(self
, node
):
413 def visit_definition(self
, node
):
414 #self.body.append('% [(visit_definition)]\n')
417 def depart_definition(self
, node
):
418 self
.body
.append('\n')
419 #self.body.append('%[(depart_definition)]\n')
421 def visit_definition_list(self
, node
):
422 self
.body
.append( '\n\\begin{description}\n' )
424 def depart_definition_list(self
, node
):
425 self
.body
.append( '\\end{description}\n' )
427 def visit_definition_list_item(self
, node
):
428 #self.body.append('%[(visit_definition_list_item)]\n')
431 def depart_definition_list_item(self
, node
):
432 #self.body.append('%[(depart_definition_list_item)]\n')
435 def visit_description(self
, node
):
436 self
.body
.append( ' ' )
438 def depart_description(self
, node
):
441 def visit_docinfo(self
, node
):
444 def depart_docinfo(self
, node
):
447 def visit_docinfo_item(self
, node
, name
):
448 self
.docinfo
[name
] = node
.astext()
450 def depart_docinfo_item(self
, node
):
453 def visit_doctest_block(self
, node
):
454 self
.body
.append('\\begin{verbatim}\n' )
457 def depart_doctest_block(self
, node
):
458 self
.body
.append( '\n\\end{verbatim}\n' )
461 def visit_document(self
, node
):
462 self
.body_prefix
.append('\\begin{document}\n')
464 def depart_document(self
, node
):
466 self
.body
.append('\\end{seealso}\n')
467 self
.body_suffix
.append('\\end{document}\n')
469 def visit_emphasis(self
, node
):
470 self
.body
.append('\\emph{')
472 def depart_emphasis(self
, node
):
473 self
.body
.append('}')
475 def visit_error(self
, node
):
476 self
.visit_admonition(node
, 'error')
478 def depart_error(self
, node
):
479 self
.depart_admonition()
481 def visit_field(self
, node
):
482 # real output is done in siblings: _argument, _body, _name
486 def depart_field(self
, node
):
487 self
.body
.append('\n')
488 ##self.body.append('%[depart_field]\n')
490 def visit_field_argument(self
, node
):
491 #self.pdebug('%% [(visit_field_argument) node: %s]\n' % str(node))
492 #self.pdebug('% [visit_field_argument]\n')
495 def depart_field_argument(self
, node
):
496 #self.pdebug('% [(depart_field_argument)]\n')
499 def visit_field_body(self
, node
):
500 #self.pdebug('%% [(visit_field_body) node: %s]\n' % str(node))
501 # BUG by attach as text we loose references.
502 ## if self.docinfo and self.current_field_name:
503 ## self.docinfo[self.current_field_name] += '%s \\\\\n' % node.astext()
504 ## raise nodes.SkipNode
507 def depart_field_body(self
, node
):
508 ## self.body.append( '\n' )
511 def visit_field_list(self
, node
):
512 ## if not self.docinfo:
513 ## self.body.append('\\begin{quote}\n')
514 ## self.body.append('\\begin{description}\n')
517 def depart_field_list(self
, node
):
518 ## if not self.docinfo:
519 ## self.body.append('\\end{description}\n')
520 ## self.body.append('\\end{quote}\n')
523 def visit_field_name(self
, node
):
524 ## self.pdebug('%% [(visit_field_name) content: %s]\n' % node.astext())
525 ## self.pdebug('%% [(visit_field_name) node: %s\n' % str(node))
526 ## self.docinfo[node.astext()] = ''
527 ## self.current_field_name = node.astext()
530 def depart_field_name(self
, node
):
533 def visit_figure(self
, node
):
534 self
.body
.append( '\\begin{figure}\n' )
536 def depart_figure(self
, node
):
537 self
.body
.append( '\\end{figure}\n' )
539 def visit_footer(self
, node
):
540 self
.context
.append(len(self
.body
))
542 def depart_footer(self
, node
):
543 start
= self
.context
.pop()
544 footer
= (['\n\\begin{center}\small\n']
545 + self
.body
[start
:] + ['\n\\end{center}\n'])
546 self
.body_suffix
[:0] = footer
547 del self
.body
[start
:]
549 def visit_footnote(self
, node
):
550 notename
= node
['id']
551 self
.body
.append('\\begin{figure}[b]')
552 self
.body
.append('\\hypertarget{%s}' % notename
)
554 def depart_footnote(self
, node
):
555 self
.body
.append('\\end{figure}\n')
557 def visit_footnote_reference(self
, node
):
559 if node
.has_key('refid'):
561 elif node
.has_key('refname'):
562 href
= self
.document
.nameids
[node
['refname']]
563 format
= self
.settings
.footnote_references
564 if format
== 'brackets':
566 self
.context
.append(']')
567 elif format
== 'superscript':
568 suffix
= '\\raisebox{.5em}[0em]{\\scriptsize'
569 self
.context
.append('}')
570 else: # shouldn't happen
571 raise AssertionError('Illegal footnote reference format.')
572 self
.body
.append('%s\\hyperlink{%s}{' % (suffix
,href
))
574 def depart_footnote_reference(self
, node
):
575 self
.body
.append('}%s' % self
.context
.pop())
577 def visit_generated(self
, node
):
580 def depart_generated(self
, node
):
583 def visit_header(self
, node
):
584 self
.body
.append('%% [(visit_header) node: %s\n' % str(node
))
585 self
.context
.append(len(self
.body
))
587 def depart_header(self
, node
):
588 start
= self
.context
.pop()
589 self
.body_prefix
.append('\n\\verb|begin_header|\n')
590 self
.body_prefix
.extend(self
.body
[start
:])
591 self
.body_prefix
.append('\n\\verb|end_header|\n')
592 del self
.body
[start
:]
594 def visit_hint(self
, node
):
595 self
.visit_admonition(node
, 'hint')
597 def depart_hint(self
, node
):
598 self
.depart_admonition()
600 def visit_image(self
, node
):
601 atts
= node
.attributes
.copy()
603 ##self.body.append('\\begin{center}\n')
604 self
.body
.append('\n\\includegraphics{%s}\n' % href
)
605 ##self.body.append('\\end{center}\n')
607 def depart_image(self
, node
):
610 def visit_important(self
, node
):
611 self
.visit_admonition(node
, 'important')
613 def depart_important(self
, node
):
614 self
.depart_admonition()
616 def visit_interpreted(self
, node
):
617 # @@@ Incomplete, pending a proper implementation on the
619 self
.visit_literal(node
)
621 def depart_interpreted(self
, node
):
622 self
.depart_literal(node
)
624 def visit_label(self
, node
):
625 # footnote/citation label
626 self
.body
.append('[')
628 def depart_label(self
, node
):
629 self
.body
.append(']')
631 def visit_legend(self
, node
):
632 self
.body
.append('{\\small ')
634 def depart_legend(self
, node
):
635 self
.body
.append('}')
637 def visit_line_block(self
, node
):
639 * whitespace (including linebreaks) is significant
640 * inline markup is supported.
643 self
.body
.append('\\begin{flushleft}\n')
644 self
.insert_none_breaking_blanks
= 1
645 self
.line_block_without_mbox
= 1
646 if self
.line_block_without_mbox
:
647 self
.insert_newline
= 1
649 self
.mbox_newline
= 1
650 self
.body
.append('\\mbox{')
652 def depart_line_block(self
, node
):
653 if self
.line_block_without_mbox
:
654 self
.insert_newline
= 0
656 self
.body
.append('}')
657 self
.mbox_newline
= 0
658 self
.insert_none_breaking_blanks
= 0
659 self
.body
.append('\n\\end{flushleft}\n')
661 def visit_list_item(self
, node
):
662 self
.body
.append('\\item ')
664 def depart_list_item(self
, node
):
665 self
.body
.append('\n')
667 def visit_literal(self
, node
):
669 content
= node
.astext().strip()
670 if re
.search('[ \t\n]', content
):
671 self
.body
.append('\\samp{%s}' % content
)
673 self
.body
.append('\\code{%s}' % content
)
676 def depart_literal(self
, node
):
679 def visit_literal_block(self
, node
):
681 self
.body
.append('\n\\begin{verbatim}\n')
683 def depart_literal_block(self
, node
):
684 self
.body
.append('\n\\end{verbatim}\n')
687 def visit_meta(self
, node
):
688 self
.body
.append('[visit_meta]\n')
690 def depart_meta(self
, node
):
691 self
.body
.append('[depart_meta]\n')
693 def visit_note(self
, node
):
694 self
.visit_admonition(node
, 'note')
696 def depart_note(self
, node
):
697 self
.depart_admonition()
700 def visit_option(self
, node
):
702 # this is not the first option
703 self
.body
.append(', ')
705 def depart_option(self
, node
):
706 # flag tha the first option is done.
707 self
.context
[-1] += 1
709 def visit_option_argument(self
, node
):
710 """The delimiter betweeen an option and its argument."""
711 self
.body
.append(node
.get('delimiter', ' '))
713 def depart_option_argument(self
, node
):
716 def visit_option_group(self
, node
):
717 self
.body
.append('\\item [')
718 self
.context
.append(0)
720 def depart_option_group(self
, node
):
721 self
.context
.pop() # the flag
722 self
.body
.append('] ')
724 def visit_option_list(self
, node
):
725 self
.body
.append('\\begin{description}\n')
727 def depart_option_list(self
, node
):
728 self
.body
.append('\\end{description}\n')
730 def visit_option_list_item(self
, node
):
733 def depart_option_list_item(self
, node
):
736 def re_repl_long(self
, mo
):
737 result
= '\\longprogramopt{%s}' % mo
.group(0)[2:]
740 def visit_option_string(self
, node
):
741 content
= node
.astext()
742 content
= re
.sub(PROGRAMOPT_RE
, self
.re_repl_long
, content
)
743 self
.body
.append(content
)
746 def depart_option_string(self
, node
):
750 def visit_organization(self
, node
):
751 self
.visit_docinfo_item(node
, 'organization')
753 def depart_organization(self
, node
):
754 self
.depart_docinfo_item(node
)
756 def visit_paragraph(self
, node
):
757 if not self
.topic_class
== 'contents':
758 self
.body
.append('\n')
760 def depart_paragraph(self
, node
):
761 self
.body
.append('\n')
763 def visit_problematic(self
, node
):
764 self
.body
.append('{\\color{red}\\bfseries{}')
766 def depart_problematic(self
, node
):
767 self
.body
.append('}')
769 def visit_raw(self
, node
):
770 if node
.has_key('format') and node
['format'].lower() == 'latex':
771 self
.body
.append(node
.astext())
774 def cleanHref(self
, href
):
775 href
= href
.replace('~', '\\~{}')
776 href
= href
.replace('#', '\\#{}')
779 ## def visit_reference(self, node):
780 ## self.pdebug('%% [(visit_reference) node: %s]\n' % str(node))
781 ## if node.has_key('refuri'):
782 ## self.href = node['refuri']
783 ## elif node.has_key('refid'):
784 ## self.href = '#' + node['refid']
785 ## elif node.has_key('refname'):
786 ## self.href = '#' + self.document.nameids[node['refname']]
787 ## self.href = self.cleanHref(self.href)
789 ## self.body.append('\\seeurl{%s}{' % self.href)
791 ## self.body.append('\\ulink{')
793 ## def depart_reference(self, node):
795 ## self.body.append('}')
797 ## self.body.append('}{%s}' % self.href)
799 def visit_reference(self
, node
):
800 #self.pdebug('%% [(visit_reference) node: %s]\n' % str(node))
802 if node
.has_key('refuri'):
803 href
= node
['refuri']
804 href
= self
.cleanHref(href
)
805 self
.body
.append('\\seeurl{%s}{' % href
)
807 if node
.has_key('refuri'):
809 self
.body
.append('\\ulink{')
810 elif node
.has_key('refid'):
813 href
= self
.cleanHref(href
)
814 self
.body
.append('\\ref{%s}' % href
)
817 def depart_reference(self
, node
):
819 self
.body
.append('}')
821 if node
.has_key('refuri'):
823 href
= node
['refuri']
824 href
= self
.cleanHref(href
)
825 self
.body
.append('}{%s}' % href
)
826 elif node
.has_key('refid'):
830 def visit_revision(self
, node
):
831 ## self.pdebug('%% [(visit_revision) node: "%s"]\n' % str(node))
832 self
.visit_docinfo_item(node
, 'revision')
835 def depart_revision(self
, node
):
836 self
.depart_docinfo_item(node
)
838 def visit_section(self
, node
):
839 # If the document title came before the first (outer-most)
840 # section, then the first section is really the first section.
841 # If the document title is *in* the first section, then
842 # the first section is a container that has the document title.
843 # So, if the document title came before the first section,
844 # then alwasy increment the section_level.
845 # But, if the document title is *in* the first section, then
846 # increment the section_level only on the *second* and
847 # subsequent sections.
848 ## self.pdebug('%% [(visit_section) node: %s]\n' % repr(node))
849 if self
.title_before_section
or self
.seen_section
:
850 self
.section_level
+= 1
851 self
.seen_section
= 1
853 def depart_section(self
, node
):
854 ## self.pdebug('%% [(depart_section) node: %s]\n' % repr(node))
855 if self
.title_before_section
:
856 self
.section_level
-= 1
858 def visit_sidebar(self
, node
):
859 # BUG: this is just a hack to make sidebars render something
860 self
.body
.append('\\begin{center}\\begin{sffamily}\n')
861 self
.body
.append('\\fbox{\\colorbox[gray]{0.80}{\\parbox{\\admonitionwidth}{\n')
863 def depart_sidebar(self
, node
):
864 self
.body
.append('}}}\n') # end parbox colorbox fbox
865 self
.body
.append('\\end{sffamily}\n\\end{center}\n');
868 attribution_formats
= {'dash': ('---', ''),
869 'parentheses': ('(', ')'),
870 'parens': ('(', ')'),
873 def visit_attribution(self
, node
):
874 prefix
, suffix
= self
.attribution_formats
[self
.settings
.attribution
]
875 self
.body
.append('\n\\begin{flushright}\n')
876 self
.body
.append(prefix
)
877 self
.context
.append(suffix
)
879 def depart_attribution(self
, node
):
880 self
.body
.append(self
.context
.pop() + '\n')
881 self
.body
.append('\\end{flushright}\n')
883 def visit_status(self
, node
):
884 self
.visit_docinfo_item(node
, 'status')
886 def depart_status(self
, node
):
887 self
.depart_docinfo_item(node
)
889 def visit_strong(self
, node
):
890 self
.body
.append('\\strong{')
892 def depart_strong(self
, node
):
893 self
.body
.append('}')
895 def visit_substitution_definition(self
, node
):
898 def visit_substitution_reference(self
, node
):
899 self
.unimplemented_visit(node
)
901 def visit_subtitle(self
, node
):
902 self
.title
= self
.title
+ \
903 '\\\\\n\\large{%s}\n' % self
.encode(node
.astext())
906 def depart_subtitle(self
, node
):
909 def visit_system_message(self
, node
):
910 if node
['level'] < self
.document
.reporter
['writer'].report_level
:
914 def depart_system_message(self
, node
):
915 self
.body
.append('\n')
917 def visit_target(self
, node
):
918 #self.pdebug('%% [(visit_target) node: %s]\n' % str(node))
920 ## if not (node.has_key('refuri') or node.has_key('refid')
921 ## or node.has_key('refname')):
922 ## self.body.append('\\hypertarget{%s}{' % node['name'])
923 ## self.context.append('}')
925 ## self.context.append('')
927 def depart_target(self
, node
):
929 ## self.body.append(self.context.pop())
931 def visit_table(self
, node
):
932 self
.tableSpec
= TableSpec()
934 def depart_table(self
, node
):
937 def visit_tgroup(self
, node
):
938 columnCount
= int(node
.get('cols', 0))
939 self
.tableSpec
.setColumnCount(columnCount
)
941 self
.body
.append('\\begin{tableii}{l|l}{textrm}')
942 elif columnCount
== 3:
943 self
.body
.append('\\begin{tableiii}{l|l|l}{textrm}')
944 elif columnCount
== 4:
945 self
.body
.append('\\begin{tableiv}{l|l|l|l}{textrm}')
946 elif columnCount
== 5:
947 self
.body
.append('\\begin{tablev}{l|l|l|l|l}{textrm}')
949 def depart_tgroup(self
, node
):
950 if self
.tableSpec
.getColumnCount() == 2:
951 self
.body
.append('\n\\end{tableii}\n')
952 elif self
.tableSpec
.getColumnCount() == 3:
953 self
.body
.append('\n\\end{tableiii}\n')
954 elif self
.tableSpec
.getColumnCount() == 4:
955 self
.body
.append('\n\\end{tableiv}\n')
956 elif self
.tableSpec
.getColumnCount() == 5:
957 self
.body
.append('\n\\end{tablev}\n')
959 def visit_thead(self
, node
):
960 self
.tableSpec
.setMode(TABLE_MODE_HEAD
)
962 def depart_thead(self
, node
):
963 self
.tableSpec
.setMode(TABLE_MODE_NONE
)
965 def visit_tbody(self
, node
):
966 self
.tableSpec
.setMode(TABLE_MODE_BODY
)
968 def depart_tbody(self
, node
):
969 self
.tableSpec
.setMode(TABLE_MODE_NONE
)
971 def visit_row(self
, node
):
972 if self
.tableSpec
.getMode() == TABLE_MODE_HEAD
:
974 elif self
.tableSpec
.getMode() == TABLE_MODE_BODY
:
975 if self
.tableSpec
.getColumnCount() == 2:
976 self
.body
.append('\n\\lineii')
977 elif self
.tableSpec
.getColumnCount() == 3:
978 self
.body
.append('\n\\lineiii')
979 elif self
.tableSpec
.getColumnCount() == 4:
980 self
.body
.append('\n\\lineiv')
981 elif self
.tableSpec
.getColumnCount() == 5:
982 self
.body
.append('\n\\linev')
984 def depart_row(self
, node
):
985 if self
.tableSpec
.getMode() == TABLE_MODE_HEAD
:
987 elif self
.tableSpec
.getMode() == TABLE_MODE_BODY
:
990 def visit_entry(self
, node
):
991 if self
.tableSpec
.getMode() == TABLE_MODE_HEAD
:
992 #self.body.append('%% [(visit_entry) text: +%s+]' % node.astext())
993 self
.body
.append('{%s}' % node
.astext().strip(' '))
995 elif self
.tableSpec
.getMode() == TABLE_MODE_BODY
:
996 #self.body.append('%% [(visit_entry) text: +%s+]' % node.astext())
997 self
.body
.append('{%s}' % node
.astext().strip(' '))
1000 def depart_entry(self
, node
):
1002 ## if self.tableSpec.getMode() == TABLE_MODE_HEAD:
1003 ## self.body.append('}')
1004 ## elif self.tableSpec.getMode() == TABLE_MODE_BODY:
1005 ## self.body.append('}')
1007 def visit_term(self
, node
):
1008 self
.body
.append('\\item[')
1010 def depart_term(self
, node
):
1011 # definition list term.
1012 self
.body
.append(':]\n')
1014 def visit_tip(self
, node
):
1015 self
.visit_admonition(node
, 'tip')
1017 def depart_tip(self
, node
):
1018 self
.depart_admonition()
1020 ## def string_to_label(self, text):
1021 ## text = text.replace(' ', '-')
1022 ## text = text.replace('_', '-')
1025 def visit_title(self
, node
):
1026 #self.pdebug('%% [(visit_title) section_level: %d node.astext: "%s"]\n' % \
1027 # (self.section_level, node.astext()))
1028 #self.pdebug('%% [(visit_title) section_level: %d node.astext.encode: "%s"]\n' % \
1029 # (self.section_level, self.encode(node.astext())))
1030 if self
.section_level
== 0:
1031 self
.title_before_section
= 1
1033 self
.body
.append('\\end{seealso}\n')
1035 if node
.astext().lower() == 'see also':
1036 self
.body
.append('\\begin{seealso}\n')
1038 raise nodes
.SkipNode
1040 if self
.section_level
== 0:
1041 # It's the document title before any section.
1042 self
.title
= self
.encode(node
.astext())
1044 # It's a title for a section in-side the document.
1045 self
.body
.append('\n\n')
1046 self
.body
.append('%' + '_' * 75)
1047 self
.body
.append('\n\n')
1048 s1
= self
.encode(node
.astext())
1049 # Remove the non-breaking space character.
1050 # Not needed. Using output-encoding=latin-1 fixes this.
1051 #s1 = s1.replace(u'\xa0', ' ')
1052 s2
= nodes
.make_id(node
.astext())
1054 #ipshell('(visit_title) Entering ipshell.\nHit Ctrl-D to exit ipshell.')
1056 if (self
.section_level
== 1):
1057 self
.body
.append('\\section{%s\\label{%s}}\n' % (s1
, s2
))
1059 # '\\section{\htmladdnormallink[%s]{%s}{}}\n' % \
1061 elif (self
.section_level
== 2):
1062 self
.body
.append('\\subsection{%s\\label{%s}}\n' % (s1
, s2
))
1063 elif (self
.section_level
== 3):
1064 self
.body
.append('\\subsubsection{%s\\label{%s}}\n' % (s1
, s2
))
1065 elif (self
.section_level
== 4):
1066 self
.body
.append('\\paragraph{%s\\label{%s}}\n' % (s1
, s2
))
1068 self
.body
.append('\\subparagraph{%s\\label{%s}}\n' % (s1
, s2
))
1069 raise nodes
.SkipNode
1071 def depart_title(self
, node
):
1074 def visit_topic(self
, node
):
1075 topic_class
= node
.get('class')
1076 if topic_class
== 'abstract':
1077 self
.docinfo
['abstract'] = node
.astext()[9:]
1078 raise nodes
.SkipNode
1080 raise nodes
.SkipNode
1082 def depart_topic(self
, node
):
1085 def visit_rubric(self
, node
):
1086 self
.body
.append('% [(visit_rubric)]\n')
1088 def depart_rubric(self
, node
):
1089 self
.body
.append('% [(depart_rubric)]\n')
1091 def visit_transition(self
, node
):
1092 self
.body
.append('\n\n')
1093 self
.body
.append('%' + '_' * 75)
1094 self
.body
.append('\n\\hrule{}\n\n')
1096 def depart_transition(self
, node
):
1099 def visit_version(self
, node
):
1100 self
.visit_docinfo_item(node
, 'version')
1102 def depart_version(self
, node
):
1103 self
.depart_docinfo_item(node
)
1105 def visit_warning(self
, node
):
1106 self
.visit_admonition(node
, 'warning')
1108 def depart_warning(self
, node
):
1109 self
.depart_admonition()
1111 def unimplemented_visit(self
, node
):
1112 raise NotImplementedError('visiting unimplemented node type: %s'
1113 % node
.__class
__.__name
__)