Vehicle-Anti-Theft-Face-Rec.../venv/Lib/site-packages/nbconvert/exporters/pdf.py

203 lines
7.4 KiB
Python
Raw Normal View History

2020-11-12 16:05:57 +00:00
"""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
import shutil
from traitlets import Integer, List, Bool, Instance, Unicode, default
from testpath.tempdir import TemporaryWorkingDirectory
from typing import Optional
from .latex import LatexExporter
class LatexFailed(IOError):
"""Exception for failed latex run
Captured latex output is in error.output.
"""
def __init__(self, output):
self.output = output
def __unicode__(self):
return u"PDF creating failed, captured latex output:\n%s" % self.output
def __str__(self):
u = self.__unicode__()
return u
def prepend_to_env_search_path(varname, value, envdict):
"""Add value to the environment variable varname in envdict
e.g. prepend_to_env_search_path('BIBINPUTS', '/home/sally/foo', os.environ)
"""
if not value:
return # Nothing to add
envdict[varname] = value + os.pathsep + envdict.get(varname, '')
class PDFExporter(LatexExporter):
"""Writer designed to write to PDF files.
This inherits from `LatexExporter`. It creates a LaTeX file in
a temporary directory using the template machinery, and then runs LaTeX
to create a pdf.
"""
export_from_notebook="PDF via LaTeX"
latex_count = Integer(3,
help="How many times latex will be called."
).tag(config=True)
latex_command = List([u"xelatex", u"{filename}", "-quiet"],
help="Shell command used to compile latex."
).tag(config=True)
bib_command = List([u"bibtex", u"{filename}"],
help="Shell command used to run bibtex."
).tag(config=True)
verbose = Bool(False,
help="Whether to display the output of latex commands."
).tag(config=True)
texinputs = Unicode(help="texinputs dir. A notebook's directory is added")
writer = Instance("nbconvert.writers.FilesWriter", args=(), kw={'build_directory': '.'})
output_mimetype = "application/pdf"
_captured_output = List()
@default('file_extension')
def _file_extension_default(self):
return '.pdf'
@default('template_extension')
def _template_extension_default(self):
return '.tex.j2'
def run_command(self, command_list, filename, count, log_function, raise_on_failure=None):
"""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.
raise_on_failure: Exception class (default None)
If provided, will raise the given exception for if an instead of
returning False on command failure.
Returns
-------
success : bool
A boolean indicating if the command was successful (True)
or failed (False).
"""
command = [c.format(filename=filename) for c in command_list]
# This will throw a clearer error if the command is not found
cmd = shutil.which(command_list[0])
if cmd is None:
link = "https://nbconvert.readthedocs.io/en/latest/install.html#installing-tex"
raise OSError("{formatter} not found on PATH, if you have not installed "
"{formatter} you may need to do so. Find further instructions "
"at {link}.".format(formatter=command_list[0], link=link))
times = 'time' if count == 1 else 'times'
self.log.info("Running %s %i %s: %s", command_list[0], count, times, command)
shell = (sys.platform == 'win32')
if shell:
command = subprocess.list2cmdline(command)
env = os.environ.copy()
prepend_to_env_search_path('TEXINPUTS', self.texinputs, env)
prepend_to_env_search_path('BIBINPUTS', self.texinputs, env)
prepend_to_env_search_path('BSTINPUTS', self.texinputs, env)
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, stderr=subprocess.STDOUT,
stdin=null, shell=shell, env=env)
out, _ = 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)
self._captured_output.append(out)
if raise_on_failure:
raise raise_on_failure(
'Failed to run "{command}" command:\n{output}'.format(
command=command, output=out))
return False # failure
return True # success
def run_latex(self, filename, raise_on_failure=LatexFailed):
"""Run xelatex 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, raise_on_failure)
def run_bib(self, filename, raise_on_failure=False):
"""Run bibtex one time."""
filename = os.path.splitext(filename)[0]
def log_error(command, out):
self.log.warning('%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, raise_on_failure)
def from_notebook_node(self, nb, resources=None, **kw):
latex, resources = super().from_notebook_node(
nb, resources=resources, **kw
)
# set texinputs directory, so that local files will be found
if resources and resources.get('metadata', {}).get('path'):
self.texinputs = resources['metadata']['path']
else:
self.texinputs = os.getcwd()
self._captured_outputs = []
with TemporaryWorkingDirectory():
notebook_name = 'notebook'
resources['output_extension'] = '.tex'
tex_file = self.writer.write(latex, resources, notebook_name=notebook_name)
self.log.info("Building PDF")
self.run_latex(tex_file)
if self.run_bib(tex_file):
self.run_latex(tex_file)
pdf_file = notebook_name + '.pdf'
if not os.path.isfile(pdf_file):
raise LatexFailed('\n'.join(self._captured_output))
self.log.info('PDF successfully created')
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'
# clear figure outputs, extracted by latex export,
# so we don't claim to be a multi-file export.
resources.pop('outputs', None)
return pdf_data, resources