##// END OF EJS Templates
solved pdf postprocess issue for nb with accented names
marcmolla -
Show More
@@ -1,149 +1,160 b''
1 1 """
2 2 Contains writer for writing nbconvert output to PDF.
3 3 """
4 4 #-----------------------------------------------------------------------------
5 5 #Copyright (c) 2013, the IPython Development Team.
6 6 #
7 7 #Distributed under the terms of the Modified BSD License.
8 8 #
9 9 #The full license is in the file COPYING.txt, distributed with this software.
10 10 #-----------------------------------------------------------------------------
11 11
12 12 #-----------------------------------------------------------------------------
13 13 # Imports
14 14 #-----------------------------------------------------------------------------
15 15
16 16 import subprocess
17 17 import os
18 18 import sys
19 19
20 20 from IPython.utils.traitlets import Integer, List, Bool
21 from IPython.utils.encoding import DEFAULT_ENCODING
21 22
22 23 from .base import PostProcessorBase
23 24
25
24 26 #-----------------------------------------------------------------------------
25 27 # Classes
26 28 #-----------------------------------------------------------------------------
27 29 class PDFPostProcessor(PostProcessorBase):
28 30 """Writer designed to write to PDF files"""
29 31
30 32 latex_count = Integer(3, config=True, help="""
31 33 How many times pdflatex will be called.
32 34 """)
33 35
34 36 latex_command = List(["pdflatex", "{filename}"], config=True, help="""
35 37 Shell command used to compile PDF.""")
36 38
37 39 bib_command = List(["bibtex", "{filename}"], config=True, help="""
38 40 Shell command used to run bibtex.""")
39 41
40 42 verbose = Bool(False, config=True, help="""
41 43 Whether or not to display the output of the compile call.
42 44 """)
43 45
44 46 temp_file_exts = List(['.aux', '.bbl', '.blg', '.idx', '.log', '.out'],
45 47 config=True, help="""
46 48 Filename extensions of temp files to remove after running.
47 49 """)
48 50 pdf_open = Bool(False, config=True, help="""
49 51 Whether or not to open the pdf after the compile call.
50 52 """)
51 53
52 54 def run_command(self, command_list, filename, count, log_function):
53 55 """Run command_list count times.
54 56
55 57 Parameters
56 58 ----------
57 59 command_list : list
58 60 A list of args to provide to Popen. Each element of this
59 61 list will be interpolated with the filename to convert.
60 62 filename : unicode
61 63 The name of the file to convert.
62 64 count : int
63 65 How many times to run the command.
64 66
65 67 Returns
66 68 -------
67 69 continue : bool
68 70 A boolean indicating if the command was successful (True)
69 71 or failed (False).
70 72 """
73 #HACK: Encode file name with the correct encoding
74 # For Windows must be cp1252 (win application) and we cannot use
75 # encoding.DEFAULT_ENCODING or sys.stdin.encoding because input
76 # encoding could be different (cp437 in case of dos console)
77 if sys.platform == 'win32':
78 filename = filename.encode('cp1252')
79 else:
80 filename = filename.encode('utf-8')
81 #END_HACK
71 82 command = [c.format(filename=filename) for c in command_list]
72 83 times = 'time' if count == 1 else 'times'
73 84 self.log.info("Running %s %i %s: %s", command_list[0], count, times, command)
74 85 with open(os.devnull, 'rb') as null:
75 86 stdout = subprocess.PIPE if not self.verbose else None
76 87 for index in range(count):
77 p = subprocess.Popen(command, stdout=stdout, stdin=null)
88 p = subprocess.Popen(command, stdout=stdout, stdin=null, shell=True)
78 89 out, err = p.communicate()
79 90 if p.returncode:
80 91 if self.verbose:
81 92 # verbose means I didn't capture stdout with PIPE,
82 93 # so it's already been displayed and `out` is None.
83 94 out = u''
84 95 else:
85 96 out = out.decode('utf-8', 'replace')
86 97 log_function(command, out)
87 98 return False # failure
88 99 return True # success
89 100
90 101 def run_latex(self, filename):
91 102 """Run pdflatex self.latex_count times."""
92 103
93 104 def log_error(command, out):
94 105 self.log.critical(u"%s failed: %s\n%s", command[0], command, out)
95 106
96 107 return self.run_command(self.latex_command, filename,
97 108 self.latex_count, log_error)
98 109
99 110 def run_bib(self, filename):
100 111 """Run bibtex self.latex_count times."""
101 112 filename = os.path.splitext(filename)[0]
102 113
103 114 def log_error(command, out):
104 115 self.log.warn('%s had problems, most likely because there were no citations',
105 116 command[0])
106 117 self.log.debug(u"%s output: %s\n%s", command[0], command, out)
107 118
108 119 return self.run_command(self.bib_command, filename, 1, log_error)
109 120
110 121 def clean_temp_files(self, filename):
111 122 """Remove temporary files created by pdflatex/bibtext."""
112 123 self.log.info("Removing temporary LaTeX files")
113 124 filename = os.path.splitext(filename)[0]
114 125 for ext in self.temp_file_exts:
115 126 try:
116 127 os.remove(filename+ext)
117 128 except OSError:
118 129 pass
119 130
120 131 def open_pdf(self, filename):
121 132 """Open the pdf in the default viewer."""
122 133 if sys.platform.startswith('darwin'):
123 134 subprocess.call(('open', filename))
124 135 elif os.name == 'nt':
125 136 os.startfile(filename)
126 137 elif os.name == 'posix':
127 138 subprocess.call(('xdg-open', filename))
128 139 return
129 140
130 141 def postprocess(self, filename):
131 142 """Build a PDF by running pdflatex and bibtex"""
132 143 self.log.info("Building PDF")
133 144 cont = self.run_latex(filename)
134 145 if cont:
135 146 cont = self.run_bib(filename)
136 147 else:
137 148 self.clean_temp_files(filename)
138 149 return
139 150 if cont:
140 151 cont = self.run_latex(filename)
141 152 self.clean_temp_files(filename)
142 153 filename = os.path.splitext(filename)[0]
143 154 if os.path.isfile(filename+'.pdf'):
144 155 self.log.info('PDF successfully created')
145 156 if self.pdf_open:
146 157 self.log.info('Viewer called')
147 158 self.open_pdf(filename+'.pdf')
148 159 return
149 160
@@ -1,218 +1,217 b''
1 1 # -*- coding: utf-8 -*-
2 2 """Test NbConvertApp"""
3 3
4 4 #-----------------------------------------------------------------------------
5 5 # Copyright (C) 2013 The IPython Development Team
6 6 #
7 7 # Distributed under the terms of the BSD License. The full license is in
8 8 # the file COPYING, distributed as part of this software.
9 9 #-----------------------------------------------------------------------------
10 10
11 11 #-----------------------------------------------------------------------------
12 12 # Imports
13 13 #-----------------------------------------------------------------------------
14 14
15 15 import os
16 16 import glob
17 17 import sys
18 18
19 19 from .base import TestsBase
20 20
21 21 import IPython.testing.tools as tt
22 22 from IPython.testing import decorators as dec
23 23
24 24
25 25 #-----------------------------------------------------------------------------
26 26 # Constants
27 27 #-----------------------------------------------------------------------------
28 28
29 29
30 30 #-----------------------------------------------------------------------------
31 31 # Classes and functions
32 32 #-----------------------------------------------------------------------------
33 33
34 34 class TestNbConvertApp(TestsBase):
35 35 """Collection of NbConvertApp tests"""
36 36
37 37
38 38 def test_notebook_help(self):
39 39 """Will help show if no notebooks are specified?"""
40 40 with self.create_temp_cwd():
41 41 out, err = self.call('nbconvert --log-level 0', ignore_return_code=True)
42 42 self.assertIn("see '--help-all'", out)
43 43
44 44 def test_help_output(self):
45 45 """ipython nbconvert --help-all works"""
46 46 tt.help_all_output_test('nbconvert')
47 47
48 48 def test_glob(self):
49 49 """
50 50 Do search patterns work for notebook names?
51 51 """
52 52 with self.create_temp_cwd(['notebook*.ipynb']):
53 53 self.call('nbconvert --to python *.ipynb --log-level 0')
54 54 assert os.path.isfile('notebook1.py')
55 55 assert os.path.isfile('notebook2.py')
56 56
57 57
58 58 def test_glob_subdir(self):
59 59 """
60 60 Do search patterns work for subdirectory notebook names?
61 61 """
62 62 with self.create_temp_cwd():
63 63 self.copy_files_to(['notebook*.ipynb'], 'subdir/')
64 64 self.call('nbconvert --to python --log-level 0 ' +
65 65 os.path.join('subdir', '*.ipynb'))
66 66 assert os.path.isfile('notebook1.py')
67 67 assert os.path.isfile('notebook2.py')
68 68
69 69
70 70 def test_explicit(self):
71 71 """
72 72 Do explicit notebook names work?
73 73 """
74 74 with self.create_temp_cwd(['notebook*.ipynb']):
75 75 self.call('nbconvert --log-level 0 --to python notebook2')
76 76 assert not os.path.isfile('notebook1.py')
77 77 assert os.path.isfile('notebook2.py')
78 78
79 79
80 80 @dec.onlyif_cmds_exist('pdflatex')
81 81 @dec.onlyif_cmds_exist('pandoc')
82 82 def test_filename_spaces(self):
83 83 """
84 84 Generate PDFs with graphics if notebooks have spaces in the name?
85 85 """
86 86 with self.create_temp_cwd(['notebook2.ipynb']):
87 87 os.rename('notebook2.ipynb', 'notebook with spaces.ipynb')
88 88 o,e = self.call('nbconvert --log-level 0 --to latex '
89 89 '"notebook with spaces" --post PDF '
90 90 '--PDFPostProcessor.verbose=True')
91 91 assert os.path.isfile('notebook with spaces.tex')
92 92 assert os.path.isdir('notebook with spaces_files')
93 93 assert os.path.isfile('notebook with spaces.pdf')
94 94
95 95 @dec.onlyif_cmds_exist('pdflatex')
96 96 @dec.onlyif_cmds_exist('pandoc')
97 97 def test_post_processor(self):
98 98 """
99 99 Do post processors work?
100 100 """
101 101 with self.create_temp_cwd(['notebook1.ipynb']):
102 102 self.call('nbconvert --log-level 0 --to latex notebook1 '
103 103 '--post PDF --PDFPostProcessor.verbose=True')
104 104 assert os.path.isfile('notebook1.tex')
105 105 assert os.path.isfile('notebook1.pdf')
106 106
107 107 @dec.onlyif_cmds_exist('pandoc')
108 108 def test_spurious_cr(self):
109 109 """Check for extra CR characters"""
110 110 with self.create_temp_cwd(['notebook2.ipynb']):
111 111 self.call('nbconvert --log-level 0 --to latex notebook2')
112 112 assert os.path.isfile('notebook2.tex')
113 113 with open('notebook2.tex') as f:
114 114 tex = f.read()
115 115 self.call('nbconvert --log-level 0 --to html notebook2')
116 116 assert os.path.isfile('notebook2.html')
117 117 with open('notebook2.html') as f:
118 118 html = f.read()
119 119 self.assertEqual(tex.count('\r'), tex.count('\r\n'))
120 120 self.assertEqual(html.count('\r'), html.count('\r\n'))
121 121
122 122 @dec.onlyif_cmds_exist('pandoc')
123 123 def test_png_base64_html_ok(self):
124 124 """Is embedded png data well formed in HTML?"""
125 125 with self.create_temp_cwd(['notebook2.ipynb']):
126 126 self.call('nbconvert --log-level 0 --to HTML '
127 127 'notebook2.ipynb --template full')
128 128 assert os.path.isfile('notebook2.html')
129 129 with open('notebook2.html') as f:
130 130 assert "data:image/png;base64,b'" not in f.read()
131 131
132 132 @dec.onlyif_cmds_exist('pandoc')
133 133 def test_template(self):
134 134 """
135 135 Do export templates work?
136 136 """
137 137 with self.create_temp_cwd(['notebook2.ipynb']):
138 138 self.call('nbconvert --log-level 0 --to slides '
139 139 'notebook2.ipynb --template reveal')
140 140 assert os.path.isfile('notebook2.slides.html')
141 141 with open('notebook2.slides.html') as f:
142 142 assert '/reveal.css' in f.read()
143 143
144 144
145 145 def test_glob_explicit(self):
146 146 """
147 147 Can a search pattern be used along with matching explicit notebook names?
148 148 """
149 149 with self.create_temp_cwd(['notebook*.ipynb']):
150 150 self.call('nbconvert --log-level 0 --to python '
151 151 '*.ipynb notebook1.ipynb notebook2.ipynb')
152 152 assert os.path.isfile('notebook1.py')
153 153 assert os.path.isfile('notebook2.py')
154 154
155 155
156 156 def test_explicit_glob(self):
157 157 """
158 158 Can explicit notebook names be used and then a matching search pattern?
159 159 """
160 160 with self.create_temp_cwd(['notebook*.ipynb']):
161 161 self.call('nbconvert --log-level 0 --to=python '
162 162 'notebook1.ipynb notebook2.ipynb *.ipynb')
163 163 assert os.path.isfile('notebook1.py')
164 164 assert os.path.isfile('notebook2.py')
165 165
166 166
167 167 def test_default_config(self):
168 168 """
169 169 Does the default config work?
170 170 """
171 171 with self.create_temp_cwd(['notebook*.ipynb', 'ipython_nbconvert_config.py']):
172 172 self.call('nbconvert --log-level 0')
173 173 assert os.path.isfile('notebook1.py')
174 174 assert not os.path.isfile('notebook2.py')
175 175
176 176
177 177 def test_override_config(self):
178 178 """
179 179 Can the default config be overriden?
180 180 """
181 181 with self.create_temp_cwd(['notebook*.ipynb',
182 182 'ipython_nbconvert_config.py',
183 183 'override.py']):
184 184 self.call('nbconvert --log-level 0 --config="override.py"')
185 185 assert not os.path.isfile('notebook1.py')
186 186 assert os.path.isfile('notebook2.py')
187 187
188 188 def test_accents_in_filename(self):
189 189 """
190 190 Can notebook names include accents?
191 191 """
192 192 with self.create_temp_cwd(['nb*.ipynb']):
193 193 self.call('nbconvert --log-level 0 --to python nb1_*')
194 194 assert os.path.isfile(u'nb1_análisis.py')
195 195
196 196 @dec.onlyif_cmds_exist('pandoc')
197 197 def test_accents_in_command_line(self):
198 198 """
199 199 Are accents allowed in arguments of command line?
200 200 """
201 201 with self.create_temp_cwd(['nb*.ipynb']):
202 202 self.call('nbconvert --to latex nb1_* '
203 203 '--SphinxTransform.author="análisis"')
204 204 assert os.path.isfile(u'nb1_análisis.tex')
205 205
206 206 @dec.onlyif_cmds_exist('pdflatex')
207 207 @dec.onlyif_cmds_exist('pandoc')
208 208 def test_filename_spaces(self):
209 209 """
210 210 Generate PDFs if notebooks have an accent in their name?
211 211 """
212 212 with self.create_temp_cwd(['nb*.ipynb']):
213 213 o,e = self.call('nbconvert --log-level 0 --to latex '
214 214 '"nb1_*" --post PDF '
215 215 '--PDFPostProcessor.verbose=True')
216 assert os.path.isfile('nb1_análisis.tex')
217 assert os.path.isdir('nb1_análisis_files')
218 assert os.path.isfile('nb1_análisis.pdf')
216 assert os.path.isfile(u'nb1_análisis.tex')
217 assert os.path.isfile(u'nb1_análisis.pdf')
General Comments 0
You need to be logged in to leave comments. Login now