diff --git a/IPython/html/nbconvert/handlers.py b/IPython/html/nbconvert/handlers.py
index 2910205..1cb9ba9 100644
--- a/IPython/html/nbconvert/handlers.py
+++ b/IPython/html/nbconvert/handlers.py
@@ -73,7 +73,7 @@ class NbconvertFileHandler(IPythonHandler):
@web.authenticated
def get(self, format, path='', name=None):
- exporter = get_exporter(format, config=self.config)
+ exporter = get_exporter(format, config=self.config, log=self.log)
path = path.strip('/')
model = self.notebook_manager.get_notebook(name=name, path=path)
diff --git a/IPython/html/static/notebook/js/menubar.js b/IPython/html/static/notebook/js/menubar.js
index bb365dc..7e496b6 100644
--- a/IPython/html/static/notebook/js/menubar.js
+++ b/IPython/html/static/notebook/js/menubar.js
@@ -1,9 +1,5 @@
-//----------------------------------------------------------------------------
-// Copyright (C) 2008-2011 The IPython Development Team
-//
-// Distributed under the terms of the BSD License. The full license is in
-// the file COPYING, distributed as part of this software.
-//----------------------------------------------------------------------------
+// Copyright (c) IPython Development Team.
+// Distributed under the terms of the Modified BSD License.
//============================================================================
// MenuBar
@@ -125,6 +121,10 @@ var IPython = (function (IPython) {
that._nbconvert('rst', true);
});
+ this.element.find('#download_pdf').click(function () {
+ that._nbconvert('pdf', true);
+ });
+
this.element.find('#rename_notebook').click(function () {
IPython.save_widget.rename_notebook();
});
diff --git a/IPython/html/templates/notebook.html b/IPython/html/templates/notebook.html
index c0abbf4..45ab6fa 100644
--- a/IPython/html/templates/notebook.html
+++ b/IPython/html/templates/notebook.html
@@ -84,6 +84,7 @@ class="notebook_app"
Python (.py)
HTML (.html)
reST (.rst)
+ PDF (.pdf)
diff --git a/IPython/nbconvert/exporters/__init__.py b/IPython/nbconvert/exporters/__init__.py
index 23f6195..6397472 100644
--- a/IPython/nbconvert/exporters/__init__.py
+++ b/IPython/nbconvert/exporters/__init__.py
@@ -4,6 +4,7 @@ from .slides import SlidesExporter
from .templateexporter import TemplateExporter
from .latex import LatexExporter
from .markdown import MarkdownExporter
+from .pdf import PDFExporter
from .python import PythonExporter
from .rst import RSTExporter
from .exporter import Exporter
diff --git a/IPython/nbconvert/exporters/export.py b/IPython/nbconvert/exporters/export.py
index e9fff80..62f909e 100644
--- a/IPython/nbconvert/exporters/export.py
+++ b/IPython/nbconvert/exporters/export.py
@@ -1,17 +1,7 @@
-"""
-Module containing single call export functions.
-"""
-#-----------------------------------------------------------------------------
-# Copyright (c) 2013, the IPython Development Team.
-#
-# Distributed under the terms of the Modified BSD License.
-#
-# The full license is in the file COPYING.txt, distributed with this software.
-#-----------------------------------------------------------------------------
+"""Module containing single call export functions."""
-#-----------------------------------------------------------------------------
-# Imports
-#-----------------------------------------------------------------------------
+# Copyright (c) IPython Development Team.
+# Distributed under the terms of the Modified BSD License.
from functools import wraps
@@ -24,6 +14,7 @@ from .templateexporter import TemplateExporter
from .html import HTMLExporter
from .slides import SlidesExporter
from .latex import LatexExporter
+from .pdf import PDFExporter
from .markdown import MarkdownExporter
from .python import PythonExporter
from .rst import RSTExporter
@@ -79,6 +70,7 @@ __all__ = [
'export_custom',
'export_slides',
'export_latex',
+ 'export_pdf',
'export_markdown',
'export_python',
'export_rst',
@@ -134,6 +126,7 @@ exporter_map = dict(
html=HTMLExporter,
slides=SlidesExporter,
latex=LatexExporter,
+ pdf=PDFExporter,
markdown=MarkdownExporter,
python=PythonExporter,
rst=RSTExporter,
diff --git a/IPython/nbconvert/postprocessors/pdf.py b/IPython/nbconvert/exporters/pdf.py
similarity index 56%
rename from IPython/nbconvert/postprocessors/pdf.py
rename to IPython/nbconvert/exporters/pdf.py
index cfd85b7..0ef2b2d 100644
--- a/IPython/nbconvert/postprocessors/pdf.py
+++ b/IPython/nbconvert/exporters/pdf.py
@@ -1,53 +1,41 @@
-"""
-Contains writer for writing nbconvert output to PDF.
-"""
-#-----------------------------------------------------------------------------
-#Copyright (c) 2013, the IPython Development Team.
-#
-#Distributed under the terms of the Modified BSD License.
-#
-#The full license is in the file COPYING.txt, distributed with this software.
-#-----------------------------------------------------------------------------
-
-#-----------------------------------------------------------------------------
-# Imports
-#-----------------------------------------------------------------------------
+"""Export to PDF via latex"""
+
+# Copyright (c) IPython Development Team.
+# Distributed under the terms of the Modified BSD License.
import subprocess
import os
import sys
-from IPython.utils.traitlets import Integer, List, Bool
+from IPython.utils.traitlets import Integer, List, Bool, Instance
+from IPython.utils.tempdir import TemporaryWorkingDirectory
+from .latex import LatexExporter
-from .base import PostProcessorBase
-#-----------------------------------------------------------------------------
-# Classes
-#-----------------------------------------------------------------------------
-class PDFPostProcessor(PostProcessorBase):
+class PDFExporter(LatexExporter):
"""Writer designed to write to PDF files"""
- latex_count = Integer(3, config=True, help="""
- How many times pdflatex will be called.
- """)
+ latex_count = Integer(3, config=True,
+ help="How many times latex will be called."
+ )
- latex_command = List([u"pdflatex", u"{filename}"], config=True, help="""
- Shell command used to compile PDF.""")
+ latex_command = List([u"pdflatex", u"{filename}"], config=True,
+ help="Shell command used to compile latex."
+ )
- bib_command = List([u"bibtex", u"{filename}"], config=True, help="""
- Shell command used to run bibtex.""")
+ bib_command = List([u"bibtex", u"{filename}"], config=True,
+ help="Shell command used to run bibtex."
+ )
- verbose = Bool(False, config=True, help="""
- Whether or not to display the output of the compile call.
- """)
+ verbose = Bool(False, config=True,
+ help="Whether to display the output of latex commands."
+ )
- temp_file_exts = List(['.aux', '.bbl', '.blg', '.idx', '.log', '.out'],
- config=True, help="""
- Filename extensions of temp files to remove after running.
- """)
- pdf_open = Bool(False, config=True, help="""
- Whether or not to open the pdf after the compile call.
- """)
+ temp_file_exts = List(['.aux', '.bbl', '.blg', '.idx', '.log', '.out'], config=True,
+ help="File extensions of temp files to remove after running."
+ )
+
+ writer = Instance("IPython.nbconvert.writers.FilesWriter", args=())
def run_command(self, command_list, filename, count, log_function):
"""Run command_list count times.
@@ -64,7 +52,7 @@ class PDFPostProcessor(PostProcessorBase):
Returns
-------
- continue : bool
+ success : bool
A boolean indicating if the command was successful (True)
or failed (False).
"""
@@ -124,33 +112,30 @@ class PDFPostProcessor(PostProcessorBase):
except OSError:
pass
- def open_pdf(self, filename):
- """Open the pdf in the default viewer."""
- if sys.platform.startswith('darwin'):
- subprocess.call(('open', filename))
- elif os.name == 'nt':
- os.startfile(filename)
- elif os.name == 'posix':
- subprocess.call(('xdg-open', filename))
- return
-
- def postprocess(self, filename):
- """Build a PDF by running pdflatex and bibtex"""
- self.log.info("Building PDF")
- cont = self.run_latex(filename)
- if cont:
- cont = self.run_bib(filename)
- else:
- self.clean_temp_files(filename)
- return
- if cont:
- cont = self.run_latex(filename)
- self.clean_temp_files(filename)
- filename = os.path.splitext(filename)[0]
- if os.path.isfile(filename+'.pdf'):
+ def from_notebook_node(self, nb, resources=None, **kw):
+ latex, resources = super(PDFExporter, self).from_notebook_node(
+ nb, resources=resources, **kw
+ )
+ with TemporaryWorkingDirectory() as td:
+ notebook_name = "notebook"
+ tex_file = self.writer.write(latex, resources, notebook_name=notebook_name)
+ self.log.info("Building PDF")
+ rc = self.run_latex(tex_file)
+ if not rc:
+ rc = self.run_bib(tex_file)
+ if not rc:
+ rc = self.run_latex(tex_file)
+
+ pdf_file = notebook_name + '.pdf'
+ if not os.path.isfile(pdf_file):
+ raise RuntimeError("PDF creating failed")
self.log.info('PDF successfully created')
- if self.pdf_open:
- self.log.info('Viewer called')
- self.open_pdf(filename+'.pdf')
- return
-
+ with open(pdf_file, 'rb') as f:
+ pdf_data = f.read()
+
+ # convert output extension to pdf
+ # the writer above required it to be tex
+ resources['output_extension'] = 'pdf'
+
+ return pdf_data, resources
+
diff --git a/IPython/nbconvert/exporters/tests/test_pdf.py b/IPython/nbconvert/exporters/tests/test_pdf.py
new file mode 100644
index 0000000..6272605
--- /dev/null
+++ b/IPython/nbconvert/exporters/tests/test_pdf.py
@@ -0,0 +1,36 @@
+"""Tests for PDF export"""
+
+# Copyright (c) IPython Development Team.
+# Distributed under the terms of the Modified BSD License.
+
+import logging
+import os
+
+from IPython.testing import decorators as dec
+
+from .base import ExportersTestsBase
+from ..pdf import PDFExporter
+
+
+#-----------------------------------------------------------------------------
+# Class
+#-----------------------------------------------------------------------------
+
+class TestPDF(ExportersTestsBase):
+ """Test PDF export"""
+
+ exporter_class = PDFExporter
+
+ def test_constructor(self):
+ """Can a PDFExporter be constructed?"""
+ self.exporter_class()
+
+
+ @dec.onlyif_cmds_exist('pdflatex')
+ @dec.onlyif_cmds_exist('pandoc')
+ def test_export(self):
+ """Smoke test PDFExporter"""
+ (output, resources) = self.exporter_class(latex_count=1).from_filename(self._get_notebook())
+ self.assertIsInstance(output, bytes)
+ assert len(output) > 0
+
diff --git a/IPython/nbconvert/nbconvertapp.py b/IPython/nbconvert/nbconvertapp.py
index 3649fda..d5b15bf 100755
--- a/IPython/nbconvert/nbconvertapp.py
+++ b/IPython/nbconvert/nbconvertapp.py
@@ -1,21 +1,12 @@
#!/usr/bin/env python
-"""NBConvert is a utility for conversion of .ipynb files.
+"""NbConvert is a utility for conversion of .ipynb files.
Command-line interface for the NbConvert conversion utility.
"""
-#-----------------------------------------------------------------------------
-#Copyright (c) 2013, the IPython Development Team.
-#
-#Distributed under the terms of the Modified BSD License.
-#
-#The full license is in the file COPYING.txt, distributed with this software.
-#-----------------------------------------------------------------------------
-#-----------------------------------------------------------------------------
-#Imports
-#-----------------------------------------------------------------------------
+# Copyright (c) IPython Development Team.
+# Distributed under the terms of the Modified BSD License.
-# Stdlib imports
from __future__ import print_function
import logging
@@ -23,7 +14,6 @@ import sys
import os
import glob
-# From IPython
from IPython.core.application import BaseIPythonApplication, base_aliases, base_flags
from IPython.core.profiledir import ProfileDir
from IPython.config import catch_config_error, Configurable
@@ -128,9 +118,9 @@ class NbConvertApp(BaseIPythonApplication):
> ipython nbconvert mynotebook.ipynb --stdout
- A post-processor can be used to compile a PDF
+ PDF is generated via latex
- > ipython nbconvert mynotebook.ipynb --to latex --post PDF
+ > ipython nbconvert mynotebook.ipynb --to pdf
You can get (and serve) a Reveal.js-powered slideshow
@@ -174,8 +164,7 @@ class NbConvertApp(BaseIPythonApplication):
postprocessor_class = DottedOrNone(config=True,
help="""PostProcessor class used to write the
results of the conversion""")
- postprocessor_aliases = {'pdf': 'IPython.nbconvert.postprocessors.pdf.PDFPostProcessor',
- 'serve': 'IPython.nbconvert.postprocessors.serve.ServePostProcessor'}
+ postprocessor_aliases = {'serve': 'IPython.nbconvert.postprocessors.serve.ServePostProcessor'}
postprocessor_factory = Type()
def _postprocessor_class_changed(self, name, old, new):
diff --git a/IPython/nbconvert/postprocessors/__init__.py b/IPython/nbconvert/postprocessors/__init__.py
index 9f954f3..75ff947 100644
--- a/IPython/nbconvert/postprocessors/__init__.py
+++ b/IPython/nbconvert/postprocessors/__init__.py
@@ -1,5 +1,4 @@
from .base import PostProcessorBase
-from .pdf import PDFPostProcessor
# protect against unavailable tornado
try:
diff --git a/IPython/nbconvert/postprocessors/tests/test_pdf.py b/IPython/nbconvert/postprocessors/tests/test_pdf.py
deleted file mode 100644
index 11da2cb..0000000
--- a/IPython/nbconvert/postprocessors/tests/test_pdf.py
+++ /dev/null
@@ -1,69 +0,0 @@
-"""
-Module with tests for the PDF post-processor
-"""
-
-#-----------------------------------------------------------------------------
-# Copyright (c) 2013, the IPython Development Team.
-#
-# Distributed under the terms of the Modified BSD License.
-#
-# The full license is in the file COPYING.txt, distributed with this software.
-#-----------------------------------------------------------------------------
-
-#-----------------------------------------------------------------------------
-# Imports
-#-----------------------------------------------------------------------------
-
-import logging
-import os
-
-from IPython.testing import decorators as dec
-
-from ...tests.base import TestsBase
-from ..pdf import PDFPostProcessor
-
-
-#-----------------------------------------------------------------------------
-# Constants
-#-----------------------------------------------------------------------------
-
-HELLO_WORLD = r"""% hello.tex - Our first LaTeX example!
-\documentclass{article}
-\begin{document}
-Hello World!
-\end{document}"""
-
-
-#-----------------------------------------------------------------------------
-# Class
-#-----------------------------------------------------------------------------
-
-class TestPDF(TestsBase):
- """Contains test functions for pdf.py"""
-
-
- def test_constructor(self):
- """Can a PDFPostProcessor be constructed?"""
- PDFPostProcessor()
-
-
- @dec.onlyif_cmds_exist('pdflatex')
- def test_pdf(self):
- """Can a PDF be made using the PDFPostProcessor?"""
-
- # Work in a temporary directory with hello world latex in it.
- with self.create_temp_cwd():
- with open('a.tex', 'w') as f:
- f.write(HELLO_WORLD)
-
- # Construct post-processor
- processor = PDFPostProcessor(log=logging.getLogger())
- processor.verbose = False
- processor('a.tex')
-
- # Check that the PDF was created.
- assert os.path.isfile('a.pdf')
-
- # Make sure that temp files are cleaned up
- for ext in processor.temp_file_exts:
- assert not os.path.isfile('a'+ext)
diff --git a/IPython/nbconvert/tests/test_nbconvertapp.py b/IPython/nbconvert/tests/test_nbconvertapp.py
index 9cb4388..24139a8 100644
--- a/IPython/nbconvert/tests/test_nbconvertapp.py
+++ b/IPython/nbconvert/tests/test_nbconvertapp.py
@@ -1,22 +1,15 @@
# -*- coding: utf-8 -*-
"""Test NbConvertApp"""
-#-----------------------------------------------------------------------------
-# Copyright (C) 2013 The IPython Development Team
-#
-# Distributed under the terms of the BSD License. The full license is in
-# the file COPYING, distributed as part of this software.
-#-----------------------------------------------------------------------------
-
-#-----------------------------------------------------------------------------
-# Imports
-#-----------------------------------------------------------------------------
+# Copyright (c) IPython Development Team.
+# Distributed under the terms of the Modified BSD License.
import os
import glob
import sys
from .base import TestsBase
+from ..postprocessors import PostProcessorBase
import IPython.testing.tools as tt
from IPython.testing import decorators as dec
@@ -25,6 +18,10 @@ from IPython.testing import decorators as dec
# Classes and functions
#-----------------------------------------------------------------------------
+class DummyPost(PostProcessorBase):
+ def postprocess(self, filename):
+ print("Dummy:%s" % filename)
+
class TestNbConvertApp(TestsBase):
"""Collection of NbConvertApp tests"""
@@ -79,24 +76,19 @@ class TestNbConvertApp(TestsBase):
"""
with self.create_temp_cwd(['notebook2.ipynb']):
os.rename('notebook2.ipynb', 'notebook with spaces.ipynb')
- self.call('nbconvert --log-level 0 --to latex '
- '"notebook with spaces" --post PDF '
- '--PDFPostProcessor.verbose=True')
- assert os.path.isfile('notebook with spaces.tex')
- assert os.path.isdir('notebook with spaces_files')
+ self.call('nbconvert --log-level 0 --to pdf'
+ ' "notebook with spaces"'
+ ' --PDFExporter.latex_count=1'
+ ' --PDFExporter.verbose=True'
+ )
assert os.path.isfile('notebook with spaces.pdf')
- @dec.onlyif_cmds_exist('pdflatex')
- @dec.onlyif_cmds_exist('pandoc')
def test_post_processor(self):
- """
- Do post processors work?
- """
+ """Do post processors work?"""
with self.create_temp_cwd(['notebook1.ipynb']):
- self.call('nbconvert --log-level 0 --to latex notebook1 '
- '--post PDF --PDFPostProcessor.verbose=True')
- assert os.path.isfile('notebook1.tex')
- assert os.path.isfile('notebook1.pdf')
+ out, err = self.call('nbconvert --log-level 0 --to python notebook1 '
+ '--post IPython.nbconvert.tests.test_nbconvertapp.DummyPost')
+ self.assertIn('Dummy:notebook1.py', out)
@dec.onlyif_cmds_exist('pandoc')
def test_spurious_cr(self):
@@ -195,10 +187,9 @@ class TestNbConvertApp(TestsBase):
"""
with self.create_temp_cwd():
self.create_empty_notebook(u'nb1_análisis.ipynb')
- self.call('nbconvert --log-level 0 --to latex '
- '"nb1_*" --post PDF '
- '--PDFPostProcessor.verbose=True')
- assert os.path.isfile(u'nb1_análisis.tex')
+ self.call('nbconvert --log-level 0 --to pdf "nb1_*"'
+ ' --PDFExporter.latex_count=1'
+ ' --PDFExporter.verbose=True')
assert os.path.isfile(u'nb1_análisis.pdf')
def test_cwd_plugin(self):
diff --git a/IPython/nbconvert/writers/files.py b/IPython/nbconvert/writers/files.py
index 35aca20..1e3e7d2 100644
--- a/IPython/nbconvert/writers/files.py
+++ b/IPython/nbconvert/writers/files.py
@@ -1,17 +1,7 @@
-"""
-Contains writer for writing nbconvert output to filesystem.
-"""
-#-----------------------------------------------------------------------------
-#Copyright (c) 2013, the IPython Development Team.
-#
-#Distributed under the terms of the Modified BSD License.
-#
-#The full license is in the file COPYING.txt, distributed with this software.
-#-----------------------------------------------------------------------------
+"""Contains writer for writing nbconvert output to filesystem."""
-#-----------------------------------------------------------------------------
-# Imports
-#-----------------------------------------------------------------------------
+# Copyright (c) IPython Development Team.
+# Distributed under the terms of the Modified BSD License.
import io
import os
@@ -19,6 +9,7 @@ import glob
from IPython.utils.traitlets import Unicode
from IPython.utils.path import link_or_copy
+from IPython.utils.py3compat import unicode_type
from .base import WriterBase
@@ -110,6 +101,11 @@ class FilesWriter(WriterBase):
# Write conversion results.
self.log.info("Writing %i bytes to %s", len(output), dest)
- with io.open(dest, 'w', encoding='utf-8') as f:
- f.write(output)
+ if isinstance(output, unicode_type):
+ with io.open(dest, 'w', encoding='utf-8') as f:
+ f.write(output)
+ else:
+ with io.open(dest, 'wb') as f:
+ f.write(output)
+
return dest
diff --git a/docs/source/whatsnew/pr/incompat-rm-pdf-post.rst b/docs/source/whatsnew/pr/incompat-rm-pdf-post.rst
new file mode 100644
index 0000000..54145db
--- /dev/null
+++ b/docs/source/whatsnew/pr/incompat-rm-pdf-post.rst
@@ -0,0 +1,2 @@
+Creating PDFs with LaTeX no longer uses a post processor.
+Use `nbconvert --to pdf` instead of `nbconvert --to latex --post pdf`.