latex.py
294 lines
| 11.7 KiB
| text/x-python
|
PythonLexer
Jonathan Frederic
|
r10479 | ||
"""Latex exporter for the notebook conversion pipeline. | |||
This module defines Exporter, a highly configurable converter | |||
that uses Jinja2 to export notebook files into different format. | |||
You can register both pre-transformers that will act on the notebook format | |||
befor conversion and jinja filter that would then be availlable in the templates | |||
""" | |||
#----------------------------------------------------------------------------- | |||
# Copyright (c) 2013, the IPython Development Team. | |||
# | |||
# Distributed under the terms of the Modified BSD License. | |||
# | |||
# The full license is in the file COPYING.txt, distributed with this software. | |||
#----------------------------------------------------------------------------- | |||
#----------------------------------------------------------------------------- | |||
# Imports | |||
#----------------------------------------------------------------------------- | |||
from .utils import highlight2latex #TODO | |||
from .transformers.latex import LatexTransformer, rm_math_space #TODO: rm_math_space from filters | |||
#Try to import the Sphinx exporter. If the user doesn't have Sphinx isntalled | |||
#on his/her machine, fail silently. | |||
try: | |||
from .sphinx_transformer import (SphinxTransformer) #TODO | |||
except ImportError: | |||
SphinxTransformer = None | |||
#----------------------------------------------------------------------------- | |||
# Globals and constants | |||
#----------------------------------------------------------------------------- | |||
#Latex Jinja2 constants | |||
LATEX_TEMPLATE_PATH = "/../templates/tex/" | |||
LATEX_TEMPLATE_SKELETON_PATH = "/../templates/tex/skeleton/" | |||
LATEX_TEMPLATE_EXTENSION = ".tplx" | |||
#Special Jinja2 syntax that will not conflict when exporting latex. | |||
LATEX_JINJA_COMMENT_BLOCK = ["((=", "=))"] | |||
LATEX_JINJA_VARIABLE_BLOCK = ["(((", ")))"] | |||
LATEX_JINJA_LOGIC_BLOCK = ["((*", "*))"] | |||
#Latex substitutions for escaping latex. | |||
LATEX_SUBS = ( | |||
(re.compile(r'\\'), r'\\textbackslash'), | |||
(re.compile(r'([{}_#%&$])'), r'\\\1'), | |||
(re.compile(r'~'), r'\~{}'), | |||
(re.compile(r'\^'), r'\^{}'), | |||
(re.compile(r'"'), r"''"), | |||
(re.compile(r'\.\.\.+'), r'\\ldots'), | |||
) | |||
#----------------------------------------------------------------------------- | |||
# Classes and functions | |||
#----------------------------------------------------------------------------- | |||
class LatexExporter(Configurable): | |||
""" A Jinja2 base converter templates | |||
Preprocess the ipynb files, feed it throug jinja templates, | |||
and spit an converted files and a data object with other data | |||
should be mostly configurable | |||
""" | |||
#Processors that process the input data prior to the export, set in the | |||
#constructor for this class. | |||
preprocessors = [] | |||
def __init__(self, preprocessors={}, jinja_filters={}, config=None, export_format, **kw): | |||
""" Init a new converter. | |||
config: the Configurable config object to pass around. | |||
preprocessors: dict of **availlable** key/value function to run on | |||
ipynb json data before conversion to extract/inline file. | |||
See `transformer.py` and `ConfigurableTransformers` | |||
set the order in which the transformers should apply | |||
with the `pre_transformer_order` trait of this class | |||
transformers registerd by this key will take precedence on | |||
default one. | |||
jinja_filters: dict of supplementary jinja filter that should be made | |||
availlable in template. If those are of Configurable Class type, | |||
they will be instanciated with the config object as argument. | |||
user defined filter will overwrite the one availlable by default. | |||
""" | |||
#Merge default config options with user specific override options. | |||
default_config = self._get_default_options() | |||
if not config == None: | |||
default_config._merge(config) | |||
config = default_config | |||
#Call the base class constructor | |||
super(Exporter, self).__init__(config=config, **kw) | |||
#Create a Latex environment if the user is exporting latex. | |||
if self.tex_environement: | |||
self.ext = LATEX_TEMPLATE_EXTENSION | |||
self.env = Environment( | |||
loader=FileSystemLoader([ | |||
os.path.dirname(os.path.realpath(__file__)) + LATEX_TEMPLATE_PATH, | |||
os.path.dirname(os.path.realpath(__file__)) + LATEX_TEMPLATE_SKELETON_PATH, | |||
]), | |||
extensions=JINJA_EXTENSIONS | |||
) | |||
#Set special Jinja2 syntax that will not conflict with latex. | |||
self.env.block_start_string = LATEX_JINJA_LOGIC_BLOCK[0] | |||
self.env.block_end_string = LATEX_JINJA_LOGIC_BLOCK[1] | |||
self.env.variable_start_string = LATEX_JINJA_VARIABLE_BLOCK[0] | |||
self.env.variable_end_string = LATEX_JINJA_VARIABLE_BLOCK[1] | |||
self.env.comment_start_string = LATEX_JINJA_COMMENT_BLOCK[0] | |||
self.env.comment_end_string = LATEX_JINJA_COMMENT_BLOCK[1] | |||
else: #Standard environment | |||
self.ext = TEMPLATE_EXTENSION | |||
self.env = Environment( | |||
loader=FileSystemLoader([ | |||
os.path.dirname(os.path.realpath(__file__)) + TEMPLATE_PATH, | |||
os.path.dirname(os.path.realpath(__file__)) + TEMPLATE_SKELETON_PATH, | |||
]), | |||
extensions=JINJA_EXTENSIONS | |||
) | |||
for name in self.pre_transformer_order: | |||
# get the user-defined transformer first | |||
transformer = preprocessors.get(name, getattr(trans, name, None)) | |||
if isinstance(transformer, MetaHasTraits): | |||
transformer = transformer(config=config) | |||
self.preprocessors.append(transformer) | |||
#For compatibility, TODO: remove later. | |||
self.preprocessors.append(trans.coalesce_streams) | |||
self.preprocessors.append(trans.ExtractFigureTransformer(config=config)) | |||
self.preprocessors.append(trans.RevealHelpTransformer(config=config)) | |||
self.preprocessors.append(trans.CSSHtmlHeaderTransformer(config=config)) | |||
self.preprocessors.append(LatexTransformer(config=config)) | |||
#Only load the sphinx transformer if the file reference worked | |||
#(Sphinx dependencies exist on the user's machine.) | |||
if SphinxTransformer: | |||
self.preprocessors.append(SphinxTransformer(config=config)) | |||
#Add filters to the Jinja2 environment | |||
self.env.filters['filter_data_type'] = DataTypeFilter(config=config) | |||
self.env.filters['pycomment'] = _python_comment | |||
self.env.filters['indent'] = indent | |||
self.env.filters['rm_fake'] = _rm_fake | |||
self.env.filters['rm_ansi'] = remove_ansi | |||
self.env.filters['markdown'] = markdown | |||
self.env.filters['ansi2html'] = ansi2html | |||
self.env.filters['markdown2latex'] = markdown_utils.markdown2latex | |||
self.env.filters['markdown2rst'] = markdown_utils.markdown2rst | |||
self.env.filters['get_lines'] = get_lines | |||
self.env.filters['wrap'] = strings.wrap | |||
self.env.filters['rm_dollars'] = strings.strip_dollars | |||
self.env.filters['rm_math_space'] = rm_math_space | |||
self.env.filters['highlight2html'] = highlight | |||
self.env.filters['highlight2latex'] = highlight2latex | |||
#Latex specific filters | |||
if self.tex_environement: | |||
self.env.filters['escape_tex'] = _escape_tex | |||
self.env.filters['highlight'] = highlight2latex | |||
else: | |||
self.env.filters['highlight'] = highlight | |||
#Load user filters. Overwrite existing filters if need be. | |||
for key, user_filter in jinja_filters.iteritems(): | |||
if isinstance(user_filter, MetaHasTraits): | |||
self.env.filters[key] = user_filter(config=config) | |||
else: | |||
self.env.filters[key] = user_filter | |||
#Load the template file. | |||
self.template = self.env.get_template(self.template_file+self.ext) | |||
def export(self, nb): | |||
"""Export notebook object | |||
nb: Notebook object to export. | |||
Returns both the converted ipynb file and a dict containing the | |||
resources created along the way via the transformers and Jinja2 | |||
processing. | |||
""" | |||
nb, resources = self._preprocess(nb) | |||
return self.template.render(nb=nb, resources=resources), resources | |||
def from_filename(self, filename): | |||
"""Read and export a notebook from a filename | |||
filename: Filename of the notebook file to export. | |||
Returns both the converted ipynb file and a dict containing the | |||
resources created along the way via the transformers and Jinja2 | |||
processing. | |||
""" | |||
with io.open(filename) as f: | |||
return self.export(nbformat.read(f, 'json')) | |||
def from_file(self, file_stream): | |||
"""Read and export a notebook from a filename | |||
file_stream: File handle of file that contains notebook data. | |||
Returns both the converted ipynb file and a dict containing the | |||
resources created along the way via the transformers and Jinja2 | |||
processing. | |||
""" | |||
return self.export(nbformat.read(file_stream, 'json')) | |||
def _preprocess(self, nb): | |||
""" Preprocess the notebook using the transformers specific | |||
for the current export format. | |||
nb: Notebook to preprocess | |||
""" | |||
#Dict of 'resources' that can be filled by the preprocessors. | |||
resources = {} | |||
#Run each transformer on the notebook. Carry the output along | |||
#to each transformer | |||
for transformer in self.preprocessors: | |||
nb, resources = transformer(nb, resources) | |||
return nb, resources | |||
def _get_default_options(self, export_format): | |||
""" Load the default options for built in formats. | |||
export_format: Format being exported to. | |||
""" | |||
c = get_config() | |||
#Set default data extraction priorities. | |||
c.GlobalConfigurable.display_data_priority =['svg', 'png', 'latex', 'jpg', 'jpeg','text'] | |||
c.ExtractFigureTransformer.display_data_priority=['svg', 'png', 'latex', 'jpg', 'jpeg','text'] | |||
c.ConverterTemplate.display_data_priority= ['svg', 'png', 'latex', 'jpg', 'jpeg','text'] | |||
#For most (or all cases), the template file name matches the format name. | |||
c.ConverterTemplate.template_file = export_format | |||
if export_format == "basichtml" or "fullhtml" or "reveal": | |||
c.CSSHtmlHeaderTransformer.enabled=True | |||
if export_format == 'reveal' | |||
c.NbconvertApp.fileext='reveal.html' | |||
else: | |||
c.NbconvertApp.fileext='html' | |||
elif export_format == "latex_sphinx_howto" or export_format == "latex_sphinx_manual": | |||
#Turn on latex environment | |||
c.ConverterTemplate.tex_environement=True | |||
#Standard latex extension | |||
c.NbconvertApp.fileext='tex' | |||
#Prioritize latex extraction for latex exports. | |||
c.GlobalConfigurable.display_data_priority =['latex', 'svg', 'png', 'jpg', 'jpeg' , 'text'] | |||
c.ExtractFigureTransformer.display_data_priority=['latex', 'svg', 'png', 'jpg', 'jpeg'] | |||
c.ExtractFigureTransformer.extra_ext_map={'svg':'pdf'} | |||
c.ExtractFigureTransformer.enabled=True | |||
# Enable latex transformers (make markdown2latex work with math $.) | |||
c.LatexTransformer.enabled=True | |||
c.SphinxTransformer.enabled = True | |||
elif export_format == 'markdown': | |||
c.NbconvertApp.fileext='md' | |||
c.ExtractFigureTransformer.enabled=True | |||
elif export_format == 'python': | |||
c.NbconvertApp.fileext='py' | |||
elif export_format == 'rst': | |||
c.NbconvertApp.fileext='rst' | |||
c.ExtractFigureTransformer.enabled=True | |||
return c |