##// END OF EJS Templates
Improve SVG support, other small fixes.
Fernando Perez -
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.dirpath = os.path.dirname(infile)
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, format):
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.dirpath, figname)
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