removed obsolete issues (many of them fixed with AE)
[docutils.git] / sandbox / dkuhlman / docutils / docutils / writers / python_latex.py
blob62f4c03634b12512e9bf7dfbc1d5e63a812c20db
1 #!/usr/bin/env python
3 """
4 :Author: Dave Kuhlman
5 :Contact: dkuhlman@rexx.com
6 :Revision: $Revision$
7 :Date: $Date$
8 :Copyright: This module has been placed in the public domain.
10 LaTeX2e document tree Writer.
11 """
13 __docformat__ = 'reStructuredText'
15 # convention deactivate code by two # e.g. ##.
17 import sys
18 import time
19 import re
20 import string
21 import urllib, urlparse
22 from docutils import writers, nodes, languages
24 ## from IPython.Shell import IPShellEmbed
25 ## args = ''
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.
32 DEBUG_FLAG = 1
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./~]+')
41 TABLE_MODE_NONE = 0
42 TABLE_MODE_HEAD = 1
43 TABLE_MODE_BODY = 2
45 class TableSpec:
46 def __init__(self):
47 self.columnCount = 0
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."""
60 settings_spec = (
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".',
65 ['--documentclass'],
66 {'default': 'howto', }),
69 #settings_defaults = {}
70 settings_defaults = {'output_encoding': 'latin-1'}
72 output = None
73 """Final translated form of `document`."""
75 def translate(self):
76 visitor = DocPyTranslator(self.document)
77 self.document.walkabout(visitor)
78 #import pdb
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)
92 self.docinfo = {}
93 self.settings = settings = document.settings
94 if self.settings.documentclass == 'module':
95 raise NotImplementedError, 'Error: module document type not yet implemented.'
96 self.head_prefix = [
97 self.latex_head % (self.settings.documentclass, ),
98 #self.encoding,
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),
103 self.generator,
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
111 self.head = [ ]
112 self.body_prefix = []
113 # separate title, so we can appen subtitle.
114 self.title = ""
115 self.body = []
116 self.body_suffix = ['\n']
117 self.section_level = 0
118 self.context = []
119 self.topic_class = ''
120 # Flags to encode
121 # ---------------
122 # verbatim: to tell encode not to encode.
123 self.verbatim = 0
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.
132 self.literal = 0
133 self.seealso = 0
134 self.title_before_section = 0
135 self.seen_section = 0
137 def pdebug(self, msg):
138 if DEBUG_FLAG >= 1:
139 self.body.append(msg)
141 def encode(self, text):
143 Encode special characters in `text` & return.
144 # $ % & ~ _ ^ \ { }
145 Escaping with a backslash does not help with backslashes, ~ and ^.
147 < > are only available in math-mode (really ?)
148 $ starts math- mode.
149 AND quotes:
152 if self.verbatim:
153 return text
154 # compile the regexps once. do it here so one can see them.
156 # first the braces.
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)
167 # then dollar
168 text = text.replace("$", '{\\$}')
169 # then all that needs math mode
170 text = text.replace("<", '{$<$}')
171 text = text.replace(">", '{$>$}')
172 # then
173 text = text.replace("&", '{\\&}')
174 text = text.replace("_", '{\\_}')
175 # the ^:
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(' ', '~')
190 # unicode !!!
191 text = text.replace(u'\u2020', '{$\\dagger$}')
192 return text
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):
200 addr = mo.group(0)
201 outtext = '\\ulink{%s}{mailto:%s}' % (addr, addr)
202 return outtext
204 def replace_web_addr(self, mo):
205 addr = mo.group(0)
206 outtext = '\\ulink{%s}{%s}' % (addr, addr)
207 return outtext
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)
214 return text2
216 def astext(self):
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')
241 result = ''.join(
242 self.head_prefix +
243 [title] +
244 self.head +
245 self.body_prefix +
246 self.body +
247 self.body_suffix)
248 return result
250 def visit_Text(self, node):
251 self.body.append(self.encode(node.astext()))
253 def depart_Text(self, node):
254 pass
256 def visit_address(self, node):
257 self.visit_docinfo_item(node, 'address')
258 raise nodes.SkipNode
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')
283 raise nodes.SkipNode
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')
291 pass
293 def depart_authors(self, node):
294 # self.depart_docinfo_item(node)
295 pass
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.
301 done = 0
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):
306 done = 1
307 if not done:
308 self.body.append('\\begin{quote}\n')
310 def depart_block_quote(self, node):
311 done = 0
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):
316 done = 1
317 if not done:
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?
352 pass
354 def depart_title_reference(self, node):
355 pass
357 def visit_citation_reference(self, node):
358 pass
360 def depart_citation_reference(self, node):
361 pass
363 def visit_classifier(self, node):
364 pass
366 def depart_classifier(self, node):
367 pass
369 def visit_colspec(self, node):
370 pass
372 def depart_colspec(self, node):
373 pass
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()))
378 raise nodes.SkipNode
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')
388 raise nodes.SkipNode
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')
402 raise nodes.SkipNode
404 def depart_date(self, node):
405 self.depart_docinfo_item(node)
407 def visit_decoration(self, node):
408 pass
410 def depart_decoration(self, node):
411 pass
413 def visit_definition(self, node):
414 #self.body.append('% [(visit_definition)]\n')
415 pass
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')
429 pass
431 def depart_definition_list_item(self, node):
432 #self.body.append('%[(depart_definition_list_item)]\n')
433 pass
435 def visit_description(self, node):
436 self.body.append( ' ' )
438 def depart_description(self, node):
439 pass
441 def visit_docinfo(self, node):
442 self.docinfo = {}
444 def depart_docinfo(self, node):
445 pass
447 def visit_docinfo_item(self, node, name):
448 self.docinfo[name] = node.astext()
450 def depart_docinfo_item(self, node):
451 pass
453 def visit_doctest_block(self, node):
454 self.body.append('\\begin{verbatim}\n' )
455 self.verbatim = 1
457 def depart_doctest_block(self, node):
458 self.body.append( '\n\\end{verbatim}\n' )
459 self.verbatim = 0
461 def visit_document(self, node):
462 self.body_prefix.append('\\begin{document}\n')
464 def depart_document(self, node):
465 if self.seealso:
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
483 raise nodes.SkipNode
484 pass
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')
493 pass
495 def depart_field_argument(self, node):
496 #self.pdebug('% [(depart_field_argument)]\n')
497 pass
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
505 pass
507 def depart_field_body(self, node):
508 ## self.body.append( '\n' )
509 pass
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')
515 pass
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')
521 pass
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()
528 pass
530 def depart_field_name(self, node):
531 pass
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):
558 href = ''
559 if node.has_key('refid'):
560 href = node['refid']
561 elif node.has_key('refname'):
562 href = self.document.nameids[node['refname']]
563 format = self.settings.footnote_references
564 if format == 'brackets':
565 suffix = '['
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):
578 pass
580 def depart_generated(self, node):
581 pass
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()
602 href = atts['uri']
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):
608 pass
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
618 # Parser/Reader end.
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):
638 """line-block:
639 * whitespace (including linebreaks) is significant
640 * inline markup is supported.
641 * serif typeface
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
648 else:
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
655 else:
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):
668 ## self.literal = 1
669 content = node.astext().strip()
670 if re.search('[ \t\n]', content):
671 self.body.append('\\samp{%s}' % content)
672 else:
673 self.body.append('\\code{%s}' % content)
674 raise nodes.SkipNode
676 def depart_literal(self, node):
677 pass
679 def visit_literal_block(self, node):
680 self.verbatim = 1
681 self.body.append('\n\\begin{verbatim}\n')
683 def depart_literal_block(self, node):
684 self.body.append('\n\\end{verbatim}\n')
685 self.verbatim = 0
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):
701 if self.context[-1]:
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):
714 pass
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):
731 pass
733 def depart_option_list_item(self, node):
734 pass
736 def re_repl_long(self, mo):
737 result = '\\longprogramopt{%s}' % mo.group(0)[2:]
738 return result
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)
744 raise nodes.SkipNode
746 def depart_option_string(self, node):
747 pass
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())
772 raise nodes.SkipNode
774 def cleanHref(self, href):
775 href = href.replace('~', '\\~{}')
776 href = href.replace('#', '\\#{}')
777 return href
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)
788 ## if self.seealso:
789 ## self.body.append('\\seeurl{%s}{' % self.href)
790 ## else:
791 ## self.body.append('\\ulink{')
793 ## def depart_reference(self, node):
794 ## if self.seealso:
795 ## self.body.append('}')
796 ## else:
797 ## self.body.append('}{%s}' % self.href)
799 def visit_reference(self, node):
800 #self.pdebug('%% [(visit_reference) node: %s]\n' % str(node))
801 if self.seealso:
802 if node.has_key('refuri'):
803 href = node['refuri']
804 href = self.cleanHref(href)
805 self.body.append('\\seeurl{%s}{' % href)
806 else:
807 if node.has_key('refuri'):
808 # External hyperlink
809 self.body.append('\\ulink{')
810 elif node.has_key('refid'):
811 # Internal hyperlink
812 href = node['refid']
813 href = self.cleanHref(href)
814 self.body.append('\\ref{%s}' % href)
815 raise nodes.SkipNode
817 def depart_reference(self, node):
818 if self.seealso:
819 self.body.append('}')
820 else:
821 if node.has_key('refuri'):
822 # External hyperlink
823 href = node['refuri']
824 href = self.cleanHref(href)
825 self.body.append('}{%s}' % href)
826 elif node.has_key('refid'):
827 # Internal hyperlink
828 pass
830 def visit_revision(self, node):
831 ## self.pdebug('%% [(visit_revision) node: "%s"]\n' % str(node))
832 self.visit_docinfo_item(node, 'revision')
833 raise nodes.SkipNode
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': ('(', ')'),
871 'none': ('', '')}
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):
896 raise nodes.SkipNode
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())
904 raise nodes.SkipNode
906 def depart_subtitle(self, node):
907 pass
909 def visit_system_message(self, node):
910 if node['level'] < self.document.reporter['writer'].report_level:
911 raise nodes.SkipNode
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))
919 pass
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('}')
924 ## else:
925 ## self.context.append('')
927 def depart_target(self, node):
928 pass
929 ## self.body.append(self.context.pop())
931 def visit_table(self, node):
932 self.tableSpec = TableSpec()
934 def depart_table(self, node):
935 self.tablSpec = None
937 def visit_tgroup(self, node):
938 columnCount = int(node.get('cols', 0))
939 self.tableSpec.setColumnCount(columnCount)
940 if columnCount == 2:
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:
973 pass
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:
986 pass
987 elif self.tableSpec.getMode() == TABLE_MODE_BODY:
988 pass
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(' '))
994 raise nodes.SkipNode
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(' '))
998 raise nodes.SkipNode
1000 def depart_entry(self, node):
1001 pass
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('_', '-')
1023 ## return text
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
1032 if self.seealso:
1033 self.body.append('\\end{seealso}\n')
1034 self.seealso = 0
1035 if node.astext().lower() == 'see also':
1036 self.body.append('\\begin{seealso}\n')
1037 self.seealso = 1
1038 raise nodes.SkipNode
1039 else:
1040 if self.section_level == 0:
1041 # It's the document title before any section.
1042 self.title = self.encode(node.astext())
1043 else:
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))
1058 #self.body.append(
1059 # '\\section{\htmladdnormallink[%s]{%s}{}}\n' % \
1060 # (s2, s1))
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))
1067 else:
1068 self.body.append('\\subparagraph{%s\\label{%s}}\n' % (s1, s2))
1069 raise nodes.SkipNode
1071 def depart_title(self, node):
1072 pass
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
1079 else:
1080 raise nodes.SkipNode
1082 def depart_topic(self, node):
1083 pass
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):
1097 pass
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__)