Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitc981774

Browse files
committed
Merge pull request#1124 from pwuertz/pgf-backend
PGF backend,fix#1116,#1118 and#1128
2 parents354721b +138d55d commitc981774

File tree

2 files changed

+149
-100
lines changed

2 files changed

+149
-100
lines changed

‎lib/matplotlib/backends/backend_pgf.py

Lines changed: 121 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
frommatplotlibimport_png,rcParams
1919
frommatplotlibimportfont_manager
2020
frommatplotlib.ft2fontimportFT2Font
21+
frommatplotlib.cbookimportis_string_like,is_writable_file_like
2122

2223
###############################################################################
2324

@@ -220,15 +221,33 @@ def _build_latex_header():
220221
# Create LaTeX header with some content, else LaTeX will load some
221222
# math fonts later when we don't expect the additional output on stdout.
222223
# 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)
224+
latex_header= [r"\documentclass{minimal}",
225+
latex_preamble,
226+
latex_fontspec,
227+
r"\begin{document}",
228+
r"text $math \mu$",# force latex to load fonts now
229+
r"\typeout{pgf_backend_query_start}"]
230+
return"\n".join(latex_header)
231+
232+
def_stdin_writeln(self,s):
233+
self.latex_stdin_utf8.write(s)
234+
self.latex_stdin_utf8.write("\n")
235+
self.latex_stdin_utf8.flush()
236+
237+
def_expect(self,s):
238+
exp=s.encode("utf8")
239+
buf=bytearray()
240+
whileTrue:
241+
b=self.latex.stdout.read(1)
242+
buf+=b
243+
ifbuf[-len(exp):]==exp:
244+
break
245+
ifnotlen(b):
246+
raiseLatexError("LaTeX process halted",buf.decode("utf8"))
247+
returnbuf.decode("utf8")
230248

231-
returnlatex_header
249+
def_expect_prompt(self):
250+
returnself._expect("\n*")
232251

233252
def__init__(self):
234253
self.texcommand=get_texcommand()
@@ -238,27 +257,23 @@ def __init__(self):
238257
# test the LaTeX setup to ensure a clean startup of the subprocess
239258
latex=subprocess.Popen([self.texcommand,"-halt-on-error"],
240259
stdin=subprocess.PIPE,
241-
stdout=subprocess.PIPE,
242-
universal_newlines=True)
243-
stdout,stderr=latex.communicate(self.latex_header+latex_end)
260+
stdout=subprocess.PIPE)
261+
test_input=self.latex_header+latex_end
262+
stdout,stderr=latex.communicate(test_input.encode("utf-8"))
244263
iflatex.returncode!=0:
245264
raiseLatexError("LaTeX returned an error, probably missing font or error in preamble:\n%s"%stdout)
246265

247-
# open LaTeX process
266+
# open LaTeX process for real work
248267
latex=subprocess.Popen([self.texcommand,"-halt-on-error"],
249268
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-
whilenotlatex.stdout.readline().startswith("*pgf_backend_query_start"):
256-
pass
257-
whilelatex.stdout.read(1)!='*':
258-
pass
269+
stdout=subprocess.PIPE)
259270
self.latex=latex
260-
self.latex_stdin=codecs.getwriter("utf-8")(latex.stdin)
261-
self.latex_stdout=codecs.getreader("utf-8")(latex.stdout)
271+
self.latex_stdin_utf8=codecs.getwriter("utf8")(self.latex.stdin)
272+
# write header with 'pgf_backend_query_start' token
273+
self._stdin_writeln(self._build_latex_header())
274+
# read all lines until our 'pgf_backend_query_start' token appears
275+
self._expect("*pgf_backend_query_start")
276+
self._expect_prompt()
262277

263278
# cache for strings already processed
264279
self.str_cache= {}
@@ -267,8 +282,8 @@ def __del__(self):
267282
ifrcParams.get("pgf.debug",False):
268283
print"deleting LatexManager"
269284
try:
270-
self.latex.terminate()
271-
self.latex.wait()
285+
self.latex_stdin_utf8.close()
286+
self.latex.communicate()
272287
except:
273288
pass
274289
try:
@@ -277,19 +292,6 @@ def __del__(self):
277292
except:
278293
pass
279294

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-
whileTrue:
286-
buf.append(self.latex_stdout.read(1))
287-
ifbuf[-1]=="*"andbuf[-2]=="\n":
288-
break
289-
ifbuf[-1]=="":
290-
raiseLatexError("LaTeX process halted",u"".join(buf))
291-
return"".join(buf)
292-
293295
defget_width_height_descent(self,text,prop):
294296
"""
295297
Get the width, total height and descent for a text typesetted by the
@@ -298,30 +300,27 @@ def get_width_height_descent(self, text, prop):
298300

299301
# apply font properties and define textbox
300302
prop_cmds=_font_properties_str(prop)
301-
textbox=u"\\sbox0{%s %s}\n"% (prop_cmds,text)
303+
textbox="\\sbox0{%s %s}"% (prop_cmds,text)
302304

303305
# check cache
304306
iftextboxinself.str_cache:
305307
returnself.str_cache[textbox]
306308

307309
# send textbox to LaTeX and wait for prompt
308-
self.latex_stdin.write(unicode(textbox))
309-
self.latex_stdin.flush()
310+
self._stdin_writeln(textbox)
310311
try:
311-
self._wait_for_prompt()
312+
self._expect_prompt()
312313
exceptLatexErrorase:
313-
msg=u"Error processing '%s'\nLaTeX Output:\n%s"% (text,e.latex_output)
314+
msg="Error processing '%s'\nLaTeX Output:\n%s"% (text,e.latex_output)
314315
raiseValueError(msg)
315316

316317
# 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()
318+
self._stdin_writeln(r"\typeout{\the\wd0,\the\ht0,\the\dp0}")
320319
# read answer from latex and advance to the next prompt
321320
try:
322-
answer=self._wait_for_prompt()
321+
answer=self._expect_prompt()
323322
exceptLatexErrorase:
324-
msg=u"Error processing '%s'\nLaTeX Output:\n%s"% (text,e.latex_output)
323+
msg="Error processing '%s'\nLaTeX Output:\n%s"% (text,e.latex_output)
325324
raiseValueError(msg)
326325

327326
# parse metrics from the answer string
@@ -625,12 +624,7 @@ def __init__(self, *args):
625624
defget_default_filetype(self):
626625
return'pdf'
627626

628-
defprint_pgf(self,filename,*args,**kwargs):
629-
"""
630-
Output pgf commands for drawing the figure so it can be included and
631-
rendered in latex documents.
632-
"""
633-
627+
def_print_pgf_to_fh(self,fh):
634628
header_text=r"""%% Creator: Matplotlib, PGF backend
635629
%%
636630
%% To include the figure in your LaTeX document, write
@@ -660,37 +654,50 @@ def print_pgf(self, filename, *args, **kwargs):
660654
# get figure size in inch
661655
w,h=self.figure.get_figwidth(),self.figure.get_figheight()
662656

663-
# start a pgfpicture environment and set a bounding box
664-
withcodecs.open(filename,"w",encoding="utf-8")asfh:
665-
fh.write(header_text)
666-
fh.write(header_info_preamble)
667-
fh.write("\n")
668-
writeln(fh,r"\begingroup")
669-
writeln(fh,r"\makeatletter")
670-
writeln(fh,r"\begin{pgfpicture}")
671-
writeln(fh,r"\pgfpathrectangle{\pgfpointorigin}{\pgfqpoint{%fin}{%fin}}"% (w,h))
672-
writeln(fh,r"\pgfusepath{use as bounding box}")
673-
674-
renderer=RendererPgf(self.figure,fh)
675-
self.figure.draw(renderer)
676-
677-
# end the pgfpicture environment
678-
writeln(fh,r"\end{pgfpicture}")
679-
writeln(fh,r"\makeatother")
680-
writeln(fh,r"\endgroup")
681-
682-
defprint_pdf(self,filename,*args,**kwargs):
657+
# create pgfpicture environment and write the pgf code
658+
fh.write(header_text)
659+
fh.write(header_info_preamble)
660+
fh.write("\n")
661+
writeln(fh,r"\begingroup")
662+
writeln(fh,r"\makeatletter")
663+
writeln(fh,r"\begin{pgfpicture}")
664+
writeln(fh,r"\pgfpathrectangle{\pgfpointorigin}{\pgfqpoint{%fin}{%fin}}"% (w,h))
665+
writeln(fh,r"\pgfusepath{use as bounding box}")
666+
renderer=RendererPgf(self.figure,fh)
667+
self.figure.draw(renderer)
668+
669+
# end the pgfpicture environment
670+
writeln(fh,r"\end{pgfpicture}")
671+
writeln(fh,r"\makeatother")
672+
writeln(fh,r"\endgroup")
673+
674+
defprint_pgf(self,fname_or_fh,*args,**kwargs):
683675
"""
684-
Use LaTeX to compile a Pgf generated figure to PDF.
676+
Output pgf commands for drawing the figure so it can be included and
677+
rendered in latex documents.
685678
"""
686-
w,h=self.figure.get_figwidth(),self.figure.get_figheight()
679+
ifkwargs.get("dryrun",False):return
680+
681+
# figure out where the pgf is to be written to
682+
ifis_string_like(fname_or_fh):
683+
withcodecs.open(fname_or_fh,"w",encoding="utf-8")asfh:
684+
self._print_pgf_to_fh(fh)
685+
elifis_writable_file_like(fname_or_fh):
686+
raiseValueError("saving pgf to a stream is not supported, "+ \
687+
"consider using the pdf option of the pgf-backend")
688+
else:
689+
raiseValueError("filename must be a path")
687690

688-
target=os.path.abspath(filename)
691+
def_print_pdf_to_fh(self,fh):
692+
w,h=self.figure.get_figwidth(),self.figure.get_figheight()
689693

690694
try:
695+
# create and switch to temporary directory
691696
tmpdir=tempfile.mkdtemp()
692697
cwd=os.getcwd()
693698
os.chdir(tmpdir)
699+
700+
# print figure to pgf and compile it with latex
694701
self.print_pgf("figure.pgf")
695702

696703
latex_preamble=get_preamble()
@@ -706,45 +713,72 @@ def print_pdf(self, filename, *args, **kwargs):
706713
\centering
707714
\input{figure.pgf}
708715
\end{document}"""% (w,h,latex_preamble,latex_fontspec)
709-
withcodecs.open("figure.tex","w","utf-8")asfh:
710-
fh.write(latexcode)
716+
withcodecs.open("figure.tex","w","utf-8")asfh_tex:
717+
fh_tex.write(latexcode)
711718

712719
texcommand=get_texcommand()
713720
cmdargs= [texcommand,"-interaction=nonstopmode","-halt-on-error","figure.tex"]
714721
try:
715-
stdout=subprocess.check_output(cmdargs,universal_newlines=True,stderr=subprocess.STDOUT)
716-
except:
717-
raiseRuntimeError("%s was not able to process your file.\n\nFull log:\n%s"% (texcommand,stdout))
718-
shutil.copyfile("figure.pdf",target)
722+
subprocess.check_output(cmdargs,stderr=subprocess.STDOUT)
723+
exceptsubprocess.CalledProcessErrorase:
724+
raiseRuntimeError("%s was not able to process your file.\n\nFull log:\n%s"% (texcommand,e.output))
725+
726+
# copy file contents to target
727+
withopen("figure.pdf","rb")asfh_src:
728+
shutil.copyfileobj(fh_src,fh)
719729
finally:
720730
os.chdir(cwd)
721731
try:
722732
shutil.rmtree(tmpdir)
723733
except:
724734
sys.stderr.write("could not delete tmp directory %s\n"%tmpdir)
725735

726-
defprint_png(self,filename,*args,**kwargs):
736+
defprint_pdf(self,fname_or_fh,*args,**kwargs):
727737
"""
728-
Use LaTeX to compile apgffigure topdf and convert it to png.
738+
Use LaTeX to compile aPgf generatedfigure toPDF.
729739
"""
740+
# figure out where the pdf is to be written to
741+
ifis_string_like(fname_or_fh):
742+
withopen(fname_or_fh,"wb")asfh:
743+
self._print_pdf_to_fh(fh)
744+
elifis_writable_file_like(fname_or_fh):
745+
self._print_pdf_to_fh(fname_or_fh)
746+
else:
747+
raiseValueError("filename must be a path or a file-like object")
730748

749+
def_print_png_to_fh(self,fh):
731750
converter=make_pdf_to_png_converter()
732751

733-
target=os.path.abspath(filename)
734752
try:
753+
# create and switch to temporary directory
735754
tmpdir=tempfile.mkdtemp()
736755
cwd=os.getcwd()
737756
os.chdir(tmpdir)
757+
# create pdf and try to convert it to png
738758
self.print_pdf("figure.pdf")
739759
converter("figure.pdf","figure.png",dpi=self.figure.dpi)
740-
shutil.copyfile("figure.png",target)
760+
# copy file contents to target
761+
withopen("figure.png","rb")asfh_src:
762+
shutil.copyfileobj(fh_src,fh)
741763
finally:
742764
os.chdir(cwd)
743765
try:
744766
shutil.rmtree(tmpdir)
745767
except:
746768
sys.stderr.write("could not delete tmp directory %s\n"%tmpdir)
747769

770+
defprint_png(self,fname_or_fh,*args,**kwargs):
771+
"""
772+
Use LaTeX to compile a pgf figure to pdf and convert it to png.
773+
"""
774+
ifis_string_like(fname_or_fh):
775+
withopen(fname_or_fh,"wb")asfh:
776+
self._print_png_to_fh(fh)
777+
elifis_writable_file_like(fname_or_fh):
778+
self._print_png_to_fh(fname_or_fh)
779+
else:
780+
raiseValueError("filename must be a path or a file-like object")
781+
748782
def_render_texts_pgf(self,fh):
749783
# TODO: currently unused code path
750784

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp