##// END OF EJS Templates
Merge pull request #5586 from minrk/pdf-exporter...
Thomas Kluyver -
r16421:fee8aced merge
parent child Browse files
Show More
@@ -0,0 +1,36 b''
1 """Tests for PDF export"""
2
3 # Copyright (c) IPython Development Team.
4 # Distributed under the terms of the Modified BSD License.
5
6 import logging
7 import os
8
9 from IPython.testing import decorators as dec
10
11 from .base import ExportersTestsBase
12 from ..pdf import PDFExporter
13
14
15 #-----------------------------------------------------------------------------
16 # Class
17 #-----------------------------------------------------------------------------
18
19 class TestPDF(ExportersTestsBase):
20 """Test PDF export"""
21
22 exporter_class = PDFExporter
23
24 def test_constructor(self):
25 """Can a PDFExporter be constructed?"""
26 self.exporter_class()
27
28
29 @dec.onlyif_cmds_exist('pdflatex')
30 @dec.onlyif_cmds_exist('pandoc')
31 def test_export(self):
32 """Smoke test PDFExporter"""
33 (output, resources) = self.exporter_class(latex_count=1).from_filename(self._get_notebook())
34 self.assertIsInstance(output, bytes)
35 assert len(output) > 0
36
@@ -0,0 +1,2 b''
1 Creating PDFs with LaTeX no longer uses a post processor.
2 Use `nbconvert --to pdf` instead of `nbconvert --to latex --post pdf`.
@@ -73,7 +73,7 b' class NbconvertFileHandler(IPythonHandler):'
73 73 @web.authenticated
74 74 def get(self, format, path='', name=None):
75 75
76 exporter = get_exporter(format, config=self.config)
76 exporter = get_exporter(format, config=self.config, log=self.log)
77 77
78 78 path = path.strip('/')
79 79 model = self.notebook_manager.get_notebook(name=name, path=path)
@@ -1,9 +1,5 b''
1 //----------------------------------------------------------------------------
2 // Copyright (C) 2008-2011 The IPython Development Team
3 //
4 // Distributed under the terms of the BSD License. The full license is in
5 // the file COPYING, distributed as part of this software.
6 //----------------------------------------------------------------------------
1 // Copyright (c) IPython Development Team.
2 // Distributed under the terms of the Modified BSD License.
7 3
8 4 //============================================================================
9 5 // MenuBar
@@ -125,6 +121,10 b' var IPython = (function (IPython) {'
125 121 that._nbconvert('rst', true);
126 122 });
127 123
124 this.element.find('#download_pdf').click(function () {
125 that._nbconvert('pdf', true);
126 });
127
128 128 this.element.find('#rename_notebook').click(function () {
129 129 IPython.save_widget.rename_notebook();
130 130 });
@@ -84,6 +84,7 b' class="notebook_app"'
84 84 <li id="download_py"><a href="#">Python (.py)</a></li>
85 85 <li id="download_html"><a href="#">HTML (.html)</a></li>
86 86 <li id="download_rst"><a href="#">reST (.rst)</a></li>
87 <li id="download_pdf"><a href="#">PDF (.pdf)</a></li>
87 88 </ul>
88 89 </li>
89 90 <li class="divider"></li>
@@ -4,6 +4,7 b' from .slides import SlidesExporter'
4 4 from .templateexporter import TemplateExporter
5 5 from .latex import LatexExporter
6 6 from .markdown import MarkdownExporter
7 from .pdf import PDFExporter
7 8 from .python import PythonExporter
8 9 from .rst import RSTExporter
9 10 from .exporter import Exporter
@@ -1,17 +1,7 b''
1 """
2 Module containing single call export functions.
3 """
4 #-----------------------------------------------------------------------------
5 # Copyright (c) 2013, the IPython Development Team.
6 #
7 # Distributed under the terms of the Modified BSD License.
8 #
9 # The full license is in the file COPYING.txt, distributed with this software.
10 #-----------------------------------------------------------------------------
1 """Module containing single call export functions."""
11 2
12 #-----------------------------------------------------------------------------
13 # Imports
14 #-----------------------------------------------------------------------------
3 # Copyright (c) IPython Development Team.
4 # Distributed under the terms of the Modified BSD License.
15 5
16 6 from functools import wraps
17 7
@@ -24,6 +14,7 b' from .templateexporter import TemplateExporter'
24 14 from .html import HTMLExporter
25 15 from .slides import SlidesExporter
26 16 from .latex import LatexExporter
17 from .pdf import PDFExporter
27 18 from .markdown import MarkdownExporter
28 19 from .python import PythonExporter
29 20 from .rst import RSTExporter
@@ -79,6 +70,7 b' __all__ = ['
79 70 'export_custom',
80 71 'export_slides',
81 72 'export_latex',
73 'export_pdf',
82 74 'export_markdown',
83 75 'export_python',
84 76 'export_rst',
@@ -134,6 +126,7 b' exporter_map = dict('
134 126 html=HTMLExporter,
135 127 slides=SlidesExporter,
136 128 latex=LatexExporter,
129 pdf=PDFExporter,
137 130 markdown=MarkdownExporter,
138 131 python=PythonExporter,
139 132 rst=RSTExporter,
@@ -1,53 +1,41 b''
1 """
2 Contains writer for writing nbconvert output to PDF.
3 """
4 #-----------------------------------------------------------------------------
5 #Copyright (c) 2013, the IPython Development Team.
6 #
7 #Distributed under the terms of the Modified BSD License.
8 #
9 #The full license is in the file COPYING.txt, distributed with this software.
10 #-----------------------------------------------------------------------------
11
12 #-----------------------------------------------------------------------------
13 # Imports
14 #-----------------------------------------------------------------------------
1 """Export to PDF via latex"""
2
3 # Copyright (c) IPython Development Team.
4 # Distributed under the terms of the Modified BSD License.
15 5
16 6 import subprocess
17 7 import os
18 8 import sys
19 9
20 from IPython.utils.traitlets import Integer, List, Bool
10 from IPython.utils.traitlets import Integer, List, Bool, Instance
11 from IPython.utils.tempdir import TemporaryWorkingDirectory
12 from .latex import LatexExporter
21 13
22 from .base import PostProcessorBase
23 14
24 #-----------------------------------------------------------------------------
25 # Classes
26 #-----------------------------------------------------------------------------
27 class PDFPostProcessor(PostProcessorBase):
15 class PDFExporter(LatexExporter):
28 16 """Writer designed to write to PDF files"""
29 17
30 latex_count = Integer(3, config=True, help="""
31 How many times pdflatex will be called.
32 """)
18 latex_count = Integer(3, config=True,
19 help="How many times latex will be called."
20 )
33 21
34 latex_command = List([u"pdflatex", u"{filename}"], config=True, help="""
35 Shell command used to compile PDF.""")
22 latex_command = List([u"pdflatex", u"{filename}"], config=True,
23 help="Shell command used to compile latex."
24 )
36 25
37 bib_command = List([u"bibtex", u"{filename}"], config=True, help="""
38 Shell command used to run bibtex.""")
26 bib_command = List([u"bibtex", u"{filename}"], config=True,
27 help="Shell command used to run bibtex."
28 )
39 29
40 verbose = Bool(False, config=True, help="""
41 Whether or not to display the output of the compile call.
42 """)
30 verbose = Bool(False, config=True,
31 help="Whether to display the output of latex commands."
32 )
43 33
44 temp_file_exts = List(['.aux', '.bbl', '.blg', '.idx', '.log', '.out'],
45 config=True, help="""
46 Filename extensions of temp files to remove after running.
47 """)
48 pdf_open = Bool(False, config=True, help="""
49 Whether or not to open the pdf after the compile call.
50 """)
34 temp_file_exts = List(['.aux', '.bbl', '.blg', '.idx', '.log', '.out'], config=True,
35 help="File extensions of temp files to remove after running."
36 )
37
38 writer = Instance("IPython.nbconvert.writers.FilesWriter", args=())
51 39
52 40 def run_command(self, command_list, filename, count, log_function):
53 41 """Run command_list count times.
@@ -64,7 +52,7 b' class PDFPostProcessor(PostProcessorBase):'
64 52
65 53 Returns
66 54 -------
67 continue : bool
55 success : bool
68 56 A boolean indicating if the command was successful (True)
69 57 or failed (False).
70 58 """
@@ -124,33 +112,30 b' class PDFPostProcessor(PostProcessorBase):'
124 112 except OSError:
125 113 pass
126 114
127 def open_pdf(self, filename):
128 """Open the pdf in the default viewer."""
129 if sys.platform.startswith('darwin'):
130 subprocess.call(('open', filename))
131 elif os.name == 'nt':
132 os.startfile(filename)
133 elif os.name == 'posix':
134 subprocess.call(('xdg-open', filename))
135 return
136
137 def postprocess(self, filename):
138 """Build a PDF by running pdflatex and bibtex"""
139 self.log.info("Building PDF")
140 cont = self.run_latex(filename)
141 if cont:
142 cont = self.run_bib(filename)
143 else:
144 self.clean_temp_files(filename)
145 return
146 if cont:
147 cont = self.run_latex(filename)
148 self.clean_temp_files(filename)
149 filename = os.path.splitext(filename)[0]
150 if os.path.isfile(filename+'.pdf'):
115 def from_notebook_node(self, nb, resources=None, **kw):
116 latex, resources = super(PDFExporter, self).from_notebook_node(
117 nb, resources=resources, **kw
118 )
119 with TemporaryWorkingDirectory() as td:
120 notebook_name = "notebook"
121 tex_file = self.writer.write(latex, resources, notebook_name=notebook_name)
122 self.log.info("Building PDF")
123 rc = self.run_latex(tex_file)
124 if not rc:
125 rc = self.run_bib(tex_file)
126 if not rc:
127 rc = self.run_latex(tex_file)
128
129 pdf_file = notebook_name + '.pdf'
130 if not os.path.isfile(pdf_file):
131 raise RuntimeError("PDF creating failed")
151 132 self.log.info('PDF successfully created')
152 if self.pdf_open:
153 self.log.info('Viewer called')
154 self.open_pdf(filename+'.pdf')
155 return
156
133 with open(pdf_file, 'rb') as f:
134 pdf_data = f.read()
135
136 # convert output extension to pdf
137 # the writer above required it to be tex
138 resources['output_extension'] = 'pdf'
139
140 return pdf_data, resources
141
@@ -1,21 +1,12 b''
1 1 #!/usr/bin/env python
2 """NBConvert is a utility for conversion of .ipynb files.
2 """NbConvert is a utility for conversion of .ipynb files.
3 3
4 4 Command-line interface for the NbConvert conversion utility.
5 5 """
6 #-----------------------------------------------------------------------------
7 #Copyright (c) 2013, the IPython Development Team.
8 #
9 #Distributed under the terms of the Modified BSD License.
10 #
11 #The full license is in the file COPYING.txt, distributed with this software.
12 #-----------------------------------------------------------------------------
13 6
14 #-----------------------------------------------------------------------------
15 #Imports
16 #-----------------------------------------------------------------------------
7 # Copyright (c) IPython Development Team.
8 # Distributed under the terms of the Modified BSD License.
17 9
18 # Stdlib imports
19 10 from __future__ import print_function
20 11
21 12 import logging
@@ -23,7 +14,6 b' import sys'
23 14 import os
24 15 import glob
25 16
26 # From IPython
27 17 from IPython.core.application import BaseIPythonApplication, base_aliases, base_flags
28 18 from IPython.core.profiledir import ProfileDir
29 19 from IPython.config import catch_config_error, Configurable
@@ -128,9 +118,9 b' class NbConvertApp(BaseIPythonApplication):'
128 118
129 119 > ipython nbconvert mynotebook.ipynb --stdout
130 120
131 A post-processor can be used to compile a PDF
121 PDF is generated via latex
132 122
133 > ipython nbconvert mynotebook.ipynb --to latex --post PDF
123 > ipython nbconvert mynotebook.ipynb --to pdf
134 124
135 125 You can get (and serve) a Reveal.js-powered slideshow
136 126
@@ -174,8 +164,7 b' class NbConvertApp(BaseIPythonApplication):'
174 164 postprocessor_class = DottedOrNone(config=True,
175 165 help="""PostProcessor class used to write the
176 166 results of the conversion""")
177 postprocessor_aliases = {'pdf': 'IPython.nbconvert.postprocessors.pdf.PDFPostProcessor',
178 'serve': 'IPython.nbconvert.postprocessors.serve.ServePostProcessor'}
167 postprocessor_aliases = {'serve': 'IPython.nbconvert.postprocessors.serve.ServePostProcessor'}
179 168 postprocessor_factory = Type()
180 169
181 170 def _postprocessor_class_changed(self, name, old, new):
@@ -1,5 +1,4 b''
1 1 from .base import PostProcessorBase
2 from .pdf import PDFPostProcessor
3 2
4 3 # protect against unavailable tornado
5 4 try:
@@ -1,22 +1,15 b''
1 1 # -*- coding: utf-8 -*-
2 2 """Test NbConvertApp"""
3 3
4 #-----------------------------------------------------------------------------
5 # Copyright (C) 2013 The IPython Development Team
6 #
7 # Distributed under the terms of the BSD License. The full license is in
8 # the file COPYING, distributed as part of this software.
9 #-----------------------------------------------------------------------------
10
11 #-----------------------------------------------------------------------------
12 # Imports
13 #-----------------------------------------------------------------------------
4 # Copyright (c) IPython Development Team.
5 # Distributed under the terms of the Modified BSD License.
14 6
15 7 import os
16 8 import glob
17 9 import sys
18 10
19 11 from .base import TestsBase
12 from ..postprocessors import PostProcessorBase
20 13
21 14 import IPython.testing.tools as tt
22 15 from IPython.testing import decorators as dec
@@ -25,6 +18,10 b' from IPython.testing import decorators as dec'
25 18 # Classes and functions
26 19 #-----------------------------------------------------------------------------
27 20
21 class DummyPost(PostProcessorBase):
22 def postprocess(self, filename):
23 print("Dummy:%s" % filename)
24
28 25 class TestNbConvertApp(TestsBase):
29 26 """Collection of NbConvertApp tests"""
30 27
@@ -79,24 +76,19 b' class TestNbConvertApp(TestsBase):'
79 76 """
80 77 with self.create_temp_cwd(['notebook2.ipynb']):
81 78 os.rename('notebook2.ipynb', 'notebook with spaces.ipynb')
82 self.call('nbconvert --log-level 0 --to latex '
83 '"notebook with spaces" --post PDF '
84 '--PDFPostProcessor.verbose=True')
85 assert os.path.isfile('notebook with spaces.tex')
86 assert os.path.isdir('notebook with spaces_files')
79 self.call('nbconvert --log-level 0 --to pdf'
80 ' "notebook with spaces"'
81 ' --PDFExporter.latex_count=1'
82 ' --PDFExporter.verbose=True'
83 )
87 84 assert os.path.isfile('notebook with spaces.pdf')
88 85
89 @dec.onlyif_cmds_exist('pdflatex')
90 @dec.onlyif_cmds_exist('pandoc')
91 86 def test_post_processor(self):
92 """
93 Do post processors work?
94 """
87 """Do post processors work?"""
95 88 with self.create_temp_cwd(['notebook1.ipynb']):
96 self.call('nbconvert --log-level 0 --to latex notebook1 '
97 '--post PDF --PDFPostProcessor.verbose=True')
98 assert os.path.isfile('notebook1.tex')
99 assert os.path.isfile('notebook1.pdf')
89 out, err = self.call('nbconvert --log-level 0 --to python notebook1 '
90 '--post IPython.nbconvert.tests.test_nbconvertapp.DummyPost')
91 self.assertIn('Dummy:notebook1.py', out)
100 92
101 93 @dec.onlyif_cmds_exist('pandoc')
102 94 def test_spurious_cr(self):
@@ -195,10 +187,9 b' class TestNbConvertApp(TestsBase):'
195 187 """
196 188 with self.create_temp_cwd():
197 189 self.create_empty_notebook(u'nb1_análisis.ipynb')
198 self.call('nbconvert --log-level 0 --to latex '
199 '"nb1_*" --post PDF '
200 '--PDFPostProcessor.verbose=True')
201 assert os.path.isfile(u'nb1_análisis.tex')
190 self.call('nbconvert --log-level 0 --to pdf "nb1_*"'
191 ' --PDFExporter.latex_count=1'
192 ' --PDFExporter.verbose=True')
202 193 assert os.path.isfile(u'nb1_análisis.pdf')
203 194
204 195 def test_cwd_plugin(self):
@@ -1,17 +1,7 b''
1 """
2 Contains writer for writing nbconvert output to filesystem.
3 """
4 #-----------------------------------------------------------------------------
5 #Copyright (c) 2013, the IPython Development Team.
6 #
7 #Distributed under the terms of the Modified BSD License.
8 #
9 #The full license is in the file COPYING.txt, distributed with this software.
10 #-----------------------------------------------------------------------------
1 """Contains writer for writing nbconvert output to filesystem."""
11 2
12 #-----------------------------------------------------------------------------
13 # Imports
14 #-----------------------------------------------------------------------------
3 # Copyright (c) IPython Development Team.
4 # Distributed under the terms of the Modified BSD License.
15 5
16 6 import io
17 7 import os
@@ -19,6 +9,7 b' import glob'
19 9
20 10 from IPython.utils.traitlets import Unicode
21 11 from IPython.utils.path import link_or_copy
12 from IPython.utils.py3compat import unicode_type
22 13
23 14 from .base import WriterBase
24 15
@@ -110,6 +101,11 b' class FilesWriter(WriterBase):'
110 101
111 102 # Write conversion results.
112 103 self.log.info("Writing %i bytes to %s", len(output), dest)
113 with io.open(dest, 'w', encoding='utf-8') as f:
114 f.write(output)
104 if isinstance(output, unicode_type):
105 with io.open(dest, 'w', encoding='utf-8') as f:
106 f.write(output)
107 else:
108 with io.open(dest, 'wb') as f:
109 f.write(output)
110
115 111 return dest
1 NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now