Show More
@@ -11,6 +11,7 b' pretty.' | |||||
11 | """ |
|
11 | """ | |
12 | from __future__ import print_function |
|
12 | from __future__ import print_function | |
13 |
|
13 | |||
|
14 | import codecs | |||
14 | import os |
|
15 | import os | |
15 | import pprint |
|
16 | import pprint | |
16 | import re |
|
17 | import re | |
@@ -79,16 +80,22 b' class ConversionException(Exception):' | |||||
79 |
|
80 | |||
80 | class Converter(object): |
|
81 | class Converter(object): | |
81 | default_encoding = 'utf-8' |
|
82 | default_encoding = 'utf-8' | |
|
83 | extension = str() | |||
82 | figures_counter = 0 |
|
84 | figures_counter = 0 | |
83 |
|
85 | infile = str() | ||
|
86 | infile_dir = str() | |||
|
87 | infile_root = str() | |||
|
88 | files_dir = str() | |||
|
89 | ||||
84 | def __init__(self, infile): |
|
90 | def __init__(self, infile): | |
85 | self.infile = infile |
|
91 | self.infile = infile | |
86 |
self.dir |
|
92 | self.infile_dir = os.path.dirname(infile) | |
87 |
|
93 | infile_root = os.path.splitext(infile)[0] | ||
88 | @property |
|
94 | files_dir = infile_root + '_files' | |
89 | def extension(self): |
|
95 | if not os.path.isdir(files_dir): | |
90 | raise ConversionException("""extension must be defined in Converter |
|
96 | os.mkdir(files_dir) | |
91 | subclass""") |
|
97 | self.infile_root = infile_root | |
|
98 | self.files_dir = files_dir | |||
92 |
|
99 | |||
93 | def dispatch(self, cell_type): |
|
100 | def dispatch(self, cell_type): | |
94 | """return cell_type dependent render method, for example render_code |
|
101 | """return cell_type dependent render method, for example render_code | |
@@ -133,14 +140,27 b' class Converter(object):' | |||||
133 | def optional_footer(self): |
|
140 | def optional_footer(self): | |
134 | return [] |
|
141 | return [] | |
135 |
|
142 | |||
136 |
def _new_figure(self, data, f |
|
143 | def _new_figure(self, data, fmt): | |
137 | basename = self.infile.replace('.ipynb', '') |
|
144 | """Create a new figure file in the given format. | |
138 | figname = '%s_fig_%02i.%s' % (basename, self.figures_counter, format) |
|
145 | ||
|
146 | Returns a path relative to the input file. | |||
|
147 | """ | |||
|
148 | figname = '%s_fig_%02i.%s' % (self.infile_root, | |||
|
149 | self.figures_counter, fmt) | |||
139 | self.figures_counter += 1 |
|
150 | self.figures_counter += 1 | |
140 |
fullname = os.path.join(self.dir |
|
151 | fullname = os.path.join(self.files_dir, figname) | |
141 | with open(fullname, 'w') as f: |
|
152 | ||
142 | f.write(data.decode('base64')) |
|
153 | # Binary files are base64-encoded, SVG is already XML | |
143 | return figname |
|
154 | if fmt in ('png', 'jpg', 'pdf'): | |
|
155 | data = data.decode('base64') | |||
|
156 | fopen = lambda fname: open(fname, 'wb') | |||
|
157 | else: | |||
|
158 | fopen = lambda fname: codecs.open(fname, 'wb', self.default_encoding) | |||
|
159 | ||||
|
160 | with fopen(fullname) as f: | |||
|
161 | f.write(data) | |||
|
162 | ||||
|
163 | return fullname | |||
144 |
|
164 | |||
145 | def render_heading(self, cell): |
|
165 | def render_heading(self, cell): | |
146 | """convert a heading cell |
|
166 | """convert a heading cell | |
@@ -173,12 +193,30 b' class Converter(object):' | |||||
173 | Returns list.""" |
|
193 | Returns list.""" | |
174 | raise NotImplementedError |
|
194 | raise NotImplementedError | |
175 |
|
195 | |||
|
196 | def _img_lines(self, img_file): | |||
|
197 | """Return list of lines to include an image file.""" | |||
|
198 | # Note: subclasses may choose to implement format-specific _FMT_lines | |||
|
199 | # methods if they so choose (FMT in {png, svg, jpg, pdf}). | |||
|
200 | raise NotImplementedError | |||
|
201 | ||||
176 | def render_display_data(self, output): |
|
202 | def render_display_data(self, output): | |
177 | """convert display data from the output of a code cell |
|
203 | """convert display data from the output of a code cell | |
178 |
|
204 | |||
179 | Returns list. |
|
205 | Returns list. | |
180 | """ |
|
206 | """ | |
181 | raise NotImplementedError |
|
207 | lines = [] | |
|
208 | ||||
|
209 | for fmt in ['png', 'svg', 'jpg', 'pdf']: | |||
|
210 | if fmt in output: | |||
|
211 | img_file = self._new_figure(output[fmt], fmt) | |||
|
212 | # Subclasses can have format-specific render functions (e.g., | |||
|
213 | # latex has to auto-convert all SVG to PDF first). | |||
|
214 | lines_fun = getattr(self, '_%s_lines' % fmt, None) | |||
|
215 | if not lines_fun: | |||
|
216 | lines_fun = self._img_lines | |||
|
217 | lines.extend(lines_fun(img_file)) | |||
|
218 | ||||
|
219 | return lines | |||
182 |
|
220 | |||
183 | def render_stream(self, cell): |
|
221 | def render_stream(self, cell): | |
184 | """convert stream part of a code cell |
|
222 | """convert stream part of a code cell | |
@@ -244,16 +282,9 b' class ConverterRST(Converter):' | |||||
244 | return lines |
|
282 | return lines | |
245 |
|
283 | |||
246 | @DocInherit |
|
284 | @DocInherit | |
247 | def render_display_data(self, output): |
|
285 | def _img_lines(self, img_file): | |
248 | lines = [] |
|
286 | return ['.. image:: %s' % figfile, ''] | |
249 |
|
287 | |||
250 | if 'png' in output: |
|
|||
251 | figfile = self._new_figure(output.png, 'png') |
|
|||
252 | lines.append('.. image:: %s' % figfile) |
|
|||
253 | lines.append('') |
|
|||
254 |
|
||||
255 | return lines |
|
|||
256 |
|
||||
257 | @DocInherit |
|
288 | @DocInherit | |
258 | def render_stream(self, output): |
|
289 | def render_stream(self, output): | |
259 | lines = [] |
|
290 | lines = [] | |
@@ -336,20 +367,8 b' class ConverterQuickHTML(Converter):' | |||||
336 | return lines |
|
367 | return lines | |
337 |
|
368 | |||
338 | @DocInherit |
|
369 | @DocInherit | |
339 | def render_display_data(self, output): |
|
370 | def _img_lines(self, img_file): | |
340 | lines = [] |
|
371 | return ['<img src="%s">' % img_file, ''] | |
341 |
|
||||
342 | if 'png' in output: |
|
|||
343 | infile = 'nb_figure_%s.png' % self.figures_counter |
|
|||
344 | fullname = os.path.join(self.dirpath, infile) |
|
|||
345 | with open(fullname, 'w') as f: |
|
|||
346 | f.write(output.png.decode('base64')) |
|
|||
347 |
|
||||
348 | self.figures_counter += 1 |
|
|||
349 | lines.append('<img src="%s">' % infile) |
|
|||
350 | lines.append('') |
|
|||
351 |
|
||||
352 | return lines |
|
|||
353 |
|
372 | |||
354 | @DocInherit |
|
373 | @DocInherit | |
355 | def render_stream(self, output): |
|
374 | def render_stream(self, output): | |
@@ -362,7 +381,31 b' class ConverterQuickHTML(Converter):' | |||||
362 |
|
381 | |||
363 |
|
382 | |||
364 | class ConverterLaTeX(Converter): |
|
383 | class ConverterLaTeX(Converter): | |
|
384 | """Converts a notebook to a .tex file suitable for pdflatex. | |||
|
385 | ||||
|
386 | Note: this converter *needs*: | |||
|
387 | ||||
|
388 | - `pandoc`: for all conversion of markdown cells. If your notebook only | |||
|
389 | has Raw cells, pandoc will not be needed. | |||
|
390 | ||||
|
391 | - `inkscape`: if your notebook has SVG figures. These need to be | |||
|
392 | converted to PDF before inclusion in the TeX file, as LaTeX doesn't | |||
|
393 | understand SVG natively. | |||
|
394 | ||||
|
395 | You will in general obtain much better final PDF results if you configure | |||
|
396 | the matplotlib backend to create SVG output with | |||
|
397 | ||||
|
398 | %config InlineBackend.figure_format = 'svg' | |||
|
399 | ||||
|
400 | (or set the equivalent flag at startup or in your configuration profile). | |||
|
401 | """ | |||
365 | extension = 'tex' |
|
402 | extension = 'tex' | |
|
403 | heading_marker = {1: r'\section', | |||
|
404 | 2: r'\subsection', | |||
|
405 | 3: r'\subsubsection', | |||
|
406 | 4: r'\paragraph', | |||
|
407 | 5: r'\subparagraph', | |||
|
408 | 6: r'\subparagraph'} | |||
366 |
|
409 | |||
367 | def env(self, environment, lines): |
|
410 | def env(self, environment, lines): | |
368 | """Return list of environment lines for input lines |
|
411 | """Return list of environment lines for input lines | |
@@ -383,13 +426,7 b' class ConverterLaTeX(Converter):' | |||||
383 |
|
426 | |||
384 | @DocInherit |
|
427 | @DocInherit | |
385 | def render_heading(self, cell): |
|
428 | def render_heading(self, cell): | |
386 | heading_marker = {1: r'\section', |
|
429 | marker = self.heading_marker[cell.level] | |
387 | 2: r'\subsection', |
|
|||
388 | 3: r'\subsubsection', |
|
|||
389 | 4: r'\paragraph', |
|
|||
390 | 5: r'\subparagraph', |
|
|||
391 | 6: r'\subparagraph'} |
|
|||
392 | marker = heading_marker[cell.level] |
|
|||
393 | return ['%s{%s}\n\n' % (marker, cell.source) ] |
|
430 | return ['%s{%s}\n\n' % (marker, cell.source) ] | |
394 |
|
431 | |||
395 | @DocInherit |
|
432 | @DocInherit | |
@@ -416,17 +453,18 b' class ConverterLaTeX(Converter):' | |||||
416 |
|
453 | |||
417 | return lines |
|
454 | return lines | |
418 |
|
455 | |||
419 | @DocInherit |
|
|||
420 | def render_display_data(self, output): |
|
|||
421 | lines = [] |
|
|||
422 |
|
||||
423 | if 'png' in output: |
|
|||
424 | figfile = self._new_figure(output.png, 'png') |
|
|||
425 |
|
456 | |||
426 | lines.extend(self.env('center', |
|
457 | @DocInherit | |
427 | [r'\includegraphics[width=3in]{%s}' % figfile, |
|
458 | def _img_lines(self, img_file): | |
428 | r'\par'])) |
|
459 | return self.env('center', | |
429 | return lines |
|
460 | [r'\includegraphics[width=3in]{%s}' % img_file, r'\par']) | |
|
461 | ||||
|
462 | def _svg_lines(self, img_file): | |||
|
463 | base_file = os.path.splitext(img_file)[0] | |||
|
464 | pdf_file = base_file + '.pdf' | |||
|
465 | subprocess.check_call(['inkscape', '--export-pdf=%s' % pdf_file, | |||
|
466 | img_file]) | |||
|
467 | return self._img_lines(pdf_file) | |||
430 |
|
468 | |||
431 | @DocInherit |
|
469 | @DocInherit | |
432 | def render_stream(self, output): |
|
470 | def render_stream(self, output): |
General Comments 0
You need to be logged in to leave comments.
Login now