pdf.py
156 lines
| 5.8 KiB
| text/x-python
|
PythonLexer
Jonathan Frederic
|
r11742 | """ | ||
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 | ||||
#----------------------------------------------------------------------------- | ||||
import subprocess | ||||
import os | ||||
jakobgager
|
r12899 | import sys | ||
Jonathan Frederic
|
r11742 | |||
Thomas Robitaille
|
r11911 | from IPython.utils.traitlets import Integer, List, Bool | ||
Jonathan Frederic
|
r11742 | |||
Jonathan Frederic
|
r11747 | from .base import PostProcessorBase | ||
Jonathan Frederic
|
r11742 | |||
#----------------------------------------------------------------------------- | ||||
# Classes | ||||
#----------------------------------------------------------------------------- | ||||
Jonathan Frederic
|
r11747 | class PDFPostProcessor(PostProcessorBase): | ||
Jonathan Frederic
|
r11742 | """Writer designed to write to PDF files""" | ||
Brian E. Granger
|
r12281 | latex_count = Integer(3, config=True, help=""" | ||
Jonathan Frederic
|
r11742 | How many times pdflatex will be called. | ||
""") | ||||
marcmolla
|
r13943 | latex_command = List([u"pdflatex", u"{filename}"], config=True, help=""" | ||
Jonathan Frederic
|
r11745 | Shell command used to compile PDF.""") | ||
marcmolla
|
r13943 | bib_command = List([u"bibtex", u"{filename}"], config=True, help=""" | ||
Brian E. Granger
|
r12269 | Shell command used to run bibtex.""") | ||
Jonathan Frederic
|
r11747 | verbose = Bool(False, config=True, help=""" | ||
Whether or not to display the output of the compile call. | ||||
""") | ||||
Brian E. Granger
|
r12269 | temp_file_exts = List(['.aux', '.bbl', '.blg', '.idx', '.log', '.out'], | ||
config=True, help=""" | ||||
Brian E. Granger
|
r12281 | Filename extensions of temp files to remove after running. | ||
Brian E. Granger
|
r12269 | """) | ||
jakobgager
|
r12902 | pdf_open = Bool(False, config=True, help=""" | ||
jakobgager
|
r12899 | Whether or not to open the pdf after the compile call. | ||
""") | ||||
Brian E. Granger
|
r12269 | |||
def run_command(self, command_list, filename, count, log_function): | ||||
Brian E. Granger
|
r12300 | """Run command_list count times. | ||
Brian E. Granger
|
r12269 | |||
Parameters | ||||
---------- | ||||
command_list : list | ||||
A list of args to provide to Popen. Each element of this | ||||
list will be interpolated with the filename to convert. | ||||
filename : unicode | ||||
The name of the file to convert. | ||||
count : int | ||||
How many times to run the command. | ||||
Returns | ||||
------- | ||||
continue : bool | ||||
A boolean indicating if the command was successful (True) | ||||
or failed (False). | ||||
""" | ||||
command = [c.format(filename=filename) for c in command_list] | ||||
marcmolla
|
r13943 | #In windows and python 2.x there is a bug in subprocess.Popen and | ||
# unicode commands are not supported | ||||
marcmolla
|
r13975 | if sys.platform == 'win32' and sys.version_info < (3,0): | ||
marcmolla
|
r13943 | #We must use cp1252 encoding for calling subprocess.Popen | ||
#Note that sys.stdin.encoding and encoding.DEFAULT_ENCODING | ||||
# could be different (cp437 in case of dos console) | ||||
command = [c.encode('cp1252') for c in command] | ||||
Brian E. Granger
|
r12269 | times = 'time' if count == 1 else 'times' | ||
self.log.info("Running %s %i %s: %s", command_list[0], count, times, command) | ||||
with open(os.devnull, 'rb') as null: | ||||
stdout = subprocess.PIPE if not self.verbose else None | ||||
for index in range(count): | ||||
marcmolla
|
r14757 | p = subprocess.Popen(command, stdout=stdout, stdin=null) | ||
Brian E. Granger
|
r12269 | out, err = p.communicate() | ||
if p.returncode: | ||||
if self.verbose: | ||||
# verbose means I didn't capture stdout with PIPE, | ||||
# so it's already been displayed and `out` is None. | ||||
out = u'' | ||||
else: | ||||
out = out.decode('utf-8', 'replace') | ||||
log_function(command, out) | ||||
return False # failure | ||||
return True # success | ||||
Brian E. Granger
|
r12281 | def run_latex(self, filename): | ||
"""Run pdflatex self.latex_count times.""" | ||||
Brian E. Granger
|
r12269 | |||
def log_error(command, out): | ||||
Brian E. Granger
|
r12281 | self.log.critical(u"%s failed: %s\n%s", command[0], command, out) | ||
Brian E. Granger
|
r12269 | |||
Brian E. Granger
|
r12281 | return self.run_command(self.latex_command, filename, | ||
self.latex_count, log_error) | ||||
def run_bib(self, filename): | ||||
"""Run bibtex self.latex_count times.""" | ||||
filename = os.path.splitext(filename)[0] | ||||
Brian E. Granger
|
r12269 | |||
def log_error(command, out): | ||||
Brian E. Granger
|
r12281 | self.log.warn('%s had problems, most likely because there were no citations', | ||
command[0]) | ||||
self.log.debug(u"%s output: %s\n%s", command[0], command, out) | ||||
Brian E. Granger
|
r12269 | |||
Brian E. Granger
|
r12281 | return self.run_command(self.bib_command, filename, 1, log_error) | ||
Brian E. Granger
|
r12269 | |||
def clean_temp_files(self, filename): | ||||
"""Remove temporary files created by pdflatex/bibtext.""" | ||||
self.log.info("Removing temporary LaTeX files") | ||||
Brian E. Granger
|
r12281 | filename = os.path.splitext(filename)[0] | ||
Brian E. Granger
|
r12269 | for ext in self.temp_file_exts: | ||
try: | ||||
os.remove(filename+ext) | ||||
except OSError: | ||||
pass | ||||
Brian E. Granger
|
r12281 | |||
jakobgager
|
r12902 | def open_pdf(self, filename): | ||
"""Open the pdf in the default viewer.""" | ||||
jakobgager
|
r12899 | if sys.platform.startswith('darwin'): | ||
jakobgager
|
r12902 | subprocess.call(('open', filename)) | ||
jakobgager
|
r12899 | elif os.name == 'nt': | ||
jakobgager
|
r12902 | os.startfile(filename) | ||
jakobgager
|
r12899 | elif os.name == 'posix': | ||
jakobgager
|
r12902 | subprocess.call(('xdg-open', filename)) | ||
jakobgager
|
r12899 | return | ||
Brian E. Granger
|
r12269 | def postprocess(self, filename): | ||
"""Build a PDF by running pdflatex and bibtex""" | ||||
self.log.info("Building PDF") | ||||
Brian E. Granger
|
r12281 | cont = self.run_latex(filename) | ||
Brian E. Granger
|
r12269 | if cont: | ||
Brian E. Granger
|
r12281 | cont = self.run_bib(filename) | ||
Brian E. Granger
|
r12269 | else: | ||
self.clean_temp_files(filename) | ||||
return | ||||
if cont: | ||||
Brian E. Granger
|
r12281 | cont = self.run_latex(filename) | ||
Brian E. Granger
|
r12269 | self.clean_temp_files(filename) | ||
Brian E. Granger
|
r12281 | filename = os.path.splitext(filename)[0] | ||
if os.path.isfile(filename+'.pdf'): | ||||
Brian E. Granger
|
r12269 | self.log.info('PDF successfully created') | ||
jakobgager
|
r12899 | if self.pdf_open: | ||
self.log.info('Viewer called') | ||||
jakobgager
|
r12902 | self.open_pdf(filename+'.pdf') | ||
Brian E. Granger
|
r12269 | return | ||
jakobgager
|
r12899 | |||