##// END OF EJS Templates
Improve SVG support, other small fixes.
Fernando Perez -
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
85 infile = str()
86 infile_dir = str()
87 infile_root = str()
88 files_dir = str()
83
89
84 def __init__(self, infile):
90 def __init__(self, infile):
85 self.infile = infile
91 self.infile = infile
86 self.dirpath = os.path.dirname(infile)
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, format):
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.dirpath, figname)
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,15 +282,8 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
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
287
257 @DocInherit
288 @DocInherit
258 def render_stream(self, output):
289 def render_stream(self, output):
@@ -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
456
423 if 'png' in output:
457 @DocInherit
424 figfile = self._new_figure(output.png, 'png')
458 def _img_lines(self, img_file):
425
459 return self.env('center',
426 lines.extend(self.env('center',
460 [r'\includegraphics[width=3in]{%s}' % img_file, r'\par'])
427 [r'\includegraphics[width=3in]{%s}' % figfile,
461
428 r'\par']))
462 def _svg_lines(self, img_file):
429 return lines
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