Source code for ipypublish.postprocessors.pdfexport

#!/usr/bin/env python
""" a module for exporting latex file to pdf
TODO could this be refactored as nbconvert postprocessor
"""
import os
import shutil
import tempfile
from subprocess import Popen, PIPE, STDOUT
import webbrowser

import six
from traitlets import Bool, Unicode

from ipypublish.postprocessors.base import IPyPostProcessor


[docs]class PDFExport(IPyPostProcessor): """ a post processor to convert tex to pdf using latexmk """ @property def allowed_mimetypes(self): return "text/latex" @property def requires_path(self): return True @property def logger_name(self): return "pdf-export" files_folder = Unicode( "_static", help="the path (relative to the main file path) " "containing external files", ).tag(config=True) convert_in_temp = Bool( False, help="run conversion in a temporary directory, " "and copy back only PDF file", ).tag(config=True) debug_mode = Bool(False, help="run in debug mode").tag(config=True) open_in_browser = Bool( False, help="launch a html page containing a pdf browser" ).tag(config=True)
[docs] def run_postprocess(self, stream, mimetype, filepath, resources): """ should not be called directly Parameters ---------- stream: str the main file contents filepath: None or pathlib.Path the path to the output file Returns ------- stream: str filepath: None or pathlib.Path """ self.logger.info("running pdf conversion") self._export_pdf(filepath) return stream, filepath, resources
def _export_pdf(self, texpath): if not texpath.exists(): self.handle_error( "the target file path does not exist: {}".format(texpath), IOError ) texname = os.path.splitext(texpath.name)[0] # NOTE outdir was originally passed, but would this ever be different # to the texpath's parent external_files = texpath.parent.joinpath(self.files_folder) if external_files.exists() and not external_files.is_dir(): self.handle_error( "the external folder path is not a directory: {}".format( external_files ), IOError, ) self.check_exe_exists( "latexmk", "requires the latexmk executable to run. " "See http://mg.readthedocs.io/latexmk.html#installation", ) if self.convert_in_temp: out_folder = tempfile.mkdtemp() try: exitcode = self._run_latexmk(texpath, out_folder, external_files) if exitcode == 0: shutil.copyfile( os.path.join(out_folder, texname + ".pdf"), str(texpath.parent.joinpath(texname + ".pdf")), ) finally: shutil.rmtree(out_folder) else: exitcode = self._run_latexmk(texpath, str(texpath.parent), external_files) if exitcode == 0: self.logger.info("pdf conversion complete") view_pdf = VIEW_PDF.format(pdf_name=texname.replace(" ", "%20") + ".pdf") view_pdf_path = texpath.parent.joinpath(texname + ".view_pdf.html") with view_pdf_path.open("w", encoding="utf-8") as fobj: fobj.write(six.u(view_pdf)) else: self.handle_error( "pdf conversion failed: " "Try running with pdf-debug flag", RuntimeError, ) if self.open_in_browser: # 2 opens the url in a new tab webbrowser.open(view_pdf_path.as_uri(), new=2) return def _run_latexmk(self, texpath, out_folder, external_files): """ run latexmk conversion """ # make sure tex file in right place outpath = os.path.join(out_folder, texpath.name) if os.path.dirname(str(texpath)) != str(out_folder): self.logger.debug( "copying tex file to: {}".format( os.path.join(str(out_folder), texpath.name) ) ) shutil.copyfile(str(texpath), os.path.join(str(out_folder), texpath.name)) # make sure the external files folder is in right place if external_files.exists(): self.logger.debug("external files folder set") outfilespath = os.path.join(out_folder, str(external_files.name)) if str(external_files) != str(outfilespath): self.logger.debug("copying external files to: {}".format(outfilespath)) if os.path.exists(outfilespath): shutil.rmtree(outfilespath) shutil.copytree(str(external_files), str(outfilespath)) # run latexmk in correct folder with change_dir(out_folder): latexmk = ["latexmk", "-xelatex", "-bibtex", "-pdf"] latexmk += [] if self.debug_mode else ["--interaction=batchmode"] latexmk += [outpath] self.logger.info("running: " + " ".join(latexmk)) def log_latexmk_output(pipe): for line in iter(pipe.readline, b""): self.logger.info("latexmk: {}".format(line.decode("utf-8").strip())) process = Popen(latexmk, stdout=PIPE, stderr=STDOUT) with process.stdout: log_latexmk_output(process.stdout) exitcode = process.wait() # 0 means success return exitcode
[docs]class change_dir: # noqa: N801 """Context manager for changing the current working directory""" def __init__(self, new_path): self.newPath = os.path.expanduser(new_path) def __enter__(self): self.savedPath = os.getcwd() os.chdir(self.newPath) def __exit__(self, etype, value, traceback): os.chdir(self.savedPath)
VIEW_PDF = r""" <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> <head> <meta http-equiv="content-type" content="text/html; charset=windows-1252"> <title>View PDF</title> <script type="text/javascript"> var filepath = "{pdf_name}"; var timer = null; function refresh(){{ var d = document.getElementById("pdf"); // gets pdf-div d.innerHTML = '<iframe style="position: absolute; height: 100%; border: none" id="ipdf" src='+window.filepath+' width="100%"></iframe>'; }} function autoRefresh(){{ timer = setTimeout("autoRefresh()", 20000); refresh(); }} function manualRefresh(){{ clearTimeout(timer); refresh(); }} function check_pdf() {{ var newfile = document.f.userFile.value; ext = newfile.substring(newfile.length-3,newfile.length); ext = ext.toLowerCase(); if(ext != 'pdf') {{ alert('You selected a .'+ext+ ' file; please select a .pdf file instead!'+filepath); return false; }} else alert(newfile); window.filepath = newfile; alert(filepath); refresh(); return true; }} </script> </head> <body> <!-- <form name=f onsubmit="return check_pdf();" action='' method='POST' enctype='multipart/form-data'> <input type='submit' name='upload_btn' value='upload'> <input type='file' name='userFile' accept="application/pdf"> </form> --> <button onclick="manualRefresh()">manual refresh</button> <button onclick="autoRefresh()">auto refresh</button> <div id="pdf"></div> </body> <script type="text/javascript">refresh();</script> </html> """ # noqa: E501