1
# Author: Engelbert Gruber
2
# Contact: grubert@users.sourceforge.net
3
# Revision: $Revision: 4242 $
4
# Date: $Date: 2006-01-06 00:28:53 +0100 (Fri, 06 Jan 2006) $
5
# Copyright: This module has been placed in the public domain.
8
LaTeX2e document tree Writer.
11
__docformat__ = 'reStructuredText'
13
# code contributions from several people included, thanks to all.
14
# some named: David Abrahams, Julien Letessier, Lele Gaifax, and others.
16
# convention deactivate code by two # e.g. ##.
22
from types import ListType
23
from docutils import frontend, nodes, languages, writers, utils
26
class Writer(writers.Writer):
28
supported = ('latex','latex2e')
29
"""Formats this writer supports."""
32
'LaTeX-Specific Options',
33
'The LaTeX "--output-encoding" default is "latin-1:strict".',
34
(('Specify documentclass. Default is "article".',
36
{'default': 'article', }),
37
('Specify document options. Multiple options can be given, '
38
'separated by commas. Default is "10pt,a4paper".',
39
['--documentoptions'],
40
{'default': '10pt,a4paper', }),
41
('Use LaTeX footnotes. LaTeX supports only numbered footnotes (does it?). '
42
'Default: no, uses figures.',
43
['--use-latex-footnotes'],
44
{'default': 0, 'action': 'store_true',
45
'validator': frontend.validate_boolean}),
46
('Format for footnote references: one of "superscript" or '
47
'"brackets". Default is "superscript".',
48
['--footnote-references'],
49
{'choices': ['superscript', 'brackets'], 'default': 'superscript',
50
'metavar': '<format>',
51
'overrides': 'trim_footnote_reference_space'}),
52
('Use LaTeX citations. '
53
'Default: no, uses figures which might get mixed with images.',
54
['--use-latex-citations'],
55
{'default': 0, 'action': 'store_true',
56
'validator': frontend.validate_boolean}),
57
('Format for block quote attributions: one of "dash" (em-dash '
58
'prefix), "parentheses"/"parens", or "none". Default is "dash".',
60
{'choices': ['dash', 'parentheses', 'parens', 'none'],
61
'default': 'dash', 'metavar': '<format>'}),
62
('Specify a stylesheet file. The file will be "input" by latex in '
63
'the document header. Default is no stylesheet (""). '
64
'Overrides --stylesheet-path.',
66
{'default': '', 'metavar': '<file>',
67
'overrides': 'stylesheet_path'}),
68
('Specify a stylesheet file, relative to the current working '
69
'directory. Overrides --stylesheet.',
70
['--stylesheet-path'],
71
{'metavar': '<file>', 'overrides': 'stylesheet'}),
72
('Table of contents by docutils (default) or latex. Latex (writer) '
73
'supports only one ToC per document, but docutils does not write '
76
{'default': 0, 'action': 'store_true',
77
'validator': frontend.validate_boolean}),
78
('Let LaTeX print author and date, do not show it in docutils '
80
['--use-latex-docinfo'],
81
{'default': 0, 'action': 'store_true',
82
'validator': frontend.validate_boolean}),
83
('Color of any hyperlinks embedded in text '
84
'(default: "blue", "0" to disable).',
85
['--hyperlink-color'], {'default': 'blue'}),
86
('Enable compound enumerators for nested enumerated lists '
87
'(e.g. "1.2.a.ii"). Default: disabled.',
88
['--compound-enumerators'],
89
{'default': None, 'action': 'store_true',
90
'validator': frontend.validate_boolean}),
91
('Disable compound enumerators for nested enumerated lists. This is '
93
['--no-compound-enumerators'],
94
{'action': 'store_false', 'dest': 'compound_enumerators'}),
95
('Enable section ("." subsection ...) prefixes for compound '
96
'enumerators. This has no effect without --compound-enumerators. '
98
['--section-prefix-for-enumerators'],
99
{'default': None, 'action': 'store_true',
100
'validator': frontend.validate_boolean}),
101
('Disable section prefixes for compound enumerators. '
102
'This is the default.',
103
['--no-section-prefix-for-enumerators'],
104
{'action': 'store_false', 'dest': 'section_prefix_for_enumerators'}),
105
('Set the separator between section number and enumerator '
106
'for compound enumerated lists. Default is "-".',
107
['--section-enumerator-separator'],
108
{'default': '-', 'metavar': '<char>'}),
109
('When possibile, use verbatim for literal-blocks. '
110
'Default is to always use the mbox environment.',
111
['--use-verbatim-when-possible'],
112
{'default': 0, 'action': 'store_true',
113
'validator': frontend.validate_boolean}),
114
('Table style. "standard" with horizontal and vertical lines, '
115
'"booktabs" (LaTeX booktabs style) only horizontal lines '
116
'above and below the table and below the header or "nolines". '
117
'Default: "standard"',
119
{'choices': ['standard', 'booktabs','nolines'], 'default': 'standard',
120
'metavar': '<format>'}),
121
('LaTeX graphicx package option. '
122
'Possible values are "dvips", "pdftex". "auto" includes LaTeX code '
123
'to use "pdftex" if processing with pdf(la)tex and dvips otherwise. '
124
'Default is no option.',
125
['--graphicx-option'],
127
('LaTeX font encoding. '
128
'Possible values are "T1", "OT1", "" or some other fontenc option. '
129
'The font encoding influences available symbols, e.g. "<<" as one '
130
'character. Default is "" which leads to package "ae" (a T1 '
131
'emulation using CM fonts).',
136
settings_defaults = {'output_encoding': 'latin-1'}
138
relative_path_settings = ('stylesheet_path',)
140
config_section = 'latex2e writer'
141
config_section_dependencies = ('writers',)
144
"""Final translated form of `document`."""
147
writers.Writer.__init__(self)
148
self.translator_class = LaTeXTranslator
151
visitor = self.translator_class(self.document)
152
self.document.walkabout(visitor)
153
self.output = visitor.astext()
154
self.head_prefix = visitor.head_prefix
155
self.head = visitor.head
156
self.body_prefix = visitor.body_prefix
157
self.body = visitor.body
158
self.body_suffix = visitor.body_suffix
164
* latex does not support multiple tocs in one document.
165
(might be no limitation except for docutils documentation)
169
* linewidth - width of a line in the local environment
170
* textwidth - the width of text on the page
172
Maybe always use linewidth ?
174
*Bug* inside a minipage a (e.g. Sidebar) the linewidth is
175
not changed, needs fix in docutils so that tables
178
So we add locallinewidth set it initially and
179
on entering sidebar and reset on exit.
183
"""Language specifics for LaTeX."""
184
# country code by a.schlock.
185
# partly manually converted from iso and babel stuff, dialects and some
187
'no': 'norsk', #XXX added by hand ( forget about nynorsk?)
188
'gd': 'scottish', #XXX added by hand
189
'hu': 'magyar', #XXX added by hand
190
'pt': 'portuguese',#XXX added by hand
200
# french, francais, canadien, acadian
201
'de': 'ngerman', #XXX rather than german
202
# ngerman, naustrian, german, germanb, austrian
205
# english, USenglish, american, UKenglish, british, canadian
231
def __init__(self,lang):
233
# pdflatex does not produce double quotes for ngerman in tt.
234
self.double_quote_replacment = None
235
if re.search('^de',self.language):
236
#self.quotes = ("\"`", "\"'")
237
self.quotes = ('{\\glqq}', '{\\grqq}')
238
self.double_quote_replacment = "{\\dq}"
240
self.quotes = ("``", "''")
243
def next_quote(self):
244
q = self.quotes[self.quote_index]
245
self.quote_index = (self.quote_index+1)%2
248
def quote_quotes(self,text):
250
for part in text.split('"'):
254
t += self.next_quote() + part
257
def double_quotes_in_tt (self,text):
258
if not self.double_quote_replacment:
260
return text.replace('"', self.double_quote_replacment)
262
def get_language(self):
263
if self._ISO639_TO_BABEL.has_key(self.language):
264
return self._ISO639_TO_BABEL[self.language]
267
l = self.language.split("_")[0]
268
if self._ISO639_TO_BABEL.has_key(l):
269
return self._ISO639_TO_BABEL[l]
274
'optionlist_environment' : [
275
'\\newcommand{\\optionlistlabel}[1]{\\bf #1 \\hfill}\n'
276
'\\newenvironment{optionlist}[1]\n'
278
' {\\setlength{\\labelwidth}{#1}\n'
279
' \\setlength{\\rightmargin}{1cm}\n'
280
' \\setlength{\\leftmargin}{\\rightmargin}\n'
281
' \\addtolength{\\leftmargin}{\\labelwidth}\n'
282
' \\addtolength{\\leftmargin}{\\labelsep}\n'
283
' \\renewcommand{\\makelabel}{\\optionlistlabel}}\n'
286
'lineblock_environment' : [
287
'\\newlength{\\lineblockindentation}\n'
288
'\\setlength{\\lineblockindentation}{2.5em}\n'
289
'\\newenvironment{lineblock}[1]\n'
291
' {\\setlength{\\partopsep}{\\parskip}\n'
292
' \\addtolength{\\partopsep}{\\baselineskip}\n'
293
' \\topsep0pt\\itemsep0.15\\baselineskip\\parsep0pt\n'
298
'footnote_floats' : [
299
'% begin: floats for footnotes tweaking.\n',
300
'\\setlength{\\floatsep}{0.5em}\n',
301
'\\setlength{\\textfloatsep}{\\fill}\n',
302
'\\addtolength{\\textfloatsep}{3em}\n',
303
'\\renewcommand{\\textfraction}{0.5}\n',
304
'\\renewcommand{\\topfraction}{0.5}\n',
305
'\\renewcommand{\\bottomfraction}{0.5}\n',
306
'\\setcounter{totalnumber}{50}\n',
307
'\\setcounter{topnumber}{50}\n',
308
'\\setcounter{bottomnumber}{50}\n',
309
'% end floats for footnotes\n',
312
'% some commands, that could be overwritten in the style file.\n'
313
'\\newcommand{\\rubric}[1]'
314
'{\\subsection*{~\\hfill {\\it #1} \\hfill ~}}\n'
315
'\\newcommand{\\titlereference}[1]{\\textsl{#1}}\n'
316
'% end of "some commands"\n',
321
"""Details of a LaTeX document class."""
323
# BUG: LaTeX has no deeper sections (actually paragrah is no
325
# BUG: No support for unknown document classes. Make 'article'
328
'book': ( 'chapter', 'section', 'subsection', 'subsubsection' ),
329
'scrbook': ( 'chapter', 'section', 'subsection', 'subsubsection' ),
330
'report': ( 'chapter', 'section', 'subsection', 'subsubsection' ),
331
'scrreprt': ( 'chapter', 'section', 'subsection', 'subsubsection' ),
332
'article': ( 'section', 'subsection', 'subsubsection' ),
333
'scrartcl': ( 'section', 'subsection', 'subsubsection' ),
335
_deepest_section = 'subsubsection'
337
def __init__(self, document_class):
338
self.document_class = document_class
340
def section(self, level):
341
""" Return the section name at the given level for the specific
344
Level is 1,2,3..., as level 0 is the title."""
346
sections = self._class_sections[self.document_class]
347
if level <= len(sections):
348
return sections[level-1]
350
return self._deepest_section
353
""" Manage a table while traversing.
354
Maybe change to a mixin defining the visit/departs, but then
355
class Table internal variables are in the Translator.
357
def __init__(self,latex_type,table_style):
358
self._latex_type = latex_type
359
self._table_style = table_style
361
# miscellaneous attributes
371
self._in_head = 0 # maybe context with search
374
self._col_specs = None
379
def used_packages(self):
380
if self._table_style == 'booktabs':
381
return '\\usepackage{booktabs}\n'
383
def get_latex_type(self):
384
return self._latex_type
386
def set(self,attr,value):
387
self._attrs[attr] = value
389
if self._attrs.has_key(attr):
390
return self._attrs[attr]
392
def get_vertical_bar(self):
393
if self._table_style == 'standard':
396
# horizontal lines are drawn below a row, because we.
397
def get_opening(self):
398
return '\\begin{%s}[c]' % self._latex_type
399
def get_closing(self):
401
if self._table_style == 'booktabs':
402
line = '\\bottomrule\n'
403
elif self._table_style == 'standard':
405
return '%s\\end{%s}' % (line,self._latex_type)
407
def visit_colspec(self,node):
408
self._col_specs.append(node)
410
def get_colspecs(self):
412
Return column specification for longtable.
414
Assumes reST line length being 80 characters.
415
Table width is hairy.
421
usually gets to narrow, therefore we add 1 (fiddlefactor).
426
# first see if we get too wide.
427
for node in self._col_specs:
428
colwidth = float(node['colwidth']+1) / width
429
total_width += colwidth
432
# donot make it full linewidth
434
if total_width > 1.0:
435
factor /= total_width
436
bar = self.get_vertical_bar()
437
latex_table_spec = ""
438
for node in self._col_specs:
439
colwidth = factor * float(node['colwidth']+1) / width
440
self._col_width.append(colwidth+0.005)
441
self._rowspan.append(0)
442
latex_table_spec += "%sp{%.2f\\locallinewidth}" % (bar,colwidth+0.005)
443
return latex_table_spec+bar
445
def get_column_width(self):
446
""" return columnwidth for current cell (not multicell)
448
return "%.2f\\locallinewidth" % self._col_width[self._cell_in_row-1]
450
def visit_thead(self):
452
if self._table_style == 'standard':
454
elif self._table_style == 'booktabs':
455
return ['\\toprule\n']
457
def depart_thead(self):
459
#if self._table_style == 'standard':
460
# a.append('\\hline\n')
461
if self._table_style == 'booktabs':
462
a.append('\\midrule\n')
463
a.append('\\endhead\n')
464
# for longtable one could add firsthead, foot and lastfoot
468
self._cell_in_row = 0
469
def depart_row(self):
471
self._cell_in_row = None # remove cell counter
472
for i in range(len(self._rowspan)):
473
if (self._rowspan[i]>0):
474
self._rowspan[i] -= 1
476
if self._table_style == 'standard':
478
for i in range(len(self._rowspan)):
479
if (self._rowspan[i]<=0):
481
if len(rowspans)==len(self._rowspan):
482
res.append('\\hline\n')
489
c_start = rowspans.pop()
492
cline += '\\cline{%d-%d}\n' % (c_start,c_start)
496
def set_rowspan(self,cell,value):
498
self._rowspan[cell] = value
501
def get_rowspan(self,cell):
503
return self._rowspan[cell]
506
def get_entry_number(self):
507
return self._cell_in_row
508
def visit_entry(self):
509
self._cell_in_row += 1
512
class LaTeXTranslator(nodes.NodeVisitor):
514
# When options are given to the documentclass, latex will pass them
515
# to other packages, as done with babel.
516
# Dummy settings might be taken from document settings
518
latex_head = '\\documentclass[%s]{%s}\n'
519
encoding = '\\usepackage[%s]{inputenc}\n'
520
linking = '\\usepackage[colorlinks=%s,linkcolor=%s,urlcolor=%s]{hyperref}\n'
521
stylesheet = '\\input{%s}\n'
522
# add a generated on day , machine by user using docutils version.
523
generator = '%% generator Docutils: http://docutils.sourceforge.net/\n'
525
# use latex tableofcontents or let docutils do it.
528
# TODO: use mixins for different implementations.
529
# list environment for option-list. else tabularx
530
use_optionlist_for_option_list = 1
531
# list environment for docinfo. else tabularx
532
use_optionlist_for_docinfo = 0 # NOT YET IN USE
534
# Use compound enumerations (1.A.1.)
535
compound_enumerators = 0
537
# If using compound enumerations, include section information.
538
section_prefix_for_enumerators = 0
540
# This is the character that separates the section ("." subsection ...)
541
# prefix from the regular list enumerator.
542
section_enumerator_separator = '-'
545
hyperlink_color = "blue"
547
def __init__(self, document):
548
nodes.NodeVisitor.__init__(self, document)
549
self.settings = settings = document.settings
550
self.latex_encoding = self.to_latex_encoding(settings.output_encoding)
551
self.use_latex_toc = settings.use_latex_toc
552
self.use_latex_docinfo = settings.use_latex_docinfo
553
self.use_latex_footnotes = settings.use_latex_footnotes
554
self._use_latex_citations = settings.use_latex_citations
555
self.hyperlink_color = settings.hyperlink_color
556
self.compound_enumerators = settings.compound_enumerators
557
self.font_encoding = settings.font_encoding
558
self.section_prefix_for_enumerators = (
559
settings.section_prefix_for_enumerators)
560
self.section_enumerator_separator = (
561
settings.section_enumerator_separator.replace('_', '\\_'))
562
if self.hyperlink_color == '0':
563
self.hyperlink_color = 'black'
564
self.colorlinks = 'false'
566
self.colorlinks = 'true'
568
# language: labels, bibliographic_fields, and author_separators.
569
# to allow writing labes for specific languages.
570
self.language = languages.get_language(settings.language_code)
571
self.babel = Babel(settings.language_code)
572
self.author_separator = self.language.author_separators[0]
573
self.d_options = self.settings.documentoptions
574
if self.babel.get_language():
575
self.d_options += ',%s' % \
576
self.babel.get_language()
578
self.d_class = DocumentClass(settings.documentclass)
579
# object for a table while proccessing.
580
self.active_table = Table('longtable',settings.table_style)
582
# HACK. Should have more sophisticated typearea handling.
583
if settings.documentclass.find('scr') == -1:
584
self.typearea = '\\usepackage[DIV12]{typearea}\n'
586
if self.d_options.find('DIV') == -1 and self.d_options.find('BCOR') == -1:
587
self.typearea = '\\typearea{12}\n'
591
if self.font_encoding == 'OT1':
593
elif self.font_encoding == '':
594
fontenc_header = '\\usepackage{ae}\n\\usepackage{aeguill}\n'
596
fontenc_header = '\\usepackage[%s]{fontenc}\n' % (self.font_encoding,)
597
input_encoding = self.encoding % self.latex_encoding
598
if self.settings.graphicx_option == '':
599
self.graphicx_package = '\\usepackage{graphicx}\n'
600
elif self.settings.graphicx_option.lower() == 'auto':
601
self.graphicx_package = '\n'.join(
602
('%Check if we are compiling under latex or pdflatex',
603
'\\ifx\\pdftexversion\\undefined',
604
' \\usepackage{graphicx}',
606
' \\usepackage[pdftex]{graphicx}',
609
self.graphicx_package = (
610
'\\usepackage[%s]{graphicx}\n' % self.settings.graphicx_option)
613
self.latex_head % (self.d_options,self.settings.documentclass),
614
'\\usepackage{babel}\n', # language is in documents settings.
616
'\\usepackage{shortvrb}\n', # allows verb in footnotes.
618
# * tabularx: for docinfo, automatic width of columns, always on one page.
619
'\\usepackage{tabularx}\n',
620
'\\usepackage{longtable}\n',
621
self.active_table.used_packages(),
622
# possible other packages.
624
# * ltxtable is a combination of tabularx and longtable (pagebreaks).
627
# extra space between text in tables and the line above them
628
'\\setlength{\\extrarowheight}{2pt}\n',
629
'\\usepackage{amsmath}\n', # what fore amsmath.
630
self.graphicx_package,
631
'\\usepackage{color}\n',
632
'\\usepackage{multirow}\n',
633
'\\usepackage{ifthen}\n', # before hyperref!
634
self.linking % (self.colorlinks, self.hyperlink_color, self.hyperlink_color),
638
'\\newlength{\\admonitionwidth}\n',
639
'\\setlength{\\admonitionwidth}{0.9\\textwidth}\n'
640
# width for docinfo tablewidth
641
'\\newlength{\\docinfowidth}\n',
642
'\\setlength{\\docinfowidth}{0.9\\textwidth}\n'
643
# linewidth of current environment, so tables are not wider
644
# than the sidebar: using locallinewidth seems to defer evaluation
645
# of linewidth, this is fixing it.
646
'\\newlength{\\locallinewidth}\n',
649
self.head_prefix.extend( latex_headings['optionlist_environment'] )
650
self.head_prefix.extend( latex_headings['lineblock_environment'] )
651
self.head_prefix.extend( latex_headings['footnote_floats'] )
652
self.head_prefix.extend( latex_headings['some_commands'] )
653
## stylesheet is last: so it might be possible to overwrite defaults.
654
stylesheet = utils.get_stylesheet_reference(settings)
656
settings.record_dependencies.add(stylesheet)
657
self.head_prefix.append(self.stylesheet % (stylesheet))
659
if self.linking: # and maybe check for pdf
661
self.pdfauthor = None
662
# pdftitle, pdfsubject, pdfauthor, pdfkeywords, pdfcreator, pdfproducer
665
# NOTE: Latex wants a date and an author, rst puts this into
666
# docinfo, so normally we donot want latex author/date handling.
667
# latex article has its own handling of date and author, deactivate.
668
# So we always emit \title{...} \author{...} \date{...}, even if the
669
# "..." are empty strings.
671
# separate title, so we can appen subtitle.
673
# if use_latex_docinfo: collects lists of author/organization/contact/address lines
674
self.author_stack = []
677
self.body_prefix = ['\\raggedbottom\n']
679
self.body_suffix = ['\n']
680
self.section_level = 0
682
self.topic_classes = []
683
# column specification for tables
684
self.table_caption = None
688
# verbatim: to tell encode not to encode.
690
# insert_newline: to tell encode to replace blanks by "~".
691
self.insert_none_breaking_blanks = 0
692
# insert_newline: to tell encode to add latex newline.
693
self.insert_newline = 0
694
# mbox_newline: to tell encode to add mbox and newline.
695
self.mbox_newline = 0
697
# enumeration is done by list environment.
700
# Stack of section counters so that we don't have to use_latex_toc.
701
# This will grow and shrink as processing occurs.
702
# Initialized for potential first-level sections.
703
self._section_number = [0]
705
# The current stack of enumerations so that we can expand
706
# them into a compound enumeration
707
self._enumeration_counters = []
713
# inside literal block: no quote mangling.
714
self.literal_block = 0
715
self.literal_block_stack = []
717
# true when encoding in math mode
720
def to_latex_encoding(self,docutils_encoding):
722
Translate docutils encoding name into latex's.
724
Default fallback method is remove "-" and "_" chars from docutils_encoding.
727
tr = { "iso-8859-1": "latin1", # west european
728
"iso-8859-2": "latin2", # east european
729
"iso-8859-3": "latin3", # esperanto, maltese
730
"iso-8859-4": "latin4", # north european,scandinavian, baltic
731
"iso-8859-5": "iso88595", # cyrillic (ISO)
732
"iso-8859-9": "latin5", # turkish
733
"iso-8859-15": "latin9", # latin9, update to latin1.
734
"mac_cyrillic": "maccyr", # cyrillic (on Mac)
735
"windows-1251": "cp1251", # cyrillic (on Windows)
736
"koi8-r": "koi8-r", # cyrillic (Russian)
737
"koi8-u": "koi8-u", # cyrillic (Ukrainian)
738
"windows-1250": "cp1250", #
739
"windows-1252": "cp1252", #
740
"us-ascii": "ascii", # ASCII (US)
741
# unmatched encodings
743
#"": "ansinew", # windows 3.1 ansi
744
#"": "ascii", # ASCII encoding for the range 32--127.
745
#"": "cp437", # dos latine us
746
#"": "cp850", # dos latin 1
747
#"": "cp852", # dos latin 2
750
#"iso-8859-6": "" # arabic
751
#"iso-8859-7": "" # greek
752
#"iso-8859-8": "" # hebrew
753
#"iso-8859-10": "" # latin6, more complete iso-8859-4
755
if tr.has_key(docutils_encoding.lower()):
756
return tr[docutils_encoding.lower()]
757
return docutils_encoding.translate(string.maketrans("",""),"_-").lower()
759
def language_label(self, docutil_label):
760
return self.language.labels[docutil_label]
762
latex_equivalents = {
772
u'\u2020' : '{\\dag}',
773
u'\u2021' : '{\\ddag}',
774
u'\u2026' : '{\\dots}',
775
u'\u2122' : '{\\texttrademark}',
776
u'\u21d4' : '{$\\Leftrightarrow$}',
779
def unicode_to_latex(self,text):
781
# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/252124
782
# Only some special chracters are translated, for documents with many
783
# utf-8 chars one should use the LaTeX unicode package.
784
for uchar in self.latex_equivalents.keys():
785
text = text.replace(uchar,self.latex_equivalents[uchar])
788
def encode(self, text):
790
Encode special characters (``# $ % & ~ _ ^ \ { }``) in `text` & return
792
# Escaping with a backslash does not help with backslashes, ~ and ^.
794
# < > are only available in math-mode or tt font. (really ?)
795
# $ starts math- mode.
799
# compile the regexps once. do it here so one can see them.
802
if not self.__dict__.has_key('encode_re_braces'):
803
self.encode_re_braces = re.compile(r'([{}])')
804
text = self.encode_re_braces.sub(r'{\\\1}',text)
805
if not self.__dict__.has_key('encode_re_bslash'):
806
# find backslash: except in the form '{\{}' or '{\}}'.
807
self.encode_re_bslash = re.compile(r'(?<!{)(\\)(?![{}]})')
808
# then the backslash: except in the form from line above:
809
# either '{\{}' or '{\}}'.
810
text = self.encode_re_bslash.sub(r'{\\textbackslash}', text)
813
text = text.replace("$", '{\\$}')
814
if not ( self.literal_block or self.literal or self.mathmode ):
815
# the vertical bar: in mathmode |,\vert or \mid
816
# in textmode \textbar
817
text = text.replace("|", '{\\textbar}')
818
text = text.replace("<", '{\\textless}')
819
text = text.replace(">", '{\\textgreater}')
821
text = text.replace("&", '{\\&}')
823
# * verb|^| does not work in mbox.
824
# * mathmode has wedge. hat{~} would also work.
825
# text = text.replace("^", '{\\ensuremath{^\\wedge}}')
826
text = text.replace("^", '{\\textasciicircum}')
827
text = text.replace("%", '{\\%}')
828
text = text.replace("#", '{\\#}')
829
text = text.replace("~", '{\\textasciitilde}')
830
# Separate compound characters, e.g. "--" to "-{}-". (The
831
# actual separation is done later; see below.)
833
if self.literal_block or self.literal:
834
# In monospace-font, we also separate ",,", "``" and "''"
835
# and some other characters which can't occur in
837
separate_chars += ',`\'"<>'
838
# pdflatex does not produce doublequotes for ngerman.
839
text = self.babel.double_quotes_in_tt(text)
840
if self.font_encoding == 'OT1':
841
# We're using OT1 font-encoding and have to replace
842
# underscore by underlined blank, because this has
844
text = text.replace('_', '{\\underline{ }}')
845
# And the tt-backslash doesn't work in OT1, so we use
847
text = text.replace('\\textbackslash', '\\reflectbox{/}')
849
text = text.replace('_', '{\\_}')
851
text = self.babel.quote_quotes(text)
852
text = text.replace("_", '{\\_}')
853
for char in separate_chars * 2:
854
# Do it twice ("* 2") becaues otherwise we would replace
856
text = text.replace(char + char, char + '{}' + char)
857
if self.insert_newline or self.literal_block:
858
# Insert a blank before the newline, to avoid
859
# ! LaTeX Error: There's no line here to end.
860
text = text.replace("\n", '~\\\\\n')
861
elif self.mbox_newline:
862
if self.literal_block:
863
closings = "}" * len(self.literal_block_stack)
864
openings = "".join(self.literal_block_stack)
868
text = text.replace("\n", "%s}\\\\\n\\mbox{%s" % (closings,openings))
869
text = text.replace('[', '{[}').replace(']', '{]}')
870
if self.insert_none_breaking_blanks:
871
text = text.replace(' ', '~')
872
if self.latex_encoding != 'utf8':
873
text = self.unicode_to_latex(text)
876
def attval(self, text,
877
whitespace=re.compile('[\n\r\t\v\f]')):
878
"""Cleanse, encode, and return attribute value text."""
879
return self.encode(whitespace.sub(' ', text))
882
if self.pdfinfo is not None:
884
self.pdfinfo.append('pdfauthor={%s}' % self.pdfauthor)
886
pdfinfo = '\\hypersetup{\n' + ',\n'.join(self.pdfinfo) + '\n}\n'
889
head = '\\title{%s}\n\\author{%s}\n\\date{%s}\n' % \
891
' \\and\n'.join(['~\\\\\n'.join(author_lines)
892
for author_lines in self.author_stack]),
894
return ''.join(self.head_prefix + [head] + self.head + [pdfinfo]
895
+ self.body_prefix + self.body + self.body_suffix)
897
def visit_Text(self, node):
898
self.body.append(self.encode(node.astext()))
900
def depart_Text(self, node):
903
def visit_address(self, node):
904
self.visit_docinfo_item(node, 'address')
906
def depart_address(self, node):
907
self.depart_docinfo_item(node)
909
def visit_admonition(self, node, name=''):
910
self.body.append('\\begin{center}\\begin{sffamily}\n')
911
self.body.append('\\fbox{\\parbox{\\admonitionwidth}{\n')
913
self.body.append('\\textbf{\\large '+ self.language.labels[name] + '}\n');
914
self.body.append('\\vspace{2mm}\n')
917
def depart_admonition(self, node=None):
918
self.body.append('}}\n') # end parbox fbox
919
self.body.append('\\end{sffamily}\n\\end{center}\n');
921
def visit_attention(self, node):
922
self.visit_admonition(node, 'attention')
924
def depart_attention(self, node):
925
self.depart_admonition()
927
def visit_author(self, node):
928
self.visit_docinfo_item(node, 'author')
930
def depart_author(self, node):
931
self.depart_docinfo_item(node)
933
def visit_authors(self, node):
934
# not used: visit_author is called anyway for each author.
937
def depart_authors(self, node):
940
def visit_block_quote(self, node):
941
self.body.append( '\\begin{quote}\n')
943
def depart_block_quote(self, node):
944
self.body.append( '\\end{quote}\n')
946
def visit_bullet_list(self, node):
947
if 'contents' in self.topic_classes:
948
if not self.use_latex_toc:
949
self.body.append( '\\begin{list}{}{}\n' )
951
self.body.append( '\\begin{itemize}\n' )
953
def depart_bullet_list(self, node):
954
if 'contents' in self.topic_classes:
955
if not self.use_latex_toc:
956
self.body.append( '\\end{list}\n' )
958
self.body.append( '\\end{itemize}\n' )
960
# Imperfect superscript/subscript handling: mathmode italicizes
961
# all letters by default.
962
def visit_superscript(self, node):
963
self.body.append('$^{')
966
def depart_superscript(self, node):
967
self.body.append('}$')
970
def visit_subscript(self, node):
971
self.body.append('$_{')
974
def depart_subscript(self, node):
975
self.body.append('}$')
978
def visit_caption(self, node):
979
self.body.append( '\\caption{' )
981
def depart_caption(self, node):
982
self.body.append('}')
984
def visit_caution(self, node):
985
self.visit_admonition(node, 'caution')
987
def depart_caution(self, node):
988
self.depart_admonition()
990
def visit_title_reference(self, node):
991
self.body.append( '\\titlereference{' )
993
def depart_title_reference(self, node):
994
self.body.append( '}' )
996
def visit_citation(self, node):
997
# TODO maybe use cite bibitems
998
if self._use_latex_citations:
999
self.context.append(len(self.body))
1001
self.body.append('\\begin{figure}[b]')
1002
for id in node['ids']:
1003
self.body.append('\\hypertarget{%s}' % id)
1005
def depart_citation(self, node):
1006
if self._use_latex_citations:
1007
size = self.context.pop()
1008
label = self.body[size]
1009
text = ''.join(self.body[size+1:])
1010
del self.body[size:]
1011
self._bibitems.append([label, text])
1013
self.body.append('\\end{figure}\n')
1015
def visit_citation_reference(self, node):
1016
if self._use_latex_citations:
1017
self.body.append('\\cite{')
1020
if node.has_key('refid'):
1021
href = node['refid']
1022
elif node.has_key('refname'):
1023
href = self.document.nameids[node['refname']]
1024
self.body.append('[\\hyperlink{%s}{' % href)
1026
def depart_citation_reference(self, node):
1027
if self._use_latex_citations:
1028
self.body.append('}')
1030
self.body.append('}]')
1032
def visit_classifier(self, node):
1033
self.body.append( '(\\textbf{' )
1035
def depart_classifier(self, node):
1036
self.body.append( '})\n' )
1038
def visit_colspec(self, node):
1039
self.active_table.visit_colspec(node)
1041
def depart_colspec(self, node):
1044
def visit_comment(self, node):
1045
# Escape end of line by a new comment start in comment text.
1046
self.body.append('%% %s \n' % node.astext().replace('\n', '\n% '))
1047
raise nodes.SkipNode
1049
def visit_compound(self, node):
1052
def depart_compound(self, node):
1055
def visit_contact(self, node):
1056
self.visit_docinfo_item(node, 'contact')
1058
def depart_contact(self, node):
1059
self.depart_docinfo_item(node)
1061
def visit_container(self, node):
1064
def depart_container(self, node):
1067
def visit_copyright(self, node):
1068
self.visit_docinfo_item(node, 'copyright')
1070
def depart_copyright(self, node):
1071
self.depart_docinfo_item(node)
1073
def visit_danger(self, node):
1074
self.visit_admonition(node, 'danger')
1076
def depart_danger(self, node):
1077
self.depart_admonition()
1079
def visit_date(self, node):
1080
self.visit_docinfo_item(node, 'date')
1082
def depart_date(self, node):
1083
self.depart_docinfo_item(node)
1085
def visit_decoration(self, node):
1088
def depart_decoration(self, node):
1091
def visit_definition(self, node):
1092
self.body.append('%[visit_definition]\n')
1094
def depart_definition(self, node):
1095
self.body.append('\n')
1096
self.body.append('%[depart_definition]\n')
1098
def visit_definition_list(self, node):
1099
self.body.append( '\\begin{description}\n' )
1101
def depart_definition_list(self, node):
1102
self.body.append( '\\end{description}\n' )
1104
def visit_definition_list_item(self, node):
1105
self.body.append('%[visit_definition_list_item]\n')
1107
def depart_definition_list_item(self, node):
1108
self.body.append('%[depart_definition_list_item]\n')
1110
def visit_description(self, node):
1111
if self.use_optionlist_for_option_list:
1112
self.body.append( ' ' )
1114
self.body.append( ' & ' )
1116
def depart_description(self, node):
1119
def visit_docinfo(self, node):
1121
self.docinfo.append('%' + '_'*75 + '\n')
1122
self.docinfo.append('\\begin{center}\n')
1123
self.docinfo.append('\\begin{tabularx}{\\docinfowidth}{lX}\n')
1125
def depart_docinfo(self, node):
1126
self.docinfo.append('\\end{tabularx}\n')
1127
self.docinfo.append('\\end{center}\n')
1128
self.body = self.docinfo + self.body
1129
# clear docinfo, so field names are no longer appended.
1132
def visit_docinfo_item(self, node, name):
1133
if name == 'author':
1134
if not self.pdfinfo == None:
1135
if not self.pdfauthor:
1136
self.pdfauthor = self.attval(node.astext())
1138
self.pdfauthor += self.author_separator + self.attval(node.astext())
1139
if self.use_latex_docinfo:
1140
if name in ('author', 'organization', 'contact', 'address'):
1141
# We attach these to the last author. If any of them precedes
1142
# the first author, put them in a separate "author" group (for
1143
# no better semantics).
1144
if name == 'author' or not self.author_stack:
1145
self.author_stack.append([])
1146
if name == 'address': # newlines are meaningful
1147
self.insert_newline = 1
1148
text = self.encode(node.astext())
1149
self.insert_newline = 0
1151
text = self.attval(node.astext())
1152
self.author_stack[-1].append(text)
1153
raise nodes.SkipNode
1154
elif name == 'date':
1155
self.date = self.attval(node.astext())
1156
raise nodes.SkipNode
1157
self.docinfo.append('\\textbf{%s}: &\n\t' % self.language_label(name))
1158
if name == 'address':
1159
self.insert_newline = 1
1160
self.docinfo.append('{\\raggedright\n')
1161
self.context.append(' } \\\\\n')
1163
self.context.append(' \\\\\n')
1164
self.context.append(self.docinfo)
1165
self.context.append(len(self.body))
1167
def depart_docinfo_item(self, node):
1168
size = self.context.pop()
1169
dest = self.context.pop()
1170
tail = self.context.pop()
1171
tail = self.body[size:] + [tail]
1172
del self.body[size:]
1174
# for address we did set insert_newline
1175
self.insert_newline = 0
1177
def visit_doctest_block(self, node):
1178
self.body.append( '\\begin{verbatim}' )
1181
def depart_doctest_block(self, node):
1182
self.body.append( '\\end{verbatim}\n' )
1185
def visit_document(self, node):
1186
self.body_prefix.append('\\begin{document}\n')
1188
if self.use_latex_docinfo or len(node) and isinstance(node[0], nodes.title):
1189
self.body_prefix.append('\\maketitle\n\n')
1190
# alternative use titlepage environment.
1192
self.body.append('\n\\setlength{\\locallinewidth}{\\linewidth}\n')
1194
def depart_document(self, node):
1195
# TODO insertion point of bibliography should none automatic.
1196
if self._use_latex_citations and len(self._bibitems)>0:
1198
for bi in self._bibitems:
1199
if len(widest_label)<len(bi[0]):
1200
widest_label = bi[0]
1201
self.body.append('\n\\begin{thebibliography}{%s}\n'%widest_label)
1202
for bi in self._bibitems:
1203
# cite_key: underscores must not be escaped
1204
cite_key = bi[0].replace(r"{\_}","_")
1205
self.body.append('\\bibitem[%s]{%s}{%s}\n' % (bi[0], cite_key, bi[1]))
1206
self.body.append('\\end{thebibliography}\n')
1208
self.body_suffix.append('\\end{document}\n')
1210
def visit_emphasis(self, node):
1211
self.body.append('\\emph{')
1212
self.literal_block_stack.append('\\emph{')
1214
def depart_emphasis(self, node):
1215
self.body.append('}')
1216
self.literal_block_stack.pop()
1218
def visit_entry(self, node):
1219
self.active_table.visit_entry()
1221
if self.active_table.get_entry_number() == 1:
1222
# if the firstrow is a multirow, this actually is the second row.
1223
# this gets hairy if rowspans follow each other.
1224
if self.active_table.get_rowspan(0):
1226
while self.active_table.get_rowspan(count):
1228
self.body.append(' & ')
1229
self.active_table.visit_entry() # increment cell count
1231
self.body.append(' & ')
1234
# IN WORK BUG TODO HACK continues here
1235
# multirow in LaTeX simply will enlarge the cell over several rows
1236
# (the following n if n is positive, the former if negative).
1237
if node.has_key('morerows') and node.has_key('morecols'):
1238
raise NotImplementedError('Cells that '
1239
'span multiple rows *and* columns are not supported, sorry.')
1240
if node.has_key('morerows'):
1241
count = node['morerows'] + 1
1242
self.active_table.set_rowspan(self.active_table.get_entry_number()-1,count)
1243
self.body.append('\\multirow{%d}{%s}{' % \
1244
(count,self.active_table.get_column_width()))
1245
self.context.append('}')
1246
# BUG following rows must have empty cells.
1247
elif node.has_key('morecols'):
1248
# the vertical bar before column is missing if it is the first column.
1249
# the one after always.
1250
if self.active_table.get_entry_number() == 1:
1251
bar1 = self.active_table.get_vertical_bar()
1254
count = node['morecols'] + 1
1255
self.body.append('\\multicolumn{%d}{%sl%s}{' % \
1256
(count, bar1, self.active_table.get_vertical_bar()))
1257
self.context.append('}')
1259
self.context.append('')
1261
# header / not header
1262
if isinstance(node.parent.parent, nodes.thead):
1263
self.body.append('\\textbf{')
1264
self.context.append('}')
1266
self.context.append('')
1268
def depart_entry(self, node):
1269
self.body.append(self.context.pop()) # header / not header
1270
self.body.append(self.context.pop()) # multirow/column
1271
# if following row is spanned from above.
1272
if self.active_table.get_rowspan(self.active_table.get_entry_number()):
1273
self.body.append(' & ')
1274
self.active_table.visit_entry() # increment cell count
1276
def visit_row(self, node):
1277
self.active_table.visit_row()
1279
def depart_row(self, node):
1280
self.body.extend(self.active_table.depart_row())
1282
def visit_enumerated_list(self, node):
1283
# We create our own enumeration list environment.
1284
# This allows to set the style and starting value
1285
# and unlimited nesting.
1288
enum_style = {'arabic':'arabic',
1289
'loweralpha':'alph',
1290
'upperalpha':'Alph',
1291
'lowerroman':'roman',
1292
'upperroman':'Roman' }
1294
if node.has_key('suffix'):
1295
enum_suffix = node['suffix']
1297
if node.has_key('prefix'):
1298
enum_prefix = node['prefix']
1299
if self.compound_enumerators:
1301
if self.section_prefix_for_enumerators and self.section_level:
1302
for i in range(self.section_level):
1303
pref += '%d.' % self._section_number[i]
1304
pref = pref[:-1] + self.section_enumerator_separator
1306
for counter in self._enumeration_counters:
1307
enum_prefix += counter + '.'
1308
enum_type = "arabic"
1309
if node.has_key('enumtype'):
1310
enum_type = node['enumtype']
1311
if enum_style.has_key(enum_type):
1312
enum_type = enum_style[enum_type]
1313
counter_name = "listcnt%d" % self._enum_cnt;
1314
self._enumeration_counters.append("\\%s{%s}" % (enum_type,counter_name))
1315
self.body.append('\\newcounter{%s}\n' % counter_name)
1316
self.body.append('\\begin{list}{%s\\%s{%s}%s}\n' % \
1317
(enum_prefix,enum_type,counter_name,enum_suffix))
1318
self.body.append('{\n')
1319
self.body.append('\\usecounter{%s}\n' % counter_name)
1320
# set start after usecounter, because it initializes to zero.
1321
if node.has_key('start'):
1322
self.body.append('\\addtocounter{%s}{%d}\n' \
1323
% (counter_name,node['start']-1))
1324
## set rightmargin equal to leftmargin
1325
self.body.append('\\setlength{\\rightmargin}{\\leftmargin}\n')
1326
self.body.append('}\n')
1328
def depart_enumerated_list(self, node):
1329
self.body.append('\\end{list}\n')
1330
self._enumeration_counters.pop()
1332
def visit_error(self, node):
1333
self.visit_admonition(node, 'error')
1335
def depart_error(self, node):
1336
self.depart_admonition()
1338
def visit_field(self, node):
1339
# real output is done in siblings: _argument, _body, _name
1342
def depart_field(self, node):
1343
self.body.append('\n')
1344
##self.body.append('%[depart_field]\n')
1346
def visit_field_argument(self, node):
1347
self.body.append('%[visit_field_argument]\n')
1349
def depart_field_argument(self, node):
1350
self.body.append('%[depart_field_argument]\n')
1352
def visit_field_body(self, node):
1353
# BUG by attach as text we loose references.
1355
self.docinfo.append('%s \\\\\n' % self.encode(node.astext()))
1356
raise nodes.SkipNode
1357
# BUG: what happens if not docinfo
1359
def depart_field_body(self, node):
1360
self.body.append( '\n' )
1362
def visit_field_list(self, node):
1363
if not self.docinfo:
1364
self.body.append('\\begin{quote}\n')
1365
self.body.append('\\begin{description}\n')
1367
def depart_field_list(self, node):
1368
if not self.docinfo:
1369
self.body.append('\\end{description}\n')
1370
self.body.append('\\end{quote}\n')
1372
def visit_field_name(self, node):
1373
# BUG this duplicates docinfo_item
1375
self.docinfo.append('\\textbf{%s}: &\n\t' % self.encode(node.astext()))
1376
raise nodes.SkipNode
1378
self.body.append('\\item [')
1380
def depart_field_name(self, node):
1381
if not self.docinfo:
1382
self.body.append(':]')
1384
def visit_figure(self, node):
1385
if not node.attributes.has_key('align'):
1388
align = 'flush'+node.attributes['align']
1389
self.body.append( '\\begin{figure}[htbp]\\begin{%s}\n' % align )
1390
self.context.append( '\\end{%s}\\end{figure}\n' % align )
1392
def depart_figure(self, node):
1393
self.body.append( self.context.pop() )
1395
def visit_footer(self, node):
1396
self.context.append(len(self.body))
1398
def depart_footer(self, node):
1399
start = self.context.pop()
1400
footer = (['\n\\begin{center}\small\n']
1401
+ self.body[start:] + ['\n\\end{center}\n'])
1402
self.body_suffix[:0] = footer
1403
del self.body[start:]
1405
def visit_footnote(self, node):
1406
if self.use_latex_footnotes:
1407
num,text = node.astext().split(None,1)
1408
num = self.encode(num.strip())
1409
self.body.append('\\footnotetext['+num+']')
1410
self.body.append('{')
1412
self.body.append('\\begin{figure}[b]')
1413
for id in node['ids']:
1414
self.body.append('\\hypertarget{%s}' % id)
1416
def depart_footnote(self, node):
1417
if self.use_latex_footnotes:
1418
self.body.append('}\n')
1420
self.body.append('\\end{figure}\n')
1422
def visit_footnote_reference(self, node):
1423
if self.use_latex_footnotes:
1424
self.body.append("\\footnotemark["+self.encode(node.astext())+"]")
1425
raise nodes.SkipNode
1427
if node.has_key('refid'):
1428
href = node['refid']
1429
elif node.has_key('refname'):
1430
href = self.document.nameids[node['refname']]
1431
format = self.settings.footnote_references
1432
if format == 'brackets':
1434
self.context.append(']')
1435
elif format == 'superscript':
1436
suffix = '\\raisebox{.5em}[0em]{\\scriptsize'
1437
self.context.append('}')
1438
else: # shouldn't happen
1439
raise AssertionError('Illegal footnote reference format.')
1440
self.body.append('%s\\hyperlink{%s}{' % (suffix,href))
1442
def depart_footnote_reference(self, node):
1443
if self.use_latex_footnotes:
1445
self.body.append('}%s' % self.context.pop())
1447
# footnote/citation label
1448
def label_delim(self, node, bracket, superscript):
1449
if isinstance(node.parent, nodes.footnote):
1450
if self.use_latex_footnotes:
1451
raise nodes.SkipNode
1452
if self.settings.footnote_references == 'brackets':
1453
self.body.append(bracket)
1455
self.body.append(superscript)
1457
assert isinstance(node.parent, nodes.citation)
1458
if not self._use_latex_citations:
1459
self.body.append(bracket)
1461
def visit_label(self, node):
1462
self.label_delim(node, '[', '$^{')
1464
def depart_label(self, node):
1465
self.label_delim(node, ']', '}$')
1467
# elements generated by the framework e.g. section numbers.
1468
def visit_generated(self, node):
1471
def depart_generated(self, node):
1474
def visit_header(self, node):
1475
self.context.append(len(self.body))
1477
def depart_header(self, node):
1478
start = self.context.pop()
1479
self.body_prefix.append('\n\\verb|begin_header|\n')
1480
self.body_prefix.extend(self.body[start:])
1481
self.body_prefix.append('\n\\verb|end_header|\n')
1482
del self.body[start:]
1484
def visit_hint(self, node):
1485
self.visit_admonition(node, 'hint')
1487
def depart_hint(self, node):
1488
self.depart_admonition()
1490
def visit_image(self, node):
1491
attrs = node.attributes
1492
# Add image URI to dependency list, assuming that it's
1493
# referring to a local file.
1494
self.settings.record_dependencies.add(attrs['uri'])
1495
pre = [] # in reverse order
1497
include_graphics_options = ""
1498
inline = isinstance(node.parent, nodes.TextElement)
1499
if attrs.has_key('scale'):
1500
# Could also be done with ``scale`` option to
1501
# ``\includegraphics``; doing it this way for consistency.
1502
pre.append('\\scalebox{%f}{' % (attrs['scale'] / 100.0,))
1504
if attrs.has_key('width'):
1505
include_graphics_options = '[width=%s]' % attrs['width']
1506
if attrs.has_key('align'):
1508
# By default latex aligns the top of an image.
1509
(1, 'top'): ('', ''),
1510
(1, 'middle'): ('\\raisebox{-0.5\\height}{', '}'),
1511
(1, 'bottom'): ('\\raisebox{-\\height}{', '}'),
1512
(0, 'center'): ('{\\hfill', '\\hfill}'),
1513
# These 2 don't exactly do the right thing. The image should
1514
# be floated alongside the paragraph. See
1515
# http://www.w3.org/TR/html4/struct/objects.html#adef-align-IMG
1516
(0, 'left'): ('{', '\\hfill}'),
1517
(0, 'right'): ('{\\hfill', '}'),}
1519
pre.append(align_prepost[inline, attrs['align']][0])
1520
post.append(align_prepost[inline, attrs['align']][1])
1522
pass # XXX complain here?
1527
self.body.extend( pre )
1528
self.body.append( '\\includegraphics%s{%s}' % (
1529
include_graphics_options, attrs['uri'] ) )
1530
self.body.extend( post )
1532
def depart_image(self, node):
1535
def visit_important(self, node):
1536
self.visit_admonition(node, 'important')
1538
def depart_important(self, node):
1539
self.depart_admonition()
1541
def visit_interpreted(self, node):
1542
# @@@ Incomplete, pending a proper implementation on the
1543
# Parser/Reader end.
1544
self.visit_literal(node)
1546
def depart_interpreted(self, node):
1547
self.depart_literal(node)
1549
def visit_legend(self, node):
1550
self.body.append('{\\small ')
1552
def depart_legend(self, node):
1553
self.body.append('}')
1555
def visit_line(self, node):
1556
self.body.append('\item[] ')
1558
def depart_line(self, node):
1559
self.body.append('\n')
1561
def visit_line_block(self, node):
1562
if isinstance(node.parent, nodes.line_block):
1563
self.body.append('\\item[] \n'
1564
'\\begin{lineblock}{\\lineblockindentation}\n')
1566
self.body.append('\n\\begin{lineblock}{0em}\n')
1568
def depart_line_block(self, node):
1569
self.body.append('\\end{lineblock}\n')
1571
def visit_list_item(self, node):
1572
# Append "{}" in case the next character is "[", which would break
1573
# LaTeX's list environment (no numbering and the "[" is not printed).
1574
self.body.append('\\item {} ')
1576
def depart_list_item(self, node):
1577
self.body.append('\n')
1579
def visit_literal(self, node):
1581
self.body.append('\\texttt{')
1583
def depart_literal(self, node):
1584
self.body.append('}')
1587
def visit_literal_block(self, node):
1589
Render a literal-block.
1591
Literal blocks are used for "::"-prefixed literal-indented
1592
blocks of text, where the inline markup is not recognized,
1593
but are also the product of the parsed-literal directive,
1594
where the markup is respected.
1596
# In both cases, we want to use a typewriter/monospaced typeface.
1597
# For "real" literal-blocks, we can use \verbatim, while for all
1598
# the others we must use \mbox.
1600
# We can distinguish between the two kinds by the number of
1601
# siblings the compose this node: if it is composed by a
1602
# single element, it's surely is either a real one, otherwise
1603
# it's a parsed-literal that does not contain any markup.
1605
if (self.settings.use_verbatim_when_possible and (len(node) == 1)
1606
# in case of a parsed-literal containing just a "**bold**" word:
1607
and isinstance(node[0], nodes.Text)):
1609
self.body.append('\\begin{quote}\\begin{verbatim}\n')
1611
self.literal_block = 1
1612
self.insert_none_breaking_blanks = 1
1613
if self.active_table.is_open():
1614
self.body.append('\n{\\ttfamily \\raggedright \\noindent\n')
1616
# no quote inside tables, to avoid vertical sppace between
1617
# table border and literal block.
1618
# BUG: fails if normal text preceeds the literal block.
1619
self.body.append('\\begin{quote}')
1620
self.body.append('{\\ttfamily \\raggedright \\noindent\n')
1621
# * obey..: is from julien and never worked for me (grubert).
1622
# self.body.append('{\\obeylines\\obeyspaces\\ttfamily\n')
1624
def depart_literal_block(self, node):
1626
self.body.append('\n\\end{verbatim}\\end{quote}\n')
1629
if self.active_table.is_open():
1630
self.body.append('\n}\n')
1632
self.body.append('\n')
1633
self.body.append('}\\end{quote}\n')
1634
self.insert_none_breaking_blanks = 0
1635
self.literal_block = 0
1636
# obey end: self.body.append('}\n')
1638
def visit_meta(self, node):
1639
self.body.append('[visit_meta]\n')
1640
# BUG maybe set keywords for pdf
1641
##self.head.append(self.starttag(node, 'meta', **node.attributes))
1643
def depart_meta(self, node):
1644
self.body.append('[depart_meta]\n')
1646
def visit_note(self, node):
1647
self.visit_admonition(node, 'note')
1649
def depart_note(self, node):
1650
self.depart_admonition()
1652
def visit_option(self, node):
1653
if self.context[-1]:
1654
# this is not the first option
1655
self.body.append(', ')
1657
def depart_option(self, node):
1658
# flag tha the first option is done.
1659
self.context[-1] += 1
1661
def visit_option_argument(self, node):
1662
"""The delimiter betweeen an option and its argument."""
1663
self.body.append(node.get('delimiter', ' '))
1665
def depart_option_argument(self, node):
1668
def visit_option_group(self, node):
1669
if self.use_optionlist_for_option_list:
1670
self.body.append('\\item [')
1672
if len(node.astext()) > 14:
1673
self.body.append('\\multicolumn{2}{l}{')
1674
self.context.append('} \\\\\n ')
1676
self.context.append('')
1677
self.body.append('\\texttt{')
1678
# flag for first option
1679
self.context.append(0)
1681
def depart_option_group(self, node):
1682
self.context.pop() # the flag
1683
if self.use_optionlist_for_option_list:
1684
self.body.append('] ')
1686
self.body.append('}')
1687
self.body.append(self.context.pop())
1689
def visit_option_list(self, node):
1690
self.body.append('% [option list]\n')
1691
if self.use_optionlist_for_option_list:
1692
self.body.append('\\begin{optionlist}{3cm}\n')
1694
self.body.append('\\begin{center}\n')
1695
# BUG: use admwidth or make it relative to textwidth ?
1696
self.body.append('\\begin{tabularx}{.9\\linewidth}{lX}\n')
1698
def depart_option_list(self, node):
1699
if self.use_optionlist_for_option_list:
1700
self.body.append('\\end{optionlist}\n')
1702
self.body.append('\\end{tabularx}\n')
1703
self.body.append('\\end{center}\n')
1705
def visit_option_list_item(self, node):
1708
def depart_option_list_item(self, node):
1709
if not self.use_optionlist_for_option_list:
1710
self.body.append('\\\\\n')
1712
def visit_option_string(self, node):
1713
##self.body.append(self.starttag(node, 'span', '', CLASS='option'))
1716
def depart_option_string(self, node):
1717
##self.body.append('</span>')
1720
def visit_organization(self, node):
1721
self.visit_docinfo_item(node, 'organization')
1723
def depart_organization(self, node):
1724
self.depart_docinfo_item(node)
1726
def visit_paragraph(self, node):
1727
index = node.parent.index(node)
1728
if not ('contents' in self.topic_classes or
1729
(isinstance(node.parent, nodes.compound) and
1731
not isinstance(node.parent[index - 1], nodes.paragraph) and
1732
not isinstance(node.parent[index - 1], nodes.compound))):
1733
self.body.append('\n')
1735
def depart_paragraph(self, node):
1736
self.body.append('\n')
1738
def visit_problematic(self, node):
1739
self.body.append('{\\color{red}\\bfseries{}')
1741
def depart_problematic(self, node):
1742
self.body.append('}')
1744
def visit_raw(self, node):
1745
if 'latex' in node.get('format', '').split():
1746
self.body.append(node.astext())
1747
raise nodes.SkipNode
1749
def visit_reference(self, node):
1750
# BUG: hash_char "#" is trouble some in LaTeX.
1751
# mbox and other environment do not like the '#'.
1753
if node.has_key('refuri'):
1754
href = node['refuri'].replace('#',hash_char)
1755
elif node.has_key('refid'):
1756
href = hash_char + node['refid']
1757
elif node.has_key('refname'):
1758
href = hash_char + self.document.nameids[node['refname']]
1760
raise AssertionError('Unknown reference.')
1761
self.body.append('\\href{%s}{' % href)
1763
def depart_reference(self, node):
1764
self.body.append('}')
1766
def visit_revision(self, node):
1767
self.visit_docinfo_item(node, 'revision')
1769
def depart_revision(self, node):
1770
self.depart_docinfo_item(node)
1772
def visit_section(self, node):
1773
self.section_level += 1
1774
# Initialize counter for potential subsections:
1775
self._section_number.append(0)
1776
# Counter for this section's level (initialized by parent section):
1777
self._section_number[self.section_level - 1] += 1
1779
def depart_section(self, node):
1780
# Remove counter for potential subsections:
1781
self._section_number.pop()
1782
self.section_level -= 1
1784
def visit_sidebar(self, node):
1785
# BUG: this is just a hack to make sidebars render something
1786
self.body.append('\n\\setlength{\\locallinewidth}{0.9\\admonitionwidth}\n')
1787
self.body.append('\\begin{center}\\begin{sffamily}\n')
1788
self.body.append('\\fbox{\\colorbox[gray]{0.80}{\\parbox{\\admonitionwidth}{\n')
1790
def depart_sidebar(self, node):
1791
self.body.append('}}}\n') # end parbox colorbox fbox
1792
self.body.append('\\end{sffamily}\n\\end{center}\n');
1793
self.body.append('\n\\setlength{\\locallinewidth}{\\linewidth}\n')
1796
attribution_formats = {'dash': ('---', ''),
1797
'parentheses': ('(', ')'),
1798
'parens': ('(', ')'),
1801
def visit_attribution(self, node):
1802
prefix, suffix = self.attribution_formats[self.settings.attribution]
1803
self.body.append('\n\\begin{flushright}\n')
1804
self.body.append(prefix)
1805
self.context.append(suffix)
1807
def depart_attribution(self, node):
1808
self.body.append(self.context.pop() + '\n')
1809
self.body.append('\\end{flushright}\n')
1811
def visit_status(self, node):
1812
self.visit_docinfo_item(node, 'status')
1814
def depart_status(self, node):
1815
self.depart_docinfo_item(node)
1817
def visit_strong(self, node):
1818
self.body.append('\\textbf{')
1819
self.literal_block_stack.append('\\textbf{')
1821
def depart_strong(self, node):
1822
self.body.append('}')
1823
self.literal_block_stack.pop()
1825
def visit_substitution_definition(self, node):
1826
raise nodes.SkipNode
1828
def visit_substitution_reference(self, node):
1829
self.unimplemented_visit(node)
1831
def visit_subtitle(self, node):
1832
if isinstance(node.parent, nodes.sidebar):
1833
self.body.append('~\\\\\n\\textbf{')
1834
self.context.append('}\n\\smallskip\n')
1835
elif isinstance(node.parent, nodes.document):
1836
self.title = self.title + \
1837
'\\\\\n\\large{%s}\n' % self.encode(node.astext())
1838
raise nodes.SkipNode
1839
elif isinstance(node.parent, nodes.section):
1840
self.body.append('\\textbf{')
1841
self.context.append('}\\vspace{0.2cm}\n\n\\noindent ')
1843
def depart_subtitle(self, node):
1844
self.body.append(self.context.pop())
1846
def visit_system_message(self, node):
1849
def depart_system_message(self, node):
1850
self.body.append('\n')
1852
def visit_table(self, node):
1853
if self.active_table.is_open():
1854
print 'nested tables are not supported'
1855
raise AssertionError
1856
self.active_table.open()
1857
self.body.append('\n' + self.active_table.get_opening())
1859
def depart_table(self, node):
1860
self.body.append(self.active_table.get_closing() + '\n')
1861
self.active_table.close()
1863
def visit_target(self, node):
1864
# BUG: why not (refuri or refid or refname) means not footnote ?
1865
if not (node.has_key('refuri') or node.has_key('refid')
1866
or node.has_key('refname')):
1867
for id in node['ids']:
1868
self.body.append('\\hypertarget{%s}{' % id)
1869
self.context.append('}' * len(node['ids']))
1871
self.context.append('')
1873
def depart_target(self, node):
1874
self.body.append(self.context.pop())
1876
def visit_tbody(self, node):
1877
# BUG write preamble if not yet done (colspecs not [])
1878
# for tables without heads.
1879
if not self.active_table.get('preamble written'):
1880
self.visit_thead(None)
1881
# self.depart_thead(None)
1883
def depart_tbody(self, node):
1886
def visit_term(self, node):
1887
self.body.append('\\item[{')
1889
def depart_term(self, node):
1890
# definition list term.
1891
self.body.append('}] ')
1893
def visit_tgroup(self, node):
1894
#self.body.append(self.starttag(node, 'colgroup'))
1895
#self.context.append('</colgroup>\n')
1898
def depart_tgroup(self, node):
1901
def visit_thead(self, node):
1902
self.body.append('{%s}\n' % self.active_table.get_colspecs())
1903
if self.active_table.caption:
1904
self.body.append('\\caption{%s}\\\\\n' % self.active_table.caption)
1905
self.active_table.set('preamble written',1)
1906
# TODO longtable supports firsthead and lastfoot too.
1907
self.body.extend(self.active_table.visit_thead())
1909
def depart_thead(self, node):
1910
# the table header written should be on every page
1912
self.body.extend(self.active_table.depart_thead())
1913
# and the firsthead => \endfirsthead
1914
# BUG i want a "continued from previous page" on every not
1915
# firsthead, but then we need the header twice.
1917
# there is a \endfoot and \endlastfoot too.
1918
# but we need the number of columns to
1919
# self.body.append('\\multicolumn{%d}{c}{"..."}\n' % number_of_columns)
1920
# self.body.append('\\hline\n\\endfoot\n')
1921
# self.body.append('\\hline\n')
1922
# self.body.append('\\endlastfoot\n')
1924
def visit_tip(self, node):
1925
self.visit_admonition(node, 'tip')
1927
def depart_tip(self, node):
1928
self.depart_admonition()
1930
def bookmark(self, node):
1931
"""Append latex href and pdfbookmarks for titles.
1933
if node.parent['ids']:
1934
for id in node.parent['ids']:
1935
self.body.append('\\hypertarget{%s}{}\n' % id)
1936
if not self.use_latex_toc:
1937
# BUG level depends on style. pdflatex allows level 0 to 3
1938
# ToC would be the only on level 0 so i choose to decrement the rest.
1939
# "Table of contents" bookmark to see the ToC. To avoid this
1940
# we set all zeroes to one.
1941
l = self.section_level
1944
# pdftex does not like "_" subscripts in titles
1945
text = self.encode(node.astext())
1946
for id in node.parent['ids']:
1947
self.body.append('\\pdfbookmark[%d]{%s}{%s}\n' % \
1950
def visit_title(self, node):
1951
"""Only 3 section levels are supported by LaTeX article (AFAIR)."""
1953
if isinstance(node.parent, nodes.topic):
1954
# section titles before the table of contents.
1956
# BUG: latex chokes on center environment with "perhaps a missing item".
1958
self.body.append('\\subsubsection*{~\\hfill ')
1959
# the closing brace for subsection.
1960
self.context.append('\\hfill ~}\n')
1961
# TODO: for admonition titles before the first section
1962
# either specify every possible node or ... ?
1963
elif isinstance(node.parent, nodes.sidebar) \
1964
or isinstance(node.parent, nodes.admonition):
1965
self.body.append('\\textbf{\\large ')
1966
self.context.append('}\n\\smallskip\n')
1967
elif isinstance(node.parent, nodes.table):
1968
# caption must be written after column spec
1969
self.active_table.caption = self.encode(node.astext())
1970
raise nodes.SkipNode
1971
elif self.section_level == 0:
1973
self.title = self.encode(node.astext())
1974
if not self.pdfinfo == None:
1975
self.pdfinfo.append( 'pdftitle={%s}' % self.encode(node.astext()) )
1976
raise nodes.SkipNode
1978
self.body.append('\n\n')
1979
self.body.append('%' + '_' * 75)
1980
self.body.append('\n\n')
1983
if self.use_latex_toc:
1988
section_name = self.d_class.section(self.section_level)
1989
self.body.append('\\%s%s{' % (section_name, section_star))
1991
self.context.append('}\n')
1993
def depart_title(self, node):
1994
self.body.append(self.context.pop())
1996
def visit_topic(self, node):
1997
self.topic_classes = node['classes']
1998
if 'contents' in node['classes'] and self.use_latex_toc:
1999
self.body.append('\\tableofcontents\n\n\\bigskip\n')
2000
self.topic_classes = []
2001
raise nodes.SkipNode
2003
def visit_inline(self, node): # titlereference
2004
self.body.append( '\\docutilsrole%s{' % node.get('class'))
2006
def depart_inline(self, node):
2007
self.body.append( '}' )
2009
def depart_topic(self, node):
2010
self.topic_classes = []
2011
self.body.append('\n')
2013
def visit_rubric(self, node):
2014
self.body.append('\\rubric{')
2015
self.context.append('}\n')
2017
def depart_rubric(self, node):
2018
self.body.append(self.context.pop())
2020
def visit_transition(self, node):
2021
self.body.append('\n\n')
2022
self.body.append('%' + '_' * 75)
2023
self.body.append('\n\\hspace*{\\fill}\\hrulefill\\hspace*{\\fill}')
2024
self.body.append('\n\n')
2026
def depart_transition(self, node):
2029
def visit_version(self, node):
2030
self.visit_docinfo_item(node, 'version')
2032
def depart_version(self, node):
2033
self.depart_docinfo_item(node)
2035
def visit_warning(self, node):
2036
self.visit_admonition(node, 'warning')
2038
def depart_warning(self, node):
2039
self.depart_admonition()
2041
def unimplemented_visit(self, node):
2042
raise NotImplementedError('visiting unimplemented node type: %s'
2043
% node.__class__.__name__)
2045
# def unknown_visit(self, node):
2046
# def default_visit(self, node):
2048
# vim: set ts=4 et ai :