""" 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 import sys from IPython.utils.traitlets import Integer, List, Bool from .base import PostProcessorBase #----------------------------------------------------------------------------- # Classes #----------------------------------------------------------------------------- class PDFPostProcessor(PostProcessorBase): """Writer designed to write to PDF files""" latex_count = Integer(3, config=True, help=""" How many times pdflatex will be called. """) latex_command = List([u"pdflatex", u"{filename}"], config=True, help=""" Shell command used to compile PDF.""") 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. """) 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. """) def run_command(self, command_list, filename, count, log_function): """Run command_list count times. 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] #In windows and python 2.x there is a bug in subprocess.Popen and # unicode commands are not supported if sys.platform == 'win32' and sys.version_info < (3,0): #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] 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): p = subprocess.Popen(command, stdout=stdout, stdin=null, shell=True) 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 def run_latex(self, filename): """Run pdflatex self.latex_count times.""" def log_error(command, out): self.log.critical(u"%s failed: %s\n%s", command[0], command, out) 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] def log_error(command, out): 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) return self.run_command(self.bib_command, filename, 1, log_error) def clean_temp_files(self, filename): """Remove temporary files created by pdflatex/bibtext.""" self.log.info("Removing temporary LaTeX files") filename = os.path.splitext(filename)[0] for ext in self.temp_file_exts: try: os.remove(filename+ext) 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'): self.log.info('PDF successfully created') if self.pdf_open: self.log.info('Viewer called') self.open_pdf(filename+'.pdf') return