Module tex

Source Code for Module tex

  1  r'''Convert LaTeX or TeX source to PDF or DVI, and escape strings for LaTeX. 
  2   
  3  **The python-tex project is obsolete!** Please have a look at Texcaller_. 
  4   
  5  .. _Texcaller: http://www.profv.de/texcaller/ 
  6   
  7  Python-tex is a convenient interface 
  8  to the TeX command line tools 
  9  that handles all kinds of errors without much fuzz. 
 10   
 11  Temporary files are always cleaned up. 
 12  The TeX interpreter is automatically re-run as often as necessary, 
 13  and an exception is thrown 
 14  in case the output fails to stabilize soon enough. 
 15  The TeX interpreter is always run in batch mode, 
 16  so it won't ever get in your way by stopping your application 
 17  when there are issues with your TeX source. 
 18  Instead, an exception is thrown 
 19  that contains all information of the TeX log. 
 20   
 21  This enables you to debug TeX related issues 
 22  directly within your application 
 23  or within an interactive Python interpreter session. 
 24   
 25  Example: 
 26   
 27  >>> from tex import latex2pdf 
 28  >>> document = ur""" 
 29  ... \documentclass{article} 
 30  ... \begin{document} 
 31  ... Hello, World! 
 32  ... \end{document} 
 33  ... """ 
 34  >>> pdf = latex2pdf(document) 
 35   
 36  >>> type(pdf) 
 37  <type 'str'> 
 38  >>> print "PDF size: %.1f KB" % (len(pdf) / 1024.0) 
 39  PDF size: 5.6 KB 
 40  >>> pdf[:5] 
 41  '%PDF-' 
 42  >>> pdf[-6:] 
 43  '%%EOF\n' 
 44  ''' 
 45   
 46  __version__      = '1.8' 
 47  __author__       = 'Volker Grabsch' 
 48  __author_email__ = 'vog@notjusthosting.com' 
 49  __url__          = 'http://www.profv.de/python-tex/' 
 50  __classifiers__  = ''' 
 51                     Development Status :: 5 - Production/Stable 
 52                     Development Status :: 6 - Mature 
 53                     Development Status :: 7 - Inactive 
 54                     Intended Audience :: Developers 
 55                     License :: OSI Approved :: MIT License 
 56                     Operating System :: OS Independent 
 57                     Programming Language :: Python 
 58                     Topic :: Documentation 
 59                     Topic :: Office/Business 
 60                     Topic :: Printing 
 61                     Topic :: Software Development :: Libraries :: Python Modules 
 62                     Topic :: Text Processing :: Markup :: LaTeX 
 63                     ''' 
 64  __license__      = ''' 
 65  Permission is hereby granted, free of charge, to any person obtaining 
 66  a copy of this software and associated documentation files (the 
 67  "Software"), to deal in the Software without restriction, including 
 68  without limitation the rights to use, copy, modify, merge, publish, 
 69  distribute, sublicense, and/or sell copies of the Software, and to 
 70  permit persons to whom the Software is furnished to do so, subject 
 71  to the following conditions: 
 72   
 73  The above copyright notice and this permission notice shall be 
 74  included in all copies or substantial portions of the Software. 
 75   
 76  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
 77  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 
 78  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 
 79  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 
 80  CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 
 81  TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 
 82  SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 
 83  ''' 
 84   
 85  import os 
 86  import os.path 
 87  import random 
 88  import shutil 
 89  import string 
 90  import subprocess 
 91  import tempfile 
 92   
93 -def _file_read(filename):
94 '''Read the content of a file and close it properly.''' 95 f = file(filename, 'rb') 96 content = f.read() 97 f.close() 98 return content
99
100 -def _file_write(filename, content):
101 '''Write into a file and close it properly.''' 102 f = file(filename, 'wb') 103 f.write(content) 104 f.close()
105
106 -def convert(tex_source, input_format, output_format, max_runs=5):
107 '''Convert LaTeX or TeX source to PDF or DVI.''' 108 # check arguments 109 assert isinstance(tex_source, unicode) 110 try: 111 (tex_cmd, output_suffix) = { 112 ('tex', 'dvi'): ('tex', '.dvi'), 113 ('latex', 'dvi'): ('latex', '.dvi'), 114 ('tex', 'pdf'): ('pdftex', '.pdf'), 115 ('latex', 'pdf'): ('pdflatex', '.pdf'), 116 }[(input_format, output_format)] 117 except KeyError: 118 raise ValueError('Unable to handle conversion: %s -> %s' 119 % (input_format, output_format)) 120 if max_runs < 2: 121 raise ValueError('max_runs must be at least 2.') 122 # create temporary directory 123 tex_dir = tempfile.mkdtemp(suffix='', prefix='tex-temp-') 124 try: 125 # create LaTeX source file 126 tex_filename = os.path.join(tex_dir, 'texput.tex') 127 _file_write(tex_filename, tex_source.encode('UTF-8')) 128 # run LaTeX processor as often as necessary 129 aux_old = None 130 for i in xrange(max_runs): 131 tex_process = subprocess.Popen( 132 [tex_cmd, 133 '-interaction=batchmode', 134 '-halt-on-error', 135 '-no-shell-escape', 136 tex_filename, 137 ], 138 stdin=file(os.devnull, 'r'), 139 stdout=file(os.devnull, 'w'), 140 stderr=subprocess.STDOUT, 141 close_fds=True, 142 shell=False, 143 cwd=tex_dir, 144 env={'PATH': os.getenv('PATH')}, 145 ) 146 tex_process.wait() 147 if tex_process.returncode != 0: 148 log = _file_read(os.path.join(tex_dir, 'texput.log')) 149 raise ValueError(log) 150 aux = _file_read(os.path.join(tex_dir, 'texput.aux')) 151 if aux == aux_old: 152 # aux file stabilized 153 try: 154 return _file_read(os.path.join(tex_dir, 'texput' + output_suffix)) 155 except: 156 raise ValueError('No output file was produced.') 157 aux_old = aux 158 # TODO: 159 # Also handle makeindex and bibtex, 160 # possibly in a similar manner as described in: 161 # http://vim-latex.sourceforge.net/documentation/latex-suite/compiling-multiple.html 162 raise ValueError("%s didn't stabilize after %i runs" 163 % ('texput.aux', max_runs)) 164 finally: 165 # remove temporary directory 166 shutil.rmtree(tex_dir)
167
168 -def tex2dvi(tex_source, **kwargs):
169 '''Convert TeX source to DVI.''' 170 return convert(tex_source, 'tex', 'dvi', **kwargs)
171
172 -def latex2dvi(tex_source, **kwargs):
173 '''Convert LaTeX source to DVI.''' 174 return convert(tex_source, 'latex', 'dvi', **kwargs)
175
176 -def tex2pdf(tex_source, **kwargs):
177 '''Convert TeX source to PDF.''' 178 return convert(tex_source, 'tex', 'pdf', **kwargs)
179
180 -def latex2pdf(tex_source, **kwargs):
181 '''Convert LaTeX source to PDF.''' 182 return convert(tex_source, 'latex', 'pdf', **kwargs)
183 184 _latex_special_chars = { 185 u'$': u'\\$', 186 u'%': u'\\%', 187 u'&': u'\\&', 188 u'#': u'\\#', 189 u'_': u'\\_', 190 u'{': u'\\{', 191 u'}': u'\\}', 192 u'[': u'{[}', 193 u']': u'{]}', 194 u'"': u"{''}", 195 u'\\': u'\\textbackslash{}', 196 u'~': u'\\textasciitilde{}', 197 u'<': u'\\textless{}', 198 u'>': u'\\textgreater{}', 199 u'^': u'\\textasciicircum{}', 200 u'`': u'{}`', # avoid ?` and !` 201 u'\n': u'\\\\', 202 } 203
204 -def escape_latex(s):
205 r'''Escape a unicode string for LaTeX. 206 207 :Warning: 208 The source string must not contain empty lines such as: 209 - u'\n...' -- empty first line 210 - u'...\n\n...' -- empty line in between 211 - u'...\n' -- empty last line 212 213 :Parameters: 214 - `s`: unicode object to escape for LaTeX 215 216 >>> s = u'\\"{}_&%a$b#\nc[]"~<>^`\\' 217 >>> escape_latex(s) 218 u"\\textbackslash{}{''}\\{\\}\\_\\&\\%a\\$b\\#\\\\c{[}{]}{''}\\textasciitilde{}\\textless{}\\textgreater{}\\textasciicircum{}{}`\\textbackslash{}" 219 >>> print s 220 \"{}_&%a$b# 221 c[]"~<>^`\ 222 >>> print escape_latex(s) 223 \textbackslash{}{''}\{\}\_\&\%a\$b\#\\c{[}{]}{''}\textasciitilde{}\textless{}\textgreater{}\textasciicircum{}{}`\textbackslash{} 224 ''' 225 return u''.join(_latex_special_chars.get(c, c) for c in s)
226
227 -def _test():
228 '''Run all doc tests of this module.''' 229 import doctest, tex 230 return doctest.testmod(tex)
231 232 if __name__ == '__main__': 233 _test() 234