Show More
@@ -1,60 +1,130 b'' | |||
|
1 | 1 | """ |
|
2 | 2 | Contains writer for writing nbconvert output to PDF. |
|
3 | 3 | """ |
|
4 | 4 | #----------------------------------------------------------------------------- |
|
5 | 5 | #Copyright (c) 2013, the IPython Development Team. |
|
6 | 6 | # |
|
7 | 7 | #Distributed under the terms of the Modified BSD License. |
|
8 | 8 | # |
|
9 | 9 | #The full license is in the file COPYING.txt, distributed with this software. |
|
10 | 10 | #----------------------------------------------------------------------------- |
|
11 | 11 | |
|
12 | 12 | #----------------------------------------------------------------------------- |
|
13 | 13 | # Imports |
|
14 | 14 | #----------------------------------------------------------------------------- |
|
15 | 15 | |
|
16 | 16 | import subprocess |
|
17 | 17 | import os |
|
18 | 18 | |
|
19 | 19 | from IPython.utils.traitlets import Integer, List, Bool |
|
20 | 20 | |
|
21 | 21 | from .base import PostProcessorBase |
|
22 | 22 | |
|
23 | 23 | #----------------------------------------------------------------------------- |
|
24 | 24 | # Classes |
|
25 | 25 | #----------------------------------------------------------------------------- |
|
26 | 26 | class PDFPostProcessor(PostProcessorBase): |
|
27 | 27 | """Writer designed to write to PDF files""" |
|
28 | 28 | |
|
29 | 29 | iteration_count = Integer(3, config=True, help=""" |
|
30 | 30 | How many times pdflatex will be called. |
|
31 | 31 | """) |
|
32 | 32 | |
|
33 | command = List(["pdflatex", "{filename}"], config=True, help=""" | |
|
33 | pdflatex_command = List(["pdflatex", "{filename}"], config=True, help=""" | |
|
34 | 34 | Shell command used to compile PDF.""") |
|
35 | 35 | |
|
36 | bibtex_command = List(["bibtex", "{filename}"], config=True, help=""" | |
|
37 | Shell command used to run bibtex.""") | |
|
38 | ||
|
36 | 39 | verbose = Bool(False, config=True, help=""" |
|
37 | 40 | Whether or not to display the output of the compile call. |
|
38 | 41 | """) |
|
39 | 42 | |
|
40 | def postprocess(self, input): | |
|
41 | """ | |
|
42 | Consume and write Jinja output a PDF. | |
|
43 | See files.py for more... | |
|
43 | temp_file_exts = List(['.aux', '.bbl', '.blg', '.idx', '.log', '.out'], | |
|
44 | config=True, help=""" | |
|
45 | Filename extensions of temp files to remove after running | |
|
46 | """) | |
|
47 | ||
|
48 | def run_command(self, command_list, filename, count, log_function): | |
|
49 | """Run pdflatex or bibtext count times. | |
|
50 | ||
|
51 | Parameters | |
|
52 | ---------- | |
|
53 | command_list : list | |
|
54 | A list of args to provide to Popen. Each element of this | |
|
55 | list will be interpolated with the filename to convert. | |
|
56 | filename : unicode | |
|
57 | The name of the file to convert. | |
|
58 | count : int | |
|
59 | How many times to run the command. | |
|
60 | ||
|
61 | Returns | |
|
62 | ------- | |
|
63 | continue : bool | |
|
64 | A boolean indicating if the command was successful (True) | |
|
65 | or failed (False). | |
|
44 | 66 |
|
|
45 |
|
|
|
46 | self.log.info("Building PDF: %s", command) | |
|
67 | command = [c.format(filename=filename) for c in command_list] | |
|
68 | times = 'time' if count == 1 else 'times' | |
|
69 | self.log.info("Running %s %i %s: %s", command_list[0], count, times, command) | |
|
47 | 70 |
|
|
48 | 71 |
|
|
49 |
|
|
|
72 | for index in range(count): | |
|
50 | 73 |
|
|
51 | 74 |
|
|
52 | 75 |
|
|
53 | 76 |
|
|
54 | 77 |
|
|
55 | 78 |
|
|
56 | 79 |
|
|
57 | 80 |
|
|
58 | 81 |
|
|
59 |
|
|
|
82 | log_function(command, out) | |
|
83 | return False # failure | |
|
84 | return True # success | |
|
85 | ||
|
86 | def run_pdflatex(self, filename): | |
|
87 | """Run pdflatex self.iteration_count times.""" | |
|
88 | ||
|
89 | def log_error(command, out): | |
|
90 | self.log.critical(u"pdflatex failed: %s\n%s", command, out) | |
|
91 | ||
|
92 | return self.run_command(self.pdflatex_command, filename, | |
|
93 | self.iteration_count, log_error) | |
|
94 | ||
|
95 | def run_bibtex(self, filename): | |
|
96 | """Run bibtex self.iteration_count times.""" | |
|
97 | filename = filename.rstrip('.tex') | |
|
98 | ||
|
99 | def log_error(command, out): | |
|
100 | self.log.warn('bibtex had problems, most likely because there were no citations') | |
|
101 | self.log.debug(u"bibtex output: %s\n%s", command, out) | |
|
102 | ||
|
103 | return self.run_command(self.bibtex_command, filename, 1, log_error) | |
|
104 | ||
|
105 | def clean_temp_files(self, filename): | |
|
106 | """Remove temporary files created by pdflatex/bibtext.""" | |
|
107 | self.log.info("Removing temporary LaTeX files") | |
|
108 | filename = filename.strip('.tex') | |
|
109 | for ext in self.temp_file_exts: | |
|
110 | try: | |
|
111 | os.remove(filename+ext) | |
|
112 | except OSError: | |
|
113 | pass | |
|
114 | ||
|
115 | def postprocess(self, filename): | |
|
116 | """Build a PDF by running pdflatex and bibtex""" | |
|
117 | self.log.info("Building PDF") | |
|
118 | cont = self.run_pdflatex(filename) | |
|
119 | if cont: | |
|
120 | cont = self.run_bibtex(filename) | |
|
121 | else: | |
|
122 | self.clean_temp_files(filename) | |
|
60 | 123 |
|
|
124 | if cont: | |
|
125 | cont = self.run_pdflatex(filename) | |
|
126 | self.clean_temp_files(filename) | |
|
127 | if os.path.isfile(filename.rstrip('.tex')+'.pdf'): | |
|
128 | self.log.info('PDF successfully created') | |
|
129 | return | |
|
130 |
@@ -1,64 +1,68 b'' | |||
|
1 | 1 | """ |
|
2 | 2 | Module with tests for the PDF post-processor |
|
3 | 3 | """ |
|
4 | 4 | |
|
5 | 5 | #----------------------------------------------------------------------------- |
|
6 | 6 | # Copyright (c) 2013, the IPython Development Team. |
|
7 | 7 | # |
|
8 | 8 | # Distributed under the terms of the Modified BSD License. |
|
9 | 9 | # |
|
10 | 10 | # The full license is in the file COPYING.txt, distributed with this software. |
|
11 | 11 | #----------------------------------------------------------------------------- |
|
12 | 12 | |
|
13 | 13 | #----------------------------------------------------------------------------- |
|
14 | 14 | # Imports |
|
15 | 15 | #----------------------------------------------------------------------------- |
|
16 | 16 | |
|
17 | 17 | import os |
|
18 | 18 | |
|
19 | 19 | from IPython.testing import decorators as dec |
|
20 | 20 | |
|
21 | 21 | from ...tests.base import TestsBase |
|
22 | 22 | from ..pdf import PDFPostProcessor |
|
23 | 23 | |
|
24 | 24 | |
|
25 | 25 | #----------------------------------------------------------------------------- |
|
26 | 26 | # Constants |
|
27 | 27 | #----------------------------------------------------------------------------- |
|
28 | 28 | |
|
29 | 29 | HELLO_WORLD = r"""% hello.tex - Our first LaTeX example! |
|
30 | 30 | \documentclass{article} |
|
31 | 31 | \begin{document} |
|
32 | 32 | Hello World! |
|
33 | 33 | \end{document}""" |
|
34 | 34 | |
|
35 | 35 | |
|
36 | 36 | #----------------------------------------------------------------------------- |
|
37 | 37 | # Class |
|
38 | 38 | #----------------------------------------------------------------------------- |
|
39 | 39 | |
|
40 | 40 | class TestPDF(TestsBase): |
|
41 | 41 | """Contains test functions for pdf.py""" |
|
42 | 42 | |
|
43 | 43 | |
|
44 | 44 | def test_constructor(self): |
|
45 | 45 | """Can a PDFPostProcessor be constructed?""" |
|
46 | 46 | PDFPostProcessor() |
|
47 | 47 | |
|
48 | 48 | |
|
49 | 49 | @dec.onlyif_cmds_exist('pdflatex') |
|
50 | 50 | def test_pdf(self): |
|
51 | 51 | """Can a PDF be made using the PDFPostProcessor?""" |
|
52 | 52 | |
|
53 | 53 | # Work in a temporary directory with hello world latex in it. |
|
54 | 54 | with self.create_temp_cwd(): |
|
55 | 55 | with open('a.tex', 'w') as f: |
|
56 | 56 | f.write(HELLO_WORLD) |
|
57 | 57 | |
|
58 | 58 | # Construct post-processor |
|
59 | 59 | processor = PDFPostProcessor() |
|
60 | 60 | processor.verbose = False |
|
61 | 61 | processor('a.tex') |
|
62 | 62 | |
|
63 | 63 | # Check that the PDF was created. |
|
64 | 64 | assert os.path.isfile('a.pdf') |
|
65 | ||
|
66 | # Make sure that temp files are cleaned up | |
|
67 | for ext in processor.temp_file_exts: | |
|
68 | assert not os.path.isfile('a'+ext) |
General Comments 0
You need to be logged in to leave comments.
Login now