##// END OF EJS Templates
flag for extracting figure
Matthias BUSSONNIER -
Show More
@@ -1,289 +1,281 b''
1 """Base classes for the notebook conversion pipeline.
1 """Base classes for the notebook conversion pipeline.
2
2
3 This module defines Converter, from which all objects designed to implement
3 This module defines Converter, from which all objects designed to implement
4 a conversion of IPython notebooks to some other format should inherit.
4 a conversion of IPython notebooks to some other format should inherit.
5 """
5 """
6 #-----------------------------------------------------------------------------
6 #-----------------------------------------------------------------------------
7 # Copyright (c) 2012, the IPython Development Team.
7 # Copyright (c) 2012, the IPython Development Team.
8 #
8 #
9 # Distributed under the terms of the Modified BSD License.
9 # Distributed under the terms of the Modified BSD License.
10 #
10 #
11 # The full license is in the file COPYING.txt, distributed with this software.
11 # The full license is in the file COPYING.txt, distributed with this software.
12 #-----------------------------------------------------------------------------
12 #-----------------------------------------------------------------------------
13
13
14 #-----------------------------------------------------------------------------
14 #-----------------------------------------------------------------------------
15 # Imports
15 # Imports
16 #-----------------------------------------------------------------------------
16 #-----------------------------------------------------------------------------
17
17
18 from __future__ import print_function, absolute_import
18 from __future__ import print_function, absolute_import
19
19
20 # Stdlib imports
20 # Stdlib imports
21 import io
21 import io
22 import os
22 import os
23 import re
23 import re
24 from IPython.utils import path
24 from IPython.utils import path
25
25
26 from jinja2 import Environment, FileSystemLoader
26 from jinja2 import Environment, FileSystemLoader
27 env = Environment(
27 env = Environment(
28 loader=FileSystemLoader('./templates/'),
28 loader=FileSystemLoader('./templates/'),
29 extensions=['jinja2.ext.loopcontrols']
29 extensions=['jinja2.ext.loopcontrols']
30 )
30 )
31
31
32 texenv = Environment(
32 texenv = Environment(
33 loader=FileSystemLoader('./templates/tex/'),
33 loader=FileSystemLoader('./templates/tex/'),
34 extensions=['jinja2.ext.loopcontrols']
34 extensions=['jinja2.ext.loopcontrols']
35 )
35 )
36
36
37 # IPython imports
37 # IPython imports
38 from IPython.nbformat import current as nbformat
38 from IPython.nbformat import current as nbformat
39 from IPython.config.configurable import Configurable
39 from IPython.config.configurable import Configurable
40 from IPython.utils.traitlets import ( Unicode, Any, List)
40 from IPython.utils.traitlets import ( Unicode, Any, List, Bool)
41
41
42 # Our own imports
42 # Our own imports
43 from IPython.utils.text import indent
43 from IPython.utils.text import indent
44 from .utils import remove_ansi
44 from .utils import remove_ansi
45 from markdown import markdown
45 from markdown import markdown
46 from .utils import highlight, ansi2html
46 from .utils import highlight, ansi2html
47 from .utils import markdown2latex
47 from .utils import markdown2latex
48 #-----------------------------------------------------------------------------
48 #-----------------------------------------------------------------------------
49 # Class declarations
49 # Class declarations
50 #-----------------------------------------------------------------------------
50 #-----------------------------------------------------------------------------
51 def rm_fake(strng):
51 def rm_fake(strng):
52 return strng.replace('/files/', '')
52 return strng.replace('/files/', '')
53
53
54 class ConversionException(Exception):
54 class ConversionException(Exception):
55 pass
55 pass
56
56
57
57
58 def python_comment(string):
58 def python_comment(string):
59 return '# '+'\n# '.join(string.split('\n'))
59 return '# '+'\n# '.join(string.split('\n'))
60
60
61
61
62
62
63 def header_body():
63 def header_body():
64 """Return the body of the header as a list of strings."""
64 """Return the body of the header as a list of strings."""
65
65
66 from pygments.formatters import HtmlFormatter
66 from pygments.formatters import HtmlFormatter
67
67
68 header = []
68 header = []
69 static = os.path.join(path.get_ipython_package_dir(),
69 static = os.path.join(path.get_ipython_package_dir(),
70 'frontend', 'html', 'notebook', 'static',
70 'frontend', 'html', 'notebook', 'static',
71 )
71 )
72 here = os.path.split(os.path.realpath(__file__))[0]
72 here = os.path.split(os.path.realpath(__file__))[0]
73 css = os.path.join(static, 'css')
73 css = os.path.join(static, 'css')
74 for sheet in [
74 for sheet in [
75 # do we need jquery and prettify?
75 # do we need jquery and prettify?
76 # os.path.join(static, 'jquery', 'css', 'themes', 'base',
76 # os.path.join(static, 'jquery', 'css', 'themes', 'base',
77 # 'jquery-ui.min.css'),
77 # 'jquery-ui.min.css'),
78 # os.path.join(static, 'prettify', 'prettify.css'),
78 # os.path.join(static, 'prettify', 'prettify.css'),
79 os.path.join(css, 'boilerplate.css'),
79 os.path.join(css, 'boilerplate.css'),
80 os.path.join(css, 'fbm.css'),
80 os.path.join(css, 'fbm.css'),
81 os.path.join(css, 'notebook.css'),
81 os.path.join(css, 'notebook.css'),
82 os.path.join(css, 'renderedhtml.css'),
82 os.path.join(css, 'renderedhtml.css'),
83 # our overrides:
83 # our overrides:
84 os.path.join(here, '..', 'css', 'static_html.css'),
84 os.path.join(here, '..', 'css', 'static_html.css'),
85 ]:
85 ]:
86
86
87 with io.open(sheet, encoding='utf-8') as f:
87 with io.open(sheet, encoding='utf-8') as f:
88 s = f.read()
88 s = f.read()
89 header.append(s)
89 header.append(s)
90
90
91 pygments_css = HtmlFormatter().get_style_defs('.highlight')
91 pygments_css = HtmlFormatter().get_style_defs('.highlight')
92 header.append(pygments_css)
92 header.append(pygments_css)
93 return header
93 return header
94
94
95 # todo, make the key part configurable.
95 # todo, make the key part configurable.
96 def _new_figure(data, fmt, count):
96 def _new_figure(data, fmt, count):
97 """Create a new figure file in the given format.
97 """Create a new figure file in the given format.
98
98
99 Returns a path relative to the input file.
99 Returns a path relative to the input file.
100 """
100 """
101 figname = '_fig_%02i.%s' % (count, fmt)
101 figname = '_fig_%02i.%s' % (count, fmt)
102
102
103 # Binary files are base64-encoded, SVG is already XML
103 # Binary files are base64-encoded, SVG is already XML
104 if fmt in ('png', 'jpg', 'pdf'):
104 if fmt in ('png', 'jpg', 'pdf'):
105 data = data.decode('base64')
105 data = data.decode('base64')
106
106
107 return figname,data
107 return figname,data
108
108
109
109
110
110
111
111
112 inlining = {}
112 inlining = {}
113 inlining['css'] = header_body()
113 inlining['css'] = header_body()
114
114
115
116
117
118
119
120 env.filters['pycomment'] = python_comment
121 env.filters['indent'] = indent
122 env.filters['rm_fake'] = rm_fake
123 env.filters['rm_ansi'] = remove_ansi
124 env.filters['markdown'] = markdown
125 env.filters['highlight'] = highlight
126 env.filters['ansi2html'] = ansi2html
127
128
129
130 LATEX_SUBS = (
115 LATEX_SUBS = (
131 (re.compile(r'\\'), r'\\textbackslash'),
116 (re.compile(r'\\'), r'\\textbackslash'),
132 (re.compile(r'([{}_#%&$])'), r'\\\1'),
117 (re.compile(r'([{}_#%&$])'), r'\\\1'),
133 (re.compile(r'~'), r'\~{}'),
118 (re.compile(r'~'), r'\~{}'),
134 (re.compile(r'\^'), r'\^{}'),
119 (re.compile(r'\^'), r'\^{}'),
135 (re.compile(r'"'), r"''"),
120 (re.compile(r'"'), r"''"),
136 (re.compile(r'\.\.\.+'), r'\\ldots'),
121 (re.compile(r'\.\.\.+'), r'\\ldots'),
137 )
122 )
138
123
139 def escape_tex(value):
124 def escape_tex(value):
140 newval = value
125 newval = value
141 for pattern, replacement in LATEX_SUBS:
126 for pattern, replacement in LATEX_SUBS:
142 newval = pattern.sub(replacement, newval)
127 newval = pattern.sub(replacement, newval)
143 return newval
128 return newval
144
129
145 texenv.block_start_string = '((*'
130 texenv.block_start_string = '((*'
146 texenv.block_end_string = '*))'
131 texenv.block_end_string = '*))'
147 texenv.variable_start_string = '((('
132 texenv.variable_start_string = '((('
148 texenv.variable_end_string = ')))'
133 texenv.variable_end_string = ')))'
149 texenv.comment_start_string = '((='
134 texenv.comment_start_string = '((='
150 texenv.comment_end_string = '=))'
135 texenv.comment_end_string = '=))'
151 texenv.filters['escape_tex'] = escape_tex
136 texenv.filters['escape_tex'] = escape_tex
152
137
153 texenv.filters['pycomment'] = python_comment
154 texenv.filters['indent'] = indent
155 texenv.filters['rm_fake'] = rm_fake
156 texenv.filters['rm_ansi'] = remove_ansi
157 texenv.filters['markdown'] = markdown
158 texenv.filters['highlight'] = highlight
159 texenv.filters['ansi2html'] = ansi2html
160 texenv.filters['markdown2latex'] = markdown2latex
161
162 def cell_preprocessor(function):
138 def cell_preprocessor(function):
163 """ wrap a function to be executed on all cells of a notebook
139 """ wrap a function to be executed on all cells of a notebook
164
140
165 wrapped function parameters :
141 wrapped function parameters :
166 cell : the cell
142 cell : the cell
167 other : external resources
143 other : external resources
168 index : index of the cell
144 index : index of the cell
169 """
145 """
170 def wrappedfunc(nb,other):
146 def wrappedfunc(nb,other):
171 for worksheet in nb.worksheets :
147 for worksheet in nb.worksheets :
172 for index, cell in enumerate(worksheet.cells):
148 for index, cell in enumerate(worksheet.cells):
173 worksheet.cells[index],other= function(cell,other,index)
149 worksheet.cells[index],other= function(cell,other,index)
174 return nb,other
150 return nb,other
175 return wrappedfunc
151 return wrappedfunc
176
152
177
153
178
154
179 @cell_preprocessor
155 @cell_preprocessor
180 def haspyout_transformer(cell, other, count):
156 def haspyout_transformer(cell, other, count):
181 """
157 """
182 Add a haspyout flag to cell that have it
158 Add a haspyout flag to cell that have it
183
159
184 Easier for templating, where you can't know in advance
160 Easier for templating, where you can't know in advance
185 wether to write the out prompt
161 wether to write the out prompt
186
162
187 """
163 """
188 cell.type = cell.cell_type
164 cell.type = cell.cell_type
189 cell.haspyout = False
165 cell.haspyout = False
190 for out in cell.get('outputs', []):
166 for out in cell.get('outputs', []):
191 if out.output_type == 'pyout':
167 if out.output_type == 'pyout':
192 cell.haspyout = True
168 cell.haspyout = True
193 break
169 break
194 return cell,other
170 return cell,other
195
171
196
172
197 @cell_preprocessor
173 @cell_preprocessor
198 def outline_figure_transformer(cell,other,count):
174 def extract_figure_transformer(cell,other,count):
199 for i,out in enumerate(cell.get('outputs', [])):
175 for i,out in enumerate(cell.get('outputs', [])):
200 for type in ['html', 'pdf', 'svg', 'latex', 'png', 'jpg', 'jpeg']:
176 for type in ['html', 'pdf', 'svg', 'latex', 'png', 'jpg', 'jpeg']:
201 if out.hasattr(type):
177 if out.hasattr(type):
202 figname,data = _new_figure(out[type], type,count)
178 figname,data = _new_figure(out[type], type,count)
203 cell.outputs[i][type] = figname
179 cell.outputs[i][type] = figname
204 out['key_'+type] = figname
180 out['key_'+type] = figname
205 other[figname] = data
181 other[figname] = data
206 count = count+1
182 count = count+1
207 return nb,other
183 return cell,other
208
184
209
185
210 class ConverterTemplate(Configurable):
186 class ConverterTemplate(Configurable):
211 """ A Jinja2 base converter templates"""
187 """ A Jinja2 base converter templates"""
212
188
213 display_data_priority = List(['html', 'pdf', 'svg', 'latex', 'png', 'jpg', 'jpeg' , 'text'],
189 display_data_priority = List(['html', 'pdf', 'svg', 'latex', 'png', 'jpg', 'jpeg' , 'text'],
214 config=True,
190 config=True,
215 help= """
191 help= """
216 A list of ast.NodeTransformer subclass instances, which will be applied
192 A list of ast.NodeTransformer subclass instances, which will be applied
217 to user input before code is run.
193 to user input before code is run.
218 """
194 """
219 )
195 )
220
196
221 extract_figures = Bool(False,
197 extract_figures = Bool(False,
222 config=True,
198 config=True,
223 help= """
199 help= """
224 wether to remove figure data from ipynb and store them in auxiliary
200 wether to remove figure data from ipynb and store them in auxiliary
225 dictionnary
201 dictionnary
226 """
202 """
227 )
203 )
228 #-------------------------------------------------------------------------
204 #-------------------------------------------------------------------------
229 # Instance-level attributes that are set in the constructor for this
205 # Instance-level attributes that are set in the constructor for this
230 # class.
206 # class.
231 #-------------------------------------------------------------------------
207 #-------------------------------------------------------------------------
232 infile = Any()
208 infile = Any()
233
209
234
210
235 infile_dir = Unicode()
211 infile_dir = Unicode()
236
212
237 def filter_data_type(self,output):
213 def filter_data_type(self,output):
238 for fmt in self.display_data_priority:
214 for fmt in self.display_data_priority:
239 if fmt in output:
215 if fmt in output:
240 return [fmt]
216 return [fmt]
241
217
242 def __init__(self, tplfile='fullhtml', preprocessors=[], config=None,tex_environement=False, **kw):
218 def __init__(self, tplfile='fullhtml', preprocessors=[], config=None,tex_environement=False, **kw):
243 """
219 """
244 tplfile : jinja template file to process.
220 tplfile : jinja template file to process.
245
221
246 config: the Configurable confg object to pass around
222 config: the Configurable confg object to pass around
247
223
248 preprocessors: list of function to run on ipynb json data before conversion
224 preprocessors: list of function to run on ipynb json data before conversion
249 to extract/inline file,
225 to extract/inline file,
250
226
251 """
227 """
228 super(ConverterTemplate, self).__init__(config=config, **kw)
252 self.env = texenv if tex_environement else env
229 self.env = texenv if tex_environement else env
253 self.ext = '.tplx' if tex_environement else '.tpl'
230 self.ext = '.tplx' if tex_environement else '.tpl'
254 self.nb = None
231 self.nb = None
255 self.preprocessors = preprocessors
232 self.preprocessors = preprocessors
256 self.preprocessors.append(haspyout_transformer)
233 self.preprocessors.append(haspyout_transformer)
257 self.preprocessors.append(outline_figure_transformer)
234 if self.extract_figures:
258 super(ConverterTemplate, self).__init__(config=config, **kw)
235 self.preprocessors.append(extract_figure_transformer)
236
259 self.env.filters['filter_data_type'] = self.filter_data_type
237 self.env.filters['filter_data_type'] = self.filter_data_type
238 self.env.filters['pycomment'] = python_comment
239 self.env.filters['indent'] = indent
240 self.env.filters['rm_fake'] = rm_fake
241 self.env.filters['rm_ansi'] = remove_ansi
242 self.env.filters['markdown'] = markdown
243 self.env.filters['highlight'] = highlight
244 self.env.filters['ansi2html'] = ansi2html
245 self.env.filters['markdown2latex'] = markdown2latex
246
260 self.template = self.env.get_template(tplfile+self.ext)
247 self.template = self.env.get_template(tplfile+self.ext)
261
248
262
249
263
250
264 def process(self):
251 def process(self):
265 """
252 """
266 preprocess the notebook json for easier use with the templates.
253 preprocess the notebook json for easier use with the templates.
267 will call all the `preprocessor`s in order before returning it.
254 will call all the `preprocessor`s in order before returning it.
268 """
255 """
269 nb = self.nb
256 nb = self.nb
270
257
258 # dict of 'resources' that could be made by the preprocessors
259 # like key/value data to extract files from ipynb like in latex conversion
260 resources = {}
261
271 for preprocessor in self.preprocessors:
262 for preprocessor in self.preprocessors:
272 nb,others = preprocessor(nb,{})
263 nb,resources = preprocessor(nb,resources)
273
264
274 return nb
265 return nb, resources
275
266
276 def convert(self):
267 def convert(self):
277 """ convert the ipynb file
268 """ convert the ipynb file
278
269
279 return both the converted ipynb file and a dict containing potential
270 return both the converted ipynb file and a dict containing potential
280 other resources
271 other resources
281 """
272 """
282 return self.template.render(nb=self.process(), inlining=inlining), {}
273 nb,resources = self.process()
274 return self.template.render(nb=nb, inlining=inlining), resources
283
275
284
276
285 def read(self, filename):
277 def read(self, filename):
286 "read and parse notebook into NotebookNode called self.nb"
278 "read and parse notebook into NotebookNode called self.nb"
287 with io.open(filename) as f:
279 with io.open(filename) as f:
288 self.nb = nbformat.read(f, 'json')
280 self.nb = nbformat.read(f, 'json')
289
281
@@ -1,65 +1,80 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 #-----------------------------------------------------------------------------
2 #-----------------------------------------------------------------------------
3 # Imports
3 # Imports
4 #-----------------------------------------------------------------------------
4 #-----------------------------------------------------------------------------
5 from __future__ import print_function
5 from __future__ import print_function
6 import sys
6 import sys
7 import io
7 import io
8
8
9 from converters.template import *
9 from converters.template import *
10 from converters.template import ConverterTemplate
10 from converters.template import ConverterTemplate
11 from converters.html import ConverterHTML
11 from converters.html import ConverterHTML
12 # From IPython
12 # From IPython
13
13
14 # All the stuff needed for the configurable things
14 # All the stuff needed for the configurable things
15 from IPython.config.application import Application
15 from IPython.config.application import Application
16 from IPython.utils.traitlets import List, Unicode, Type, Bool, Dict, CaselessStrEnum
16 from IPython.utils.traitlets import List, Unicode, Type, Bool, Dict, CaselessStrEnum
17
17
18
18
19 class NbconvertApp(Application):
19 class NbconvertApp(Application):
20
20
21
21
22 def __init__(self, **kwargs):
22 def __init__(self, **kwargs):
23 super(NbconvertApp, self).__init__(**kwargs)
23 super(NbconvertApp, self).__init__(**kwargs)
24 self.classes.insert(0,ConverterTemplate)
24 self.classes.insert(0,ConverterTemplate)
25 # ensure those are registerd
25 # ensure those are registerd
26
26
27
27
28 def initialize(self, argv=None):
28 def initialize(self, argv=None):
29 self.parse_command_line(argv)
29 self.parse_command_line(argv)
30 cl_config = self.config
30 cl_config = self.config
31 self.update_config(cl_config)
31 self.update_config(cl_config)
32
32
33
33
34
34
35 def run(self):
35 def run(self):
36 """Convert a notebook to html in one step"""
36 """Convert a notebook to html in one step"""
37 template_file = (self.extra_args or [None])[0]
37 template_file = (self.extra_args or [None])[0]
38 ipynb_file = (self.extra_args or [None])[1]
38 ipynb_file = (self.extra_args or [None])[1]
39
39
40 template_file = sys.argv[1]
40 template_file = sys.argv[1]
41
41
42 if template_file.startswith('latex'):
42 if template_file.startswith('latex'):
43 tex_environement=True
43 tex_environement=True
44 else:
44 else:
45 tex_environement=False
45 tex_environement=False
46
46
47 C = ConverterTemplate(tplfile=sys.argv[1], tex_environement=tex_environement, config=self.config)
47 C = ConverterTemplate(tplfile=sys.argv[1],
48 tex_environement=tex_environement,
49 config=self.config)
48 C.read(ipynb_file)
50 C.read(ipynb_file)
49
51
50 output,rest = C.convert()
52 output,resources = C.convert()
51
53
52 print(output.encode('utf-8'))
54 print(output.encode('utf-8'))
53
55
56 keys = resources.keys()
57 if keys :
58 print('''
59 ====================== Keys in Resources ==================================
60 ''')
61 print(resources.keys())
62 print("""
63 ===========================================================================
64 you are responsible from writing those data do a file in the right place if
65 they need to be.
66 ===========================================================================
67 """)
68
54 def main():
69 def main():
55 """Convert a notebook to html in one step"""
70 """Convert a notebook to html in one step"""
56 app = NbconvertApp.instance()
71 app = NbconvertApp.instance()
57 app.initialize()
72 app.initialize()
58 app.start()
73 app.start()
59 app.run()
74 app.run()
60 #-----------------------------------------------------------------------------
75 #-----------------------------------------------------------------------------
61 # Script main
76 # Script main
62 #-----------------------------------------------------------------------------
77 #-----------------------------------------------------------------------------
63
78
64 if __name__ == '__main__':
79 if __name__ == '__main__':
65 main()
80 main()
General Comments 0
You need to be logged in to leave comments. Login now