@@ -220,15 +220,33 @@ def _build_latex_header():
220220 # Create LaTeX header with some content, else LaTeX will load some
221221 # math fonts later when we don't expect the additional output on stdout.
222222 # TODO: is this sufficient?
223- latex_header = u"""\\ documentclass{minimal}
224- %s
225- %s
226- \\ begin{document}
227- text $math \mu$ %% force latex to load fonts now
228- \\ typeout{pgf_backend_query_start}
229- """ % (latex_preamble , latex_fontspec )
223+ latex_header = [r"\documentclass{minimal}" ,
224+ latex_preamble ,
225+ latex_fontspec ,
226+ r"\begin{document}" ,
227+ r"text $math \mu$" , # force latex to load fonts now
228+ r"\typeout{pgf_backend_query_start}" ]
229+ return "\n " .join (latex_header )
230+
231+ def _stdin_writeln (self , s ):
232+ self .latex_stdin_utf8 .write (s )
233+ self .latex_stdin_utf8 .write ("\n " )
234+ self .latex_stdin_utf8 .flush ()
235+
236+ def _expect (self , s ):
237+ exp = s .encode ("utf8" )
238+ buf = bytearray ()
239+ while True :
240+ b = self .latex .stdout .read (1 )
241+ buf += b
242+ if buf [- len (exp ):] == exp :
243+ break
244+ if not len (b ):
245+ raise LatexError ("LaTeX process halted" , buf .decode ("utf8" ))
246+ return buf .decode ("utf8" )
230247
231- return latex_header
248+ def _expect_prompt (self ):
249+ return self ._expect ("\n *" )
232250
233251 def __init__ (self ):
234252 self .texcommand = get_texcommand ()
@@ -238,27 +256,23 @@ def __init__(self):
238256 # test the LaTeX setup to ensure a clean startup of the subprocess
239257 latex = subprocess .Popen ([self .texcommand , "-halt-on-error" ],
240258 stdin = subprocess .PIPE ,
241- stdout = subprocess .PIPE ,
242- universal_newlines = True )
243- stdout , stderr = latex .communicate (self . latex_header + latex_end )
259+ stdout = subprocess .PIPE )
260+ test_input = self . latex_header + latex_end
261+ stdout , stderr = latex .communicate (test_input . encode ( "utf-8" ) )
244262 if latex .returncode != 0 :
245263 raise LatexError ("LaTeX returned an error, probably missing font or error in preamble:\n %s" % stdout )
246264
247- # open LaTeX process
265+ # open LaTeX process for real work
248266 latex = subprocess .Popen ([self .texcommand , "-halt-on-error" ],
249267 stdin = subprocess .PIPE ,
250- stdout = subprocess .PIPE ,
251- universal_newlines = True )
252- latex .stdin .write (self .latex_header )
253- latex .stdin .flush ()
254- # read all lines until our 'pgf_backend_query_start' token appears
255- while not latex .stdout .readline ().startswith ("*pgf_backend_query_start" ):
256- pass
257- while latex .stdout .read (1 ) != '*' :
258- pass
268+ stdout = subprocess .PIPE )
259269 self .latex = latex
260- self .latex_stdin = codecs .getwriter ("utf-8" )(latex .stdin )
261- self .latex_stdout = codecs .getreader ("utf-8" )(latex .stdout )
270+ self .latex_stdin_utf8 = codecs .getwriter ("utf8" )(self .latex .stdin )
271+ # write header with 'pgf_backend_query_start' token
272+ self ._stdin_writeln (self ._build_latex_header ())
273+ # read all lines until our 'pgf_backend_query_start' token appears
274+ self ._expect ("*pgf_backend_query_start" )
275+ self ._expect_prompt ()
262276
263277 # cache for strings already processed
264278 self .str_cache = {}
@@ -277,19 +291,6 @@ def __del__(self):
277291 except :
278292 pass
279293
280- def _wait_for_prompt (self ):
281- """
282- Read all bytes from LaTeX stdout until a new line starts with a *.
283- """
284- buf = ["" ]
285- while True :
286- buf .append (self .latex_stdout .read (1 ))
287- if buf [- 1 ] == "*" and buf [- 2 ] == "\n " :
288- break
289- if buf [- 1 ] == "" :
290- raise LatexError ("LaTeX process halted" , u"" .join (buf ))
291- return "" .join (buf )
292-
293294 def get_width_height_descent (self , text , prop ):
294295 """
295296 Get the width, total height and descent for a text typesetted by the
@@ -298,30 +299,27 @@ def get_width_height_descent(self, text, prop):
298299
299300 # apply font properties and define textbox
300301 prop_cmds = _font_properties_str (prop )
301- textbox = u "\\ sbox0{%s %s}\n " % (prop_cmds , text )
302+ textbox = "\\ sbox0{%s %s}" % (prop_cmds , text )
302303
303304 # check cache
304305 if textbox in self .str_cache :
305306 return self .str_cache [textbox ]
306307
307308 # send textbox to LaTeX and wait for prompt
308- self .latex_stdin .write (unicode (textbox ))
309- self .latex_stdin .flush ()
309+ self ._stdin_writeln (textbox )
310310 try :
311- self ._wait_for_prompt ()
311+ self ._expect_prompt ()
312312 except LatexError as e :
313- msg = u "Error processing '%s'\n LaTeX Output:\n %s" % (text , e .latex_output )
313+ msg = "Error processing '%s'\n LaTeX Output:\n %s" % (text , e .latex_output )
314314 raise ValueError (msg )
315315
316316 # typeout width, height and text offset of the last textbox
317- query = "\\ typeout{\\ the\\ wd0,\\ the\\ ht0,\\ the\\ dp0}\n "
318- self .latex_stdin .write (query )
319- self .latex_stdin .flush ()
317+ self ._stdin_writeln (r"\typeout{\the\wd0,\the\ht0,\the\dp0}" )
320318 # read answer from latex and advance to the next prompt
321319 try :
322- answer = self ._wait_for_prompt ()
320+ answer = self ._expect_prompt ()
323321 except LatexError as e :
324- msg = u "Error processing '%s'\n LaTeX Output:\n %s" % (text , e .latex_output )
322+ msg = "Error processing '%s'\n LaTeX Output:\n %s" % (text , e .latex_output )
325323 raise ValueError (msg )
326324
327325 # parse metrics from the answer string
@@ -712,7 +710,7 @@ def print_pdf(self, filename, *args, **kwargs):
712710 texcommand = get_texcommand ()
713711 cmdargs = [texcommand , "-interaction=nonstopmode" , "-halt-on-error" , "figure.tex" ]
714712 try :
715- stdout = subprocess .check_output (cmdargs , universal_newlines = True , stderr = subprocess .STDOUT )
713+ stdout = subprocess .check_output (cmdargs , stderr = subprocess .STDOUT )
716714 except :
717715 raise RuntimeError ("%s was not able to process your file.\n \n Full log:\n %s" % (texcommand , stdout ))
718716 shutil .copyfile ("figure.pdf" , target )
0 commit comments