Show More
@@ -0,0 +1,295 b'' | |||||
|
1 | ||||
|
2 | """Latex exporter for the notebook conversion pipeline. | |||
|
3 | ||||
|
4 | This module defines Exporter, a highly configurable converter | |||
|
5 | that uses Jinja2 to export notebook files into different format. | |||
|
6 | ||||
|
7 | You can register both pre-transformers that will act on the notebook format | |||
|
8 | befor conversion and jinja filter that would then be availlable in the templates | |||
|
9 | """ | |||
|
10 | ||||
|
11 | #----------------------------------------------------------------------------- | |||
|
12 | # Copyright (c) 2013, the IPython Development Team. | |||
|
13 | # | |||
|
14 | # Distributed under the terms of the Modified BSD License. | |||
|
15 | # | |||
|
16 | # The full license is in the file COPYING.txt, distributed with this software. | |||
|
17 | #----------------------------------------------------------------------------- | |||
|
18 | ||||
|
19 | #----------------------------------------------------------------------------- | |||
|
20 | # Imports | |||
|
21 | #----------------------------------------------------------------------------- | |||
|
22 | from .utils import highlight2latex #TODO | |||
|
23 | ||||
|
24 | from .transformers.latex import LatexTransformer, rm_math_space #TODO: rm_math_space from filters | |||
|
25 | ||||
|
26 | #Try to import the Sphinx exporter. If the user doesn't have Sphinx isntalled | |||
|
27 | #on his/her machine, fail silently. | |||
|
28 | try: | |||
|
29 | from .sphinx_transformer import (SphinxTransformer) #TODO | |||
|
30 | except ImportError: | |||
|
31 | SphinxTransformer = None | |||
|
32 | ||||
|
33 | #----------------------------------------------------------------------------- | |||
|
34 | # Globals and constants | |||
|
35 | #----------------------------------------------------------------------------- | |||
|
36 | ||||
|
37 | #Latex Jinja2 constants | |||
|
38 | LATEX_TEMPLATE_PATH = "/../templates/tex/" | |||
|
39 | LATEX_TEMPLATE_SKELETON_PATH = "/../templates/tex/skeleton/" | |||
|
40 | LATEX_TEMPLATE_EXTENSION = ".tplx" | |||
|
41 | ||||
|
42 | #Special Jinja2 syntax that will not conflict when exporting latex. | |||
|
43 | LATEX_JINJA_COMMENT_BLOCK = ["((=", "=))"] | |||
|
44 | LATEX_JINJA_VARIABLE_BLOCK = ["(((", ")))"] | |||
|
45 | LATEX_JINJA_LOGIC_BLOCK = ["((*", "*))"] | |||
|
46 | ||||
|
47 | #Latex substitutions for escaping latex. | |||
|
48 | LATEX_SUBS = ( | |||
|
49 | (re.compile(r'\\'), r'\\textbackslash'), | |||
|
50 | (re.compile(r'([{}_#%&$])'), r'\\\1'), | |||
|
51 | (re.compile(r'~'), r'\~{}'), | |||
|
52 | (re.compile(r'\^'), r'\^{}'), | |||
|
53 | (re.compile(r'"'), r"''"), | |||
|
54 | (re.compile(r'\.\.\.+'), r'\\ldots'), | |||
|
55 | ) | |||
|
56 | ||||
|
57 | #----------------------------------------------------------------------------- | |||
|
58 | # Classes and functions | |||
|
59 | #----------------------------------------------------------------------------- | |||
|
60 | class LatexExporter(Configurable): | |||
|
61 | """ A Jinja2 base converter templates | |||
|
62 | ||||
|
63 | Preprocess the ipynb files, feed it throug jinja templates, | |||
|
64 | and spit an converted files and a data object with other data | |||
|
65 | should be mostly configurable | |||
|
66 | """ | |||
|
67 | ||||
|
68 | #Processors that process the input data prior to the export, set in the | |||
|
69 | #constructor for this class. | |||
|
70 | preprocessors = [] | |||
|
71 | ||||
|
72 | def __init__(self, preprocessors={}, jinja_filters={}, config=None, export_format, **kw): | |||
|
73 | """ Init a new converter. | |||
|
74 | ||||
|
75 | config: the Configurable config object to pass around. | |||
|
76 | ||||
|
77 | preprocessors: dict of **availlable** key/value function to run on | |||
|
78 | ipynb json data before conversion to extract/inline file. | |||
|
79 | See `transformer.py` and `ConfigurableTransformers` | |||
|
80 | ||||
|
81 | set the order in which the transformers should apply | |||
|
82 | with the `pre_transformer_order` trait of this class | |||
|
83 | ||||
|
84 | transformers registerd by this key will take precedence on | |||
|
85 | default one. | |||
|
86 | ||||
|
87 | jinja_filters: dict of supplementary jinja filter that should be made | |||
|
88 | availlable in template. If those are of Configurable Class type, | |||
|
89 | they will be instanciated with the config object as argument. | |||
|
90 | ||||
|
91 | user defined filter will overwrite the one availlable by default. | |||
|
92 | """ | |||
|
93 | ||||
|
94 | #Merge default config options with user specific override options. | |||
|
95 | default_config = self._get_default_options() | |||
|
96 | if not config == None: | |||
|
97 | default_config._merge(config) | |||
|
98 | config = default_config | |||
|
99 | ||||
|
100 | #Call the base class constructor | |||
|
101 | super(Exporter, self).__init__(config=config, **kw) | |||
|
102 | ||||
|
103 | #Create a Latex environment if the user is exporting latex. | |||
|
104 | if self.tex_environement: | |||
|
105 | self.ext = LATEX_TEMPLATE_EXTENSION | |||
|
106 | self.env = Environment( | |||
|
107 | loader=FileSystemLoader([ | |||
|
108 | os.path.dirname(os.path.realpath(__file__)) + LATEX_TEMPLATE_PATH, | |||
|
109 | os.path.dirname(os.path.realpath(__file__)) + LATEX_TEMPLATE_SKELETON_PATH, | |||
|
110 | ]), | |||
|
111 | extensions=JINJA_EXTENSIONS | |||
|
112 | ) | |||
|
113 | ||||
|
114 | #Set special Jinja2 syntax that will not conflict with latex. | |||
|
115 | self.env.block_start_string = LATEX_JINJA_LOGIC_BLOCK[0] | |||
|
116 | self.env.block_end_string = LATEX_JINJA_LOGIC_BLOCK[1] | |||
|
117 | self.env.variable_start_string = LATEX_JINJA_VARIABLE_BLOCK[0] | |||
|
118 | self.env.variable_end_string = LATEX_JINJA_VARIABLE_BLOCK[1] | |||
|
119 | self.env.comment_start_string = LATEX_JINJA_COMMENT_BLOCK[0] | |||
|
120 | self.env.comment_end_string = LATEX_JINJA_COMMENT_BLOCK[1] | |||
|
121 | ||||
|
122 | else: #Standard environment | |||
|
123 | self.ext = TEMPLATE_EXTENSION | |||
|
124 | self.env = Environment( | |||
|
125 | loader=FileSystemLoader([ | |||
|
126 | os.path.dirname(os.path.realpath(__file__)) + TEMPLATE_PATH, | |||
|
127 | os.path.dirname(os.path.realpath(__file__)) + TEMPLATE_SKELETON_PATH, | |||
|
128 | ]), | |||
|
129 | extensions=JINJA_EXTENSIONS | |||
|
130 | ) | |||
|
131 | ||||
|
132 | for name in self.pre_transformer_order: | |||
|
133 | # get the user-defined transformer first | |||
|
134 | transformer = preprocessors.get(name, getattr(trans, name, None)) | |||
|
135 | if isinstance(transformer, MetaHasTraits): | |||
|
136 | transformer = transformer(config=config) | |||
|
137 | self.preprocessors.append(transformer) | |||
|
138 | ||||
|
139 | #For compatibility, TODO: remove later. | |||
|
140 | self.preprocessors.append(trans.coalesce_streams) | |||
|
141 | self.preprocessors.append(trans.ExtractFigureTransformer(config=config)) | |||
|
142 | self.preprocessors.append(trans.RevealHelpTransformer(config=config)) | |||
|
143 | self.preprocessors.append(trans.CSSHtmlHeaderTransformer(config=config)) | |||
|
144 | self.preprocessors.append(LatexTransformer(config=config)) | |||
|
145 | ||||
|
146 | #Only load the sphinx transformer if the file reference worked | |||
|
147 | #(Sphinx dependencies exist on the user's machine.) | |||
|
148 | if SphinxTransformer: | |||
|
149 | self.preprocessors.append(SphinxTransformer(config=config)) | |||
|
150 | ||||
|
151 | #Add filters to the Jinja2 environment | |||
|
152 | self.env.filters['filter_data_type'] = DataTypeFilter(config=config) | |||
|
153 | self.env.filters['pycomment'] = _python_comment | |||
|
154 | self.env.filters['indent'] = indent | |||
|
155 | self.env.filters['rm_fake'] = _rm_fake | |||
|
156 | self.env.filters['rm_ansi'] = remove_ansi | |||
|
157 | self.env.filters['markdown'] = markdown | |||
|
158 | self.env.filters['ansi2html'] = ansi2html | |||
|
159 | self.env.filters['markdown2latex'] = markdown_utils.markdown2latex | |||
|
160 | self.env.filters['markdown2rst'] = markdown_utils.markdown2rst | |||
|
161 | self.env.filters['get_lines'] = get_lines | |||
|
162 | self.env.filters['wrap'] = strings.wrap | |||
|
163 | self.env.filters['rm_dollars'] = strings.strip_dollars | |||
|
164 | self.env.filters['rm_math_space'] = rm_math_space | |||
|
165 | self.env.filters['highlight2html'] = highlight | |||
|
166 | self.env.filters['highlight2latex'] = highlight2latex | |||
|
167 | ||||
|
168 | #Latex specific filters | |||
|
169 | if self.tex_environement: | |||
|
170 | self.env.filters['escape_tex'] = _escape_tex | |||
|
171 | self.env.filters['highlight'] = highlight2latex | |||
|
172 | else: | |||
|
173 | self.env.filters['highlight'] = highlight | |||
|
174 | ||||
|
175 | #Load user filters. Overwrite existing filters if need be. | |||
|
176 | for key, user_filter in jinja_filters.iteritems(): | |||
|
177 | if isinstance(user_filter, MetaHasTraits): | |||
|
178 | self.env.filters[key] = user_filter(config=config) | |||
|
179 | else: | |||
|
180 | self.env.filters[key] = user_filter | |||
|
181 | ||||
|
182 | #Load the template file. | |||
|
183 | self.template = self.env.get_template(self.template_file+self.ext) | |||
|
184 | ||||
|
185 | ||||
|
186 | def export(self, nb): | |||
|
187 | """Export notebook object | |||
|
188 | ||||
|
189 | nb: Notebook object to export. | |||
|
190 | ||||
|
191 | Returns both the converted ipynb file and a dict containing the | |||
|
192 | resources created along the way via the transformers and Jinja2 | |||
|
193 | processing. | |||
|
194 | """ | |||
|
195 | ||||
|
196 | nb, resources = self._preprocess(nb) | |||
|
197 | return self.template.render(nb=nb, resources=resources), resources | |||
|
198 | ||||
|
199 | ||||
|
200 | def from_filename(self, filename): | |||
|
201 | """Read and export a notebook from a filename | |||
|
202 | ||||
|
203 | filename: Filename of the notebook file to export. | |||
|
204 | ||||
|
205 | Returns both the converted ipynb file and a dict containing the | |||
|
206 | resources created along the way via the transformers and Jinja2 | |||
|
207 | processing. | |||
|
208 | """ | |||
|
209 | with io.open(filename) as f: | |||
|
210 | return self.export(nbformat.read(f, 'json')) | |||
|
211 | ||||
|
212 | ||||
|
213 | def from_file(self, file_stream): | |||
|
214 | """Read and export a notebook from a filename | |||
|
215 | ||||
|
216 | file_stream: File handle of file that contains notebook data. | |||
|
217 | ||||
|
218 | Returns both the converted ipynb file and a dict containing the | |||
|
219 | resources created along the way via the transformers and Jinja2 | |||
|
220 | processing. | |||
|
221 | """ | |||
|
222 | ||||
|
223 | return self.export(nbformat.read(file_stream, 'json')) | |||
|
224 | ||||
|
225 | ||||
|
226 | def _preprocess(self, nb): | |||
|
227 | """ Preprocess the notebook using the transformers specific | |||
|
228 | for the current export format. | |||
|
229 | ||||
|
230 | nb: Notebook to preprocess | |||
|
231 | """ | |||
|
232 | ||||
|
233 | #Dict of 'resources' that can be filled by the preprocessors. | |||
|
234 | resources = {} | |||
|
235 | ||||
|
236 | #Run each transformer on the notebook. Carry the output along | |||
|
237 | #to each transformer | |||
|
238 | for transformer in self.preprocessors: | |||
|
239 | nb, resources = transformer(nb, resources) | |||
|
240 | return nb, resources | |||
|
241 | ||||
|
242 | ||||
|
243 | def _get_default_options(self, export_format): | |||
|
244 | """ Load the default options for built in formats. | |||
|
245 | ||||
|
246 | export_format: Format being exported to. | |||
|
247 | """ | |||
|
248 | ||||
|
249 | c = get_config() | |||
|
250 | ||||
|
251 | #Set default data extraction priorities. | |||
|
252 | c.GlobalConfigurable.display_data_priority =['svg', 'png', 'latex', 'jpg', 'jpeg','text'] | |||
|
253 | c.ExtractFigureTransformer.display_data_priority=['svg', 'png', 'latex', 'jpg', 'jpeg','text'] | |||
|
254 | c.ConverterTemplate.display_data_priority= ['svg', 'png', 'latex', 'jpg', 'jpeg','text'] | |||
|
255 | ||||
|
256 | #For most (or all cases), the template file name matches the format name. | |||
|
257 | c.ConverterTemplate.template_file = export_format | |||
|
258 | ||||
|
259 | if export_format == "basichtml" or "fullhtml" or "reveal": | |||
|
260 | c.CSSHtmlHeaderTransformer.enabled=True | |||
|
261 | if export_format == 'reveal' | |||
|
262 | c.NbconvertApp.fileext='reveal.html' | |||
|
263 | else: | |||
|
264 | c.NbconvertApp.fileext='html' | |||
|
265 | ||||
|
266 | elif export_format == "latex_sphinx_howto" or export_format == "latex_sphinx_manual": | |||
|
267 | ||||
|
268 | #Turn on latex environment | |||
|
269 | c.ConverterTemplate.tex_environement=True | |||
|
270 | ||||
|
271 | #Standard latex extension | |||
|
272 | c.NbconvertApp.fileext='tex' | |||
|
273 | ||||
|
274 | #Prioritize latex extraction for latex exports. | |||
|
275 | c.GlobalConfigurable.display_data_priority =['latex', 'svg', 'png', 'jpg', 'jpeg' , 'text'] | |||
|
276 | c.ExtractFigureTransformer.display_data_priority=['latex', 'svg', 'png', 'jpg', 'jpeg'] | |||
|
277 | c.ExtractFigureTransformer.extra_ext_map={'svg':'pdf'} | |||
|
278 | c.ExtractFigureTransformer.enabled=True | |||
|
279 | ||||
|
280 | # Enable latex transformers (make markdown2latex work with math $.) | |||
|
281 | c.LatexTransformer.enabled=True | |||
|
282 | c.SphinxTransformer.enabled = True | |||
|
283 | ||||
|
284 | elif export_format == 'markdown': | |||
|
285 | c.NbconvertApp.fileext='md' | |||
|
286 | c.ExtractFigureTransformer.enabled=True | |||
|
287 | ||||
|
288 | elif export_format == 'python': | |||
|
289 | c.NbconvertApp.fileext='py' | |||
|
290 | ||||
|
291 | ||||
|
292 | elif export_format == 'rst': | |||
|
293 | c.NbconvertApp.fileext='rst' | |||
|
294 | c.ExtractFigureTransformer.enabled=True | |||
|
295 | return c No newline at end of file |
1 | NO CONTENT: file renamed from api/exporter.py to api/base.py |
|
NO CONTENT: file renamed from api/exporter.py to api/base.py |
1 | NO CONTENT: file renamed from api/exceptions.py to utils/exceptions.py |
|
NO CONTENT: file renamed from api/exceptions.py to utils/exceptions.py |
General Comments 0
You need to be logged in to leave comments.
Login now