latex.py
191 lines
| 6.3 KiB
| text/x-python
|
PythonLexer
/ converters / latex.py
Matthias BUSSONNIER
|
r8618 | from converters.base import Converter | ||
from converters.utils import markdown2latex, remove_ansi | ||||
import os, sys | ||||
import subprocess | ||||
inkscape = 'inkscape' | ||||
if sys.platform == 'darwin': | ||||
inkscape = '/Applications/Inkscape.app/Contents/Resources/bin/inkscape' | ||||
if not os.path.exists(inkscape): | ||||
inkscape = None | ||||
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. | ||||
- `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. | ||||
You will in general obtain much better final PDF results if you configure | ||||
the matplotlib backend to create SVG output with | ||||
%config InlineBackend.figure_format = 'svg' | ||||
(or set the equivalent flag at startup or in your configuration profile). | ||||
""" | ||||
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'} | ||||
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 | ||||
def convert(self): | ||||
# 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. | ||||
body = super(ConverterLaTeX, self).convert() | ||||
if not self.with_preamble: | ||||
return body | ||||
# But if preamble is on, then we need to construct a proper, standalone | ||||
# tex file. | ||||
# Tag the document at the top and set latex class | ||||
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), | ||||
'', | ||||
] | ||||
# 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__) | ||||
with open(os.path.join(os.path.dirname(myfile), '../preamble.tex')) as f: | ||||
final.append(f.read()) | ||||
# 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()) | ||||
# Include document body | ||||
final.extend([ r'\begin{document}', '', | ||||
body, | ||||
r'\end{document}', '']) | ||||
# Retun value must be a string | ||||
return '\n'.join(final) | ||||
def render_heading(self, cell): | ||||
marker = self.heading_map[cell.level] | ||||
return ['%s{%s}' % (marker, cell.source) ] | ||||
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}'] | ||||
lines.extend(self.in_env('codeinput', | ||||
self.in_env('lstlisting', cell.input))) | ||||
outlines = [] | ||||
for output in cell.outputs: | ||||
conv_fn = self.dispatch(output.output_type) | ||||
outlines.extend(conv_fn(output)) | ||||
# 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' | ||||
subprocess.check_call([ inkscape, '--export-pdf=%s' % pdf_file, | ||||
img_file]) | ||||
return self._img_lines(pdf_file) | ||||
def render_markdown(self, cell): | ||||
return [markdown2latex(cell.source)] | ||||
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', | ||||
self.in_env('verbatim', | ||||
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 [] | ||||