##// 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,227 +1,339 b''
1 from converters.markdown import ConverterMarkdown
2 from IPython.utils.text import indent, dedent
3 from converters.utils import highlight, remove_ansi
4 import io
1 from __future__ import absolute_import
2
3 from converters.html import ConverterHTML
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 10 import os
11 import io
6 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 102 def render_heading(self, cell):
103 marker = cell.level
43 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 107 def render_code(self, cell):
47 108 if not cell.input:
48 109 return []
110
49 111 lines = []
50 112 meta_code = self.meta2str(cell.metadata)
51 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 118 n = self._get_prompt_number(cell)
54 if self.show_prompts and not self.inline_prompt:
55 lines.extend(['*In[%s]:*' % n, ''])
56 if self.show_prompts and self.inline_prompt:
57 prompt = 'In[%s]: ' % n
58 input_lines = cell.input.split('\n')
59 src = (prompt + input_lines[0] + '\n' +
60 indent('\n'.join(input_lines[1:]), nspaces=len(prompt)))
61 else:
62 src = cell.input
63 src = highlight(src) if self.highlight_source else indent(src)
64 lines.extend([src, ''])
65 if cell.outputs and self.show_prompts and not self.inline_prompt:
66 lines.extend(['*Out[%s]:*' % n, ''])
67 for output in cell.outputs:
68 conv_fn = self.dispatch(output.output_type)
69 lines.extend(conv_fn(output))
70 #lines.append('')
119 lines.append(
120 '<div class="prompt input_prompt">In&nbsp;[%s]:</div>' % n
121 )
122 lines.append('<div class="input_area box-flex1">')
123 lines.append(highlight(cell.input))
124 lines.append('</div>') # input_area
125 lines.append('</div>') # input
126
127 if cell.outputs:
128 lines.append('<div class="vbox output_wrapper">')
129 lines.append('<div class="output vbox">')
130
131 for output in coalesce_streams(cell.outputs):
132 conv_fn = self.dispatch(output.output_type)
133 lines.extend(conv_fn(output))
134
135 lines.append('</div>') # output
136 lines.append('</div>') # output_wrapper
137
138 lines.append('</div>') # cell
139
71 140 return lines
72 141
142 @text_cell
73 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 146 def render_raw(self, cell):
77 147 if self.raw_as_verbatim:
78 return [indent(self.meta2str(cell.metadata)),
79 indent(cell.source), '']
148 return [self.in_tag('pre', self.meta2str(cell.metadata)),
149 self.in_tag('pre', cell.source)] # testing
80 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):
84 for fmt in ['html', 'latex', 'png', 'jpeg', 'svg', 'text']:
85 if fmt in output:
86 conv_fn = self.dispatch_display_format(fmt)
87 return conv_fn(output)
88 return []
167 #@output_container
168 #def render_pyerr(self, output):
169 ## Note: a traceback is a *list* of frames.
170 ## lines = []
89 171
90 def render_pyerr(self, output):
91 # Note: a traceback is a *list* of frames.
92 return [indent(remove_ansi('\n'.join(output.traceback))), '']
172 ## stb =
173 #return self._ansi_colored('\n'.join(output.traceback))
93 174
94 def _img_lines(self, img_file):
95 return ['![](%s)' % img_file, '']
175 #def _img_lines(self, img_file):
176 #return ['<img src="%s">' % img_file, '</img>']
96 177
97 def render_display_format_png(self, output):
98 return ['<img src="data:image/png;base64,%s"></img>' % output.png, '']
178 #def _unknown_lines(self, data):
179 #return ['<h2>Warning:: Unknown cell</h2>'] + self.in_tag('pre', data)
99 180
100 def render_display_format_svg(self, output):
101 return [output.svg, '']
181 #def render_display_format_png(self, output):
182 #return ['<img src="data:image/png;base64,%s"></img>' % output.png]
102 183
103 def render_display_format_jpeg(self, output):
104 return ['<img src="data:image/jpeg;base64,%s"></img>' % output.jpeg, '']
184 #def render_display_format_svg(self, output):
185 #return [output.svg]
105 186
106 def render_display_format_text(self, output):
107 return [indent(output.text), '']
187 #def render_display_format_jpeg(self, output):
188 #return ['<img src="data:image/jpeg;base64,%s"></img>' % output.jpeg]
108 189
109 def _unknown_lines(self, data):
110 return ['Warning: Unknown cell', data, '']
190 #def render_display_format_text(self, output):
191 #return self._ansi_colored(output.text)
111 192
112 def render_display_format_html(self, output):
113 return [dedent(output.html), '']
193 #def render_display_format_html(self, output):
194 #return [output.html]
114 195
115 def render_display_format_latex(self, output):
116 return ['LaTeX::', indent(output.latex), '']
196 #def render_display_format_latex(self, output):
197 #return [output.latex]
117 198
118 def render_display_format_json(self, output):
119 return ['JSON:', indent(output.json), '']
199 #def render_display_format_json(self, output):
200 ## html ignores json
201 #return []
120 202
121 def render_display_format_javascript(self, output):
122 return ['JavaScript:', indent(output.javascript), '']
203 #def render_display_format_javascript(self, output):
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 215 def convert(self, cell_separator='\n'):
125 216 """
126 217 Specific method to converts notebook to a string representation.
127 218
128 219 Parameters
129 220 ----------
130 221 cell_separator : string
131 222 Character or string to join cells with. Default is "\n"
132 223
133 224 Returns
134 225 -------
135 226 out : string
136 227 """
137 228
138 229 lines = []
139 230 lines.extend(self.optional_header())
140 231 begin = ['<div class="reveal"><div class="slides">']
141 232 lines.extend(begin)
142 slides_list = self.build_slides(cell_separator)
233 slides_list = self.build_slides()
143 234 lines.extend(slides_list)
144 235 end = ['</div></div>']
145 236 lines.extend(end)
146 237 lines.extend(self.optional_footer())
147 238 return u'\n'.join(lines)
148 239
149 def build_slides(self, cell_separator='\n'):
150 "build the slides structure from text list and delimiters"
240 def clean_text(self, cell_separator='\n'):
241 "clean and reorganize the text list to be slided"
151 242 text = self.main_body(cell_separator)
152 delim_false = [u'new_section = False',
153 u'new_subsection = False',
154 u'new_fragment = False']
155 text = [x for x in text if not x in delim_false]
156 left = '<section data-markdown><script type="text/template">'
157 right = '</script></section>'
158 # build lists representing each slide delimited by new_section
243 self.delim = [u'slide_type = untouched',
244 u'slide_type = -',
245 u'slide_type = header_slide',
246 u'slide_type = slide',
247 u'slide_type = fragment',
248 u'slide_type = skip']
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 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 289 for slide in slides:
162 slide.insert(0, u'') # for proper interline in html file
163 290 slide.insert(0, left)
164 291 slide.append(right)
165 # build each vertical slide delimited by new_subsection
166 if slide[2] == u'new_subsection = True':
167 slide.pop(2)
292 # encapsulation of each fragment
293 for i, j in enumerate(slide):
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 299 slide.insert(0, '<section>')
169 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 301 for i, j in enumerate(slide):
187 if j == u'new_fragment = True':
188 slide[i] = '<p class="fragment">'
189 slide[i + 2] = '</p>'
190 slide.insert(i + 3, u'') # for proper interline
302 if j == u'slide_type = slide':
303 slide[i] = right + left
191 304 return list(itertools.chain(*slides))
192 305
193 306 def save(self, outfile=None, encoding=None):
194 307 "read and parse notebook into self.nb"
195 308 if outfile is None:
196 309 outfile = self.outbase + '_slides.' + 'html'
197 310 if encoding is None:
198 311 encoding = self.default_encoding
199 312 with io.open(outfile, 'w', encoding=encoding) as f:
200 313 f.write(self.output)
201 314 return os.path.abspath(outfile)
202 315
203 316 def template_read(self):
204 317 "read the reveal_template.html"
205 318 here = os.path.split(os.path.realpath(__file__))[0]
206 319 reveal_template = os.path.join(here, '..', 'templates',
207 320 'reveal_base.html')
208 321 with io.open(reveal_template, 'r', encoding='utf-8') as f:
209 322 template = f.readlines()
210 323 template = [s.strip() for s in template]
211 324 return template
212 325
213 326 def template_split(self):
214 327 "split the reveal_template.html in header and footer lists"
215 328 temp = self.template_read()
216 329 splitted_temp = [list(x[1]) for x in itertools.groupby(temp,
217 330 lambda x: x == u'%slides%') if not x[0]]
218 331 return splitted_temp
219 332
220 333 def optional_header(self):
221 334 optional_header_body = self.template_split()
222 335 return optional_header_body[0]
223 336
224 337 def optional_footer(self):
225 338 optional_footer_body = self.template_split()
226 return optional_footer_body[1]
227
339 return optional_footer_body[1] No newline at end of file
1 NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now