2 # -*- coding: ISO-8859-1 -*-
5 # Copyright (C) 2002 Jörg Lehmann <joergl@users.sourceforge.net>
6 # Copyright (C) 2002 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
39 # Code snippets from former attrlist module (which has been removed from the
40 # CVS tree). We keep them here, until they are finally removed together with
43 class AttrlistError(base
.PyXExcept
):
48 def attrcheck(self
, attrs
, allowonce
=(), allowmulti
=()):
51 for once
in allowonce
:
52 if isinstance(attr
, once
):
59 for multi
in allowmulti
:
60 if isinstance(attr
, multi
):
65 def attrgetall(self
, attrs
, get
, default
=helper
.nodefault
):
68 if isinstance(attr
, get
):
75 if default
is helper
.nodefault
:
81 def attrcount(self
, attrs
, check
):
82 return len(self
.attrgetall(attrs
, check
, ()))
84 def attrget(self
, attrs
, get
, default
=helper
.nodefault
):
86 result
= self
.attrgetall(attrs
, get
)
88 if default
is helper
.nodefault
:
96 def attrgetfirst(self
, attrs
, get
, default
=helper
.nodefault
):
98 result
= self
.attrgetall(attrs
, get
)
100 if default
is helper
.nodefault
:
106 def attrgetlast(self
, attrs
, get
, default
=helper
.nodefault
):
108 result
= self
.attrgetall(attrs
, get
)
109 except AttrlistError
:
110 if default
is helper
.nodefault
:
116 def attrdel(self
, attrs
, remove
):
119 if not isinstance(attr
, remove
):
124 ################################################################################
126 ################################################################################
130 """base class for all TeX attributes"""
135 class fontsize(_texattr
):
137 """fontsize TeX attribute"""
139 def __init__(self
, value
):
146 fontsize
.tiny
= fontsize("tiny")
147 fontsize
.scriptsize
= fontsize("scriptsize")
148 fontsize
.footnotesize
= fontsize("footnotesize")
149 fontsize
.small
= fontsize("small")
150 fontsize
.normalsize
= fontsize("normalsize")
151 fontsize
.large
= fontsize("large")
152 fontsize
.Large
= fontsize("Large")
153 fontsize
.LARGE
= fontsize("LARGE")
154 fontsize
.huge
= fontsize("huge")
155 fontsize
.Huge
= fontsize("Huge")
158 class halign(_texattr
):
160 """tex horizontal align attribute"""
162 def __init__(self
, value
):
165 def __cmp__(self
, other
):
166 if other
is None: return 1
167 return cmp(self
.value
, other
.value
)
172 halign
.left
= halign("left")
173 halign
.center
= halign("center")
174 halign
.right
= halign("right")
177 class valign(_texattr
):
179 """abstract tex vertical align attribute"""
181 def __init__(self
, hsize
):
185 class _valignvtop(valign
):
187 """tex top vertical align attribute"""
192 valign
.top
= _valignvtop
195 class _valignvbox(valign
):
197 """tex bottom vertical align attribute"""
202 valign
.bottom
= _valignvbox
205 class direction(_texattr
):
207 """tex output direction attribute"""
209 def __init__(self
, value
):
213 return "%.5f" % self
.value
217 direction
.horizontal
= direction(0)
218 direction
.vertical
= direction(90)
219 direction
.upsidedown
= direction(180)
220 direction
.rvertical
= direction(270)
223 class style(_texattr
):
225 """tex style modification attribute"""
227 def __init__(self
, praefix
, suffix
):
228 self
.praefix
= praefix
231 def ModifyCmd(self
, cmd
):
232 return self
.praefix
+ cmd
+ self
.suffix
235 style
.text
= style("", "")
236 style
.math
= style("$\displaystyle{}", "$")
239 ################################################################################
240 # TeX message handlers
241 ################################################################################
243 class msghandler(_texattr
):
245 """abstract base class for tex message handlers
247 A message handler has to provide a parsemsg method. It gets a string and
248 returns a string. Within the parsemsg method the handler may remove any
249 part of the message it is familiar with."""
251 def removeemptylines(self
, msg
):
252 """any message parser may use this method to remove empty lines"""
254 msg
= re
.sub("^(\n)*", "", msg
)
255 msg
= re
.sub("(\n){3,}", "\n\n", msg
)
256 msg
= re
.sub("(\n)+$", "\n", msg
)
260 class _msghandlershowall(msghandler
):
262 """a message handler, which shows all messages"""
264 def parsemsg(self
, msg
):
268 msghandler
.showall
= _msghandlershowall()
270 class _msghandlerhideload(msghandler
):
272 """a message handler, which hides all messages inside proper '(filename' and ')'
273 the string filename has to be a readable file"""
275 def parsemsg(self
, msg
):
280 if newlevel
and (c
in (list(string
.whitespace
) + ["(", ")"])):
281 if filestr
not in ("c", "C"):
284 if not os
.access(filestr
,os
.R_OK
):
300 # replace msg only if loop was completed and no ")" is missing
302 msg
= self
.removeemptylines(newstr
)
306 msghandler
.hideload
= _msghandlerhideload()
309 class _msghandlerhidegraphicsload(msghandler
):
311 """a message handler, which hides all messages like '<filename>'
312 the string filename has to be a readable file"""
314 def parsemsg(self
, msg
):
327 if not os
.access(filestr
,os
.R_OK
):
328 newstr
+= "<" + filestr
+ ">"
334 # replace msg only if loop was completed and no ">" missing
336 msg
= self
.removeemptylines(newstr
)
340 msghandler
.hidegraphicsload
= _msghandlerhidegraphicsload()
343 class _msghandlerhidefontwarning(msghandler
):
345 """a message handler, which hides LaTeX font warnings, e.g.
346 Messages starting with 'LaTeX Font Warning: ' which might be
347 continued on following lines by '(Font) '"""
349 def parsemsg(self
, msg
):
350 msglines
= string
.split(msg
, "\n")
353 for line
in msglines
:
354 if fontwarning
and line
[:20] != "(Font) ":
356 if not fontwarning
and line
[:20] == "LaTeX Font Warning: ":
359 newmsglines
.append(line
)
360 newmsg
= reduce(lambda x
, y
: x
+ y
+ "\n", newmsglines
, "")
361 return self
.removeemptylines(newmsg
)
364 msghandler
.hidefontwarning
= _msghandlerhidefontwarning()
367 class _msghandlerhidebuterror(msghandler
):
369 """a message handler, hides all messages whenever they do
370 not contain a line starting with '! '"""
372 def parsemsg(self
, msg
):
373 # the "\n" + msg instead of msg itself is needed, if the message starts with "! "
374 if string
.find("\n" + msg
, "\n! ") != -1:
380 msghandler
.hidebuterror
= _msghandlerhidebuterror()
383 class _msghandlerhideall(msghandler
):
385 """a message handler, which hides all messages"""
387 def parsemsg(self
, msg
):
391 msghandler
.hideall
= _msghandlerhideall()
394 ################################################################################
396 ################################################################################
398 class missextents(_texattr
):
400 """abstract base class for handling missing extents
402 A miss extent class has to provide a misshandler method."""
405 _missextentsreturnzero_report
= 0
406 def _missextentsreturnzero_printreport():
408 pyx.tex: Some requested extents were missing and have been replaced by zero.
409 Please run the file again to get correct extents.\n""")
411 class _missextentsreturnzero(missextents
):
413 def misshandler(self
, texinstance
):
414 global _missextentsreturnzero_report
415 if not _missextentsreturnzero_report
:
416 atexit
.register(_missextentsreturnzero_printreport
)
417 _missextentsreturnzero_report
= 1
418 return map(lambda x
: unit
.t_pt(0), texinstance
.BoxCmds
[0].CmdExtents
)
421 missextents
.returnzero
= _missextentsreturnzero()
424 class _missextentsreturnzeroquiet(missextents
):
426 def misshandler(self
, texinstance
):
427 return map(lambda x
: unit
.t_pt(0), texinstance
.BoxCmds
[0].CmdExtents
)
430 missextents
.returnzeroquiet
= _missextentsreturnzeroquiet()
433 class _missextentsraiseerror(missextents
):
435 def misshandler(self
, texinstance
):
436 raise TexMissExtentError
439 missextents
.raiseerror
= _missextentsraiseerror()
442 class _missextentscreateextents(missextents
):
444 def misshandler(self
, texinstance
):
445 if isinstance(texinstance
, latex
):
446 storeauxfilename
= texinstance
.auxfilename
447 texinstance
.auxfilename
= None
448 texinstance
.DoneRunTex
= 0
450 texinstance
.DoneRunTex
= 0
451 if isinstance(texinstance
, latex
):
452 texinstance
.auxfilename
= storeauxfilename
453 return texinstance
.BoxCmds
[0].Extents(texinstance
.BoxCmds
[0].CmdExtents
,
454 missextents
.returnzero
, texinstance
)
457 missextents
.createextents
= _missextentscreateextents()
460 class _missextentscreateallextents(missextents
):
462 def misshandler(self
, texinstance
):
463 if isinstance(texinstance
, latex
):
464 storeauxfilename
= texinstance
.auxfilename
465 texinstance
.auxfilename
= None
466 texinstance
.DoneRunTex
= 0
467 storeextents
= texinstance
.BoxCmds
[0].CmdExtents
[0]
468 texinstance
.BoxCmds
[0].CmdExtents
= [_extent
.wd
, _extent
.ht
, _extent
.dp
]
470 texinstance
.BoxCmds
[0].CmdExtents
[0] = storeextents
471 texinstance
.DoneRunTex
= 0
472 if isinstance(texinstance
, latex
):
473 texinstance
.auxfilename
= storeauxfilename
474 return texinstance
.BoxCmds
[0].Extents(texinstance
.BoxCmds
[0].CmdExtents
,
475 missextents
.returnzero
, texinstance
)
478 missextents
.createallextents
= _missextentscreateallextents()
481 ################################################################################
483 ################################################################################
485 class TexExcept(base
.PyXExcept
):
490 class TexLeftParenthesisError(TexExcept
):
493 return "no matching parenthesis for '{' found"
496 class TexRightParenthesisError(TexExcept
):
499 return "no matching parenthesis for '}' found"
502 class TexHalignError(TexExcept
):
505 return "unkown halign"
508 class TexValignError(TexExcept
):
511 return "unkown valign"
514 class TexDefAfterBoxError(TexExcept
):
517 return "definition commands not allowed after output commands"
520 class TexMissExtentError(TexExcept
):
523 return "requested tex extent not available"
526 ################################################################################
527 # modules internal stuff
528 ################################################################################
532 def __init__(self
, value
):
539 _extent
.wd
= _extent("wd")
540 _extent
.ht
= _extent("ht")
541 _extent
.dp
= _extent("dp")
546 """class for all user supplied commands"""
548 PyxMarker
= "PyxMarker"
549 BeginPyxMarker
= "Begin" + PyxMarker
550 EndPyxMarker
= "End" + PyxMarker
552 def __init__(self
, Marker
, Stack
, msghandlers
):
555 self
.msghandlers
= msghandlers
557 def TexParenthesisCheck(self
, Cmd
):
558 """check for proper usage of "{" and "}" in Cmd"""
563 if c
== "{" and not esc
:
565 if c
== "}" and not esc
:
568 raise TexRightParenthesisError
574 raise TexLeftParenthesisError
576 def BeginMarkerStr(self
):
577 return "%s[%s]" % (self
.BeginPyxMarker
, self
.Marker
, )
579 def WriteBeginMarker(self
, file):
580 file.write("\\immediate\\write16{%s}%%\n" % self
.BeginMarkerStr())
582 def EndMarkerStr(self
):
583 return "%s[%s]" % (self
.EndPyxMarker
, self
.Marker
, )
585 def WriteEndMarker(self
, file):
586 file.write("\\immediate\\write16{%s}%%\n" % self
.EndMarkerStr())
588 def WriteError(self
, msg
):
589 sys
.stderr
.write("Traceback (innermost last):\n")
590 traceback
.print_list(self
.Stack
)
591 sys
.stderr
.write("(La)TeX Message:\n" + msg
+ "\n")
593 def CheckMarkerError(self
, file):
594 """read markers and identify the message"""
596 line
= file.readline()
597 while (line
!= "") and (line
[:-1] != self
.BeginMarkerStr()):
598 line
= file.readline()
600 line
= file.readline()
601 while (line
!= "") and (line
[:-1] != self
.EndMarkerStr()):
603 line
= file.readline()
608 # check if message can be ignored
611 for msghandler
in self
.msghandlers
:
612 parsedmsg
= msghandler
.parsemsg(parsedmsg
)
614 if c
not in string
.whitespace
:
615 self
.WriteError(parsedmsg
)
619 class _DefCmd(_TexCmd
):
621 """definition commands"""
623 def __init__(self
, DefCmd
, Marker
, Stack
, msghandlers
):
624 _TexCmd
.__init
__(self
, Marker
, Stack
, msghandlers
)
625 self
.TexParenthesisCheck(DefCmd
)
626 self
.DefCmd
= "%s%%\n" % DefCmd
628 def write(self
, file):
629 self
.WriteBeginMarker(file)
630 file.write(self
.DefCmd
)
631 self
.WriteEndMarker(file)
636 """print parameters for a BoxCmd (data structure)"""
638 def __init__(self
, x
, y
, halign
, direction
, color
):
642 self
.direction
= direction
646 class _BoxCmd(_TexCmd
):
648 """BoxCmd (for printing text and getting extents)"""
650 def __init__(self
, DefCmdsStr
, BoxCmd
, style
, fontsize
, valign
, Marker
, Stack
, msghandlers
):
651 _TexCmd
.__init
__(self
, Marker
, Stack
, msghandlers
)
652 self
.TexParenthesisCheck(BoxCmd
)
653 self
.DefCmdsStr
= DefCmdsStr
654 self
.BoxCmd
= "{%s}%%\n" % BoxCmd
# add another "{" to ensure, that everything goes into the Box
655 self
.CmdPuts
= [] # list, where to put the command
656 self
.CmdExtents
= [] # list, which extents are requested
658 self
.BoxCmd
= style
.ModifyCmd(self
.BoxCmd
)
659 if valign
is not None:
660 if isinstance(valign
, _valignvtop
):
661 self
.BoxCmd
= "\\linewidth%.5ftruept\\vtop{\\hsize\\linewidth{%s}}" % \
662 (unit
.topt(valign
.hsize
) * 72.27/72, self
.BoxCmd
, )
663 elif isinstance(valign
, _valignvbox
):
664 self
.BoxCmd
= "\\linewidth%.5ftruept\\vbox{\\hsize\\linewidth{%s}}" % \
665 (unit
.topt(valign
.hsize
) * 72.27/72, self
.BoxCmd
, )
668 self
.BoxCmd
= "\\setbox\\localbox=\\hbox{\\%s%s}%%\n" % (fontsize
, self
.BoxCmd
, )
670 def __cmp__(self
, other
):
671 if other
is None: return 1
672 return cmp(self
.BoxCmd
, other
.BoxCmd
)
676 def write(self
, file):
677 self
.WriteBeginMarker(file)
678 file.write(self
.BoxCmd
)
679 self
.WriteEndMarker(file)
680 for CmdExtent
in self
.CmdExtents
:
681 file.write("\\immediate\\write\\sizefile{%s:%s:%s:\\the\\%s\\localbox}%%\n" %
682 (self
.MD5(), CmdExtent
, time
.time(), CmdExtent
, ))
683 for CmdPut
in self
.CmdPuts
:
685 file.write("{\\vbox to0pt{\\kern%.5ftruept\\hbox{\\kern%.5ftruept\\ht\\localbox0pt" %
686 (-CmdPut
.y
, CmdPut
.x
))
688 if CmdPut
.direction
!= direction
.horizontal
:
689 file.write("\\special{ps: gsave currentpoint currentpoint translate " +
690 str(CmdPut
.direction
) + " neg rotate neg exch neg exch translate }")
691 if CmdPut
.color
!= color
.gray
.black
:
692 file.write("\\special{ps: ")
693 CmdPut
.color
.write(file)
695 if CmdPut
.halign
== halign
.left
:
697 elif CmdPut
.halign
== halign
.center
:
698 file.write("\kern-.5\wd\localbox")
699 elif CmdPut
.halign
== halign
.right
:
700 file.write("\kern-\wd\localbox")
703 file.write("\\copy\\localbox")
705 if CmdPut
.color
!= color
.gray
.black
:
706 file.write("\\special{ps: ")
707 color
.gray
.black
.write(file)
709 if CmdPut
.direction
!= direction
.horizontal
:
710 file.write("\\special{ps: currentpoint grestore moveto }")
711 file.write("}\\vss}\\nointerlineskip}%\n")
714 """creates an MD5 hex string for texinit + Cmd"""
718 s
= md5
.md5(self
.DefCmdsStr
+ self
.BoxCmd
).digest()
721 r
= r
+ h
[(i
>> 4) & 0xF] + h
[i
& 0xF]
724 def Put(self
, x
, y
, halign
, direction
, color
):
725 self
.CmdPuts
.append(_CmdPut(x
, y
, halign
, direction
, color
))
727 def Extents(self
, extents
, missextents
, texinstance
):
728 """get sizes from previous LaTeX run"""
730 for extent
in extents
:
731 if extent
not in self
.CmdExtents
:
732 self
.CmdExtents
.append(extent
)
735 for extent
in extents
:
736 s
= self
.MD5() + ":" + str(extent
)
737 for size
in texinstance
.Sizes
:
738 if size
[:len(s
)] == s
:
739 texpt
= float(string
.rstrip(size
.split(":")[3][:-3]))
740 result
.append(unit
.t_pt(texpt
* 72.0 / 72.27))
747 # extent was not found --- temporarily remove all other commands in
748 # order to allow the misshandler to access everything it ever wants
749 storeboxcmds
= texinstance
.BoxCmds
750 storecmdputs
= self
.CmdPuts
751 storecmdextents
= self
.CmdExtents
752 texinstance
.BoxCmds
= [self
, ]
754 self
.CmdExtents
= extents
756 result
= missextents
.misshandler(texinstance
)
758 texinstance
.BoxCmds
= storeboxcmds
759 self
.CmdPuts
= storecmdputs
760 self
.CmdExtents
= storecmdextents
764 ################################################################################
766 ################################################################################
768 class _tex(base
.PSCmd
, attrlist
):
770 """major parts are of tex and latex class are shared and implemented here"""
772 def __init__(self
, defaultmsghandlers
=msghandler
.hideload
,
773 defaultmissextents
=missextents
.returnzero
,
775 if isinstance(defaultmsghandlers
, msghandler
):
776 self
.defaultmsghandlers
= (defaultmsghandlers
,)
778 self
.defaultmsghandlers
= defaultmsghandlers
779 self
.defaultmissextents
= defaultmissextents
780 self
.texfilename
= texfilename
782 self
.DefCmdsStr
= None
786 if len(os
.path
.basename(sys
.argv
[0])):
787 basename
= os
.path
.basename(sys
.argv
[0])
788 if basename
[-3:] == ".py":
789 basename
= basename
[:-3]
790 self
.SizeFileName
= os
.path
.join(os
.getcwd(), basename
+ ".size")
792 self
.SizeFileName
= os
.path
.join(os
.getcwd(), "pyxput.size")
794 file = open(self
.SizeFileName
, "r")
795 self
.Sizes
= file.readlines()
800 def _execute(self
, command
):
801 if os
.system(command
):
802 sys
.stderr
.write("The exit code of the following command was non-zero:\n" + command
+
803 """\nUsually, additional information causing this trouble appears closeby.
804 However, you may check the origin by keeping all temporary files.
805 In order to achieve this, you have to specify a texfilename in the
806 constructor of the class pyx.(la)tex. You can then try to run the
807 command by yourself.\n""")
809 def _createaddfiles(self
, tempname
):
812 def _removeaddfiles(self
, tempname
):
815 def _executetex(self
, tempname
):
818 def _executedvips(self
, tempname
):
819 self
._execute
("dvips -O0in,11in -E -o %(t)s.eps %(t)s.dvi > %(t)s.dvipsout 2> %(t)s.dvipserr" % {"t": tempname
})
822 """run (La)TeX & dvips, report errors, fill self.abbox & self.epsdata"""
828 mktemp
= str(self
.texfilename
)
830 storetempdir
= tempfile
.tempdir
831 tempfile
.tempdir
= os
.curdir
832 mktemp
= tempfile
.mktemp()
833 tempfile
.tempdir
= storetempdir
834 tempname
= os
.path
.basename(mktemp
)
836 self
._createaddfiles
(tempname
)
838 texfile
= open(tempname
+ ".tex", "w")
840 texfile
.write("\\nonstopmode%\n")
841 texfile
.write("\\def\PyX{P\\kern-.3em\\lower.5ex\\hbox{Y}\\kern-.18em X}%\n")
842 texfile
.write("\\newwrite\\sizefile%\n\\newbox\\localbox%\n\\newbox\\pagebox%\n")
843 texfile
.write("{\\catcode`\\~=12\\immediate\\openout\\sizefile=%s.size\\relax}%%\n" % tempname
)
845 for Cmd
in self
.DefCmds
:
848 texfile
.write("\\setbox\\pagebox=\\vbox{%\n")
850 for Cmd
in self
.BoxCmds
:
853 texfile
.write("}\n\\immediate\\closeout\\sizefile\n\\shipout\\copy\\pagebox\n")
854 texfile
.write(self
._endcmd
())
857 self
._executetex
(tempname
)
860 outfile
= open(tempname
+ ".texout", "r")
861 for Cmd
in self
.DefCmds
+ self
.BoxCmds
:
862 Cmd
.CheckMarkerError(outfile
)
865 sys
.stderr
.write("""An unexpected error occured while reading the (La)TeX output.
866 May be, you just have no disk space available. Or something badly
867 in your commands caused (La)TeX to give up completely. Or your
868 (La)TeX installation might be broken at all.
869 You may try to check the origin by keeping all temporary files.
870 In order to achieve this, you have to specify a texfilename in the
871 constructor of the class pyx.tex. You can then try to run (La)TeX
874 if not os
.access(tempname
+ ".dvi", 0):
875 sys
.stderr
.write("""Can't find the dvi file which should be produced by (La)TeX.
876 May be, you just have no disk space available. Or something badly
877 in your commands caused (La)TeX to give up completely. Or your
878 (La)TeX installation might be broken at all.
879 You may try to check the origin by keeping all temporary files.
880 In order to achieve this, you have to specify a texfilename in the
881 constructor of the class pyx.tex. You can then try to run (La)TeX
885 self
._executedvips
(tempname
)
886 if not os
.access(tempname
+ ".eps", 0):
887 sys
.stderr
.write("""Error reading the eps file which should be produced by dvips.
888 May be, you just have no disk space available. Or something badly
889 in your commands caused dvips to give up completely. Or your
890 (La)TeX installation might be broken at all.
891 You may try to check the origin by keeping all temporary files.
892 In order to achieve this, you have to specify a texfilename in the
893 constructor of the class pyx.tex. You can then try to run dvips
896 aepsfile
= epsfile
.epsfile(0, 0, tempname
+ ".eps", translatebbox
=0, clip
=0)
897 self
.abbox
= aepsfile
.bbox()
898 self
.aprolog
= aepsfile
.prolog()
899 epsdatafile
= StringIO
.StringIO()
900 aepsfile
.write(epsdatafile
)
901 self
.epsdata
= epsdatafile
.getvalue()
905 OldSizes
= self
.Sizes
908 NewSizeFile
= open(tempname
+ ".size", "r")
909 NewSizes
= NewSizeFile
.readlines()
914 if (len(NewSizes
) != 0) or (len(OldSizes
) != 0):
915 SizeFile
= open(self
.SizeFileName
, "w")
916 SizeFile
.writelines(NewSizes
)
917 self
.Sizes
= NewSizes
918 for OldSize
in OldSizes
:
919 OldSizeSplit
= OldSize
.split(":")
920 for NewSize
in NewSizes
:
921 if NewSize
.split(":")[0:2] == OldSizeSplit
[0:2]:
924 if time
.time() < float(OldSizeSplit
[2]) + 60*60*24: # we keep size results for one day
925 SizeFile
.write(OldSize
)
926 self
.Sizes
.append(OldSize
)
928 if not self
.texfilename
:
929 for suffix
in ("tex", "log", "size", "dvi", "eps", "texout", "texerr", "dvipsout", "dvipserr", ):
931 os
.unlink(tempname
+ "." + suffix
)
935 self
._removeaddfiles
(tempname
)
946 def write(self
, file):
948 file.writelines(self
.epsdata
)
950 def define(self
, Cmd
, *attrs
):
951 if len(self
.BoxCmds
):
952 raise TexDefAfterBoxError
954 self
.attrcheck(attrs
, (), (msghandler
,))
955 self
.DefCmds
.append(_DefCmd(Cmd
,
956 len(self
.DefCmds
)+ len(self
.BoxCmds
),
957 traceback
.extract_stack(),
958 self
.attrgetall(attrs
, msghandler
, self
.defaultmsghandlers
)))
960 def _insertcmd(self
, Cmd
, *attrs
):
961 if not len(self
.BoxCmds
):
963 self
.DefCmdsStr
= reduce(lambda x
,y
: x
+ y
.DefCmd
, self
.DefCmds
, "")
964 mystyle
= self
.attrget(attrs
, style
, style
.text
)
965 myfontsize
= self
.attrget(attrs
, fontsize
, fontsize
.normalsize
)
966 myvalign
= self
.attrget(attrs
, valign
, None)
967 mymsghandlers
= self
.attrgetall(attrs
, msghandler
, self
.defaultmsghandlers
)
968 MyCmd
= _BoxCmd(self
.DefCmdsStr
, Cmd
, mystyle
, myfontsize
, myvalign
,
969 len(self
.DefCmds
) + len(self
.BoxCmds
), traceback
.extract_stack(), mymsghandlers
)
970 if MyCmd
not in self
.BoxCmds
:
971 self
.BoxCmds
.append(MyCmd
)
972 for Cmd
in self
.BoxCmds
:
974 UseCmd
= Cmd
# we could use MyCmd directly if we have just inserted it before
975 # (that's due to the side effect, that append doesn't make a copy of the element,
976 # but we ignore this here -- we don't want to depend on this side effect)
979 def _text(self
, x
, y
, Cmd
, *attrs
):
980 """print Cmd at (x, y) --- position parameters in postscipt points"""
983 self
.attrcheck(attrs
, (style
, fontsize
, halign
, valign
, direction
, color
.color
), (msghandler
,))
984 myhalign
= self
.attrget(attrs
, halign
, halign
.left
)
985 mydirection
= self
.attrget(attrs
, direction
, direction
.horizontal
)
986 mycolor
= self
.attrget(attrs
, color
.color
, color
.gray
.black
)
987 self
._insertcmd
(Cmd
, *attrs
).Put(x
* 72.27 / 72.0, y
* 72.27 / 72.0, myhalign
, mydirection
, mycolor
)
989 def text(self
, x
, y
, Cmd
, *attrs
):
990 """print Cmd at (x, y)"""
992 self
._text
(unit
.topt(x
), unit
.topt(y
), Cmd
, *attrs
)
994 def textwd(self
, Cmd
, *attrs
):
995 """get width of Cmd"""
998 self
.attrcheck(attrs
, (style
, fontsize
, missextents
), (msghandler
,))
999 mymissextents
= self
.attrget(attrs
, missextents
, self
.defaultmissextents
)
1000 return self
._insertcmd
(Cmd
, *attrs
).Extents((_extent
.wd
, ), mymissextents
, self
)[0]
1002 def textht(self
, Cmd
, *attrs
):
1003 """get height of Cmd"""
1006 self
.attrcheck(attrs
, (style
, fontsize
, valign
, missextents
), (msghandler
,))
1007 mymissextents
= self
.attrget(attrs
, missextents
, self
.defaultmissextents
)
1008 return self
._insertcmd
(Cmd
, *attrs
).Extents((_extent
.ht
, ), mymissextents
, self
)[0]
1011 def textdp(self
, Cmd
, *attrs
):
1012 """get depth of Cmd"""
1015 self
.attrcheck(attrs
, (style
, fontsize
, valign
, missextents
), (msghandler
,))
1016 mymissextents
= self
.attrget(attrs
, missextents
, self
.defaultmissextents
)
1017 return self
._insertcmd
(Cmd
, *attrs
).Extents((_extent
.dp
, ), mymissextents
, self
)[0]
1022 """tex class adds the specializations to _tex needed for tex"""
1024 def __init__(self
, lfs
="10pt", **addargs
):
1025 _tex
.__init
__(self
, **addargs
)
1026 # XXX other ways for creating font sizes?
1028 LocalLfsName
= str(lfs
) + ".lfs"
1029 lfsdef
= open(LocalLfsName
, "r").read()
1033 SysLfsName
= os
.path
.join(sys
.prefix
, "share", "pyx", str(lfs
) + ".lfs")
1034 lfsdef
= open(SysLfsName
, "r").read()
1036 SysLfsName
= os
.path
.join(os
.path
.dirname(__file__
), "lfs", str(lfs
) + ".lfs")
1037 lfsdef
= open(SysLfsName
, "r").read()
1039 files
= map(lambda x
: x
[:-4],
1040 filter(lambda x
: x
[-4:] == ".lfs",
1042 os
.listdir(os
.path
.join(sys
.prefix
, "share", "pyx")),
1043 os
.listdir(os
.path
.join(os
.path
.dirname(__file__
), "lfs"))))
1044 raise IOError("file '%s.lfs' not found. Available latex font sizes:\n%s" % (lfs
, files
))
1046 self
.define("\\newdimen\\linewidth%\n\\hsize0truein%\n\\vsize0truein%\n\\hoffset-1truein%\n\\voffset-1truein")
1048 def _beginboxcmds(self
):
1054 def _executetex(self
, tempname
):
1055 self
._execute
("tex %(t)s.tex > %(t)s.texout 2> %(t)s.texerr" % {"t": tempname
})
1060 """latex class adds the specializations to _tex needed for latex"""
1062 def __init__(self
, docclass
="article", docopt
=None, auxfilename
=None, **addargs
):
1063 _tex
.__init
__(self
, **addargs
)
1064 self
.auxfilename
= auxfilename
1066 self
.define("\\documentclass[" + str(docopt
) + "]{" + str(docclass
) + "}")
1068 self
.define("\\documentclass{" + str(docclass
) + "}")
1069 self
.define("\\hsize0truein%\n\\vsize0truein%\n\\hoffset-1truein%\n\\voffset-1truein")
1071 def _beginboxcmds(self
):
1072 self
.define("\\begin{document}")
1075 return "\\end{document}\n"
1077 def _createaddfiles(self
, tempname
):
1078 if self
.auxfilename
is not None:
1081 os
.rename(self
.auxfilename
+ ".aux", tempname
+ ".aux")
1087 auxfile
= open(tempname
+ ".aux", "w")
1088 auxfile
.write("\\relax\n")
1091 def _executetex(self
, tempname
):
1092 self
._execute
("latex %(t)s.tex > %(t)s.texout 2> %(t)s.texerr" % {"t": tempname
})
1094 def _removeaddfiles(self
, tempname
):
1095 if self
.auxfilename
is not None:
1096 os
.rename(tempname
+ ".aux", self
.auxfilename
+ ".aux")
1098 os
.unlink(tempname
+ ".aux")