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: 0 * unit.t_pt, texinstance.BoxCmds[0].CmdExtents)
422
missextents.returnzero = _missextentsreturnzero()
425
class _missextentsreturnzeroquiet(missextents):
427
def misshandler(self, texinstance):
428
return map(lambda x: 0 * unit.t_pt, 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.canvasitem, 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")