##// END OF EJS Templates
New implementation for reveal option in nbconvert bypassing markdown step. The structure behind the implementaion follows the discussion in IPEP 9
damianavila -
Show More
@@ -1,125 +1,216 b''
1 from converters.markdown import ConverterMarkdown
1 from __future__ import absolute_import
2 from IPython.utils.text import indent, dedent
2
3 from converters.utils import highlight, remove_ansi
3 from converters.html import ConverterHTML
4 import io
4 from converters.utils import text_cell#, output_container
5 from converters.utils import highlight, coalesce_streams#, ansi2html
6
7 #from IPython.utils import path
8 #from IPython.utils.text import indent, dedent
9 from markdown import markdown
5 import os
10 import os
11 import io
6 import itertools
12 import itertools
7
13
8 class ConverterReveal(ConverterMarkdown):
9 """
10 Convert a notebook to a html slideshow.
11
12 It generates a static html slideshow based in markdown and reveal.js.
13 The delimiters for each slide, subslide, and fragment are retrieved
14 from the 'slideshow' metadata.
15 """
16
17 def __init__(self, infile, highlight_source=False, show_prompts=True,
18 inline_prompt=True):
19 super(ConverterReveal, self).__init__(infile)
20 self.highlight_source = highlight_source
21 self.show_prompts = show_prompts
22 self.inline_prompt = inline_prompt
23
24 def switch_meta(self, m_list):
25 "sort order m_list to [new_section, new_subsection, new_fragment]"
26 if len(m_list) > 1:
27 # do not sort when m_list = [new_subsection, new_fragment]
28 if not (len(m_list) == 2 and m_list[1] == [u'new_fragment = True']):
29 m_list[0], m_list[1] = m_list[1], m_list[0]
30 return m_list
31
32 def meta2str(self, meta):
33 "transform metadata dict (containing slides delimiters) to string "
34 try:
35 meta_tuple = meta[u'slideshow'].items()
36 except KeyError as e:
37 meta_tuple = () # if there is not slideshow metadata
38 meta_list = [[x + ' = ' + unicode(y)] for x, y in meta_tuple]
39 meta_list = self.switch_meta(meta_list)
40 return u'\n'.join(list(itertools.chain(*meta_list)))
41
14
15 class ConverterReveal(ConverterHTML):
16 #"""
17 #Convert a notebook to a html slideshow.
18
19 #It generates a static html slideshow based in markdown and reveal.js.
20 #The delimiters for each slide, subslide, and fragment are retrieved
21 #from the 'slideshow' metadata.
22 #"""
23
24 #def __init__(self):
25 #super(ConverterReveal, self).__init__()
26
27 #extension = 'html'
28 #blank_symbol = ' '
29
30 #def in_tag(self, tag, src, attrs=None):
31 #"""Return a list of elements bracketed by the given tag"""
32 #attr_s = '' if attrs is None else \
33 #' '.join("%s=%s" % (attr, value)
34 #for attr, value in attrs.iteritems())
35 #return ['<%s %s>' % (tag, attr_s), src, '</%s>' % tag]
36
37 #def _ansi_colored(self, text):
38 #return ['<pre>%s</pre>' % ansi2html(text)]
39
40 #def _stylesheet(self, fname):
41 #with io.open(fname, encoding='utf-8') as f:
42 #s = f.read()
43 #return self.in_tag('style', s, dict(type='"text/css"'))
44
45 #def _out_prompt(self, output):
46 #if output.output_type == 'pyout':
47 #content = 'Out[%s]:' % self._get_prompt_number(output)
48 #else:
49 #content = ''
50 #return ['<div class="prompt output_prompt">%s</div>' % content]
51
52 #def header_body(self):
53 #"""Return the body of the header as a list of strings."""
54
55 #from pygments.formatters import HtmlFormatter
56
57 #header = []
58 #static = os.path.join(path.get_ipython_package_dir(),
59 #'frontend', 'html', 'notebook', 'static',
60 #)
61 #here = os.path.split(os.path.realpath(__file__))[0]
62 #css = os.path.join(static, 'css')
63 #for sheet in [
64 ## do we need jquery and prettify?
65 ## os.path.join(static, 'jquery', 'css', 'themes', 'base',
66 ## 'jquery-ui.min.css'),
67 ## os.path.join(static, 'prettify', 'prettify.css'),
68 #os.path.join(css, 'boilerplate.css'),
69 #os.path.join(css, 'fbm.css'),
70 #os.path.join(css, 'notebook.css'),
71 #os.path.join(css, 'renderedhtml.css'),
72 ## our overrides:
73 #os.path.join(here, '..', 'css', 'static_html.css'),
74 #]:
75 #header.extend(self._stylesheet(sheet))
76
77 ## pygments css
78 #pygments_css = HtmlFormatter().get_style_defs('.highlight')
79 #header.extend(['<meta charset="UTF-8">'])
80 #header.extend(self.in_tag('style', pygments_css,
81 #dict(type='"text/css"')))
82
83 ## TODO: this should be allowed to use local mathjax:
84 #header.extend(self.in_tag('script', '', {'type': '"text/javascript"',
85 #'src': '"https://c328740.ssl.cf1.rackcdn.com/mathjax/'
86 #'latest/MathJax.js?config=TeX-AMS_HTML"',
87 #}))
88 #with io.open(os.path.join(here, '..', 'js', 'initmathjax.js'),
89 #encoding='utf-8') as f:
90 #header.extend(self.in_tag('script', f.read(),
91 #{'type': '"text/javascript"'}))
92 #return header
93
94 #def optional_header(self):
95 #return ['<html>', '<head>'] + self.header_body() + \
96 #['</head>', '<body>']
97
98 #def optional_footer(self):
99 #return ['</body>', '</html>']
100
101 @text_cell
42 def render_heading(self, cell):
102 def render_heading(self, cell):
103 marker = cell.level
43 return [self.meta2str(cell.metadata),
104 return [self.meta2str(cell.metadata),
44 '{0} {1}'.format('#' * cell.level, cell.source), '']
105 u'<h{1}>\n {0}\n</h{1}>'.format(cell.source, marker)]
45
106
46 def render_code(self, cell):
107 def render_code(self, cell):
47 if not cell.input:
108 if not cell.input:
48 return []
109 return []
110
49 lines = []
111 lines = []
50 meta_code = self.meta2str(cell.metadata)
112 meta_code = self.meta2str(cell.metadata)
51 lines.extend([meta_code])
113 lines.extend([meta_code])
52 lines.extend(['<!-- hack -->']) # to be proper parsed
114
115 lines.extend(['<div class="cell border-box-sizing code_cell vbox">'])
116
117 lines.append('<div class="input hbox">')
53 n = self._get_prompt_number(cell)
118 n = self._get_prompt_number(cell)
54 if self.show_prompts and not self.inline_prompt:
119 lines.append(
55 lines.extend(['*In[%s]:*' % n, ''])
120 '<div class="prompt input_prompt">In&nbsp;[%s]:</div>' % n
56 if self.show_prompts and self.inline_prompt:
121 )
57 prompt = 'In[%s]: ' % n
122 lines.append('<div class="input_area box-flex1">')
58 input_lines = cell.input.split('\n')
123 lines.append(highlight(cell.input))
59 src = (prompt + input_lines[0] + '\n' +
124 lines.append('</div>') # input_area
60 indent('\n'.join(input_lines[1:]), nspaces=len(prompt)))
125 lines.append('</div>') # input
61 else:
126
62 src = cell.input
127 if cell.outputs:
63 src = highlight(src) if self.highlight_source else indent(src)
128 lines.append('<div class="vbox output_wrapper">')
64 lines.extend([src, ''])
129 lines.append('<div class="output vbox">')
65 if cell.outputs and self.show_prompts and not self.inline_prompt:
130
66 lines.extend(['*Out[%s]:*' % n, ''])
131 for output in coalesce_streams(cell.outputs):
67 for output in cell.outputs:
132 conv_fn = self.dispatch(output.output_type)
68 conv_fn = self.dispatch(output.output_type)
133 lines.extend(conv_fn(output))
69 lines.extend(conv_fn(output))
134
70 #lines.append('')
135 lines.append('</div>') # output
136 lines.append('</div>') # output_wrapper
137
138 lines.append('</div>') # cell
139
71 return lines
140 return lines
72
141
142 @text_cell
73 def render_markdown(self, cell):
143 def render_markdown(self, cell):
74 return [self.meta2str(cell.metadata), cell.source, '']
144 return [self.meta2str(cell.metadata), markdown(cell.source)]
75
145
76 def render_raw(self, cell):
146 def render_raw(self, cell):
77 if self.raw_as_verbatim:
147 if self.raw_as_verbatim:
78 return [indent(self.meta2str(cell.metadata)),
148 return [self.in_tag('pre', self.meta2str(cell.metadata)),
79 indent(cell.source), '']
149 self.in_tag('pre', cell.source)] # testing
80 else:
150 else:
81 return [self.meta2str(cell.metadata), cell.source, '']
151 return [self.meta2str(cell.metadata), cell.source]
152
153 #@output_container
154 #def render_pyout(self, output):
155 #for fmt in ['html', 'latex', 'png', 'jpeg', 'svg', 'text']:
156 #if fmt in output:
157 #conv_fn = self.dispatch_display_format(fmt)
158 #return conv_fn(output)
159 #return []
160
161 #render_display_data = render_pyout
162
163 #@output_container
164 #def render_stream(self, output):
165 #return self._ansi_colored(output.text)
82
166
83 def render_pyout(self, output):
167 #@output_container
84 for fmt in ['html', 'latex', 'png', 'jpeg', 'svg', 'text']:
168 #def render_pyerr(self, output):
85 if fmt in output:
169 ## Note: a traceback is a *list* of frames.
86 conv_fn = self.dispatch_display_format(fmt)
170 ## lines = []
87 return conv_fn(output)
88 return []
89
171
90 def render_pyerr(self, output):
172 ## stb =
91 # Note: a traceback is a *list* of frames.
173 #return self._ansi_colored('\n'.join(output.traceback))
92 return [indent(remove_ansi('\n'.join(output.traceback))), '']
93
174
94 def _img_lines(self, img_file):
175 #def _img_lines(self, img_file):
95 return ['![](%s)' % img_file, '']
176 #return ['<img src="%s">' % img_file, '</img>']
96
177
97 def render_display_format_png(self, output):
178 #def _unknown_lines(self, data):
98 return ['<img src="data:image/png;base64,%s"></img>' % output.png, '']
179 #return ['<h2>Warning:: Unknown cell</h2>'] + self.in_tag('pre', data)
99
180
100 def render_display_format_svg(self, output):
181 #def render_display_format_png(self, output):
101 return [output.svg, '']
182 #return ['<img src="data:image/png;base64,%s"></img>' % output.png]
102
183
103 def render_display_format_jpeg(self, output):
184 #def render_display_format_svg(self, output):
104 return ['<img src="data:image/jpeg;base64,%s"></img>' % output.jpeg, '']
185 #return [output.svg]
105
186
106 def render_display_format_text(self, output):
187 #def render_display_format_jpeg(self, output):
107 return [indent(output.text), '']
188 #return ['<img src="data:image/jpeg;base64,%s"></img>' % output.jpeg]
108
189
109 def _unknown_lines(self, data):
190 #def render_display_format_text(self, output):
110 return ['Warning: Unknown cell', data, '']
191 #return self._ansi_colored(output.text)
111
192
112 def render_display_format_html(self, output):
193 #def render_display_format_html(self, output):
113 return [dedent(output.html), '']
194 #return [output.html]
114
195
115 def render_display_format_latex(self, output):
196 #def render_display_format_latex(self, output):
116 return ['LaTeX::', indent(output.latex), '']
197 #return [output.latex]
117
198
118 def render_display_format_json(self, output):
199 #def render_display_format_json(self, output):
119 return ['JSON:', indent(output.json), '']
200 ## html ignores json
201 #return []
120
202
121 def render_display_format_javascript(self, output):
203 #def render_display_format_javascript(self, output):
122 return ['JavaScript:', indent(output.javascript), '']
204 #return [output.javascript]
205
206 def meta2str(self, meta):
207 "transform metadata dict (containing slides delimiters) to string "
208 try:
209 meta_tuple = meta[u'slideshow'].items()
210 except KeyError as e: # if there is not slideshow metadata
211 meta_tuple = [(u'slide_type', u'untouched')]
212 meta_list = [[x + ' = ' + unicode(y)] for x, y in meta_tuple]
213 return u'\n'.join(list(itertools.chain(*meta_list)))
123
214
124 def convert(self, cell_separator='\n'):
215 def convert(self, cell_separator='\n'):
125 """
216 """
@@ -139,55 +230,77 b' class ConverterReveal(ConverterMarkdown):'
139 lines.extend(self.optional_header())
230 lines.extend(self.optional_header())
140 begin = ['<div class="reveal"><div class="slides">']
231 begin = ['<div class="reveal"><div class="slides">']
141 lines.extend(begin)
232 lines.extend(begin)
142 slides_list = self.build_slides(cell_separator)
233 slides_list = self.build_slides()
143 lines.extend(slides_list)
234 lines.extend(slides_list)
144 end = ['</div></div>']
235 end = ['</div></div>']
145 lines.extend(end)
236 lines.extend(end)
146 lines.extend(self.optional_footer())
237 lines.extend(self.optional_footer())
147 return u'\n'.join(lines)
238 return u'\n'.join(lines)
148
239
149 def build_slides(self, cell_separator='\n'):
240 def clean_text(self, cell_separator='\n'):
150 "build the slides structure from text list and delimiters"
241 "clean and reorganize the text list to be slided"
151 text = self.main_body(cell_separator)
242 text = self.main_body(cell_separator)
152 delim_false = [u'new_section = False',
243 self.delim = [u'slide_type = untouched',
153 u'new_subsection = False',
244 u'slide_type = -',
154 u'new_fragment = False']
245 u'slide_type = header_slide',
155 text = [x for x in text if not x in delim_false]
246 u'slide_type = slide',
156 left = '<section data-markdown><script type="text/template">'
247 u'slide_type = fragment',
157 right = '</script></section>'
248 u'slide_type = skip']
158 # build lists representing each slide delimited by new_section
249 text_cell_render = \
250 u'<div class="text_cell_render border-box-sizing rendered_html">'
251 for i, j in enumerate(text):
252 if j in self.delim and text[i - 1] == text_cell_render:
253 if j == self.delim[0]:
254 text[i - 1] = self.delim[0]
255 elif j == self.delim[1]:
256 text[i - 1] = self.delim[1]
257 elif j == self.delim[2]:
258 text[i - 1] = self.delim[2]
259 elif j == self.delim[3]:
260 text[i - 1] = self.delim[3]
261 elif j == self.delim[4]:
262 text[i - 1] = self.delim[4]
263 else:
264 text[i - 1] = self.delim[5]
265 text[i] = text_cell_render
266 text[0] = u'slide_type = header_slide' # defensive code
267 text.append(u'slide_type = untouched') # to end search of skipped
268 return text
269
270 def build_slides(self):
271 "build the slides structure from text list and delimiters"
272 text = self.clean_text()
273 left = '<section>'
274 right = '</section>'
275 set_delim = self.delim[:5]
276 #elimination of skipped cells
277 for i, j in enumerate(text):
278 if j == u'slide_type = skip':
279 text.pop(i)
280 while not text[i] in set_delim:
281 text.pop(i)
282 # elimination of none names
283 for i, j in enumerate(text):
284 if j in [u'slide_type = untouched', u'slide_type = -']:
285 text.pop(i)
286 #generation of slides as a list of list
159 slides = [list(x[1]) for x in itertools.groupby(text,
287 slides = [list(x[1]) for x in itertools.groupby(text,
160 lambda x: x == u'new_section = True') if not x[0]]
288 lambda x: x == u'slide_type = header_slide') if not x[0]]
161 for slide in slides:
289 for slide in slides:
162 slide.insert(0, u'') # for proper interline in html file
163 slide.insert(0, left)
290 slide.insert(0, left)
164 slide.append(right)
291 slide.append(right)
165 # build each vertical slide delimited by new_subsection
292 # encapsulation of each fragment
166 if slide[2] == u'new_subsection = True':
293 for i, j in enumerate(slide):
167 slide.pop(2)
294 if j == u'slide_type = fragment':
295 slide.pop(i)
296 slide[i] = slide[i][:4] + ' class="fragment"' + slide[i][4:]
297 # encapsulation of each nested slide
298 if u'slide_type = slide' in slide:
168 slide.insert(0, '<section>')
299 slide.insert(0, '<section>')
169 slide.append('</section>')
300 slide.append('</section>')
170 for i, j in enumerate(slide):
171 if j == u'new_subsection = True':
172 slide[i] = right + left
173 slide.insert(i + 1, u'') # for proper interline
174 #defensive: if user do not begin new_subsection with a new_section
175 elif slide[4] == u'new_subsection = True':
176 slide[4] = right
177 slide.insert(5, u'') # for proper interline
178 slide.insert(5, left)
179 slide.insert(5, '<section>')
180 slide.append('</section>')
181 for i, j in enumerate(slide):
182 if j == u'new_subsection = True':
183 slide[i] = right + left
184 slide.insert(i + 1, u'') # for proper interline
185 # build each fragment delimited by new_fragment
186 for i, j in enumerate(slide):
301 for i, j in enumerate(slide):
187 if j == u'new_fragment = True':
302 if j == u'slide_type = slide':
188 slide[i] = '<p class="fragment">'
303 slide[i] = right + left
189 slide[i + 2] = '</p>'
190 slide.insert(i + 3, u'') # for proper interline
191 return list(itertools.chain(*slides))
304 return list(itertools.chain(*slides))
192
305
193 def save(self, outfile=None, encoding=None):
306 def save(self, outfile=None, encoding=None):
@@ -223,5 +336,4 b' class ConverterReveal(ConverterMarkdown):'
223
336
224 def optional_footer(self):
337 def optional_footer(self):
225 optional_footer_body = self.template_split()
338 optional_footer_body = self.template_split()
226 return optional_footer_body[1]
339 return optional_footer_body[1] No newline at end of file
227
1 NO CONTENT: file was removed
NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now