2 # -*- coding: ISO-8859-1 -*-
5 # Copyright (C) 2002-2005 Jörg Lehmann <joergl@users.sourceforge.net>
6 # Copyright (C) 2003-2004 Michael Schindler <m-schindler@users.sourceforge.net>
7 # Copyright (C) 2002-2005 André Wobst <wobsta@users.sourceforge.net>
9 # This file is part of PyX (http://pyx.sourceforge.net/).
11 # PyX is free software; you can redistribute it and/or modify
12 # it under the terms of the GNU General Public License as published by
13 # the Free Software Foundation; either version 2 of the License, or
14 # (at your option) any later version.
16 # PyX is distributed in the hope that it will be useful,
17 # but WITHOUT ANY WARRANTY; without even the implied warranty of
18 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 # GNU General Public License for more details.
21 # You should have received a copy of the GNU General Public License
22 # along with PyX; if not, write to the Free Software
23 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 import copy
, cStringIO
, exceptions
, re
, struct
, string
, sys
, warnings
26 import unit
, epsfile
, bbox
, canvas
, color
, trafo
, path
, pykpathsea
, resource
31 def __init__(self
, filename
, mode
="r"):
32 self
.file = open(filename
, mode
)
38 return self
.file.tell()
41 return self
.file.eof()
43 def read(self
, bytes
):
44 return self
.file.read(bytes
)
46 def readint(self
, bytes
=4, signed
=0):
50 value
= ord(self
.file.read(1))
51 if first
and signed
and value
> 127:
54 result
= 256 * result
+ value
59 return struct
.unpack(">l", self
.file.read(4))[0]
62 return struct
.unpack(">L", self
.file.read(4))[0]
66 return struct
.unpack(">l", "\0"+self
.file.read(3))[0]
70 return struct
.unpack(">L", "\0"+self
.file.read(3))[0]
73 return struct
.unpack(">h", self
.file.read(2))[0]
76 return struct
.unpack(">H", self
.file.read(2))[0]
79 return struct
.unpack("b", self
.file.read(1))[0]
82 return struct
.unpack("B", self
.file.read(1))[0]
84 def readstring(self
, bytes
):
86 assert l
<= bytes
-1, "inconsistency in file: string too long"
87 return self
.file.read(bytes
-1)[:l
]
89 class stringbinfile(binfile
):
91 def __init__(self
, s
):
92 self
.file = cStringIO
.StringIO(s
)
96 # """ ascii file containing tokens separated by spaces.
98 # Comments beginning with % are ignored. Strings containing spaces
99 # are not handled correctly
102 # def __init__(self, filename):
103 # self.file = open(filename, "r")
106 # def gettoken(self):
107 # """ return next token or None if EOF """
108 # while not self.line:
109 # line = self.file.readline()
112 # self.line = line.split("%")[0].split()
113 # token = self.line[0]
114 # self.line = self.line[1:]
121 ##############################################################################
123 ##############################################################################
125 class TFMError(exceptions
.Exception): pass
128 class char_info_word
:
129 def __init__(self
, word
):
130 self
.width_index
= int((word
& 0xFF000000L
) >> 24) #make sign-safe
131 self
.height_index
= (word
& 0x00F00000) >> 20
132 self
.depth_index
= (word
& 0x000F0000) >> 16
133 self
.italic_index
= (word
& 0x0000FC00) >> 10
134 self
.tag
= (word
& 0x00000300) >> 8
135 self
.remainder
= (word
& 0x000000FF)
139 def __init__(self
, name
, debug
=0):
140 self
.file = binfile(name
, "rb")
146 self
.lf
= self
.file.readint16()
147 self
.lh
= self
.file.readint16()
148 self
.bc
= self
.file.readint16()
149 self
.ec
= self
.file.readint16()
150 self
.nw
= self
.file.readint16()
151 self
.nh
= self
.file.readint16()
152 self
.nd
= self
.file.readint16()
153 self
.ni
= self
.file.readint16()
154 self
.nl
= self
.file.readint16()
155 self
.nk
= self
.file.readint16()
156 self
.ne
= self
.file.readint16()
157 self
.np
= self
.file.readint16()
159 if not (self
.bc
-1 <= self
.ec
<= 255 and
161 self
.lf
== 6+self
.lh
+(self
.ec
-self
.bc
+1)+self
.nw
+self
.nh
+self
.nd
162 +self
.ni
+self
.nl
+self
.nk
+self
.ne
+self
.np
):
163 raise TFMError
, "error in TFM pre-header"
166 print "lh=%d" % self
.lh
172 self
.checksum
= self
.file.readint32()
173 self
.designsize
= self
.file.readint32()
174 assert self
.designsize
> 0, "invald design size"
176 assert self
.lh
> 11, "inconsistency in TFM file: incomplete field"
177 self
.charcoding
= self
.file.readstring(40)
179 self
.charcoding
= None
182 assert self
.lh
> 16, "inconsistency in TFM file: incomplete field"
183 self
.fontfamily
= self
.file.readstring(20)
185 self
.fontfamily
= None
188 print "(FAMILY %s)" % self
.fontfamily
189 print "(CODINGSCHEME %s)" % self
.charcoding
190 print "(DESINGSIZE R %f)" % 16.0*self
.designsize
/16777216L
193 self
.sevenbitsave
= self
.file.readuchar()
194 # ignore the following two bytes
195 self
.file.readint16()
196 facechar
= self
.file.readuchar()
197 # decode ugly face specification into the Knuth suggested string
209 self
.face
= "L" + self
.face
212 self
.face
= "B" + self
.face
215 self
.face
= "M" + self
.face
218 self
.face
= self
.face
[0] + "I" + self
.face
[1]
220 self
.face
= self
.face
[0] + "R" + self
.face
[1]
225 self
.sevenbitsave
= self
.face
= None
228 # just ignore the rest
229 print self
.file.read((self
.lh
-18)*4)
235 self
.char_info
= [None]*(self
.ec
+1)
236 for charcode
in range(self
.bc
, self
.ec
+1):
237 self
.char_info
[charcode
] = char_info_word(self
.file.readint32())
238 if self
.char_info
[charcode
].width_index
== 0:
239 # disable character if width_index is zero
240 self
.char_info
[charcode
] = None
246 self
.width
= [None for width_index
in range(self
.nw
)]
247 for width_index
in range(self
.nw
):
248 self
.width
[width_index
] = self
.file.readint32()
254 self
.height
= [None for height_index
in range(self
.nh
)]
255 for height_index
in range(self
.nh
):
256 self
.height
[height_index
] = self
.file.readint32()
262 self
.depth
= [None for depth_index
in range(self
.nd
)]
263 for depth_index
in range(self
.nd
):
264 self
.depth
[depth_index
] = self
.file.readint32()
270 self
.italic
= [None for italic_index
in range(self
.ni
)]
271 for italic_index
in range(self
.ni
):
272 self
.italic
[italic_index
] = self
.file.readint32()
278 # XXX decode to lig_kern_command
280 self
.lig_kern
= [None for lig_kern_index
in range(self
.nl
)]
281 for lig_kern_index
in range(self
.nl
):
282 self
.lig_kern
[lig_kern_index
] = self
.file.readint32()
288 self
.kern
= [None for kern_index
in range(self
.nk
)]
289 for kern_index
in range(self
.nk
):
290 self
.kern
[kern_index
] = self
.file.readint32()
296 # XXX decode to extensible_recipe
298 self
.exten
= [None for exten_index
in range(self
.ne
)]
299 for exten_index
in range(self
.ne
):
300 self
.exten
[exten_index
] = self
.file.readint32()
308 self
.param
= [None for param_index
in range(self
.np
)]
309 for param_index
in range(self
.np
):
310 self
.param
[param_index
] = self
.file.readint32()
315 # class FontEncoding:
317 # def __init__(self, filename):
318 # """ font encoding contained in filename """
319 # encpath = pykpathsea.find_file(filename, pykpathsea.kpse_tex_ps_header_format)
320 # encfile = tokenfile(encpath)
323 # self.encname = encfile.gettoken()
324 # token = encfile.gettoken()
326 # raise RuntimeError("cannot parse encoding file '%s', expecting '[' got '%s'" % (filename, token))
327 # self.encvector = []
328 # for i in range(256):
329 # token = encfile.gettoken()
330 # if token is None or token=="]":
331 # raise RuntimeError("not enough charcodes in encoding file '%s'" % filename)
332 # self.encvector.append(token)
333 # if encfile.gettoken() != "]":
334 # raise RuntimeError("too many charcodes in encoding file '%s'" % filename)
335 # token = encfile.gettoken()
337 # raise RuntimeError("cannot parse encoding file '%s', expecting 'def' got '%s'" % (filename, token))
338 # token = encfile.gettoken()
340 # raise RuntimeError("encoding file '%s' too long" % filename)
343 # def encode(self, charcode):
344 # return self.encvector[charcode]
346 ##############################################################################
348 ##############################################################################
351 # PostScript font selection and output primitives
354 class _begintextobject(canvas
.canvasitem
):
355 def outputPS(self
, file):
358 def outputPDF(self
, file, writer
, context
):
362 class _endtextobject(canvas
.canvasitem
):
363 def outputPS(self
, file):
366 def outputPDF(self
, file, writer
, context
):
370 class _selectfont(canvas
.canvasitem
):
371 # XXX this should go away and be merged with selectfont
372 def __init__(self
, name
, size
):
376 def outputPS(self
, file):
377 file.write("/%s %f selectfont\n" % (self
.name
, self
.size
))
379 def outputPDF(self
, file, writer
, context
):
380 file.write("/%s %f Tf\n" % (self
.name
, self
.size
))
383 class selectfont(canvas
.canvasitem
):
385 def __init__(self
, font
):
386 # XXX maybe we should change the calling convention here and only pass the
387 # name, size, encoding, usedchars of the font
389 self
.size
= font
.getsize_pt()
393 fontresource
= resource
.font(self
.font
)
394 self
.fontid
= fontresource
.id
395 return [fontresource
]
397 def outputPS(self
, file):
398 file.write("/%s %f selectfont\n" % (self
.fontid
, self
.size
))
400 def outputPDF(self
, file, writer
, context
):
401 file.write("/%s %f Tf\n" % (self
.fontid
, self
.size
))
404 class _show(canvas
.canvasitem
):
406 def __init__(self
, x
, y
):
414 def addchar(self
, width
, height
, depth
, char
):
416 if height
> self
.height
:
418 if depth
> self
.depth
:
420 self
.chars
.append(char
)
423 return bbox
.bbox_pt(self
.x
, self
.y
-self
.depth
, self
.x
+self
.width
, self
.y
+self
.height
)
425 def outputPS(self
, file):
427 for char
in self
.chars
:
428 if char
> 32 and char
< 127 and chr(char
) not in "()[]<>\\":
429 ascii
= "%s" % chr(char
)
431 ascii
= "\\%03o" % char
433 file.write("%g %g moveto (%s) show\n" % (self
.x
, self
.y
, outstring
))
435 def outputPDF(self
, file, writer
, context
):
437 for char
in self
.chars
:
438 if char
> 32 and char
< 127 and chr(char
) not in "()[]<>\\":
439 ascii
= "%s" % chr(char
)
441 ascii
= "\\%03o" % char
443 # file.write("%f %f Td (%s) Tj\n" % (self.x, self.y, outstring))
444 file.write("1 0 0 1 %f %f Tm (%s) Tj\n" % (self
.x
, self
.y
, outstring
))
449 tokenpattern
= re
.compile(r
'"(.*?)("\s+|"$|$)|(.*?)(\s+|$)')
451 def __init__(self
, s
):
452 """ construct font mapping from line s of font mapping file """
453 self
.texname
= self
.basepsname
= self
.fontfile
= None
456 self
.encodingfile
= None
458 # supported postscript fragments occuring in psfonts.map
459 self
.reencodefont
= self
.extendfont
= self
.slantfont
= None
463 match
= self
.tokenpattern
.match(s
)
465 if match
.groups()[0]:
466 tokens
.append('"%s"' % match
.groups()[0])
468 tokens
.append(match
.groups()[2])
471 raise RuntimeError("wrong syntax")
474 if token
.startswith("<"):
475 if token
.startswith("<<"):
476 # XXX: support non-partial download here
477 self
.fontfile
= token
[2:]
478 elif token
.startswith("<["):
479 self
.encodingfile
= token
[2:]
480 elif token
.endswith(".pfa") or token
.endswith(".pfb"):
481 self
.fontfile
= token
[1:]
482 elif token
.endswith(".enc"):
483 self
.encodingfile
= token
[1:]
485 raise RuntimeError("wrong syntax")
486 elif token
.startswith('"'):
487 pscode
= token
[1:-1].split()
488 # parse standard postscript code fragments
491 arg
, cmd
= pscode
[:2]
493 raise RuntimeError("Unsupported Postscript fragment '%s'" % pscode
)
495 if cmd
== "ReEncodeFont":
496 self
.reencodefont
= arg
497 elif cmd
== "ExtendFont":
498 self
.extendfont
= arg
499 elif cmd
== "SlantFont":
502 raise RuntimeError("Unsupported Postscript fragment '%s %s'" % (arg
, cmd
))
504 if self
.texname
is None:
507 self
.basepsname
= token
508 if self
.basepsname
is None:
509 self
.basepsname
= self
.texname
512 return ("'%s' is '%s' read from '%s' encoded as '%s'" %
513 (self
.texname
, self
.basepsname
, self
.fontfile
, repr(self
.encodingfile
)))
517 def readfontmap(filenames
):
518 """ read font map from filename (without path) """
520 for filename
in filenames
:
521 mappath
= pykpathsea
.find_file(filename
, pykpathsea
.kpse_fontmap_format
)
522 # try also the oft-used registration as dvips config file
524 mappath
= pykpathsea
.find_file(filename
, pykpathsea
.kpse_dvips_config_format
)
526 raise RuntimeError("cannot find font mapping file '%s'" % filename
)
527 mapfile
= open(mappath
, "r")
529 for line
in mapfile
.readlines():
532 if not (line
=="" or line
[0] in (" ", "%", "*", ";" , "#")):
534 fm
= fontmapping(line
)
535 except RuntimeError, e
:
536 warnings
.warn("Ignoring line %i in mapping file '%s': %s" % (lineno
, filename
, e
))
538 fontmap
[fm
.texname
] = fm
544 def __init__(self
, name
, c
, q
, d
, tfmconv
, pyxconv
, debug
=0):
546 self
.q
= q
# desired size of font (fix_word) in TeX points
547 self
.d
= d
# design size of font (fix_word) in TeX points
548 self
.tfmconv
= tfmconv
# conversion factor from tfm units to dvi units
549 self
.pyxconv
= pyxconv
# conversion factor from dvi units to PostScript points
550 tfmpath
= pykpathsea
.find_file("%s.tfm" % self
.name
, pykpathsea
.kpse_tfm_format
)
552 raise TFMError("cannot find %s.tfm" % self
.name
)
553 self
.tfmfile
= tfmfile(tfmpath
, debug
)
555 # We only check for equality of font checksums if none of them
556 # is zero. The case c == 0 happend in some VF files and
557 # according to the VFtoVP documentation, paragraph 40, a check
558 # is only performed if tfmfile.checksum > 0. Anyhow, being
559 # more generous here seems to be reasonable
560 if self
.tfmfile
.checksum
!= c
and self
.tfmfile
.checksum
!= 0 and c
!= 0:
561 raise DVIError("check sums do not agree: %d vs. %d" %
562 (self
.tfmfile
.checksum
, c
))
564 # Check whether the given design size matches the one defined in the tfm file
565 if abs(self
.tfmfile
.designsize
- d
) > 2:
566 raise DVIError("design sizes do not agree: %d vs. %d" % (self
.tfmfile
.designsize
, d
))
567 #if q < 0 or q > 134217728:
568 # raise DVIError("font '%s' not loaded: bad scale" % self.name)
569 if d
< 0 or d
> 134217728:
570 raise DVIError("font '%s' not loaded: bad design size" % self
.name
)
574 # for bookkeeping of used characters
575 self
.usedchars
= [0] * 256
578 return "font %s designed at %g TeX pts used at %g TeX pts" % (self
.name
,
579 16.0*self
.d
/16777216L,
580 16.0*self
.q
/16777216L)
585 def getsize_pt(self
):
586 """ return size of font in (PS) points """
587 # The factor 16L/16777216L=2**(-20) converts a fix_word (here self.q)
588 # to the corresponding float. Furthermore, we have to convert from TeX
589 # points to points, hence the factor 72/72.27.
590 return 16L*self
.q
/16777216L*72/72.27
592 def _convert_tfm_to_dvi(self
, length
):
593 # doing the integer math with long integers will lead to different roundings
594 # return 16*length*int(round(self.q*self.tfmconv))/16777216
596 # Knuth instead suggests the following algorithm based on 4 byte integer logic only
597 # z = int(round(self.q*self.tfmconv))
598 # b0, b1, b2, b3 = [ord(c) for c in struct.pack(">L", length)]
599 # assert b0 == 0 or b0 == 255
601 # while z >= 8388608:
605 # result = ( ( ( ( ( b3 * z ) >> 8 ) + ( b2 * z ) ) >> 8 ) + ( b1 * z ) ) >> shift
607 # result = result - (z << (8-shift))
609 # however, we can simplify this using a single long integer multiplication,
610 # but take into account the transformation of z
611 z
= int(round(self
.q
*self
.tfmconv
))
612 assert -16777216 <= length
< 16777216 # -(1 << 24) <= length < (1 << 24)
613 assert z
< 134217728 # 1 << 27
615 while z
>= 8388608: # 1 << 23
618 # length*z is a long integer, but the result will be a regular integer
619 return int(length
*long(z
) >> shift
)
621 # we do not need that ...
622 def _convert_tfm_to_pt(self
, length
):
623 return (16*long(round(length
*self
.q
*self
.tfmconv
))/16777216) * self
.pyxconv
625 # routines returning lengths as integers in dvi units
627 def getwidth_dvi(self
, charcode
):
628 return self
._convert
_tfm
_to
_dvi
(self
.tfmfile
.width
[self
.tfmfile
.char_info
[charcode
].width_index
])
630 def getheight_dvi(self
, charcode
):
631 return self
._convert
_tfm
_to
_dvi
(self
.tfmfile
.height
[self
.tfmfile
.char_info
[charcode
].height_index
])
633 def getdepth_dvi(self
, charcode
):
634 return self
._convert
_tfm
_to
_dvi
(self
.tfmfile
.depth
[self
.tfmfile
.char_info
[charcode
].depth_index
])
636 def getitalic_dvi(self
, charcode
):
637 return self
._convert
_tfm
_to
_dvi
(self
.tfmfile
.italic
[self
.tfmfile
.char_info
[charcode
].italic_index
])
639 # routines returning lengths as floats in PostScript points
641 def getwidth_pt(self
, charcode
):
642 return self
._convert
_tfm
_to
_pt
(self
.tfmfile
.width
[self
.tfmfile
.char_info
[charcode
].width_index
])
644 # def getheight_pt(self, charcode):
645 # return self._convert_tfm_to_pt(self.tfmfile.height[self.tfmfile.char_info[charcode].height_index])
647 # def getdepth_pt(self, charcode):
648 # return self._convert_tfm_to_pt(self.tfmfile.depth[self.tfmfile.char_info[charcode].depth_index])
650 # def getitalic_pt(self, charcode):
651 # return self._convert_tfm_to_pt(self.tfmfile.italic[self.tfmfile.char_info[charcode].italic_index])
653 def markcharused(self
, charcode
):
654 self
.usedchars
[charcode
] = 1
656 def mergeusedchars(self
, otherfont
):
657 for i
in range(len(self
.usedchars
)):
658 self
.usedchars
[i
] = self
.usedchars
[i
] or otherfont
.usedchars
[i
]
660 def clearusedchars(self
):
661 self
.usedchars
= [0] * 256
664 class type1font(font
):
665 def __init__(self
, name
, c
, q
, d
, tfmconv
, pyxconv
, fontmap
, debug
=0):
666 font
.__init
__(self
, name
, c
, q
, d
, tfmconv
, pyxconv
, debug
)
667 self
.fontmapping
= fontmap
.get(name
)
668 if self
.fontmapping
is None:
669 raise RuntimeError("no information for font '%s' found in font mapping file, aborting" % name
)
671 def getbasepsname(self
):
672 return self
.fontmapping
.basepsname
675 if self
.fontmapping
.reencodefont
:
676 return "%s-%s" % (self
.fontmapping
.basepsname
, self
.fontmapping
.reencodefont
)
678 return self
.fontmapping
.basepsname
680 def getfontfile(self
):
681 return self
.fontmapping
.fontfile
683 def getencoding(self
):
684 return self
.fontmapping
.reencodefont
686 def getencodingfile(self
):
687 return self
.fontmapping
.encodingfile
690 class virtualfont(font
):
691 def __init__(self
, name
, c
, q
, d
, tfmconv
, pyxconv
, fontmap
, debug
=0):
692 font
.__init
__(self
, name
, c
, q
, d
, tfmconv
, pyxconv
, debug
)
693 fontpath
= pykpathsea
.find_file(name
, pykpathsea
.kpse_vf_format
)
694 if fontpath
is None or not len(fontpath
):
696 self
.vffile
= vffile(fontpath
, self
.scale
, tfmconv
, pyxconv
, fontmap
, debug
> 1)
699 """ return fonts used in virtual font itself """
700 return self
.vffile
.getfonts()
702 def getchar(self
, cc
):
703 """ return dvi chunk corresponding to char code cc """
704 return self
.vffile
.getchar(cc
)
707 ##############################################################################
709 ##############################################################################
711 _DVI_CHARMIN
= 0 # typeset a character and move right (range min)
712 _DVI_CHARMAX
= 127 # typeset a character and move right (range max)
713 _DVI_SET1234
= 128 # typeset a character and move right
714 _DVI_SETRULE
= 132 # typeset a rule and move right
715 _DVI_PUT1234
= 133 # typeset a character
716 _DVI_PUTRULE
= 137 # typeset a rule
717 _DVI_NOP
= 138 # no operation
718 _DVI_BOP
= 139 # beginning of page
719 _DVI_EOP
= 140 # ending of page
720 _DVI_PUSH
= 141 # save the current positions (h, v, w, x, y, z)
721 _DVI_POP
= 142 # restore positions (h, v, w, x, y, z)
722 _DVI_RIGHT1234
= 143 # move right
723 _DVI_W0
= 147 # move right by w
724 _DVI_W1234
= 148 # move right and set w
725 _DVI_X0
= 152 # move right by x
726 _DVI_X1234
= 153 # move right and set x
727 _DVI_DOWN1234
= 157 # move down
728 _DVI_Y0
= 161 # move down by y
729 _DVI_Y1234
= 162 # move down and set y
730 _DVI_Z0
= 166 # move down by z
731 _DVI_Z1234
= 167 # move down and set z
732 _DVI_FNTNUMMIN
= 171 # set current font (range min)
733 _DVI_FNTNUMMAX
= 234 # set current font (range max)
734 _DVI_FNT1234
= 235 # set current font
735 _DVI_SPECIAL1234
= 239 # special (dvi extention)
736 _DVI_FNTDEF1234
= 243 # define the meaning of a font number
737 _DVI_PRE
= 247 # preamble
738 _DVI_POST
= 248 # postamble beginning
739 _DVI_POSTPOST
= 249 # postamble ending
741 _DVI_VERSION
= 2 # dvi version
743 # position variable indices
755 _READ_POST
= 4 # XXX not used
756 _READ_POSTPOST
= 5 # XXX not used
760 class DVIError(exceptions
.Exception): pass
762 # save and restore colors
764 class _savecolor(canvas
.canvasitem
):
765 def outputPS(self
, file):
766 file.write("currentcolor currentcolorspace\n")
768 def outputPDF(self
, file, writer
, context
):
772 class _restorecolor(canvas
.canvasitem
):
773 def outputPS(self
, file):
774 file.write("setcolorspace setcolor\n")
776 def outputPDF(self
, file, writer
, context
):
779 class _savetrafo(canvas
.canvasitem
):
780 def outputPS(self
, file):
781 file.write("matrix currentmatrix\n")
783 def outputPDF(self
, file, writer
, context
):
787 class _restoretrafo(canvas
.canvasitem
):
788 def outputPS(self
, file):
789 file.write("setmatrix\n")
791 def outputPDF(self
, file, writer
, context
):
797 def __init__(self
, filename
, fontmap
, debug
=0, debugfile
=sys
.stdout
):
798 """ opens the dvi file and reads the preamble """
799 self
.filename
= filename
800 self
.fontmap
= fontmap
802 self
.debugfile
= debugfile
806 self
.activefont
= None
808 # stack of fonts and fontscale currently used (used for VFs)
812 # pointer to currently active page
815 # currently active output: show instance currently used and
816 # the corresponding type 1 font
817 self
.activeshow
= None
818 self
.activetype1font
= None
820 # stack for self.file, self.fonts and self.stack, needed for VF inclusion
823 self
.file = binfile(self
.filename
, "rb")
825 # currently read byte in file (for debugging output)
833 """ flush currently active string """
834 if self
.activeshow
is not None:
836 self
.debugfile
.write("[%s]\n" % "".join([chr(char
) for char
in self
.activeshow
.chars
]))
837 self
.actpage
.insert(self
.activeshow
)
838 self
.activeshow
= None
841 """ activate the font if is not yet active, closing a currently active
842 text object and flushing the output"""
843 if isinstance(self
.activefont
, type1font
):
845 if self
.activetype1font
!= self
.activefont
and self
.activefont
:
846 self
.actpage
.insert(_begintextobject())
847 self
.actpage
.insert(selectfont(self
.activefont
))
848 self
.activetype1font
= self
.activefont
852 if self
.activetype1font
:
853 self
.actpage
.insert(_endtextobject())
854 self
.activetype1font
= None
856 def putrule(self
, height
, width
, advancepos
=1):
858 x1
= self
.pos
[_POS_H
] * self
.pyxconv
859 y1
= -self
.pos
[_POS_V
] * self
.pyxconv
860 w
= width
* self
.pyxconv
861 h
= height
* self
.pyxconv
863 if height
> 0 and width
> 0:
865 self
.debugfile
.write("%d: %srule height %d, width %d (???x??? pixels)\n" %
866 (self
.filepos
, advancepos
and "set" or "put", height
, width
))
867 self
.actpage
.fill(path
.rect_pt(x1
, y1
, w
, h
))
870 self
.debugfile
.write("%d: %srule height %d, width %d (invisible)\n" %
871 (self
.filepos
, advancepos
and "set" or "put", height
, width
))
875 self
.debugfile
.write(" h:=%d+%d=%d, hh:=???\n" %
876 (self
.pos
[_POS_H
], width
, self
.pos
[_POS_H
]+width
))
877 self
.pos
[_POS_H
] += width
879 def putchar(self
, char
, advancepos
=1, id1234
=0):
880 dx
= advancepos
and self
.activefont
.getwidth_dvi(char
) or 0
883 self
.debugfile
.write("%d: %s%s%d h:=%d+%d=%d, hh:=???\n" %
885 advancepos
and "set" or "put",
886 id1234
and "%i " % id1234
or "char",
888 self
.pos
[_POS_H
], dx
, self
.pos
[_POS_H
]+dx
))
890 if isinstance(self
.activefont
, type1font
):
891 if self
.activeshow
is None:
892 # XXX: begintext would lead to massive number of selectfonts being issued
893 # OTOH is it save to remove begintext here? I think so ...
895 self
.activeshow
= _show(self
.pos
[_POS_H
] * self
.pyxconv
, -self
.pos
[_POS_V
] * self
.pyxconv
)
896 width
= self
.activefont
.getwidth_dvi(char
) * self
.pyxconv
897 height
= self
.activefont
.getheight_dvi(char
) * self
.pyxconv
898 depth
= self
.activefont
.getdepth_dvi(char
) * self
.pyxconv
899 self
.activeshow
.addchar(width
, height
, depth
, char
)
901 self
.activefont
.markcharused(char
)
902 self
.pos
[_POS_H
] += dx
904 # virtual font handling
905 afterpos
= list(self
.pos
)
906 afterpos
[_POS_H
] += dx
907 self
._push
_dvistring
(self
.activefont
.getchar(char
), self
.activefont
.getfonts(), afterpos
,
908 self
.activefont
.getsize_pt())
915 def usefont(self
, fontnum
, id1234
=0):
916 self
.activefont
= self
.fonts
[fontnum
]
919 self
.debugfile
.write("%d: fnt%s%i current font is %s\n" %
921 id1234
and "%i " % id1234
or "num",
923 self
.fonts
[fontnum
].name
))
926 def definefont(self
, cmdnr
, num
, c
, q
, d
, fontname
):
927 # cmdnr: type of fontdef command (only used for debugging output)
929 # q: scaling factor (fix_word)
930 # Note that q is actually s in large parts of the documentation.
931 # d: design size (fix_word)
934 font
= virtualfont(fontname
, c
, q
/self
.tfmconv
, d
/self
.tfmconv
, self
.tfmconv
, self
.pyxconv
, self
.fontmap
, self
.debug
> 1)
935 except (TypeError, RuntimeError):
936 font
= type1font(fontname
, c
, q
/self
.tfmconv
, d
/self
.tfmconv
, self
.tfmconv
, self
.pyxconv
, self
.fontmap
, self
.debug
> 1)
938 self
.fonts
[num
] = font
941 self
.debugfile
.write("%d: fntdef%d %i: %s\n" % (self
.filepos
, cmdnr
, num
, fontname
))
943 # scale = round((1000.0*self.conv*q)/(self.trueconv*d))
945 # scalestring = scale!=1000 and " scaled %d" % scale or ""
946 # print ("Font %i: %s%s---loaded at size %d DVI units" %
947 # (num, fontname, scalestring, q))
949 # print " (this font is magnified %d%%)" % round(scale/10)
951 def special(self
, s
):
952 x
= self
.pos
[_POS_H
] * self
.pyxconv
953 y
= -self
.pos
[_POS_V
] * self
.pyxconv
955 self
.debugfile
.write("%d: xxx '%s'\n" % (self
.filepos
, s
))
956 if not s
.startswith("PyX:"):
957 warnings
.warn("ignoring special '%s'" % s
)
960 # it is in general not safe to continue using the currently active font because
961 # the specials may involve some gsave/grestore operations
964 command
, args
= s
[4:].split()[0], s
[4:].split()[1:]
965 if command
=="color_begin":
967 c
= color
.cmyk(float(args
[1]), float(args
[2]), float(args
[3]), float(args
[4]))
968 elif args
[0]=="gray":
969 c
= color
.gray(float(args
[1]))
971 c
= color
.hsb(float(args
[1]), float(args
[2]), float(args
[3]))
973 c
= color
.rgb(float(args
[1]), float(args
[2]), float(args
[3]))
975 c
= color
.rgb(int(args
[1])/255.0, int(args
[2])/255.0, int(args
[3])/255.0)
976 elif args
[0]=="texnamed":
978 c
= getattr(color
.cmyk
, args
[1])
979 except AttributeError:
980 raise RuntimeError("unknown TeX color '%s', aborting" % args
[1])
982 raise RuntimeError("color model '%s' cannot be handled by PyX, aborting" % args
[0])
983 self
.actpage
.insert(_savecolor())
984 self
.actpage
.insert(c
)
985 elif command
=="color_end":
986 self
.actpage
.insert(_restorecolor())
987 elif command
=="rotate_begin":
988 self
.actpage
.insert(_savetrafo())
989 self
.actpage
.insert(trafo
.rotate_pt(float(args
[0]), x
, y
))
990 elif command
=="rotate_end":
991 self
.actpage
.insert(_restoretrafo())
992 elif command
=="scale_begin":
993 self
.actpage
.insert(_savetrafo())
994 self
.actpage
.insert(trafo
.scale_pt(float(args
[0]), float(args
[1]), x
, y
))
995 elif command
=="scale_end":
996 self
.actpage
.insert(_restoretrafo())
997 elif command
=="epsinclude":
1001 name
, value
= arg
.split("=")
1002 argdict
[name
] = value
1004 # construct kwargs for epsfile constructor
1006 epskwargs
["filename"] = argdict
["file"]
1007 epskwargs
["bbox"] = bbox
.bbox_pt(float(argdict
["llx"]), float(argdict
["lly"]),
1008 float(argdict
["urx"]), float(argdict
["ury"]))
1009 if argdict
.has_key("width"):
1010 epskwargs
["width"] = float(argdict
["width"]) * unit
.t_pt
1011 if argdict
.has_key("height"):
1012 epskwargs
["height"] = float(argdict
["height"]) * unit
.t_pt
1013 if argdict
.has_key("clip"):
1014 epskwargs
["clip"] = int(argdict
["clip"])
1015 self
.actpage
.insert(epsfile
.epsfile(x
* unit
.t_pt
, y
* unit
.t_pt
, **epskwargs
))
1016 elif command
=="marker":
1018 raise RuntimeError("marker contains spaces")
1020 if c
not in string
.digits
+ string
.letters
+ "@":
1021 raise RuntimeError("marker contains invalid characters")
1022 if self
.actpage
.markers
.has_key(args
[0]):
1023 raise RuntimeError("marker name occurred several times")
1024 self
.actpage
.markers
[args
[0]] = x
* unit
.t_pt
, y
* unit
.t_pt
1026 raise RuntimeError("unknown PyX special '%s', aborting" % command
)
1029 # routines for pushing and popping different dvi chunks on the reader
1031 def _push_dvistring(self
, dvi
, fonts
, afterpos
, fontsize
):
1032 """ push dvi string with defined fonts on top of reader
1033 stack. Every positions gets scaled relatively by the factor
1034 scale. After the interpreting of the dvi chunk has been finished,
1035 continue with self.pos=afterpos. The designsize of the virtual
1036 font is passed as a fix_word
1041 # self.debugfile.write("executing new dvi chunk\n")
1042 self
.debugstack
.append(self
.debug
)
1045 self
.statestack
.append((self
.file, self
.fonts
, self
.activefont
, afterpos
, self
.stack
, self
.pyxconv
, self
.tfmconv
))
1047 # units in vf files are relative to the size of the font and given as fix_words
1048 # which can be converted to floats by diving by 2**20
1049 oldpyxconv
= self
.pyxconv
1050 self
.pyxconv
= fontsize
/2**20
1051 rescale
= self
.pyxconv
/oldpyxconv
1053 self
.file = stringbinfile(dvi
)
1058 # rescale self.pos in order to be consistent with the new scaling
1059 self
.pos
= map(lambda x
, rescale
=rescale
:1.0*x
/rescale
, self
.pos
)
1061 # since tfmconv converts from tfm units to dvi units, rescale it as well
1062 self
.tfmconv
/= rescale
1066 def _pop_dvistring(self
):
1069 # self.debugfile.write("finished executing dvi chunk\n")
1070 self
.debug
= self
.debugstack
.pop()
1073 self
.file, self
.fonts
, self
.activefont
, self
.pos
, self
.stack
, self
.pyxconv
, self
.tfmconv
= self
.statestack
.pop()
1075 # routines corresponding to the different reader states of the dvi maschine
1077 def _read_pre(self
):
1080 self
.filepos
= afile
.tell()
1081 cmd
= afile
.readuchar()
1084 elif cmd
== _DVI_PRE
:
1085 if afile
.readuchar() != _DVI_VERSION
: raise DVIError
1086 num
= afile
.readuint32()
1087 den
= afile
.readuint32()
1088 self
.mag
= afile
.readuint32()
1090 # For the interpretation of the lengths in dvi and tfm files,
1091 # three conversion factors are relevant:
1092 # - self.tfmconv: tfm units -> dvi units
1093 # - self.pyxconv: dvi units -> (PostScript) points
1094 # - self.conv: dvi units -> pixels
1095 self
.tfmconv
= (25400000.0/num
)*(den
/473628672.0)/16.0
1097 # calculate conv as described in the DVIType docu using
1098 # a given resolution in dpi
1099 self
.resolution
= 300.0
1100 self
.conv
= (num
/254000.0)*(self
.resolution
/den
)
1102 # self.pyxconv is the conversion factor from the dvi units
1103 # to (PostScript) points. It consists of
1104 # - self.mag/1000.0: magstep scaling
1105 # - self.conv: conversion from dvi units to pixels
1106 # - 1/self.resolution: conversion from pixels to inch
1107 # - 72 : conversion from inch to points
1108 self
.pyxconv
= self
.mag
/1000.0*self
.conv
/self
.resolution
*72
1110 comment
= afile
.read(afile
.readuchar())
1115 def readpage(self
, pageid
=None):
1116 """ reads a page from the dvi file
1118 This routine reads a page from the dvi file which is
1119 returned as a canvas. When there is no page left in the
1120 dvifile, None is returned and the file is closed properly."""
1123 self
.filepos
= self
.file.tell()
1124 cmd
= self
.file.readuchar()
1127 elif cmd
== _DVI_BOP
:
1129 ispageid
= [self
.file.readuint32() for i
in range(10)]
1130 if pageid
is not None and ispageid
!= pageid
:
1131 raise DVIError("invalid pageid")
1133 self
.debugfile
.write("%d: beginning of page %i\n" % (self
.filepos
, ispageid
[0]))
1134 self
.file.readuint32()
1136 elif cmd
== _DVI_POST
:
1138 return None # nothing left
1142 actpage
= canvas
.canvas()
1143 self
.actpage
= actpage
# XXX should be removed ...
1144 self
.actpage
.markers
= {}
1145 self
.pos
= [0, 0, 0, 0, 0, 0]
1146 self
.activetype1font
= None
1148 # Since we do not know which dvi pages the actual PS file contains later on,
1149 # we have to keep track of used char informations separately for each dvi page.
1150 # In order to do so, the already defined fonts have to be copied and their
1151 # used char informations have to be reset
1152 for nr
in self
.fonts
.keys():
1153 self
.fonts
[nr
] = copy
.copy(self
.fonts
[nr
])
1154 self
.fonts
[nr
].clearusedchars()
1158 self
.filepos
= afile
.tell()
1160 cmd
= afile
.readuchar()
1161 except struct
.error
:
1162 # we most probably (if the dvi file is not corrupt) hit the end of a dvi chunk,
1163 # so we have to continue with the rest of the dvi file
1164 self
._pop
_dvistring
()
1168 if cmd
>= _DVI_CHARMIN
and cmd
<= _DVI_CHARMAX
:
1170 elif cmd
>= _DVI_SET1234
and cmd
< _DVI_SET1234
+ 4:
1171 self
.putchar(afile
.readint(cmd
- _DVI_SET1234
+ 1), id1234
=cmd
-_DVI_SET1234
+1)
1172 elif cmd
== _DVI_SETRULE
:
1173 self
.putrule(afile
.readint32(), afile
.readint32())
1174 elif cmd
>= _DVI_PUT1234
and cmd
< _DVI_PUT1234
+ 4:
1175 self
.putchar(afile
.readint(cmd
- _DVI_PUT1234
+ 1), advancepos
=0, id1234
=cmd
-_DVI_SET1234
+1)
1176 elif cmd
== _DVI_PUTRULE
:
1177 self
.putrule(afile
.readint32(), afile
.readint32(), 0)
1178 elif cmd
== _DVI_EOP
:
1181 self
.debugfile
.write("%d: eop\n \n" % self
.filepos
)
1183 elif cmd
== _DVI_PUSH
:
1184 self
.stack
.append(list(self
.pos
))
1186 self
.debugfile
.write("%s: push\n"
1187 "level %d:(h=%d,v=%d,w=%d,x=%d,y=%d,z=%d,hh=???,vv=???)\n" %
1188 ((self
.filepos
, len(self
.stack
)-1) + tuple(self
.pos
)))
1189 elif cmd
== _DVI_POP
:
1191 self
.pos
= self
.stack
.pop()
1193 self
.debugfile
.write("%s: pop\n"
1194 "level %d:(h=%d,v=%d,w=%d,x=%d,y=%d,z=%d,hh=???,vv=???)\n" %
1195 ((self
.filepos
, len(self
.stack
)) + tuple(self
.pos
)))
1196 elif cmd
>= _DVI_RIGHT1234
and cmd
< _DVI_RIGHT1234
+ 4:
1198 dh
= afile
.readint(cmd
- _DVI_RIGHT1234
+ 1, 1)
1200 self
.debugfile
.write("%d: right%d %d h:=%d%+d=%d, hh:=???\n" %
1202 cmd
- _DVI_RIGHT1234
+ 1,
1206 self
.pos
[_POS_H
]+dh
))
1207 self
.pos
[_POS_H
] += dh
1208 elif cmd
== _DVI_W0
:
1211 self
.debugfile
.write("%d: w0 %d h:=%d%+d=%d, hh:=???\n" %
1216 self
.pos
[_POS_H
]+self
.pos
[_POS_W
]))
1217 self
.pos
[_POS_H
] += self
.pos
[_POS_W
]
1218 elif cmd
>= _DVI_W1234
and cmd
< _DVI_W1234
+ 4:
1220 self
.pos
[_POS_W
] = afile
.readint(cmd
- _DVI_W1234
+ 1, 1)
1222 self
.debugfile
.write("%d: w%d %d h:=%d%+d=%d, hh:=???\n" %
1224 cmd
- _DVI_W1234
+ 1,
1228 self
.pos
[_POS_H
]+self
.pos
[_POS_W
]))
1229 self
.pos
[_POS_H
] += self
.pos
[_POS_W
]
1230 elif cmd
== _DVI_X0
:
1233 self
.debugfile
.write("%d: x0 %d h:=%d%+d=%d, hh:=???\n" %
1238 self
.pos
[_POS_H
]+self
.pos
[_POS_X
]))
1239 self
.pos
[_POS_H
] += self
.pos
[_POS_X
]
1240 elif cmd
>= _DVI_X1234
and cmd
< _DVI_X1234
+ 4:
1242 self
.pos
[_POS_X
] = afile
.readint(cmd
- _DVI_X1234
+ 1, 1)
1244 self
.debugfile
.write("%d: x%d %d h:=%d%+d=%d, hh:=???\n" %
1246 cmd
- _DVI_X1234
+ 1,
1250 self
.pos
[_POS_H
]+self
.pos
[_POS_X
]))
1251 self
.pos
[_POS_H
] += self
.pos
[_POS_X
]
1252 elif cmd
>= _DVI_DOWN1234
and cmd
< _DVI_DOWN1234
+ 4:
1254 dv
= afile
.readint(cmd
- _DVI_DOWN1234
+ 1, 1)
1256 self
.debugfile
.write("%d: down%d %d v:=%d%+d=%d, vv:=???\n" %
1258 cmd
- _DVI_DOWN1234
+ 1,
1262 self
.pos
[_POS_V
]+dv
))
1263 self
.pos
[_POS_V
] += dv
1264 elif cmd
== _DVI_Y0
:
1267 self
.debugfile
.write("%d: y0 %d v:=%d%+d=%d, vv:=???\n" %
1272 self
.pos
[_POS_V
]+self
.pos
[_POS_Y
]))
1273 self
.pos
[_POS_V
] += self
.pos
[_POS_Y
]
1274 elif cmd
>= _DVI_Y1234
and cmd
< _DVI_Y1234
+ 4:
1276 self
.pos
[_POS_Y
] = afile
.readint(cmd
- _DVI_Y1234
+ 1, 1)
1278 self
.debugfile
.write("%d: y%d %d v:=%d%+d=%d, vv:=???\n" %
1280 cmd
- _DVI_Y1234
+ 1,
1284 self
.pos
[_POS_V
]+self
.pos
[_POS_Y
]))
1285 self
.pos
[_POS_V
] += self
.pos
[_POS_Y
]
1286 elif cmd
== _DVI_Z0
:
1289 self
.debugfile
.write("%d: z0 %d v:=%d%+d=%d, vv:=???\n" %
1294 self
.pos
[_POS_V
]+self
.pos
[_POS_Z
]))
1295 self
.pos
[_POS_V
] += self
.pos
[_POS_Z
]
1296 elif cmd
>= _DVI_Z1234
and cmd
< _DVI_Z1234
+ 4:
1298 self
.pos
[_POS_Z
] = afile
.readint(cmd
- _DVI_Z1234
+ 1, 1)
1300 self
.debugfile
.write("%d: z%d %d v:=%d%+d=%d, vv:=???\n" %
1302 cmd
- _DVI_Z1234
+ 1,
1306 self
.pos
[_POS_V
]+self
.pos
[_POS_Z
]))
1307 self
.pos
[_POS_V
] += self
.pos
[_POS_Z
]
1308 elif cmd
>= _DVI_FNTNUMMIN
and cmd
<= _DVI_FNTNUMMAX
:
1309 self
.usefont(cmd
- _DVI_FNTNUMMIN
, 0)
1310 elif cmd
>= _DVI_FNT1234
and cmd
< _DVI_FNT1234
+ 4:
1311 # note that according to the DVI docs, for four byte font numbers,
1312 # the font number is signed. Don't ask why!
1313 fntnum
= afile
.readint(cmd
- _DVI_FNT1234
+ 1, cmd
== _DVI_FNT1234
+ 3)
1314 self
.usefont(fntnum
, id1234
=cmd
-_DVI_FNT1234
+1)
1315 elif cmd
>= _DVI_SPECIAL1234
and cmd
< _DVI_SPECIAL1234
+ 4:
1316 self
.special(afile
.read(afile
.readint(cmd
- _DVI_SPECIAL1234
+ 1)))
1317 elif cmd
>= _DVI_FNTDEF1234
and cmd
< _DVI_FNTDEF1234
+ 4:
1318 if cmd
== _DVI_FNTDEF1234
:
1319 num
= afile
.readuchar()
1320 elif cmd
== _DVI_FNTDEF1234
+1:
1321 num
= afile
.readuint16()
1322 elif cmd
== _DVI_FNTDEF1234
+2:
1323 num
= afile
.readuint24()
1324 elif cmd
== _DVI_FNTDEF1234
+3:
1325 # Cool, here we have according to docu a signed int. Why?
1326 num
= afile
.readint32()
1327 self
.definefont(cmd
-_DVI_FNTDEF1234
+1,
1332 afile
.read(afile
.readuchar()+afile
.readuchar()))
1337 ##############################################################################
1339 ##############################################################################
1341 _VF_LONG_CHAR
= 242 # character packet (long version)
1342 _VF_FNTDEF1234
= _DVI_FNTDEF1234
# font definition
1343 _VF_PRE
= _DVI_PRE
# preamble
1344 _VF_POST
= _DVI_POST
# postamble
1346 _VF_ID
= 202 # VF id byte
1348 class VFError(exceptions
.Exception): pass
1351 def __init__(self
, filename
, scale
, tfmconv
, pyxconv
, fontmap
, debug
=0):
1352 self
.filename
= filename
1354 self
.tfmconv
= tfmconv
1355 self
.pyxconv
= pyxconv
1356 self
.fontmap
= fontmap
1358 self
.fonts
= {} # used fonts
1359 self
.widths
= {} # widths of defined chars
1360 self
.chardefs
= {} # dvi chunks for defined chars
1362 afile
= binfile(self
.filename
, "rb")
1364 cmd
= afile
.readuchar()
1366 if afile
.readuchar() != _VF_ID
: raise VFError
1367 comment
= afile
.read(afile
.readuchar())
1368 self
.cs
= afile
.readuint32()
1369 self
.ds
= afile
.readuint32()
1374 cmd
= afile
.readuchar()
1375 if cmd
>= _VF_FNTDEF1234
and cmd
< _VF_FNTDEF1234
+ 4:
1377 if cmd
== _VF_FNTDEF1234
:
1378 num
= afile
.readuchar()
1379 elif cmd
== _VF_FNTDEF1234
+1:
1380 num
= afile
.readuint16()
1381 elif cmd
== _VF_FNTDEF1234
+2:
1382 num
= afile
.readuint24()
1383 elif cmd
== _VF_FNTDEF1234
+3:
1384 num
= afile
.readint32()
1385 c
= afile
.readint32()
1386 s
= afile
.readint32() # relative scaling used for font (fix_word)
1387 d
= afile
.readint32() # design size of font
1388 fontname
= afile
.read(afile
.readuchar()+afile
.readuchar())
1390 # rescaled size of font: s is relative to the scaling
1391 # of the virtual font itself. Note that realscale has
1392 # to be a fix_word (like s)
1393 # XXX: check rounding
1394 reals
= int(round(self
.scale
* (16*self
.ds
/16777216L) * s
))
1396 # print ("defining font %s -- VF scale: %g, VF design size: %d, relative font size: %d => real size: %d" %
1397 # (fontname, self.scale, self.ds, s, reals)
1400 # XXX allow for virtual fonts here too
1401 self
.fonts
[num
] = type1font(fontname
, c
, reals
, d
, self
.tfmconv
, self
.pyxconv
, self
.fontmap
, self
.debug
> 1)
1402 elif cmd
== _VF_LONG_CHAR
:
1403 # character packet (long form)
1404 pl
= afile
.readuint32() # packet length
1405 cc
= afile
.readuint32() # char code (assumed unsigned, but anyhow only 0 <= cc < 255 is actually used)
1406 tfm
= afile
.readuint24() # character width
1407 dvi
= afile
.read(pl
) # dvi code of character
1408 self
.widths
[cc
] = tfm
1409 self
.chardefs
[cc
] = dvi
1410 elif cmd
< _VF_LONG_CHAR
:
1411 # character packet (short form)
1412 cc
= afile
.readuchar() # char code
1413 tfm
= afile
.readuint24() # character width
1414 dvi
= afile
.read(cmd
)
1415 self
.widths
[cc
] = tfm
1416 self
.chardefs
[cc
] = dvi
1417 elif cmd
== _VF_POST
:
1427 def getchar(self
, cc
):
1428 return self
.chardefs
[cc
]