Show More
@@ -11,6 +11,7 b' pretty.' | |||
|
11 | 11 | """ |
|
12 | 12 | from __future__ import print_function |
|
13 | 13 | |
|
14 | import codecs | |
|
14 | 15 | import os |
|
15 | 16 | import pprint |
|
16 | 17 | import re |
@@ -79,16 +80,22 b' class ConversionException(Exception):' | |||
|
79 | 80 | |
|
80 | 81 | class Converter(object): |
|
81 | 82 | default_encoding = 'utf-8' |
|
83 | extension = str() | |
|
82 | 84 | figures_counter = 0 |
|
85 | infile = str() | |
|
86 | infile_dir = str() | |
|
87 | infile_root = str() | |
|
88 | files_dir = str() | |
|
83 | 89 | |
|
84 | 90 | def __init__(self, infile): |
|
85 | 91 | self.infile = infile |
|
86 |
self.dir |
|
|
87 | ||
|
88 | @property | |
|
89 | def extension(self): | |
|
90 | raise ConversionException("""extension must be defined in Converter | |
|
91 | subclass""") | |
|
92 | self.infile_dir = os.path.dirname(infile) | |
|
93 | infile_root = os.path.splitext(infile)[0] | |
|
94 | files_dir = infile_root + '_files' | |
|
95 | if not os.path.isdir(files_dir): | |
|
96 | os.mkdir(files_dir) | |
|
97 | self.infile_root = infile_root | |
|
98 | self.files_dir = files_dir | |
|
92 | 99 | |
|
93 | 100 | def dispatch(self, cell_type): |
|
94 | 101 | """return cell_type dependent render method, for example render_code |
@@ -133,14 +140,27 b' class Converter(object):' | |||
|
133 | 140 | def optional_footer(self): |
|
134 | 141 | return [] |
|
135 | 142 | |
|
136 |
def _new_figure(self, data, f |
|
|
137 | basename = self.infile.replace('.ipynb', '') | |
|
138 | figname = '%s_fig_%02i.%s' % (basename, self.figures_counter, format) | |
|
143 | def _new_figure(self, data, fmt): | |
|
144 | """Create a new figure file in the given 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 | 150 | self.figures_counter += 1 |
|
140 |
fullname = os.path.join(self.dir |
|
|
141 | with open(fullname, 'w') as f: | |
|
142 | f.write(data.decode('base64')) | |
|
143 | return figname | |
|
151 | fullname = os.path.join(self.files_dir, figname) | |
|
152 | ||
|
153 | # Binary files are base64-encoded, SVG is already XML | |
|
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 | 165 | def render_heading(self, cell): |
|
146 | 166 | """convert a heading cell |
@@ -173,12 +193,30 b' class Converter(object):' | |||
|
173 | 193 | Returns list.""" |
|
174 | 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 | 202 | def render_display_data(self, output): |
|
177 | 203 | """convert display data from the output of a code cell |
|
178 | 204 | |
|
179 | 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 | 221 | def render_stream(self, cell): |
|
184 | 222 | """convert stream part of a code cell |
@@ -244,15 +282,8 b' class ConverterRST(Converter):' | |||
|
244 | 282 | return lines |
|
245 | 283 | |
|
246 | 284 | @DocInherit |
|
247 | def render_display_data(self, output): | |
|
248 | lines = [] | |
|
249 | ||
|
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 | |
|
285 | def _img_lines(self, img_file): | |
|
286 | return ['.. image:: %s' % figfile, ''] | |
|
256 | 287 | |
|
257 | 288 | @DocInherit |
|
258 | 289 | def render_stream(self, output): |
@@ -336,20 +367,8 b' class ConverterQuickHTML(Converter):' | |||
|
336 | 367 | return lines |
|
337 | 368 | |
|
338 | 369 | @DocInherit |
|
339 | def render_display_data(self, output): | |
|
340 | lines = [] | |
|
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 | |
|
370 | def _img_lines(self, img_file): | |
|
371 | return ['<img src="%s">' % img_file, ''] | |
|
353 | 372 | |
|
354 | 373 | @DocInherit |
|
355 | 374 | def render_stream(self, output): |
@@ -362,7 +381,31 b' class ConverterQuickHTML(Converter):' | |||
|
362 | 381 | |
|
363 | 382 | |
|
364 | 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 | 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 | 410 | def env(self, environment, lines): |
|
368 | 411 | """Return list of environment lines for input lines |
@@ -383,13 +426,7 b' class ConverterLaTeX(Converter):' | |||
|
383 | 426 | |
|
384 | 427 | @DocInherit |
|
385 | 428 | def render_heading(self, cell): |
|
386 | heading_marker = {1: r'\section', | |
|
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] | |
|
429 | marker = self.heading_marker[cell.level] | |
|
393 | 430 | return ['%s{%s}\n\n' % (marker, cell.source) ] |
|
394 | 431 | |
|
395 | 432 | @DocInherit |
@@ -416,17 +453,18 b' class ConverterLaTeX(Converter):' | |||
|
416 | 453 | |
|
417 | 454 | return lines |
|
418 | 455 | |
|
419 | @DocInherit | |
|
420 | def render_display_data(self, output): | |
|
421 | lines = [] | |
|
422 | 456 | |
|
423 | if 'png' in output: | |
|
424 | figfile = self._new_figure(output.png, 'png') | |
|
425 | ||
|
426 | lines.extend(self.env('center', | |
|
427 | [r'\includegraphics[width=3in]{%s}' % figfile, | |
|
428 | r'\par'])) | |
|
429 | return lines | |
|
457 | @DocInherit | |
|
458 | def _img_lines(self, img_file): | |
|
459 | return self.env('center', | |
|
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 | 469 | @DocInherit |
|
432 | 470 | def render_stream(self, output): |
General Comments 0
You need to be logged in to leave comments.
Login now