##// END OF EJS Templates
Added an option to disable syntax highlighting in code blocks. Simply add the -p or --plain_output tag to the command. This is a fix for issue #21
Ivan Djokic -
Show More
@@ -1,365 +1,366 b''
1 from __future__ import print_function, absolute_import
1 from __future__ import print_function, absolute_import
2 from converters.utils import remove_fake_files_url
2 from converters.utils import remove_fake_files_url
3
3
4 # Stdlib
4 # Stdlib
5 import codecs
5 import codecs
6 import io
6 import io
7 import logging
7 import logging
8 import os
8 import os
9 import pprint
9 import pprint
10 import re
10 import re
11 from types import FunctionType
11 from types import FunctionType
12
12
13 # From IPython
13 # From IPython
14 from IPython.nbformat import current as nbformat
14 from IPython.nbformat import current as nbformat
15
15
16 # local
16 # local
17
17
18 def clean_filename(filename):
18 def clean_filename(filename):
19 """
19 """
20 Remove non-alphanumeric characters from filenames.
20 Remove non-alphanumeric characters from filenames.
21
21
22 Parameters
22 Parameters
23 ----------
23 ----------
24 filename : str
24 filename : str
25 The filename to be sanitized.
25 The filename to be sanitized.
26
26
27 Returns
27 Returns
28 -------
28 -------
29 clean : str
29 clean : str
30 A sanitized filename that contains only alphanumeric
30 A sanitized filename that contains only alphanumeric
31 characters and underscores.
31 characters and underscores.
32 """
32 """
33 filename = re.sub(r'[^a-zA-Z0-9_]', '_', filename)
33 filename = re.sub(r'[^a-zA-Z0-9_]', '_', filename)
34 return filename
34 return filename
35
35
36 #-----------------------------------------------------------------------------
36 #-----------------------------------------------------------------------------
37 # Class declarations
37 # Class declarations
38 #-----------------------------------------------------------------------------
38 #-----------------------------------------------------------------------------
39
39
40
40
41 class ConversionException(Exception):
41 class ConversionException(Exception):
42 pass
42 pass
43
43
44
44
45 class DocStringInheritor(type):
45 class DocStringInheritor(type):
46 """
46 """
47 This metaclass will walk the list of bases until the desired
47 This metaclass will walk the list of bases until the desired
48 superclass method is found AND if that method has a docstring and only
48 superclass method is found AND if that method has a docstring and only
49 THEN does it attach the superdocstring to the derived class method.
49 THEN does it attach the superdocstring to the derived class method.
50
50
51 Please use carefully, I just did the metaclass thing by following
51 Please use carefully, I just did the metaclass thing by following
52 Michael Foord's Metaclass tutorial
52 Michael Foord's Metaclass tutorial
53 (http://www.voidspace.org.uk/python/articles/metaclasses.shtml), I may
53 (http://www.voidspace.org.uk/python/articles/metaclasses.shtml), I may
54 have missed a step or two.
54 have missed a step or two.
55
55
56 source:
56 source:
57 http://groups.google.com/group/comp.lang.python/msg/26f7b4fcb4d66c95
57 http://groups.google.com/group/comp.lang.python/msg/26f7b4fcb4d66c95
58 by Paul McGuire
58 by Paul McGuire
59 """
59 """
60 def __new__(meta, classname, bases, classDict):
60 def __new__(meta, classname, bases, classDict):
61 newClassDict = {}
61 newClassDict = {}
62 for attributeName, attribute in classDict.items():
62 for attributeName, attribute in classDict.items():
63 if type(attribute) == FunctionType:
63 if type(attribute) == FunctionType:
64 # look through bases for matching function by name
64 # look through bases for matching function by name
65 for baseclass in bases:
65 for baseclass in bases:
66 if hasattr(baseclass, attributeName):
66 if hasattr(baseclass, attributeName):
67 basefn = getattr(baseclass, attributeName)
67 basefn = getattr(baseclass, attributeName)
68 if basefn.__doc__:
68 if basefn.__doc__:
69 attribute.__doc__ = basefn.__doc__
69 attribute.__doc__ = basefn.__doc__
70 break
70 break
71 newClassDict[attributeName] = attribute
71 newClassDict[attributeName] = attribute
72 return type.__new__(meta, classname, bases, newClassDict)
72 return type.__new__(meta, classname, bases, newClassDict)
73
73
74
74
75 class Converter(object):
75 class Converter(object):
76 __metaclass__ = DocStringInheritor
76 __metaclass__ = DocStringInheritor
77 default_encoding = 'utf-8'
77 default_encoding = 'utf-8'
78 extension = str()
78 extension = str()
79 figures_counter = 0
79 figures_counter = 0
80 infile = str()
80 infile = str()
81 infile_dir = str()
81 infile_dir = str()
82 infile_root = str()
82 infile_root = str()
83 files_dir = str()
83 files_dir = str()
84 with_preamble = True
84 with_preamble = True
85 user_preamble = None
85 user_preamble = None
86 output = unicode()
86 output = unicode()
87 raw_as_verbatim = False
87 raw_as_verbatim = False
88 blank_symbol = " "
88 blank_symbol = " "
89 # Which display data format is best? Subclasses can override if
89 # Which display data format is best? Subclasses can override if
90 # they have specific requirements.
90 # they have specific requirements.
91 display_data_priority = ['pdf', 'svg', 'png', 'jpg', 'text']
91 display_data_priority = ['pdf', 'svg', 'png', 'jpg', 'text']
92
92
93 def __init__(self, infile):
93 def __init__(self, infile, highlight):
94 self.infile = infile
94 self.infile = infile
95 self.highlight = highlight
95 self.infile_dir, infile_root = os.path.split(infile)
96 self.infile_dir, infile_root = os.path.split(infile)
96 infile_root = os.path.splitext(infile_root)[0]
97 infile_root = os.path.splitext(infile_root)[0]
97 self.clean_name = clean_filename(infile_root)
98 self.clean_name = clean_filename(infile_root)
98 files_dir = os.path.join(self.infile_dir, self.clean_name + '_files')
99 files_dir = os.path.join(self.infile_dir, self.clean_name + '_files')
99 if not os.path.isdir(files_dir):
100 if not os.path.isdir(files_dir):
100 os.mkdir(files_dir)
101 os.mkdir(files_dir)
101 self.infile_root = infile_root
102 self.infile_root = infile_root
102 self.files_dir = files_dir
103 self.files_dir = files_dir
103 self.outbase = os.path.join(self.infile_dir, infile_root)
104 self.outbase = os.path.join(self.infile_dir, infile_root)
104
105
105 def __del__(self):
106 def __del__(self):
106 if os.path.isdir(self.files_dir) and not os.listdir(self.files_dir):
107 if os.path.isdir(self.files_dir) and not os.listdir(self.files_dir):
107 os.rmdir(self.files_dir)
108 os.rmdir(self.files_dir)
108
109
109 def _get_prompt_number(self, cell):
110 def _get_prompt_number(self, cell):
110 return cell.prompt_number if hasattr(cell, 'prompt_number') \
111 return cell.prompt_number if hasattr(cell, 'prompt_number') \
111 else self.blank_symbol
112 else self.blank_symbol
112
113
113 def dispatch(self, cell_type):
114 def dispatch(self, cell_type):
114 """return cell_type dependent render method, for example render_code
115 """return cell_type dependent render method, for example render_code
115 """
116 """
116 return getattr(self, 'render_' + cell_type, self.render_unknown)
117 return getattr(self, 'render_' + cell_type, self.render_unknown)
117
118
118 def dispatch_display_format(self, format):
119 def dispatch_display_format(self, format):
119 """
120 """
120 return output_type dependent render method, for example
121 return output_type dependent render method, for example
121 render_output_text
122 render_output_text
122 """
123 """
123 return getattr(self, 'render_display_format_' + format,
124 return getattr(self, 'render_display_format_' + format,
124 self.render_unknown_display)
125 self.render_unknown_display)
125
126
126 def convert(self, cell_separator='\n'):
127 def convert(self, cell_separator='\n'):
127 """
128 """
128 Generic method to converts notebook to a string representation.
129 Generic method to converts notebook to a string representation.
129
130
130 This is accomplished by dispatching on the cell_type, so subclasses of
131 This is accomplished by dispatching on the cell_type, so subclasses of
131 Convereter class do not need to re-implement this method, but just
132 Convereter class do not need to re-implement this method, but just
132 need implementation for the methods that will be dispatched.
133 need implementation for the methods that will be dispatched.
133
134
134 Parameters
135 Parameters
135 ----------
136 ----------
136 cell_separator : string
137 cell_separator : string
137 Character or string to join cells with. Default is "\n"
138 Character or string to join cells with. Default is "\n"
138
139
139 Returns
140 Returns
140 -------
141 -------
141 out : string
142 out : string
142 """
143 """
143 lines = []
144 lines = []
144 lines.extend(self.optional_header())
145 lines.extend(self.optional_header())
145 lines.extend(self.main_body(cell_separator))
146 lines.extend(self.main_body(cell_separator))
146 lines.extend(self.optional_footer())
147 lines.extend(self.optional_footer())
147 return u'\n'.join(lines)
148 return u'\n'.join(lines)
148
149
149 def main_body(self, cell_separator='\n'):
150 def main_body(self, cell_separator='\n'):
150 converted_cells = []
151 converted_cells = []
151 for worksheet in self.nb.worksheets:
152 for worksheet in self.nb.worksheets:
152 for cell in worksheet.cells:
153 for cell in worksheet.cells:
153 #print(cell.cell_type) # dbg
154 #print(cell.cell_type) # dbg
154 conv_fn = self.dispatch(cell.cell_type)
155 conv_fn = self.dispatch(cell.cell_type)
155 if cell.cell_type in ('markdown', 'raw'):
156 if cell.cell_type in ('markdown', 'raw'):
156 remove_fake_files_url(cell)
157 remove_fake_files_url(cell)
157 converted_cells.append('\n'.join(conv_fn(cell)))
158 converted_cells.append('\n'.join(conv_fn(cell)))
158 cell_lines = cell_separator.join(converted_cells).split('\n')
159 cell_lines = cell_separator.join(converted_cells).split('\n')
159 return cell_lines
160 return cell_lines
160
161
161 def render(self):
162 def render(self):
162 "read, convert, and save self.infile"
163 "read, convert, and save self.infile"
163 if not hasattr(self, 'nb'):
164 if not hasattr(self, 'nb'):
164 self.read()
165 self.read()
165 self.output = self.convert()
166 self.output = self.convert()
166 assert(type(self.output) == unicode)
167 assert(type(self.output) == unicode)
167 return self.save()
168 return self.save()
168
169
169 def read(self):
170 def read(self):
170 "read and parse notebook into NotebookNode called self.nb"
171 "read and parse notebook into NotebookNode called self.nb"
171 with open(self.infile) as f:
172 with open(self.infile) as f:
172 self.nb = nbformat.read(f, 'json')
173 self.nb = nbformat.read(f, 'json')
173
174
174 def save(self, outfile=None, encoding=None):
175 def save(self, outfile=None, encoding=None):
175 "read and parse notebook into self.nb"
176 "read and parse notebook into self.nb"
176 if outfile is None:
177 if outfile is None:
177 outfile = self.outbase + '.' + self.extension
178 outfile = self.outbase + '.' + self.extension
178 if encoding is None:
179 if encoding is None:
179 encoding = self.default_encoding
180 encoding = self.default_encoding
180 with io.open(outfile, 'w', encoding=encoding) as f:
181 with io.open(outfile, 'w', encoding=encoding) as f:
181 f.write(self.output)
182 f.write(self.output)
182 return os.path.abspath(outfile)
183 return os.path.abspath(outfile)
183
184
184 def optional_header(self):
185 def optional_header(self):
185 """
186 """
186 Optional header to insert at the top of the converted notebook
187 Optional header to insert at the top of the converted notebook
187
188
188 Returns a list
189 Returns a list
189 """
190 """
190 return []
191 return []
191
192
192 def optional_footer(self):
193 def optional_footer(self):
193 """
194 """
194 Optional footer to insert at the end of the converted notebook
195 Optional footer to insert at the end of the converted notebook
195
196
196 Returns a list
197 Returns a list
197 """
198 """
198 return []
199 return []
199
200
200 def _new_figure(self, data, fmt):
201 def _new_figure(self, data, fmt):
201 """Create a new figure file in the given format.
202 """Create a new figure file in the given format.
202
203
203 Returns a path relative to the input file.
204 Returns a path relative to the input file.
204 """
205 """
205 figname = '%s_fig_%02i.%s' % (self.clean_name,
206 figname = '%s_fig_%02i.%s' % (self.clean_name,
206 self.figures_counter, fmt)
207 self.figures_counter, fmt)
207 self.figures_counter += 1
208 self.figures_counter += 1
208 fullname = os.path.join(self.files_dir, figname)
209 fullname = os.path.join(self.files_dir, figname)
209
210
210 # Binary files are base64-encoded, SVG is already XML
211 # Binary files are base64-encoded, SVG is already XML
211 if fmt in ('png', 'jpg', 'pdf'):
212 if fmt in ('png', 'jpg', 'pdf'):
212 data = data.decode('base64')
213 data = data.decode('base64')
213 fopen = lambda fname: open(fname, 'wb')
214 fopen = lambda fname: open(fname, 'wb')
214 else:
215 else:
215 fopen = lambda fname: codecs.open(fname, 'wb',
216 fopen = lambda fname: codecs.open(fname, 'wb',
216 self.default_encoding)
217 self.default_encoding)
217
218
218 with fopen(fullname) as f:
219 with fopen(fullname) as f:
219 f.write(data)
220 f.write(data)
220
221
221 return fullname
222 return fullname
222
223
223 def render_heading(self, cell):
224 def render_heading(self, cell):
224 """convert a heading cell
225 """convert a heading cell
225
226
226 Returns list."""
227 Returns list."""
227 raise NotImplementedError
228 raise NotImplementedError
228
229
229 def render_code(self, cell):
230 def render_code(self, cell):
230 """Convert a code cell
231 """Convert a code cell
231
232
232 Returns list."""
233 Returns list."""
233 raise NotImplementedError
234 raise NotImplementedError
234
235
235 def render_markdown(self, cell):
236 def render_markdown(self, cell):
236 """convert a markdown cell
237 """convert a markdown cell
237
238
238 Returns list."""
239 Returns list."""
239 raise NotImplementedError
240 raise NotImplementedError
240
241
241 def _img_lines(self, img_file):
242 def _img_lines(self, img_file):
242 """Return list of lines to include an image file."""
243 """Return list of lines to include an image file."""
243 # Note: subclasses may choose to implement format-specific _FMT_lines
244 # Note: subclasses may choose to implement format-specific _FMT_lines
244 # methods if they so choose (FMT in {png, svg, jpg, pdf}).
245 # methods if they so choose (FMT in {png, svg, jpg, pdf}).
245 raise NotImplementedError
246 raise NotImplementedError
246
247
247 def render_display_data(self, output):
248 def render_display_data(self, output):
248 """convert display data from the output of a code cell
249 """convert display data from the output of a code cell
249
250
250 Returns list.
251 Returns list.
251 """
252 """
252 for fmt in self.display_data_priority:
253 for fmt in self.display_data_priority:
253 if fmt in output:
254 if fmt in output:
254 break
255 break
255 else:
256 else:
256 for fmt in output:
257 for fmt in output:
257 if fmt != 'output_type':
258 if fmt != 'output_type':
258 break
259 break
259 else:
260 else:
260 raise RuntimeError('no display data')
261 raise RuntimeError('no display data')
261
262
262 # Is it an image?
263 # Is it an image?
263 if fmt in ['png', 'svg', 'jpg', 'pdf']:
264 if fmt in ['png', 'svg', 'jpg', 'pdf']:
264 img_file = self._new_figure(output[fmt], fmt)
265 img_file = self._new_figure(output[fmt], fmt)
265 # Subclasses can have format-specific render functions (e.g.,
266 # Subclasses can have format-specific render functions (e.g.,
266 # latex has to auto-convert all SVG to PDF first).
267 # latex has to auto-convert all SVG to PDF first).
267 lines_fun = getattr(self, '_%s_lines' % fmt, None)
268 lines_fun = getattr(self, '_%s_lines' % fmt, None)
268 if not lines_fun:
269 if not lines_fun:
269 lines_fun = self._img_lines
270 lines_fun = self._img_lines
270 lines = lines_fun(img_file)
271 lines = lines_fun(img_file)
271 else:
272 else:
272 lines_fun = self.dispatch_display_format(fmt)
273 lines_fun = self.dispatch_display_format(fmt)
273 lines = lines_fun(output)
274 lines = lines_fun(output)
274
275
275 return lines
276 return lines
276
277
277 def render_raw(self, cell):
278 def render_raw(self, cell):
278 """convert a cell with raw text
279 """convert a cell with raw text
279
280
280 Returns list."""
281 Returns list."""
281 raise NotImplementedError
282 raise NotImplementedError
282
283
283 def render_unknown(self, cell):
284 def render_unknown(self, cell):
284 """Render cells of unkown type
285 """Render cells of unkown type
285
286
286 Returns list."""
287 Returns list."""
287 data = pprint.pformat(cell)
288 data = pprint.pformat(cell)
288 logging.warning('Unknown cell: %s' % cell.cell_type)
289 logging.warning('Unknown cell: %s' % cell.cell_type)
289 return self._unknown_lines(data)
290 return self._unknown_lines(data)
290
291
291 def render_unknown_display(self, output, type):
292 def render_unknown_display(self, output, type):
292 """Render cells of unkown type
293 """Render cells of unkown type
293
294
294 Returns list."""
295 Returns list."""
295 data = pprint.pformat(output)
296 data = pprint.pformat(output)
296 logging.warning('Unknown output: %s' % output.output_type)
297 logging.warning('Unknown output: %s' % output.output_type)
297 return self._unknown_lines(data)
298 return self._unknown_lines(data)
298
299
299 def render_stream(self, output):
300 def render_stream(self, output):
300 """render the stream part of an output
301 """render the stream part of an output
301
302
302 Returns list.
303 Returns list.
303
304
304 Identical to render_display_format_text
305 Identical to render_display_format_text
305 """
306 """
306 return self.render_display_format_text(output)
307 return self.render_display_format_text(output)
307
308
308 def render_pyout(self, output):
309 def render_pyout(self, output):
309 """convert pyout part of a code cell
310 """convert pyout part of a code cell
310
311
311 Returns list."""
312 Returns list."""
312 raise NotImplementedError
313 raise NotImplementedError
313
314
314 def render_pyerr(self, output):
315 def render_pyerr(self, output):
315 """convert pyerr part of a code cell
316 """convert pyerr part of a code cell
316
317
317 Returns list."""
318 Returns list."""
318 raise NotImplementedError
319 raise NotImplementedError
319
320
320 def _unknown_lines(self, data):
321 def _unknown_lines(self, data):
321 """Return list of lines for an unknown cell.
322 """Return list of lines for an unknown cell.
322
323
323 Parameters
324 Parameters
324 ----------
325 ----------
325 data : str
326 data : str
326 The content of the unknown data as a single string.
327 The content of the unknown data as a single string.
327 """
328 """
328 raise NotImplementedError
329 raise NotImplementedError
329
330
330 # These are the possible format types in an output node
331 # These are the possible format types in an output node
331
332
332 def render_display_format_text(self, output):
333 def render_display_format_text(self, output):
333 """render the text part of an output
334 """render the text part of an output
334
335
335 Returns list.
336 Returns list.
336 """
337 """
337 raise NotImplementedError
338 raise NotImplementedError
338
339
339 def render_display_format_html(self, output):
340 def render_display_format_html(self, output):
340 """render the html part of an output
341 """render the html part of an output
341
342
342 Returns list.
343 Returns list.
343 """
344 """
344 raise NotImplementedError
345 raise NotImplementedError
345
346
346 def render_display_format_latex(self, output):
347 def render_display_format_latex(self, output):
347 """render the latex part of an output
348 """render the latex part of an output
348
349
349 Returns list.
350 Returns list.
350 """
351 """
351 raise NotImplementedError
352 raise NotImplementedError
352
353
353 def render_display_format_json(self, output):
354 def render_display_format_json(self, output):
354 """render the json part of an output
355 """render the json part of an output
355
356
356 Returns list.
357 Returns list.
357 """
358 """
358 raise NotImplementedError
359 raise NotImplementedError
359
360
360 def render_display_format_javascript(self, output):
361 def render_display_format_javascript(self, output):
361 """render the javascript part of an output
362 """render the javascript part of an output
362
363
363 Returns list.
364 Returns list.
364 """
365 """
365 raise NotImplementedError
366 raise NotImplementedError
@@ -1,185 +1,185 b''
1 from __future__ import absolute_import
1 from __future__ import absolute_import
2
2
3 from converters.base import Converter
3 from converters.base import Converter
4 from converters.utils import text_cell, output_container
4 from converters.utils import text_cell, output_container
5 from converters.utils import highlight, coalesce_streams, ansi2html
5 from converters.utils import highlight, coalesce_streams, ansi2html
6
6
7 from IPython.utils import path
7 from IPython.utils import path
8 from markdown import markdown
8 from markdown import markdown
9 import os
9 import os
10 import io
10 import io
11
11
12
12
13 class ConverterHTML(Converter):
13 class ConverterHTML(Converter):
14 extension = 'html'
14 extension = 'html'
15 blank_symbol = ' '
15 blank_symbol = ' '
16
16
17 def in_tag(self, tag, src, attrs=None):
17 def in_tag(self, tag, src, attrs=None):
18 """Return a list of elements bracketed by the given tag"""
18 """Return a list of elements bracketed by the given tag"""
19 attr_s = '' if attrs is None else \
19 attr_s = '' if attrs is None else \
20 ' '.join("%s=%s" % (attr, value)
20 ' '.join("%s=%s" % (attr, value)
21 for attr, value in attrs.iteritems())
21 for attr, value in attrs.iteritems())
22 return ['<%s %s>' % (tag, attr_s), src, '</%s>' % tag]
22 return ['<%s %s>' % (tag, attr_s), src, '</%s>' % tag]
23
23
24 def _ansi_colored(self, text):
24 def _ansi_colored(self, text):
25 return ['<pre>%s</pre>' % ansi2html(text)]
25 return ['<pre>%s</pre>' % ansi2html(text)]
26
26
27 def _stylesheet(self, fname):
27 def _stylesheet(self, fname):
28 with io.open(fname, encoding='utf-8') as f:
28 with io.open(fname, encoding='utf-8') as f:
29 s = f.read()
29 s = f.read()
30 return self.in_tag('style', s, dict(type='"text/css"'))
30 return self.in_tag('style', s, dict(type='"text/css"'))
31
31
32 def _out_prompt(self, output):
32 def _out_prompt(self, output):
33 if output.output_type == 'pyout':
33 if output.output_type == 'pyout':
34 content = 'Out[%s]:' % self._get_prompt_number(output)
34 content = 'Out[%s]:' % self._get_prompt_number(output)
35 else:
35 else:
36 content = ''
36 content = ''
37 return ['<div class="prompt output_prompt">%s</div>' % content]
37 return ['<div class="prompt output_prompt">%s</div>' % content]
38
38
39 def header_body(self):
39 def header_body(self):
40 """Return the body of the header as a list of strings."""
40 """Return the body of the header as a list of strings."""
41
41
42 from pygments.formatters import HtmlFormatter
42 from pygments.formatters import HtmlFormatter
43
43
44 header = []
44 header = []
45 static = os.path.join(path.get_ipython_package_dir(),
45 static = os.path.join(path.get_ipython_package_dir(),
46 'frontend', 'html', 'notebook', 'static',
46 'frontend', 'html', 'notebook', 'static',
47 )
47 )
48 here = os.path.split(os.path.realpath(__file__))[0]
48 here = os.path.split(os.path.realpath(__file__))[0]
49 css = os.path.join(static, 'css')
49 css = os.path.join(static, 'css')
50 for sheet in [
50 for sheet in [
51 # do we need jquery and prettify?
51 # do we need jquery and prettify?
52 # os.path.join(static, 'jquery', 'css', 'themes', 'base',
52 # os.path.join(static, 'jquery', 'css', 'themes', 'base',
53 # 'jquery-ui.min.css'),
53 # 'jquery-ui.min.css'),
54 # os.path.join(static, 'prettify', 'prettify.css'),
54 # os.path.join(static, 'prettify', 'prettify.css'),
55 os.path.join(css, 'boilerplate.css'),
55 os.path.join(css, 'boilerplate.css'),
56 os.path.join(css, 'fbm.css'),
56 os.path.join(css, 'fbm.css'),
57 os.path.join(css, 'notebook.css'),
57 os.path.join(css, 'notebook.css'),
58 os.path.join(css, 'renderedhtml.css'),
58 os.path.join(css, 'renderedhtml.css'),
59 # our overrides:
59 # our overrides:
60 os.path.join(here, '..', 'css', 'static_html.css'),
60 os.path.join(here, '..', 'css', 'static_html.css'),
61 ]:
61 ]:
62 header.extend(self._stylesheet(sheet))
62 header.extend(self._stylesheet(sheet))
63
63
64 # pygments css
64 # pygments css
65 pygments_css = HtmlFormatter().get_style_defs('.highlight')
65 pygments_css = HtmlFormatter().get_style_defs('.highlight')
66 header.extend(['<meta charset="UTF-8">'])
66 header.extend(['<meta charset="UTF-8">'])
67 header.extend(self.in_tag('style', pygments_css,
67 header.extend(self.in_tag('style', pygments_css,
68 dict(type='"text/css"')))
68 dict(type='"text/css"')))
69
69
70 # TODO: this should be allowed to use local mathjax:
70 # TODO: this should be allowed to use local mathjax:
71 header.extend(self.in_tag('script', '', {'type': '"text/javascript"',
71 header.extend(self.in_tag('script', '', {'type': '"text/javascript"',
72 'src': '"https://c328740.ssl.cf1.rackcdn.com/mathjax/'
72 'src': '"https://c328740.ssl.cf1.rackcdn.com/mathjax/'
73 'latest/MathJax.js?config=TeX-AMS_HTML"',
73 'latest/MathJax.js?config=TeX-AMS_HTML"',
74 }))
74 }))
75 with io.open(os.path.join(here, '..', 'js', 'initmathjax.js'),
75 with io.open(os.path.join(here, '..', 'js', 'initmathjax.js'),
76 encoding='utf-8') as f:
76 encoding='utf-8') as f:
77 header.extend(self.in_tag('script', f.read(),
77 header.extend(self.in_tag('script', f.read(),
78 {'type': '"text/javascript"'}))
78 {'type': '"text/javascript"'}))
79 return header
79 return header
80
80
81 def optional_header(self):
81 def optional_header(self):
82 return ['<html>', '<head>'] + self.header_body() + \
82 return ['<html>', '<head>'] + self.header_body() + \
83 ['</head>', '<body>']
83 ['</head>', '<body>']
84
84
85 def optional_footer(self):
85 def optional_footer(self):
86 return ['</body>', '</html>']
86 return ['</body>', '</html>']
87
87
88 @text_cell
88 @text_cell
89 def render_heading(self, cell):
89 def render_heading(self, cell):
90 marker = cell.level
90 marker = cell.level
91 return [u'<h{1}>\n {0}\n</h{1}>'.format(cell.source, marker)]
91 return [u'<h{1}>\n {0}\n</h{1}>'.format(cell.source, marker)]
92
92
93 def render_code(self, cell):
93 def render_code(self, cell):
94 if not cell.input:
94 if not cell.input:
95 return []
95 return []
96
96
97 lines = ['<div class="cell border-box-sizing code_cell vbox">']
97 lines = ['<div class="cell border-box-sizing code_cell vbox">']
98
98
99 lines.append('<div class="input hbox">')
99 lines.append('<div class="input hbox">')
100 n = self._get_prompt_number(cell)
100 n = self._get_prompt_number(cell)
101 lines.append(
101 lines.append(
102 '<div class="prompt input_prompt">In&nbsp;[%s]:</div>' % n
102 '<div class="prompt input_prompt">In&nbsp;[%s]:</div>' % n
103 )
103 )
104 lines.append('<div class="input_area box-flex1">')
104 lines.append('<div class="input_area box-flex1">')
105 lines.append(highlight(cell.input))
105 lines.append(highlight(cell.input) if self.highlight else cell.input)
106 lines.append('</div>') # input_area
106 lines.append('</div>') # input_area
107 lines.append('</div>') # input
107 lines.append('</div>') # input
108
108
109 if cell.outputs:
109 if cell.outputs:
110 lines.append('<div class="vbox output_wrapper">')
110 lines.append('<div class="vbox output_wrapper">')
111 lines.append('<div class="output vbox">')
111 lines.append('<div class="output vbox">')
112
112
113 for output in coalesce_streams(cell.outputs):
113 for output in coalesce_streams(cell.outputs):
114 conv_fn = self.dispatch(output.output_type)
114 conv_fn = self.dispatch(output.output_type)
115 lines.extend(conv_fn(output))
115 lines.extend(conv_fn(output))
116
116
117 lines.append('</div>') # output
117 lines.append('</div>') # output
118 lines.append('</div>') # output_wrapper
118 lines.append('</div>') # output_wrapper
119
119
120 lines.append('</div>') # cell
120 lines.append('</div>') # cell
121
121
122 return lines
122 return lines
123
123
124 @text_cell
124 @text_cell
125 def render_markdown(self, cell):
125 def render_markdown(self, cell):
126 return [markdown(cell.source)]
126 return [markdown(cell.source)]
127
127
128 def render_raw(self, cell):
128 def render_raw(self, cell):
129 if self.raw_as_verbatim:
129 if self.raw_as_verbatim:
130 return self.in_tag('pre', cell.source)
130 return self.in_tag('pre', cell.source)
131 else:
131 else:
132 return [cell.source]
132 return [cell.source]
133
133
134 @output_container
134 @output_container
135 def render_pyout(self, output):
135 def render_pyout(self, output):
136 for fmt in ['html', 'latex', 'png', 'jpeg', 'svg', 'text']:
136 for fmt in ['html', 'latex', 'png', 'jpeg', 'svg', 'text']:
137 if fmt in output:
137 if fmt in output:
138 conv_fn = self.dispatch_display_format(fmt)
138 conv_fn = self.dispatch_display_format(fmt)
139 return conv_fn(output)
139 return conv_fn(output)
140 return []
140 return []
141
141
142 render_display_data = render_pyout
142 render_display_data = render_pyout
143
143
144 @output_container
144 @output_container
145 def render_stream(self, output):
145 def render_stream(self, output):
146 return self._ansi_colored(output.text)
146 return self._ansi_colored(output.text)
147
147
148 @output_container
148 @output_container
149 def render_pyerr(self, output):
149 def render_pyerr(self, output):
150 # Note: a traceback is a *list* of frames.
150 # Note: a traceback is a *list* of frames.
151 # lines = []
151 # lines = []
152
152
153 # stb =
153 # stb =
154 return self._ansi_colored('\n'.join(output.traceback))
154 return self._ansi_colored('\n'.join(output.traceback))
155
155
156 def _img_lines(self, img_file):
156 def _img_lines(self, img_file):
157 return ['<img src="%s">' % img_file, '</img>']
157 return ['<img src="%s">' % img_file, '</img>']
158
158
159 def _unknown_lines(self, data):
159 def _unknown_lines(self, data):
160 return ['<h2>Warning:: Unknown cell</h2>'] + self.in_tag('pre', data)
160 return ['<h2>Warning:: Unknown cell</h2>'] + self.in_tag('pre', data)
161
161
162 def render_display_format_png(self, output):
162 def render_display_format_png(self, output):
163 return ['<img src="data:image/png;base64,%s"></img>' % output.png]
163 return ['<img src="data:image/png;base64,%s"></img>' % output.png]
164
164
165 def render_display_format_svg(self, output):
165 def render_display_format_svg(self, output):
166 return [output.svg]
166 return [output.svg]
167
167
168 def render_display_format_jpeg(self, output):
168 def render_display_format_jpeg(self, output):
169 return ['<img src="data:image/jpeg;base64,%s"></img>' % output.jpeg]
169 return ['<img src="data:image/jpeg;base64,%s"></img>' % output.jpeg]
170
170
171 def render_display_format_text(self, output):
171 def render_display_format_text(self, output):
172 return self._ansi_colored(output.text)
172 return self._ansi_colored(output.text)
173
173
174 def render_display_format_html(self, output):
174 def render_display_format_html(self, output):
175 return [output.html]
175 return [output.html]
176
176
177 def render_display_format_latex(self, output):
177 def render_display_format_latex(self, output):
178 return [output.latex]
178 return [output.latex]
179
179
180 def render_display_format_json(self, output):
180 def render_display_format_json(self, output):
181 # html ignores json
181 # html ignores json
182 return []
182 return []
183
183
184 def render_display_format_javascript(self, output):
184 def render_display_format_javascript(self, output):
185 return [output.javascript]
185 return [output.javascript]
@@ -1,85 +1,86 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 """Convert IPython notebooks to other formats, such as ReST, and HTML.
2 """Convert IPython notebooks to other formats, such as ReST, and HTML.
3
3
4 Example:
4 Example:
5 ./nbconvert.py --format rst file.ipynb
5 ./nbconvert.py --format rst file.ipynb
6
6
7 Produces 'file.rst', along with auto-generated figure files
7 Produces 'file.rst', along with auto-generated figure files
8 called nb_figure_NN.png.
8 called nb_figure_NN.png.
9 """
9 """
10 #-----------------------------------------------------------------------------
10 #-----------------------------------------------------------------------------
11 # Imports
11 # Imports
12 #-----------------------------------------------------------------------------
12 #-----------------------------------------------------------------------------
13 from __future__ import print_function
13 from __future__ import print_function
14
14
15 # From IPython
15 # From IPython
16 from IPython.external import argparse
16 from IPython.external import argparse
17
17
18 # local
18 # local
19 from converters.html import ConverterHTML
19 from converters.html import ConverterHTML
20 from converters.markdown import ConverterMarkdown
20 from converters.markdown import ConverterMarkdown
21 from converters.bloggerhtml import ConverterBloggerHTML
21 from converters.bloggerhtml import ConverterBloggerHTML
22 from converters.rst import ConverterRST
22 from converters.rst import ConverterRST
23 from converters.latex import ConverterLaTeX
23 from converters.latex import ConverterLaTeX
24 from converters.python import ConverterPy
24 from converters.python import ConverterPy
25
25
26
26
27 # When adding a new format, make sure to add it to the `converters`
27 # When adding a new format, make sure to add it to the `converters`
28 # dictionary below. This is used to create the list of known formats,
28 # dictionary below. This is used to create the list of known formats,
29 # which gets printed in case an unknown format is encounteres, as well
29 # which gets printed in case an unknown format is encounteres, as well
30 # as in the help
30 # as in the help
31
31
32 converters = {
32 converters = {
33 'rst': ConverterRST,
33 'rst': ConverterRST,
34 'markdown': ConverterMarkdown,
34 'markdown': ConverterMarkdown,
35 'html': ConverterHTML,
35 'html': ConverterHTML,
36 'blogger-html': ConverterBloggerHTML,
36 'blogger-html': ConverterBloggerHTML,
37 'latex': ConverterLaTeX,
37 'latex': ConverterLaTeX,
38 'py': ConverterPy,
38 'py': ConverterPy,
39 }
39 }
40
40
41 default_format = 'rst'
41 default_format = 'rst'
42
42
43 # Extract the list of known formats and mark the first format as the default.
43 # Extract the list of known formats and mark the first format as the default.
44 known_formats = ', '.join([key + " (default)" if key == default_format else key
44 known_formats = ', '.join([key + " (default)" if key == default_format else key
45 for key in converters])
45 for key in converters])
46
46
47
47
48 def main(infile, format='rst', preamble=None, exclude=None):
48 def main(infile, highlight, format='rst', preamble=None, exclude=None):
49 """Convert a notebook to html in one step"""
49 """Convert a notebook to html in one step"""
50 try:
50 try:
51 ConverterClass = converters[format]
51 ConverterClass = converters[format]
52 except KeyError:
52 except KeyError:
53 raise SystemExit("Unknown format '%s', " % format +
53 raise SystemExit("Unknown format '%s', " % format +
54 "known formats are: " + known_formats)
54 "known formats are: " + known_formats)
55
55
56 converter = ConverterClass(infile)
56 converter = ConverterClass(infile, highlight)
57 converter.render()
57 converter.render()
58
58
59 #-----------------------------------------------------------------------------
59 #-----------------------------------------------------------------------------
60 # Script main
60 # Script main
61 #-----------------------------------------------------------------------------
61 #-----------------------------------------------------------------------------
62
62
63 if __name__ == '__main__':
63 if __name__ == '__main__':
64 parser = argparse.ArgumentParser(description=__doc__,
64 parser = argparse.ArgumentParser(description=__doc__,
65 formatter_class=argparse.RawTextHelpFormatter)
65 formatter_class=argparse.RawTextHelpFormatter)
66 # TODO: consider passing file like object around, rather than filenames
66 # TODO: consider passing file like object around, rather than filenames
67 # would allow us to process stdin, or even http streams
67 # would allow us to process stdin, or even http streams
68 #parser.add_argument('infile', nargs='?', type=argparse.FileType('r'),
68 #parser.add_argument('infile', nargs='?', type=argparse.FileType('r'),
69 # default=sys.stdin)
69 # default=sys.stdin)
70
70
71 #Require a filename as a positional argument
71 #Require a filename as a positional argument
72 parser.add_argument('infile', nargs=1)
72 parser.add_argument('infile', nargs=1)
73 parser.add_argument('-f', '--format', default='rst',
73 parser.add_argument('-f', '--format', default='rst',
74 help='Output format. Supported formats: \n' +
74 help='Output format. Supported formats: \n' +
75 known_formats)
75 known_formats)
76 parser.add_argument('-p', '--preamble',
76 parser.add_argument('-p', '--preamble',
77 help='Path to a user-specified preamble file')
77 help='Path to a user-specified preamble file')
78 parser.add_argument('-e', '--exclude', default='',
78 parser.add_argument('-e', '--exclude', default='',
79 help='Comma-separated list of cells to exclude')
79 help='Comma-separated list of cells to exclude')
80
80 parser.add_argument('-p', '--plain_output', action='store_false',
81 help='Plain output which will contain no syntax highlighting.')
81 args = parser.parse_args()
82 args = parser.parse_args()
82 exclude_cells = [s.strip() for s in args.exclude.split(',')]
83 exclude_cells = [s.strip() for s in args.exclude.split(',')]
83
84
84 main(infile=args.infile[0], format=args.format,
85 main(infile=args.infile[0], highlight=args.plain_output,
85 preamble=args.preamble, exclude=exclude_cells)
86 format=args.format, preamble=args.preamble, exclude=exclude_cells)
General Comments 0
You need to be logged in to leave comments. Login now