latex.py
198 lines
| 6.6 KiB
| text/x-python
|
PythonLexer
/ converters / latex.py
|
r8618 | from converters.base import Converter | ||
from converters.utils import markdown2latex, remove_ansi | ||||
|
r8749 | import os | ||
|
r8618 | import subprocess | ||
|
r8749 | import sys | ||
|
r8618 | |||
inkscape = 'inkscape' | ||||
if sys.platform == 'darwin': | ||||
inkscape = '/Applications/Inkscape.app/Contents/Resources/bin/inkscape' | ||||
if not os.path.exists(inkscape): | ||||
inkscape = None | ||||
|
r8749 | |||
|
r8618 | class ConverterLaTeX(Converter): | ||
"""Converts a notebook to a .tex file suitable for pdflatex. | ||||
Note: this converter *needs*: | ||||
- `pandoc`: for all conversion of markdown cells. If your notebook only | ||||
has Raw cells, pandoc will not be needed. | ||||
|
r8749 | |||
|
r8618 | - `inkscape`: if your notebook has SVG figures. These need to be | ||
converted to PDF before inclusion in the TeX file, as LaTeX doesn't | ||||
understand SVG natively. | ||||
|
r8749 | |||
|
r8618 | You will in general obtain much better final PDF results if you configure | ||
|
r8749 | the matplotlib backend to create SVG output with | ||
|
r8618 | |||
%config InlineBackend.figure_format = 'svg' | ||||
(or set the equivalent flag at startup or in your configuration profile). | ||||
""" | ||||
|
r8649 | inkscape = inkscape | ||
|
r8618 | extension = 'tex' | ||
documentclass = 'article' | ||||
documentclass_options = '11pt,english' | ||||
heading_map = {1: r'\section', | ||||
2: r'\subsection', | ||||
3: r'\subsubsection', | ||||
4: r'\paragraph', | ||||
5: r'\subparagraph', | ||||
6: r'\subparagraph'} | ||||
|
r8750 | user_preamble = None | ||
|
r8753 | exclude_cells = [] | ||
|
r8618 | |||
def in_env(self, environment, lines): | ||||
"""Return list of environment lines for input lines | ||||
Parameters | ||||
---------- | ||||
env : string | ||||
Name of the environment to bracket with begin/end. | ||||
lines: """ | ||||
out = [ur'\begin{%s}' % environment] | ||||
if isinstance(lines, basestring): | ||||
out.append(lines) | ||||
else: # list | ||||
out.extend(lines) | ||||
out.append(ur'\end{%s}' % environment) | ||||
return out | ||||
|
r8749 | def convert(self, *args, **kwargs): | ||
|
r8618 | # The main body is done by the logic in the parent class, and that's | ||
# all we need if preamble support has been turned off. | ||||
|
r8749 | body = super(ConverterLaTeX, self).convert(*args, **kwargs) | ||
|
r8618 | if not self.with_preamble: | ||
return body | ||||
# But if preamble is on, then we need to construct a proper, standalone | ||||
# tex file. | ||||
|
r8749 | |||
|
r8618 | # Tag the document at the top and set latex class | ||
|
r8749 | final = [r'%% This file was auto-generated by IPython, do NOT edit', | ||
r'%% Conversion from the original notebook file:', | ||||
r'%% {0}'.format(self.infile), | ||||
r'%%', | ||||
r'\documentclass[%s]{%s}' % (self.documentclass_options, | ||||
self.documentclass), | ||||
'', | ||||
|
r8618 | ] | ||
# Load our own preamble, which is stored next to the main file. We | ||||
# need to be careful in case the script entry point is a symlink | ||||
myfile = os.path.realpath(__file__) | ||||
|
r8749 | preamble = '../preamble.tex' | ||
with open(os.path.join(os.path.dirname(myfile), preamble)) as f: | ||||
|
r8618 | final.append(f.read()) | ||
|
r8749 | |||
|
r8618 | # Load any additional user-supplied preamble | ||
if self.user_preamble: | ||||
final.extend(['', '%% Adding user preamble from file:', | ||||
'%% {0}'.format(self.user_preamble), '']) | ||||
with open(self.user_preamble) as f: | ||||
final.append(f.read()) | ||||
|
r8749 | |||
|
r8618 | # Include document body | ||
|
r8749 | final.extend([r'\begin{document}', '', | ||
body, | ||||
r'\end{document}', '']) | ||||
|
r8618 | # Retun value must be a string | ||
return '\n'.join(final) | ||||
|
r8749 | |||
|
r8618 | def render_heading(self, cell): | ||
marker = self.heading_map[cell.level] | ||||
|
r8749 | return ['%s{%s}' % (marker, cell.source)] | ||
|
r8618 | |||
def render_code(self, cell): | ||||
if not cell.input: | ||||
return [] | ||||
# Cell codes first carry input code, we use lstlisting for that | ||||
lines = [ur'\begin{codecell}'] | ||||
|
r8753 | |||
if 'source' not in self.exclude_cells: | ||||
lines.extend(self.in_env('codeinput', | ||||
self.in_env('lstlisting', cell.input))) | ||||
else: | ||||
# Empty output is still needed for LaTeX formatting | ||||
lines.extend(self.in_env('codeinput', '')) | ||||
|
r8618 | |||
outlines = [] | ||||
|
r8753 | if 'output' not in self.exclude_cells: | ||
for output in cell.outputs: | ||||
conv_fn = self.dispatch(output.output_type) | ||||
outlines.extend(conv_fn(output)) | ||||
|
r8618 | |||
# And then output of many possible types; use a frame for all of it. | ||||
if outlines: | ||||
lines.extend(self.in_env('codeoutput', outlines)) | ||||
lines.append(ur'\end{codecell}') | ||||
return lines | ||||
def _img_lines(self, img_file): | ||||
return self.in_env('center', | ||||
[r'\includegraphics[width=6in]{%s}' % img_file, r'\par']) | ||||
def _svg_lines(self, img_file): | ||||
base_file = os.path.splitext(img_file)[0] | ||||
pdf_file = base_file + '.pdf' | ||||
|
r8749 | subprocess.check_call([self.inkscape, '--export-pdf=%s' % pdf_file, | ||
|
r8618 | img_file]) | ||
return self._img_lines(pdf_file) | ||||
def render_markdown(self, cell): | ||||
return [markdown2latex(cell.source)] | ||||
|
r8749 | |||
|
r8618 | def render_pyout(self, output): | ||
lines = [] | ||||
# output is a dictionary like object with type as a key | ||||
if 'latex' in output: | ||||
lines.extend(output.latex) | ||||
if 'text' in output: | ||||
lines.extend(self.in_env('verbatim', output.text)) | ||||
return lines | ||||
def render_pyerr(self, output): | ||||
# Note: a traceback is a *list* of frames. | ||||
return self.in_env('traceback', | ||||
|
r8749 | self.in_env('verbatim', | ||
|
r8618 | remove_ansi('\n'.join(output.traceback)))) | ||
def render_raw(self, cell): | ||||
if self.raw_as_verbatim: | ||||
return self.in_env('verbatim', cell.source) | ||||
else: | ||||
return [cell.source] | ||||
def _unknown_lines(self, data): | ||||
return [r'{\vspace{5mm}\bf WARNING:: unknown cell:}'] + \ | ||||
self.in_env('verbatim', data) | ||||
def render_display_format_text(self, output): | ||||
lines = [] | ||||
if 'text' in output: | ||||
lines.extend(self.in_env('verbatim', output.text.strip())) | ||||
return lines | ||||
def render_display_format_html(self, output): | ||||
return [] | ||||
def render_display_format_latex(self, output): | ||||
if type(output.latex) == type([]): | ||||
return output.latex | ||||
return [output.latex] | ||||
def render_display_format_json(self, output): | ||||
# latex ignores json | ||||
return [] | ||||
def render_display_format_javascript(self, output): | ||||
# latex ignores javascript | ||||
return [] | ||||