2 # -*- coding: ISO-8859-1 -*-
5 # Copyright (C) 2002-2004 Jörg Lehmann <joergl@users.sourceforge.net>
6 # Copyright (C) 2002-2004 André Wobst <wobsta@users.sourceforge.net>
8 # This file is part of PyX (http://pyx.sourceforge.net/).
10 # PyX is free software; you can redistribute it and/or modify
11 # it under the terms of the GNU General Public License as published by
12 # the Free Software Foundation; either version 2 of the License, or
13 # (at your option) any later version.
15 # PyX is distributed in the hope that it will be useful,
16 # but WITHOUT ANY WARRANTY; without even the implied warranty of
17 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 # GNU General Public License for more details.
20 # You should have received a copy of the GNU General Public License
21 # along with PyX; if not, write to the Free Software
22 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
26 (La)TeX interface of PyX
28 This module provides the classes tex and latex, which can be inserted into a
29 PyX canvas. The method (la)tex.text prints text, while (la)tex.textwd,
30 (la)tex.textht, and (la)tex.textdp appraise the width, height, and depth of a
31 text, respectively. The method (la)tex.define can be used to define macros in
35 import os
, string
, tempfile
, sys
, md5
, traceback
, time
, StringIO
, re
, atexit
36 import base
, helper
, unit
, epsfile
, color
38 sys
.stderr
.write("*** PyX Warning: the tex module is obsolete, consider the text module instead\n")
40 # Code snippets from former attrlist module (which has been removed from the
41 # CVS tree). We keep them here, until they are finally removed together with
44 class AttrlistError(base
.PyXExcept
):
49 def attrcheck(self
, attrs
, allowonce
=(), allowmulti
=()):
52 for once
in allowonce
:
53 if isinstance(attr
, once
):
60 for multi
in allowmulti
:
61 if isinstance(attr
, multi
):
66 def attrgetall(self
, attrs
, get
, default
=helper
.nodefault
):
69 if isinstance(attr
, get
):
76 if default
is helper
.nodefault
:
82 def attrcount(self
, attrs
, check
):
83 return len(self
.attrgetall(attrs
, check
, ()))
85 def attrget(self
, attrs
, get
, default
=helper
.nodefault
):
87 result
= self
.attrgetall(attrs
, get
)
89 if default
is helper
.nodefault
:
97 def attrgetfirst(self
, attrs
, get
, default
=helper
.nodefault
):
99 result
= self
.attrgetall(attrs
, get
)
100 except AttrlistError
:
101 if default
is helper
.nodefault
:
107 def attrgetlast(self
, attrs
, get
, default
=helper
.nodefault
):
109 result
= self
.attrgetall(attrs
, get
)
110 except AttrlistError
:
111 if default
is helper
.nodefault
:
117 def attrdel(self
, attrs
, remove
):
120 if not isinstance(attr
, remove
):
125 ################################################################################
127 ################################################################################
131 """base class for all TeX attributes"""
136 class fontsize(_texattr
):
138 """fontsize TeX attribute"""
140 def __init__(self
, value
):
147 fontsize
.tiny
= fontsize("tiny")
148 fontsize
.scriptsize
= fontsize("scriptsize")
149 fontsize
.footnotesize
= fontsize("footnotesize")
150 fontsize
.small
= fontsize("small")
151 fontsize
.normalsize
= fontsize("normalsize")
152 fontsize
.large
= fontsize("large")
153 fontsize
.Large
= fontsize("Large")
154 fontsize
.LARGE
= fontsize("LARGE")
155 fontsize
.huge
= fontsize("huge")
156 fontsize
.Huge
= fontsize("Huge")
159 class halign(_texattr
):
161 """tex horizontal align attribute"""
163 def __init__(self
, value
):
166 def __cmp__(self
, other
):
167 if other
is None: return 1
168 return cmp(self
.value
, other
.value
)
173 halign
.left
= halign("left")
174 halign
.center
= halign("center")
175 halign
.right
= halign("right")
178 class valign(_texattr
):
180 """abstract tex vertical align attribute"""
182 def __init__(self
, hsize
):
186 class _valignvtop(valign
):
188 """tex top vertical align attribute"""
193 valign
.top
= _valignvtop
196 class _valignvbox(valign
):
198 """tex bottom vertical align attribute"""
203 valign
.bottom
= _valignvbox
206 class direction(_texattr
):
208 """tex output direction attribute"""
210 def __init__(self
, value
):
214 return "%.5f" % self
.value
218 direction
.horizontal
= direction(0)
219 direction
.vertical
= direction(90)
220 direction
.upsidedown
= direction(180)
221 direction
.rvertical
= direction(270)
224 class style(_texattr
):
226 """tex style modification attribute"""
228 def __init__(self
, praefix
, suffix
):
229 self
.praefix
= praefix
232 def ModifyCmd(self
, cmd
):
233 return self
.praefix
+ cmd
+ self
.suffix
236 style
.text
= style("", "")
237 style
.math
= style("$\displaystyle{}", "$")
240 ################################################################################
241 # TeX message handlers
242 ################################################################################
244 class msghandler(_texattr
):
246 """abstract base class for tex message handlers
248 A message handler has to provide a parsemsg method. It gets a string and
249 returns a string. Within the parsemsg method the handler may remove any
250 part of the message it is familiar with."""
252 def removeemptylines(self
, msg
):
253 """any message parser may use this method to remove empty lines"""
255 msg
= re
.sub("^(\n)*", "", msg
)
256 msg
= re
.sub("(\n){3,}", "\n\n", msg
)
257 msg
= re
.sub("(\n)+$", "\n", msg
)
261 class _msghandlershowall(msghandler
):
263 """a message handler, which shows all messages"""
265 def parsemsg(self
, msg
):
269 msghandler
.showall
= _msghandlershowall()
271 class _msghandlerhideload(msghandler
):
273 """a message handler, which hides all messages inside proper '(filename' and ')'
274 the string filename has to be a readable file"""
276 def parsemsg(self
, msg
):
281 if newlevel
and (c
in (list(string
.whitespace
) + ["(", ")"])):
282 if filestr
not in ("c", "C"):
285 if not os
.access(filestr
,os
.R_OK
):
301 # replace msg only if loop was completed and no ")" is missing
303 msg
= self
.removeemptylines(newstr
)
307 msghandler
.hideload
= _msghandlerhideload()
310 class _msghandlerhidegraphicsload(msghandler
):
312 """a message handler, which hides all messages like '<filename>'
313 the string filename has to be a readable file"""
315 def parsemsg(self
, msg
):
328 if not os
.access(filestr
,os
.R_OK
):
329 newstr
+= "<" + filestr
+ ">"
335 # replace msg only if loop was completed and no ">" missing
337 msg
= self
.removeemptylines(newstr
)
341 msghandler
.hidegraphicsload
= _msghandlerhidegraphicsload()
344 class _msghandlerhidefontwarning(msghandler
):
346 """a message handler, which hides LaTeX font warnings, e.g.
347 Messages starting with 'LaTeX Font Warning: ' which might be
348 continued on following lines by '(Font) '"""
350 def parsemsg(self
, msg
):
351 msglines
= string
.split(msg
, "\n")
354 for line
in msglines
:
355 if fontwarning
and line
[:20] != "(Font) ":
357 if not fontwarning
and line
[:20] == "LaTeX Font Warning: ":
360 newmsglines
.append(line
)
361 newmsg
= reduce(lambda x
, y
: x
+ y
+ "\n", newmsglines
, "")
362 return self
.removeemptylines(newmsg
)
365 msghandler
.hidefontwarning
= _msghandlerhidefontwarning()
368 class _msghandlerhidebuterror(msghandler
):
370 """a message handler, hides all messages whenever they do
371 not contain a line starting with '! '"""
373 def parsemsg(self
, msg
):
374 # the "\n" + msg instead of msg itself is needed, if the message starts with "! "
375 if string
.find("\n" + msg
, "\n! ") != -1:
381 msghandler
.hidebuterror
= _msghandlerhidebuterror()
384 class _msghandlerhideall(msghandler
):
386 """a message handler, which hides all messages"""
388 def parsemsg(self
, msg
):
392 msghandler
.hideall
= _msghandlerhideall()
395 ################################################################################
397 ################################################################################
399 class missextents(_texattr
):
401 """abstract base class for handling missing extents
403 A miss extent class has to provide a misshandler method."""
406 _missextentsreturnzero_report
= 0
407 def _missextentsreturnzero_printreport():
409 pyx.tex: Some requested extents were missing and have been replaced by zero.
410 Please run the file again to get correct extents.\n""")
412 class _missextentsreturnzero(missextents
):
414 def misshandler(self
, texinstance
):
415 global _missextentsreturnzero_report
416 if not _missextentsreturnzero_report
:
417 atexit
.register(_missextentsreturnzero_printreport
)
418 _missextentsreturnzero_report
= 1
419 return map(lambda x
: unit
.t_pt(0), texinstance
.BoxCmds
[0].CmdExtents
)
422 missextents
.returnzero
= _missextentsreturnzero()
425 class _missextentsreturnzeroquiet(missextents
):
427 def misshandler(self
, texinstance
):
428 return map(lambda x
: unit
.t_pt(0), texinstance
.BoxCmds
[0].CmdExtents
)
431 missextents
.returnzeroquiet
= _missextentsreturnzeroquiet()
434 class _missextentsraiseerror(missextents
):
436 def misshandler(self
, texinstance
):
437 raise TexMissExtentError
440 missextents
.raiseerror
= _missextentsraiseerror()
443 class _missextentscreateextents(missextents
):
445 def misshandler(self
, texinstance
):
446 if isinstance(texinstance
, latex
):
447 storeauxfilename
= texinstance
.auxfilename
448 texinstance
.auxfilename
= None
449 texinstance
.DoneRunTex
= 0
451 texinstance
.DoneRunTex
= 0
452 if isinstance(texinstance
, latex
):
453 texinstance
.auxfilename
= storeauxfilename
454 return texinstance
.BoxCmds
[0].Extents(texinstance
.BoxCmds
[0].CmdExtents
,
455 missextents
.returnzero
, texinstance
)
458 missextents
.createextents
= _missextentscreateextents()
461 class _missextentscreateallextents(missextents
):
463 def misshandler(self
, texinstance
):
464 if isinstance(texinstance
, latex
):
465 storeauxfilename
= texinstance
.auxfilename
466 texinstance
.auxfilename
= None
467 texinstance
.DoneRunTex
= 0
468 storeextents
= texinstance
.BoxCmds
[0].CmdExtents
[0]
469 texinstance
.BoxCmds
[0].CmdExtents
= [_extent
.wd
, _extent
.ht
, _extent
.dp
]
471 texinstance
.BoxCmds
[0].CmdExtents
[0] = storeextents
472 texinstance
.DoneRunTex
= 0
473 if isinstance(texinstance
, latex
):
474 texinstance
.auxfilename
= storeauxfilename
475 return texinstance
.BoxCmds
[0].Extents(texinstance
.BoxCmds
[0].CmdExtents
,
476 missextents
.returnzero
, texinstance
)
479 missextents
.createallextents
= _missextentscreateallextents()
482 ################################################################################
484 ################################################################################
486 class TexExcept(base
.PyXExcept
):
491 class TexLeftParenthesisError(TexExcept
):
494 return "no matching parenthesis for '{' found"
497 class TexRightParenthesisError(TexExcept
):
500 return "no matching parenthesis for '}' found"
503 class TexHalignError(TexExcept
):
506 return "unkown halign"
509 class TexValignError(TexExcept
):
512 return "unkown valign"
515 class TexDefAfterBoxError(TexExcept
):
518 return "definition commands not allowed after output commands"
521 class TexMissExtentError(TexExcept
):
524 return "requested tex extent not available"
527 ################################################################################
528 # modules internal stuff
529 ################################################################################
533 def __init__(self
, value
):
540 _extent
.wd
= _extent("wd")
541 _extent
.ht
= _extent("ht")
542 _extent
.dp
= _extent("dp")
547 """class for all user supplied commands"""
549 PyxMarker
= "PyxMarker"
550 BeginPyxMarker
= "Begin" + PyxMarker
551 EndPyxMarker
= "End" + PyxMarker
553 def __init__(self
, Marker
, Stack
, msghandlers
):
556 self
.msghandlers
= msghandlers
558 def TexParenthesisCheck(self
, Cmd
):
559 """check for proper usage of "{" and "}" in Cmd"""
564 if c
== "{" and not esc
:
566 if c
== "}" and not esc
:
569 raise TexRightParenthesisError
575 raise TexLeftParenthesisError
577 def BeginMarkerStr(self
):
578 return "%s[%s]" % (self
.BeginPyxMarker
, self
.Marker
, )
580 def WriteBeginMarker(self
, file):
581 file.write("\\immediate\\write16{%s}%%\n" % self
.BeginMarkerStr())
583 def EndMarkerStr(self
):
584 return "%s[%s]" % (self
.EndPyxMarker
, self
.Marker
, )
586 def WriteEndMarker(self
, file):
587 file.write("\\immediate\\write16{%s}%%\n" % self
.EndMarkerStr())
589 def WriteError(self
, msg
):
590 sys
.stderr
.write("Traceback (innermost last):\n")
591 traceback
.print_list(self
.Stack
)
592 sys
.stderr
.write("(La)TeX Message:\n" + msg
+ "\n")
594 def CheckMarkerError(self
, file):
595 """read markers and identify the message"""
597 line
= file.readline()
598 while (line
!= "") and (line
[:-1] != self
.BeginMarkerStr()):
599 line
= file.readline()
601 line
= file.readline()
602 while (line
!= "") and (line
[:-1] != self
.EndMarkerStr()):
604 line
= file.readline()
609 # check if message can be ignored
612 for msghandler
in self
.msghandlers
:
613 parsedmsg
= msghandler
.parsemsg(parsedmsg
)
615 if c
not in string
.whitespace
:
616 self
.WriteError(parsedmsg
)
620 class _DefCmd(_TexCmd
):
622 """definition commands"""
624 def __init__(self
, DefCmd
, Marker
, Stack
, msghandlers
):
625 _TexCmd
.__init
__(self
, Marker
, Stack
, msghandlers
)
626 self
.TexParenthesisCheck(DefCmd
)
627 self
.DefCmd
= "%s%%\n" % DefCmd
629 def write(self
, file):
630 self
.WriteBeginMarker(file)
631 file.write(self
.DefCmd
)
632 self
.WriteEndMarker(file)
637 """print parameters for a BoxCmd (data structure)"""
639 def __init__(self
, x
, y
, halign
, direction
, color
):
643 self
.direction
= direction
647 class _BoxCmd(_TexCmd
):
649 """BoxCmd (for printing text and getting extents)"""
651 def __init__(self
, DefCmdsStr
, BoxCmd
, style
, fontsize
, valign
, Marker
, Stack
, msghandlers
):
652 _TexCmd
.__init
__(self
, Marker
, Stack
, msghandlers
)
653 self
.TexParenthesisCheck(BoxCmd
)
654 self
.DefCmdsStr
= DefCmdsStr
655 self
.BoxCmd
= "{%s}%%\n" % BoxCmd
# add another "{" to ensure, that everything goes into the Box
656 self
.CmdPuts
= [] # list, where to put the command
657 self
.CmdExtents
= [] # list, which extents are requested
659 self
.BoxCmd
= style
.ModifyCmd(self
.BoxCmd
)
660 if valign
is not None:
661 if isinstance(valign
, _valignvtop
):
662 self
.BoxCmd
= "\\linewidth%.5ftruept\\vtop{\\hsize\\linewidth{%s}}" % \
663 (unit
.topt(valign
.hsize
) * 72.27/72, self
.BoxCmd
, )
664 elif isinstance(valign
, _valignvbox
):
665 self
.BoxCmd
= "\\linewidth%.5ftruept\\vbox{\\hsize\\linewidth{%s}}" % \
666 (unit
.topt(valign
.hsize
) * 72.27/72, self
.BoxCmd
, )
669 self
.BoxCmd
= "\\setbox\\localbox=\\hbox{\\%s%s}%%\n" % (fontsize
, self
.BoxCmd
, )
671 def __cmp__(self
, other
):
672 if other
is None: return 1
673 return cmp(self
.BoxCmd
, other
.BoxCmd
)
677 def write(self
, file):
678 self
.WriteBeginMarker(file)
679 file.write(self
.BoxCmd
)
680 self
.WriteEndMarker(file)
681 for CmdExtent
in self
.CmdExtents
:
682 file.write("\\immediate\\write\\sizefile{%s:%s:%s:\\the\\%s\\localbox}%%\n" %
683 (self
.MD5(), CmdExtent
, time
.time(), CmdExtent
, ))
684 for CmdPut
in self
.CmdPuts
:
686 file.write("{\\vbox to0pt{\\kern%.5ftruept\\hbox{\\kern%.5ftruept\\ht\\localbox0pt" %
687 (-CmdPut
.y
, CmdPut
.x
))
689 if CmdPut
.direction
!= direction
.horizontal
:
690 file.write("\\special{ps: gsave currentpoint currentpoint translate " +
691 str(CmdPut
.direction
) + " neg rotate neg exch neg exch translate }")
692 if CmdPut
.color
!= color
.gray
.black
:
693 file.write("\\special{ps: ")
694 CmdPut
.color
.outputPS(file)
696 if CmdPut
.halign
== halign
.left
:
698 elif CmdPut
.halign
== halign
.center
:
699 file.write("\kern-.5\wd\localbox")
700 elif CmdPut
.halign
== halign
.right
:
701 file.write("\kern-\wd\localbox")
704 file.write("\\copy\\localbox")
706 if CmdPut
.color
!= color
.gray
.black
:
707 file.write("\\special{ps: ")
708 color
.gray
.black
.outputPS(file)
710 if CmdPut
.direction
!= direction
.horizontal
:
711 file.write("\\special{ps: currentpoint grestore moveto }")
712 file.write("}\\vss}\\nointerlineskip}%\n")
715 """creates an MD5 hex string for texinit + Cmd"""
719 s
= md5
.md5(self
.DefCmdsStr
+ self
.BoxCmd
).digest()
722 r
= r
+ h
[(i
>> 4) & 0xF] + h
[i
& 0xF]
725 def Put(self
, x
, y
, halign
, direction
, color
):
726 self
.CmdPuts
.append(_CmdPut(x
, y
, halign
, direction
, color
))
728 def Extents(self
, extents
, missextents
, texinstance
):
729 """get sizes from previous LaTeX run"""
731 for extent
in extents
:
732 if extent
not in self
.CmdExtents
:
733 self
.CmdExtents
.append(extent
)
736 for extent
in extents
:
737 s
= self
.MD5() + ":" + str(extent
)
738 for size
in texinstance
.Sizes
:
739 if size
[:len(s
)] == s
:
740 texpt
= float(string
.rstrip(size
.split(":")[3][:-3]))
741 result
.append(unit
.t_pt(texpt
* 72.0 / 72.27))
748 # extent was not found --- temporarily remove all other commands in
749 # order to allow the misshandler to access everything it ever wants
750 storeboxcmds
= texinstance
.BoxCmds
751 storecmdputs
= self
.CmdPuts
752 storecmdextents
= self
.CmdExtents
753 texinstance
.BoxCmds
= [self
, ]
755 self
.CmdExtents
= extents
757 result
= missextents
.misshandler(texinstance
)
759 texinstance
.BoxCmds
= storeboxcmds
760 self
.CmdPuts
= storecmdputs
761 self
.CmdExtents
= storecmdextents
765 ################################################################################
767 ################################################################################
769 class _tex(base
.PSCmd
, attrlist
):
771 """major parts are of tex and latex class are shared and implemented here"""
773 def __init__(self
, defaultmsghandlers
=msghandler
.hideload
,
774 defaultmissextents
=missextents
.returnzero
,
776 if isinstance(defaultmsghandlers
, msghandler
):
777 self
.defaultmsghandlers
= (defaultmsghandlers
,)
779 self
.defaultmsghandlers
= defaultmsghandlers
780 self
.defaultmissextents
= defaultmissextents
781 self
.texfilename
= texfilename
783 self
.DefCmdsStr
= None
787 if len(os
.path
.basename(sys
.argv
[0])):
788 basename
= os
.path
.basename(sys
.argv
[0])
789 if basename
[-3:] == ".py":
790 basename
= basename
[:-3]
791 self
.SizeFileName
= os
.path
.join(os
.getcwd(), basename
+ ".size")
793 self
.SizeFileName
= os
.path
.join(os
.getcwd(), "pyxput.size")
795 file = open(self
.SizeFileName
, "r")
796 self
.Sizes
= file.readlines()
801 def _execute(self
, command
):
802 if os
.system(command
):
803 sys
.stderr
.write("The exit code of the following command was non-zero:\n" + command
+
804 """\nUsually, additional information causing this trouble appears closeby.
805 However, you may check the origin by keeping all temporary files.
806 In order to achieve this, you have to specify a texfilename in the
807 constructor of the class pyx.(la)tex. You can then try to run the
808 command by yourself.\n""")
810 def _createaddfiles(self
, tempname
):
813 def _removeaddfiles(self
, tempname
):
816 def _executetex(self
, tempname
):
819 def _executedvips(self
, tempname
):
820 self
._execute
("dvips -O0in,11in -E -o %(t)s.eps %(t)s.dvi > %(t)s.dvipsout 2> %(t)s.dvipserr" % {"t": tempname
})
823 """run (La)TeX & dvips, report errors, fill self.abbox & self.epsdata"""
829 mktemp
= str(self
.texfilename
)
831 storetempdir
= tempfile
.tempdir
832 tempfile
.tempdir
= os
.curdir
833 mktemp
= tempfile
.mktemp()
834 tempfile
.tempdir
= storetempdir
835 tempname
= os
.path
.basename(mktemp
)
837 self
._createaddfiles
(tempname
)
839 texfile
= open(tempname
+ ".tex", "w")
841 texfile
.write("\\nonstopmode%\n")
842 texfile
.write("\\def\PyX{P\\kern-.3em\\lower.5ex\\hbox{Y}\\kern-.18em X}%\n")
843 texfile
.write("\\newwrite\\sizefile%\n\\newbox\\localbox%\n\\newbox\\pagebox%\n")
844 texfile
.write("{\\catcode`\\~=12\\immediate\\openout\\sizefile=%s.size\\relax}%%\n" % tempname
)
846 for Cmd
in self
.DefCmds
:
849 texfile
.write("\\setbox\\pagebox=\\vbox{%\n")
851 for Cmd
in self
.BoxCmds
:
854 texfile
.write("}\n\\immediate\\closeout\\sizefile\n\\shipout\\copy\\pagebox\n")
855 texfile
.write(self
._endcmd
())
858 self
._executetex
(tempname
)
861 outfile
= open(tempname
+ ".texout", "r")
862 for Cmd
in self
.DefCmds
+ self
.BoxCmds
:
863 Cmd
.CheckMarkerError(outfile
)
866 sys
.stderr
.write("""An unexpected error occured while reading the (La)TeX output.
867 May be, you just have no disk space available. Or something badly
868 in your commands caused (La)TeX to give up completely. Or your
869 (La)TeX installation might be broken at all.
870 You may try to check the origin by keeping all temporary files.
871 In order to achieve this, you have to specify a texfilename in the
872 constructor of the class pyx.tex. You can then try to run (La)TeX
875 if not os
.access(tempname
+ ".dvi", 0):
876 sys
.stderr
.write("""Can't find the dvi file which should be produced by (La)TeX.
877 May be, you just have no disk space available. Or something badly
878 in your commands caused (La)TeX to give up completely. Or your
879 (La)TeX installation might be broken at all.
880 You may try to check the origin by keeping all temporary files.
881 In order to achieve this, you have to specify a texfilename in the
882 constructor of the class pyx.tex. You can then try to run (La)TeX
886 self
._executedvips
(tempname
)
887 if not os
.access(tempname
+ ".eps", 0):
888 sys
.stderr
.write("""Error reading the eps file which should be produced by dvips.
889 May be, you just have no disk space available. Or something badly
890 in your commands caused dvips to give up completely. Or your
891 (La)TeX installation might be broken at all.
892 You may try to check the origin by keeping all temporary files.
893 In order to achieve this, you have to specify a texfilename in the
894 constructor of the class pyx.tex. You can then try to run dvips
897 aepsfile
= epsfile
.epsfile(0, 0, tempname
+ ".eps", translatebbox
=0, clip
=0)
898 self
.abbox
= aepsfile
.bbox()
899 self
.aprolog
= aepsfile
.prolog()
900 epsdatafile
= StringIO
.StringIO()
901 aepsfile
.outputPS(epsdatafile
)
902 self
.epsdata
= epsdatafile
.getvalue()
906 OldSizes
= self
.Sizes
909 NewSizeFile
= open(tempname
+ ".size", "r")
910 NewSizes
= NewSizeFile
.readlines()
915 if (len(NewSizes
) != 0) or (len(OldSizes
) != 0):
916 SizeFile
= open(self
.SizeFileName
, "w")
917 SizeFile
.writelines(NewSizes
)
918 self
.Sizes
= NewSizes
919 for OldSize
in OldSizes
:
920 OldSizeSplit
= OldSize
.split(":")
921 for NewSize
in NewSizes
:
922 if NewSize
.split(":")[0:2] == OldSizeSplit
[0:2]:
925 if time
.time() < float(OldSizeSplit
[2]) + 60*60*24: # we keep size results for one day
926 SizeFile
.write(OldSize
)
927 self
.Sizes
.append(OldSize
)
929 if not self
.texfilename
:
930 for suffix
in ("tex", "log", "size", "dvi", "eps", "texout", "texerr", "dvipsout", "dvipserr", ):
932 os
.unlink(tempname
+ "." + suffix
)
936 self
._removeaddfiles
(tempname
)
947 def outputPS(self
, file):
949 file.writelines(self
.epsdata
)
951 def define(self
, Cmd
, *attrs
):
952 if len(self
.BoxCmds
):
953 raise TexDefAfterBoxError
955 self
.attrcheck(attrs
, (), (msghandler
,))
956 self
.DefCmds
.append(_DefCmd(Cmd
,
957 len(self
.DefCmds
)+ len(self
.BoxCmds
),
958 traceback
.extract_stack(),
959 self
.attrgetall(attrs
, msghandler
, self
.defaultmsghandlers
)))
961 def _insertcmd(self
, Cmd
, *attrs
):
962 if not len(self
.BoxCmds
):
964 self
.DefCmdsStr
= reduce(lambda x
,y
: x
+ y
.DefCmd
, self
.DefCmds
, "")
965 mystyle
= self
.attrget(attrs
, style
, style
.text
)
966 myfontsize
= self
.attrget(attrs
, fontsize
, fontsize
.normalsize
)
967 myvalign
= self
.attrget(attrs
, valign
, None)
968 mymsghandlers
= self
.attrgetall(attrs
, msghandler
, self
.defaultmsghandlers
)
969 MyCmd
= _BoxCmd(self
.DefCmdsStr
, Cmd
, mystyle
, myfontsize
, myvalign
,
970 len(self
.DefCmds
) + len(self
.BoxCmds
), traceback
.extract_stack(), mymsghandlers
)
971 if MyCmd
not in self
.BoxCmds
:
972 self
.BoxCmds
.append(MyCmd
)
973 for Cmd
in self
.BoxCmds
:
975 UseCmd
= Cmd
# we could use MyCmd directly if we have just inserted it before
976 # (that's due to the side effect, that append doesn't make a copy of the element,
977 # but we ignore this here -- we don't want to depend on this side effect)
980 def _text(self
, x
, y
, Cmd
, *attrs
):
981 """print Cmd at (x, y) --- position parameters in postscipt points"""
984 self
.attrcheck(attrs
, (style
, fontsize
, halign
, valign
, direction
, color
.color
), (msghandler
,))
985 myhalign
= self
.attrget(attrs
, halign
, halign
.left
)
986 mydirection
= self
.attrget(attrs
, direction
, direction
.horizontal
)
987 mycolor
= self
.attrget(attrs
, color
.color
, color
.gray
.black
)
988 self
._insertcmd
(Cmd
, *attrs
).Put(x
* 72.27 / 72.0, y
* 72.27 / 72.0, myhalign
, mydirection
, mycolor
)
990 def text(self
, x
, y
, Cmd
, *attrs
):
991 """print Cmd at (x, y)"""
993 self
._text
(unit
.topt(x
), unit
.topt(y
), Cmd
, *attrs
)
995 def textwd(self
, Cmd
, *attrs
):
996 """get width of Cmd"""
999 self
.attrcheck(attrs
, (style
, fontsize
, missextents
), (msghandler
,))
1000 mymissextents
= self
.attrget(attrs
, missextents
, self
.defaultmissextents
)
1001 return self
._insertcmd
(Cmd
, *attrs
).Extents((_extent
.wd
, ), mymissextents
, self
)[0]
1003 def textht(self
, Cmd
, *attrs
):
1004 """get height of Cmd"""
1007 self
.attrcheck(attrs
, (style
, fontsize
, valign
, missextents
), (msghandler
,))
1008 mymissextents
= self
.attrget(attrs
, missextents
, self
.defaultmissextents
)
1009 return self
._insertcmd
(Cmd
, *attrs
).Extents((_extent
.ht
, ), mymissextents
, self
)[0]
1012 def textdp(self
, Cmd
, *attrs
):
1013 """get depth of Cmd"""
1016 self
.attrcheck(attrs
, (style
, fontsize
, valign
, missextents
), (msghandler
,))
1017 mymissextents
= self
.attrget(attrs
, missextents
, self
.defaultmissextents
)
1018 return self
._insertcmd
(Cmd
, *attrs
).Extents((_extent
.dp
, ), mymissextents
, self
)[0]
1023 """tex class adds the specializations to _tex needed for tex"""
1025 def __init__(self
, lfs
="10pt", **addargs
):
1026 _tex
.__init
__(self
, **addargs
)
1027 # XXX other ways for creating font sizes?
1029 LocalLfsName
= str(lfs
) + ".lfs"
1030 lfsdef
= open(LocalLfsName
, "r").read()
1034 SysLfsName
= os
.path
.join(sys
.prefix
, "share", "pyx", str(lfs
) + ".lfs")
1035 lfsdef
= open(SysLfsName
, "r").read()
1037 SysLfsName
= os
.path
.join(os
.path
.dirname(__file__
), "lfs", str(lfs
) + ".lfs")
1038 lfsdef
= open(SysLfsName
, "r").read()
1040 files
= map(lambda x
: x
[:-4],
1041 filter(lambda x
: x
[-4:] == ".lfs",
1043 os
.listdir(os
.path
.join(sys
.prefix
, "share", "pyx")),
1044 os
.listdir(os
.path
.join(os
.path
.dirname(__file__
), "lfs"))))
1045 raise IOError("file '%s.lfs' not found. Available latex font sizes:\n%s" % (lfs
, files
))
1047 self
.define("\\newdimen\\linewidth%\n\\hsize0truein%\n\\vsize0truein%\n\\hoffset-1truein%\n\\voffset-1truein")
1049 def _beginboxcmds(self
):
1055 def _executetex(self
, tempname
):
1056 self
._execute
("tex %(t)s.tex > %(t)s.texout 2> %(t)s.texerr" % {"t": tempname
})
1061 """latex class adds the specializations to _tex needed for latex"""
1063 def __init__(self
, docclass
="article", docopt
=None, auxfilename
=None, **addargs
):
1064 _tex
.__init
__(self
, **addargs
)
1065 self
.auxfilename
= auxfilename
1067 self
.define("\\documentclass[" + str(docopt
) + "]{" + str(docclass
) + "}")
1069 self
.define("\\documentclass{" + str(docclass
) + "}")
1070 self
.define("\\hsize0truein%\n\\vsize0truein%\n\\hoffset-1truein%\n\\voffset-1truein")
1072 def _beginboxcmds(self
):
1073 self
.define("\\begin{document}")
1076 return "\\end{document}\n"
1078 def _createaddfiles(self
, tempname
):
1079 if self
.auxfilename
is not None:
1082 os
.rename(self
.auxfilename
+ ".aux", tempname
+ ".aux")
1088 auxfile
= open(tempname
+ ".aux", "w")
1089 auxfile
.write("\\relax\n")
1092 def _executetex(self
, tempname
):
1093 self
._execute
("latex %(t)s.tex > %(t)s.texout 2> %(t)s.texerr" % {"t": tempname
})
1095 def _removeaddfiles(self
, tempname
):
1096 if self
.auxfilename
is not None:
1097 os
.rename(tempname
+ ".aux", self
.auxfilename
+ ".aux")
1099 os
.unlink(tempname
+ ".aux")