2 # -*- coding: ISO-8859-1 -*-
5 # Copyright (C) 2002-2004 Jörg Lehmann <joergl@users.sourceforge.net>
6 # Copyright (C) 2003-2004 Michael Schindler <m-schindler@users.sourceforge.net>
7 # Copyright (C) 2002-2004 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
26 import unit
, epsfile
, bbox
, base
, canvas
, color
, trafo
, path
, prolog
, pykpathsea
, resource
29 def __init__(self
, word
):
35 self
.precomma
= abs(word
) >> 20
36 self
.postcomma
= abs(word
) & 0xFFFFF
39 return self
.sign
* (self
.precomma
+ 1.0*self
.postcomma
/0xFFFFF)
41 def __mul__(self
, other
):
45 result
.sign
= self
.sign
*other
.sign
46 c
= self
.postcomma
*other
.precomma
+ self
.precomma
*other
.postcomma
47 result
.precomma
= self
.precomma
*other
.precomma
+ (c
>> 20)
48 result
.postcomma
= c
& 0xFFFFF + ((self
.postcomma
*other
.postcomma
) >> 40)
54 def __init__(self
, filename
, mode
="r"):
55 self
.file = open(filename
, mode
)
61 return self
.file.tell()
64 return self
.file.eof()
66 def read(self
, bytes
):
67 return self
.file.read(bytes
)
69 def readint(self
, bytes
=4, signed
=0):
73 value
= ord(self
.file.read(1))
74 if first
and signed
and value
> 127:
77 result
= 256 * result
+ value
82 return struct
.unpack(">l", self
.file.read(4))[0]
85 return struct
.unpack(">L", self
.file.read(4))[0]
89 return struct
.unpack(">l", "\0"+self
.file.read(3))[0]
93 return struct
.unpack(">L", "\0"+self
.file.read(3))[0]
96 return struct
.unpack(">h", self
.file.read(2))[0]
99 return struct
.unpack(">H", self
.file.read(2))[0]
102 return struct
.unpack("b", self
.file.read(1))[0]
105 return struct
.unpack("B", self
.file.read(1))[0]
107 def readstring(self
, bytes
):
109 assert l
<= bytes
-1, "inconsistency in file: string too long"
110 return self
.file.read(bytes
-1)[:l
]
112 class stringbinfile(binfile
):
114 def __init__(self
, s
):
115 self
.file = cStringIO
.StringIO(s
)
119 # """ ascii file containing tokens separated by spaces.
121 # Comments beginning with % are ignored. Strings containing spaces
122 # are not handled correctly
125 # def __init__(self, filename):
126 # self.file = open(filename, "r")
129 # def gettoken(self):
130 # """ return next token or None if EOF """
131 # while not self.line:
132 # line = self.file.readline()
135 # self.line = line.split("%")[0].split()
136 # token = self.line[0]
137 # self.line = self.line[1:]
144 ##############################################################################
146 ##############################################################################
148 class TFMError(exceptions
.Exception): pass
151 class char_info_word
:
152 def __init__(self
, word
):
153 self
.width_index
= int((word
& 0xFF000000L
) >> 24) #make sign-safe
154 self
.height_index
= (word
& 0x00F00000) >> 20
155 self
.depth_index
= (word
& 0x000F0000) >> 16
156 self
.italic_index
= (word
& 0x0000FC00) >> 10
157 self
.tag
= (word
& 0x00000300) >> 8
158 self
.remainder
= (word
& 0x000000FF)
162 def __init__(self
, name
, debug
=0):
163 self
.file = binfile(name
, "rb")
170 self
.lf
= self
.file.readint16()
171 self
.lh
= self
.file.readint16()
172 self
.bc
= self
.file.readint16()
173 self
.ec
= self
.file.readint16()
174 self
.nw
= self
.file.readint16()
175 self
.nh
= self
.file.readint16()
176 self
.nd
= self
.file.readint16()
177 self
.ni
= self
.file.readint16()
178 self
.nl
= self
.file.readint16()
179 self
.nk
= self
.file.readint16()
180 self
.ne
= self
.file.readint16()
181 self
.np
= self
.file.readint16()
183 if not (self
.bc
-1 <= self
.ec
<= 255 and
185 self
.lf
== 6+self
.lh
+(self
.ec
-self
.bc
+1)+self
.nw
+self
.nh
+self
.nd
186 +self
.ni
+self
.nl
+self
.nk
+self
.ne
+self
.np
):
187 raise TFMError
, "error in TFM pre-header"
190 print "lh=%d" % self
.lh
196 self
.checksum
= self
.file.readint32()
197 self
.designsizeraw
= self
.file.readint32()
198 assert self
.designsizeraw
> 0, "invald design size"
199 self
.designsize
= fix_word(self
.designsizeraw
)
201 assert self
.lh
> 11, "inconsistency in TFM file: incomplete field"
202 self
.charcoding
= self
.file.readstring(40)
204 self
.charcoding
= None
207 assert self
.lh
> 16, "inconsistency in TFM file: incomplete field"
208 self
.fontfamily
= self
.file.readstring(20)
210 self
.fontfamily
= None
213 print "(FAMILY %s)" % self
.fontfamily
214 print "(CODINGSCHEME %s)" % self
.charcoding
215 print "(DESINGSIZE R %f)" % self
.designsize
218 self
.sevenbitsave
= self
.file.readuchar()
219 # ignore the following two bytes
220 self
.file.readint16()
221 facechar
= self
.file.readuchar()
222 # decode ugly face specification into the Knuth suggested string
234 self
.face
= "L" + self
.face
237 self
.face
= "B" + self
.face
240 self
.face
= "M" + self
.face
243 self
.face
= self
.face
[0] + "I" + self
.face
[1]
245 self
.face
= self
.face
[0] + "R" + self
.face
[1]
250 self
.sevenbitsave
= self
.face
= None
253 # just ignore the rest
254 print self
.file.read((self
.lh
-18)*4)
260 self
.char_info
= [None]*(self
.ec
+1)
261 for charcode
in range(self
.bc
, self
.ec
+1):
262 self
.char_info
[charcode
] = char_info_word(self
.file.readint32())
263 if self
.char_info
[charcode
].width_index
== 0:
264 # disable character if width_index is zero
265 self
.char_info
[charcode
] = None
271 self
.width
= [None for width_index
in range(self
.nw
)]
272 for width_index
in range(self
.nw
):
273 # self.width[width_index] = fix_word(self.file.readint32())
274 self
.width
[width_index
] = self
.file.readint32()
280 self
.height
= [None for height_index
in range(self
.nh
)]
281 for height_index
in range(self
.nh
):
282 # self.height[height_index] = fix_word(self.file.readint32())
283 self
.height
[height_index
] = self
.file.readint32()
289 self
.depth
= [None for depth_index
in range(self
.nd
)]
290 for depth_index
in range(self
.nd
):
291 # self.depth[depth_index] = fix_word(self.file.readint32())
292 self
.depth
[depth_index
] = self
.file.readint32()
298 self
.italic
= [None for italic_index
in range(self
.ni
)]
299 for italic_index
in range(self
.ni
):
300 # self.italic[italic_index] = fix_word(self.file.readint32())
301 self
.italic
[italic_index
] = self
.file.readint32()
307 # XXX decode to lig_kern_command
309 self
.lig_kern
= [None for lig_kern_index
in range(self
.nl
)]
310 for lig_kern_index
in range(self
.nl
):
311 self
.lig_kern
[lig_kern_index
] = self
.file.readint32()
317 self
.kern
= [None for kern_index
in range(self
.nk
)]
318 for kern_index
in range(self
.nk
):
319 # self.kern[kern_index] = fix_word(self.file.readint32())
320 self
.kern
[kern_index
] = self
.file.readint32()
326 # XXX decode to extensible_recipe
328 self
.exten
= [None for exten_index
in range(self
.ne
)]
329 for exten_index
in range(self
.ne
):
330 self
.exten
[exten_index
] = self
.file.readint32()
338 self
.param
= [None for param_index
in range(self
.np
)]
339 for param_index
in range(self
.np
):
340 self
.param
[param_index
] = self
.file.readint32()
345 # class FontEncoding:
347 # def __init__(self, filename):
348 # """ font encoding contained in filename """
349 # encpath = pykpathsea.find_file(filename, pykpathsea.kpse_tex_ps_header_format)
350 # encfile = tokenfile(encpath)
353 # self.encname = encfile.gettoken()
354 # token = encfile.gettoken()
356 # raise RuntimeError("cannot parse encoding file '%s', expecting '[' got '%s'" % (filename, token))
357 # self.encvector = []
358 # for i in range(256):
359 # token = encfile.gettoken()
360 # if token is None or token=="]":
361 # raise RuntimeError("not enough charcodes in encoding file '%s'" % filename)
362 # self.encvector.append(token)
363 # if encfile.gettoken() != "]":
364 # raise RuntimeError("too many charcodes in encoding file '%s'" % filename)
365 # token = encfile.gettoken()
367 # raise RuntimeError("cannot parse encoding file '%s', expecting 'def' got '%s'" % (filename, token))
368 # token = encfile.gettoken()
370 # raise RuntimeError("encoding file '%s' too long" % filename)
373 # def encode(self, charcode):
374 # return self.encvector[charcode]
376 ##############################################################################
378 ##############################################################################
380 _ReEncodeFont
= prolog
.definition("ReEncodeFont", """{
383 /newencoding exch def
384 /newfontname exch def
385 /basefontname exch def
386 /basefontdict basefontname findfont def
387 /newfontdict basefontdict maxlength dict def
389 exch dup dup /FID ne exch /Encoding ne and
390 { exch newfontdict 3 1 roll put }
394 newfontdict /FontName newfontname put
395 newfontdict /Encoding newencoding put
396 newfontname newfontdict definefont pop
401 # PostScript font selection and output primitives
404 class _begintextobject(base
.canvasitem
):
405 def outputPS(self
, file):
408 def outputPDF(self
, file):
412 class _endtextobject(base
.canvasitem
):
413 def outputPS(self
, file):
416 def outputPDF(self
, file):
420 class _selectfont(base
.canvasitem
):
421 # XXX this should go away and be merged with selectfont
422 def __init__(self
, name
, size
):
426 def outputPS(self
, file):
427 file.write("/%s %f selectfont\n" % (self
.name
, self
.size
))
429 def outputPDF(self
, file):
430 file.write("/%s %f Tf\n" % (self
.name
, self
.size
))
433 class selectfont(base
.canvasitem
):
434 def __init__(self
, font
):
435 # XXX maybe we should change the calling convention here and only pass the
436 # name, size, encoding, usedchars of the font
438 self
.size
= font
.getsize_pt()
440 self
.fontid
= font
.getpsname()
443 result
= [prolog
.fontdefinition(self
.font
,
444 self
.font
.getbasepsname(),
445 self
.font
.getfontfile(),
446 self
.font
.getencodingfile(),
447 self
.font
.usedchars
)]
448 if self
.font
.getencoding():
449 result
.append(_ReEncodeFont
)
450 result
.append(prolog
.fontencoding(self
.font
.getencoding(), self
.font
.getencodingfile()))
451 result
.append(prolog
.fontreencoding(self
.font
.getpsname(), self
.font
.getbasepsname(), self
.font
.getencoding()))
454 def outputPS(self
, file):
455 file.write("/%s %f selectfont\n" % (self
.fontid
, self
.size
))
457 def outputPDF(self
, file):
458 file.write("/%s %f Tf\n" % (self
.fontid
, self
.size
))
460 def registerresources(self
, registry
):
461 fontresource
= resource
.font(self
.font
.getpsname(), self
.font
.getencoding(), self
.font
.usedchars
)
462 registry
.registerresource(fontresource
)
463 self
.fontid
= fontresource
.id
466 class _show(base
.canvasitem
):
468 def __init__(self
, x
, y
):
476 def addchar(self
, width
, height
, depth
, char
):
478 if height
> self
.height
:
480 if depth
> self
.depth
:
482 self
.chars
.append(char
)
485 return bbox
.bbox_pt(self
.x
, self
.y
-self
.depth
, self
.x
+self
.width
, self
.y
+self
.height
)
487 def outputPS(self
, file):
489 for char
in self
.chars
:
490 if char
> 32 and char
< 127 and chr(char
) not in "()[]<>\\":
491 ascii
= "%s" % chr(char
)
493 ascii
= "\\%03o" % char
495 file.write("%g %g moveto (%s) show\n" % (self
.x
, self
.y
, outstring
))
497 def outputPDF(self
, file):
499 for char
in self
.chars
:
500 if char
> 32 and char
< 127 and chr(char
) not in "()[]<>\\":
501 ascii
= "%s" % chr(char
)
503 ascii
= "\\%03o" % char
505 # file.write("%f %f Td (%s) Tj\n" % (self.x, self.y, outstring))
506 file.write("1 0 0 1 %f %f Tm (%s) Tj\n" % (self
.x
, self
.y
, outstring
))
511 tokenpattern
= re
.compile(r
'"(.*?)("\s+|"$|$)|(.*?)(\s+|$)')
513 def __init__(self
, s
):
514 """ construct font mapping from line s of font mapping file """
515 self
.texname
= self
.basepsname
= self
.fontfile
= None
518 self
.encodingfile
= None
520 # supported postscript fragments occuring in psfonts.map
521 self
.reencodefont
= self
.extendfont
= self
.slantfont
= None
525 match
= self
.tokenpattern
.match(s
)
527 if match
.groups()[0]:
528 tokens
.append('"%s"' % match
.groups()[0])
530 tokens
.append(match
.groups()[2])
533 raise RuntimeError("wrong syntax")
536 if token
.startswith("<"):
537 if token
.startswith("<<"):
538 # XXX: support non-partial download here
539 self
.fontfile
= token
[2:]
540 elif token
.startswith("<["):
541 self
.encodingfile
= token
[2:]
542 elif token
.endswith(".pfa") or token
.endswith(".pfb"):
543 self
.fontfile
= token
[1:]
544 elif token
.endswith(".enc"):
545 self
.encodingfile
= token
[1:]
547 raise RuntimeError("wrong syntax")
548 elif token
.startswith('"'):
549 pscode
= token
[1:-1].split()
550 # parse standard postscript code fragments
553 arg
, cmd
= pscode
[:2]
555 raise RuntimeError("Unsupported Postscript fragment '%s'" % pscode
)
557 if cmd
== "ReEncodeFont":
558 self
.reencodefont
= arg
559 elif cmd
== "ExtendFont":
560 self
.extendfont
= arg
561 elif cmd
== "SlantFont":
564 raise RuntimeError("Unsupported Postscript fragment '%s %s'" % (arg
, cmd
))
566 if self
.texname
is None:
569 self
.basepsname
= token
570 if self
.basepsname
is None:
571 self
.basepsname
= self
.texname
574 return ("'%s' is '%s' read from '%s' encoded as '%s'" %
575 (self
.texname
, self
.basepsname
, self
.fontfile
, repr(self
.encodingfile
)))
579 def readfontmap(filenames
):
580 """ read font map from filename (without path) """
582 for filename
in filenames
:
583 mappath
= pykpathsea
.find_file(filename
, pykpathsea
.kpse_fontmap_format
)
584 # try also the oft-used registration as dvips config file
586 mappath
= pykpathsea
.find_file(filename
, pykpathsea
.kpse_dvips_config_format
)
588 raise RuntimeError("cannot find font mapping file '%s'" % filename
)
589 mapfile
= open(mappath
, "r")
591 for line
in mapfile
.readlines():
594 if not (line
=="" or line
[0] in (" ", "%", "*", ";" , "#")):
596 fm
= fontmapping(line
)
597 except RuntimeError, e
:
598 sys
.stderr
.write("*** PyX Warning: Ignoring line %i in mapping file '%s': %s\n" % (lineno
, filename
, e
))
600 fontmap
[fm
.texname
] = fm
606 def __init__(self
, name
, c
, q
, d
, tfmconv
, pyxconv
, debug
=0):
608 self
.q
= q
# desired size of font (fix_word) in tex points
609 self
.d
= d
# design size of font (fix_word) in tex points
610 self
.tfmconv
= tfmconv
# conversion factor from tfm units to dvi units
611 self
.pyxconv
= pyxconv
# conversion factor from dvi units to PostScript points
612 tfmpath
= pykpathsea
.find_file("%s.tfm" % self
.name
, pykpathsea
.kpse_tfm_format
)
614 raise TFMError("cannot find %s.tfm" % self
.name
)
615 self
.tfmfile
= tfmfile(tfmpath
, debug
)
617 # We only check for equality of font checksums if none of them
618 # is zero. The case c == 0 happend in some VF files and
619 # according to the VFtoVP documentation, paragraph 40, a check
620 # is only performed if tfmfile.checksum > 0. Anyhow, being
621 # more generous here seems to be reasonable
622 if self
.tfmfile
.checksum
!= c
and self
.tfmfile
.checksum
!= 0 and c
!= 0:
623 raise DVIError("check sums do not agree: %d vs. %d" %
624 (self
.tfmfile
.checksum
, c
))
626 # tfmfile.designsizeraw is the design size of the font as a fix_word
627 if abs(self
.tfmfile
.designsizeraw
- d
) > 2:
628 raise DVIError("design sizes do not agree: %d vs. %d" % (self
.tfmfile
.designsizeraw
, d
))
629 if q
< 0 or q
> 134217728:
630 raise DVIError("font '%s' not loaded: bad scale" % self
.name
)
631 if d
< 0 or d
> 134217728:
632 raise DVIError("font '%s' not loaded: bad design size" % self
.name
)
636 # for bookkeeping of used characters
637 self
.usedchars
= [0] * 256
640 return "font %s designed at %g tex pts used at %g tex pts" % (self
.name
,
641 16.0*self
.d
/16777216L,
642 16.0*self
.q
/16777216L)
647 def getsize_pt(self
):
648 """ return size of font in (PS) points """
649 # The factor 16L/16777216L=2**(-20) converts a fix_word (here self.q)
650 # to the corresponding float. Furthermore, we have to convert from TeX
651 # points to points, hence the factor 72/72.27.
652 return 16L*self
.q
/16777216L*72/72.27
654 def _convert_tfm_to_dvi(self
, length
):
655 return int(round(16L*length
*self
.q
/16777216L*self
.tfmconv
))
657 def _convert_tfm_to_pt(self
, length
):
658 return int(round(16L*length
*self
.q
/16777216L*self
.tfmconv
)) * self
.pyxconv
660 # routines returning lengths as integers in dvi units
662 def getwidth_dvi(self
, charcode
):
663 return self
._convert
_tfm
_to
_dvi
(self
.tfmfile
.width
[self
.tfmfile
.char_info
[charcode
].width_index
])
665 def getheight_dvi(self
, charcode
):
666 return self
._convert
_tfm
_to
_dvi
(self
.tfmfile
.height
[self
.tfmfile
.char_info
[charcode
].height_index
])
668 def getdepth_dvi(self
, charcode
):
669 return self
._convert
_tfm
_to
_dvi
(self
.tfmfile
.depth
[self
.tfmfile
.char_info
[charcode
].depth_index
])
671 def getitalic_dvi(self
, charcode
):
672 return self
._convert
_tfm
_to
_dvi
(self
.tfmfile
.italic
[self
.tfmfile
.char_info
[charcode
].italic_index
])
674 # routines returning lengths as floats in PostScript points
676 def getwidth_pt(self
, charcode
):
677 return self
._convert
_tfm
_to
_pt
(self
.tfmfile
.width
[self
.tfmfile
.char_info
[charcode
].width_index
])
679 def getheight_pt(self
, charcode
):
680 return self
._convert
_tfm
_to
_pt
(self
.tfmfile
.height
[self
.tfmfile
.char_info
[charcode
].height_index
])
682 def getdepth_pt(self
, charcode
):
683 return self
._convert
_tfm
_to
_pt
(self
.tfmfile
.depth
[self
.tfmfile
.char_info
[charcode
].depth_index
])
685 def getitalic_pt(self
, charcode
):
686 return self
._convert
_tfm
_to
_pt
(self
.tfmfile
.italic
[self
.tfmfile
.char_info
[charcode
].italic_index
])
688 def markcharused(self
, charcode
):
689 self
.usedchars
[charcode
] = 1
691 def mergeusedchars(self
, otherfont
):
692 for i
in range(len(self
.usedchars
)):
693 self
.usedchars
[i
] = self
.usedchars
[i
] or otherfont
.usedchars
[i
]
695 def clearusedchars(self
):
696 self
.usedchars
= [0] * 256
699 class type1font(font
):
700 def __init__(self
, name
, c
, q
, d
, tfmconv
, pyxconv
, fontmap
, debug
=0):
701 font
.__init
__(self
, name
, c
, q
, d
, tfmconv
, pyxconv
, debug
)
702 self
.fontmapping
= fontmap
.get(name
)
703 if self
.fontmapping
is None:
704 raise RuntimeError("no information for font '%s' found in font mapping file, aborting" % name
)
706 def getbasepsname(self
):
707 return self
.fontmapping
.basepsname
710 if self
.fontmapping
.reencodefont
:
711 return "%s-%s" % (self
.fontmapping
.basepsname
, self
.fontmapping
.reencodefont
)
713 return self
.fontmapping
.basepsname
715 def getfontfile(self
):
716 return self
.fontmapping
.fontfile
718 def getencoding(self
):
719 return self
.fontmapping
.reencodefont
721 def getencodingfile(self
):
722 return self
.fontmapping
.encodingfile
725 class virtualfont(font
):
726 def __init__(self
, name
, c
, q
, d
, tfmconv
, pyxconv
, fontmap
, debug
=0):
727 font
.__init
__(self
, name
, c
, q
, d
, tfmconv
, pyxconv
, debug
)
728 fontpath
= pykpathsea
.find_file(name
, pykpathsea
.kpse_vf_format
)
729 if fontpath
is None or not len(fontpath
):
731 self
.vffile
= vffile(fontpath
, self
.scale
, tfmconv
, pyxconv
, fontmap
, debug
> 1)
734 """ return fonts used in virtual font itself """
735 return self
.vffile
.getfonts()
737 def getchar(self
, cc
):
738 """ return dvi chunk corresponding to char code cc """
739 return self
.vffile
.getchar(cc
)
742 ##############################################################################
744 ##############################################################################
746 _DVI_CHARMIN
= 0 # typeset a character and move right (range min)
747 _DVI_CHARMAX
= 127 # typeset a character and move right (range max)
748 _DVI_SET1234
= 128 # typeset a character and move right
749 _DVI_SETRULE
= 132 # typeset a rule and move right
750 _DVI_PUT1234
= 133 # typeset a character
751 _DVI_PUTRULE
= 137 # typeset a rule
752 _DVI_NOP
= 138 # no operation
753 _DVI_BOP
= 139 # beginning of page
754 _DVI_EOP
= 140 # ending of page
755 _DVI_PUSH
= 141 # save the current positions (h, v, w, x, y, z)
756 _DVI_POP
= 142 # restore positions (h, v, w, x, y, z)
757 _DVI_RIGHT1234
= 143 # move right
758 _DVI_W0
= 147 # move right by w
759 _DVI_W1234
= 148 # move right and set w
760 _DVI_X0
= 152 # move right by x
761 _DVI_X1234
= 153 # move right and set x
762 _DVI_DOWN1234
= 157 # move down
763 _DVI_Y0
= 161 # move down by y
764 _DVI_Y1234
= 162 # move down and set y
765 _DVI_Z0
= 166 # move down by z
766 _DVI_Z1234
= 167 # move down and set z
767 _DVI_FNTNUMMIN
= 171 # set current font (range min)
768 _DVI_FNTNUMMAX
= 234 # set current font (range max)
769 _DVI_FNT1234
= 235 # set current font
770 _DVI_SPECIAL1234
= 239 # special (dvi extention)
771 _DVI_FNTDEF1234
= 243 # define the meaning of a font number
772 _DVI_PRE
= 247 # preamble
773 _DVI_POST
= 248 # postamble beginning
774 _DVI_POSTPOST
= 249 # postamble ending
776 _DVI_VERSION
= 2 # dvi version
778 # position variable indices
790 _READ_POST
= 4 # XXX not used
791 _READ_POSTPOST
= 5 # XXX not used
795 class DVIError(exceptions
.Exception): pass
797 # save and restore colors
799 class _savecolor(base
.canvasitem
):
800 def outputPS(self
, file):
801 file.write("currentcolor currentcolorspace\n")
803 def outputPDF(self
, file):
807 class _restorecolor(base
.canvasitem
):
808 def outputPS(self
, file):
809 file.write("setcolorspace setcolor\n")
811 def outputPDF(self
, file):
814 class _savetrafo(base
.canvasitem
):
815 def outputPS(self
, file):
816 file.write("matrix currentmatrix\n")
818 def outputPDF(self
, file):
822 class _restoretrafo(base
.canvasitem
):
823 def outputPS(self
, file):
824 file.write("setmatrix\n")
826 def outputPDF(self
, file):
832 def __init__(self
, filename
, fontmap
, debug
=0):
833 """ opens the dvi file and reads the preamble """
834 self
.filename
= filename
835 self
.fontmap
= fontmap
839 self
.activefont
= None
841 # stack of fonts and fontscale currently used (used for VFs)
845 # pointer to currently active page
848 # currently active output: show instance currently used and
849 # the corresponding type 1 font
850 self
.activeshow
= None
851 self
.activetype1font
= None
853 # stack for self.file, self.fonts and self.stack, needed for VF inclusion
856 self
.file = binfile(self
.filename
, "rb")
858 # currently read byte in file (for debugging output)
866 """ flush currently active string """
867 if self
.activeshow
is not None:
869 print "[%s]" % "".join([chr(char
) for char
in self
.activeshow
.chars
])
870 self
.actpage
.insert(self
.activeshow
)
871 self
.activeshow
= None
874 """ activate the font if is not yet active, closing a currently active
875 text object and flushing the output"""
876 if isinstance(self
.activefont
, type1font
):
878 if self
.activetype1font
!= self
.activefont
and self
.activefont
:
879 self
.actpage
.insert(_begintextobject())
880 self
.actpage
.insert(selectfont(self
.activefont
))
881 self
.activetype1font
= self
.activefont
885 if self
.activetype1font
:
886 self
.actpage
.insert(_endtextobject())
887 self
.activetype1font
= None
889 def putrule(self
, height
, width
, advancepos
=1):
891 x1
= self
.pos
[_POS_H
] * self
.pyxconv
892 y1
= -self
.pos
[_POS_V
] * self
.pyxconv
893 w
= width
* self
.pyxconv
894 h
= height
* self
.pyxconv
896 if height
> 0 and width
> 0:
898 pixelw
= int(width
*self
.conv
*self
.mag
/1000.0)
899 if pixelw
< width
*self
.pyxconv
: pixelw
+= 1
900 pixelh
= int(height
*self
.conv
*self
.mag
/1000.0)
901 if pixelh
< height
*self
.pyxconv
: pixelh
+= 1
903 print ("%d: %srule height %d, width %d (%dx%d pixels)" %
904 (self
.filepos
, advancepos
and "set" or "put", height
, width
, pixelh
, pixelw
))
905 self
.actpage
.fill(path
.rect_pt(x1
, y1
, w
, h
))
908 print ("%d: %srule height %d, width %d (invisible)" %
909 (self
.filepos
, advancepos
and "set" or "put", height
, width
))
913 print (" h:=%d+%d=%d, hh:=%d" %
914 (self
.pos
[_POS_H
], width
, self
.pos
[_POS_H
]+width
, 0))
915 self
.pos
[_POS_H
] += width
917 def putchar(self
, char
, advancepos
=1):
918 dx
= advancepos
and self
.activefont
.getwidth_dvi(char
) or 0
921 print ("%d: %schar%d h:=%d+%d=%d, hh:=%d" %
923 advancepos
and "set" or "put",
925 self
.pos
[_POS_H
], dx
, self
.pos
[_POS_H
]+dx
,
928 if isinstance(self
.activefont
, type1font
):
929 if self
.activeshow
is None:
930 # XXX: begintext would lead to massive number of selectfonts being issued
931 # OTOH is it save to remove begintext here? I think so ...
933 self
.activeshow
= _show(self
.pos
[_POS_H
] * self
.pyxconv
, -self
.pos
[_POS_V
] * self
.pyxconv
)
934 width
= self
.activefont
.getwidth_dvi(char
) * self
.pyxconv
935 height
= self
.activefont
.getheight_dvi(char
) * self
.pyxconv
936 depth
= self
.activefont
.getdepth_dvi(char
) * self
.pyxconv
937 self
.activeshow
.addchar(width
, height
, depth
, char
)
939 self
.activefont
.markcharused(char
)
940 self
.pos
[_POS_H
] += dx
942 # virtual font handling
943 afterpos
= list(self
.pos
)
944 afterpos
[_POS_H
] += dx
945 self
._push
_dvistring
(self
.activefont
.getchar(char
), self
.activefont
.getfonts(), afterpos
,
946 self
.activefont
.getsize_pt())
953 def usefont(self
, fontnum
):
955 print ("%d: fntnum%i current font is %s" %
957 fontnum
, self
.fonts
[fontnum
].name
))
959 self
.activefont
= self
.fonts
[fontnum
]
962 def definefont(self
, cmdnr
, num
, c
, q
, d
, fontname
):
963 # cmdnr: type of fontdef command (only used for debugging output)
965 # q: scaling factor (fix_word)
966 # Note that q is actually s in large parts of the documentation.
967 # d: design size (fix_word)
970 font
= virtualfont(fontname
, c
, q
/self
.tfmconv
, d
/self
.tfmconv
, self
.tfmconv
, self
.pyxconv
, self
.fontmap
, self
.debug
> 1)
971 except (TypeError, RuntimeError):
972 font
= type1font(fontname
, c
, q
/self
.tfmconv
, d
/self
.tfmconv
, self
.tfmconv
, self
.pyxconv
, self
.fontmap
, self
.debug
> 1)
974 self
.fonts
[num
] = font
977 print "%d: fntdef%d %i: %s" % (self
.filepos
, cmdnr
, num
, fontname
)
979 # scale = round((1000.0*self.conv*q)/(self.trueconv*d))
981 # scalestring = scale!=1000 and " scaled %d" % scale or ""
982 # print ("Font %i: %s%s---loaded at size %d DVI units" %
983 # (num, fontname, scalestring, q))
985 # print " (this font is magnified %d%%)" % round(scale/10)
987 def special(self
, s
):
988 x
= self
.pos
[_POS_H
] * self
.pyxconv
989 y
= -self
.pos
[_POS_V
] * self
.pyxconv
991 print "%d: xxx '%s'" % (self
.filepos
, s
)
992 if not s
.startswith("PyX:"):
993 if s
.startswith("Warning:"):
994 sys
.stderr
.write("*** PyX Warning: ignoring special '%s'\n" % s
)
997 raise RuntimeError("the special '%s' cannot be handled by PyX, aborting" % s
)
999 # it is in general not safe to continue using the currently active font because
1000 # the specials may involve some gsave/grestore operations
1003 command
, args
= s
[4:].split()[0], s
[4:].split()[1:]
1004 if command
=="color_begin":
1006 c
= color
.cmyk(float(args
[1]), float(args
[2]), float(args
[3]), float(args
[4]))
1007 elif args
[0]=="gray":
1008 c
= color
.gray(float(args
[1]))
1009 elif args
[0]=="hsb":
1010 c
= color
.hsb(float(args
[1]), float(args
[2]), float(args
[3]))
1011 elif args
[0]=="rgb":
1012 c
= color
.rgb(float(args
[1]), float(args
[2]), float(args
[3]))
1013 elif args
[0]=="RGB":
1014 c
= color
.rgb(int(args
[1])/255.0, int(args
[2])/255.0, int(args
[3])/255.0)
1015 elif args
[0]=="texnamed":
1017 c
= getattr(color
.cmyk
, args
[1])
1018 except AttributeError:
1019 raise RuntimeError("unknown TeX color '%s', aborting" % args
[1])
1021 raise RuntimeError("color model '%s' cannot be handled by PyX, aborting" % args
[0])
1022 self
.actpage
.insert(_savecolor())
1023 self
.actpage
.insert(c
)
1024 elif command
=="color_end":
1025 self
.actpage
.insert(_restorecolor())
1026 elif command
=="rotate_begin":
1027 self
.actpage
.insert(_savetrafo())
1028 self
.actpage
.insert(trafo
.rotate_pt(float(args
[0]), x
, y
))
1029 elif command
=="rotate_end":
1030 self
.actpage
.insert(_restoretrafo())
1031 elif command
=="scale_begin":
1032 self
.actpage
.insert(_savetrafo())
1033 self
.actpage
.insert(trafo
.scale_pt(float(args
[0]), float(args
[1]), x
, y
))
1034 elif command
=="scale_end":
1035 self
.actpage
.insert(_restoretrafo())
1036 elif command
=="epsinclude":
1040 name
, value
= arg
.split("=")
1041 argdict
[name
] = value
1043 # construct kwargs for epsfile constructor
1045 epskwargs
["filename"] = argdict
["file"]
1046 epskwargs
["bbox"] = bbox
.bbox_pt(float(argdict
["llx"]), float(argdict
["lly"]),
1047 float(argdict
["urx"]), float(argdict
["ury"]))
1048 if argdict
.has_key("width"):
1049 epskwargs
["width"] = float(argdict
["width"]) * unit
.t_pt
1050 if argdict
.has_key("height"):
1051 epskwargs
["height"] = float(argdict
["height"]) * unit
.t_pt
1052 if argdict
.has_key("clip"):
1053 epskwargs
["clip"] = int(argdict
["clip"])
1054 self
.actpage
.insert(epsfile
.epsfile(x
* unit
.t_pt
, y
* unit
.t_pt
, **epskwargs
))
1055 elif command
=="marker":
1057 raise RuntimeError("marker contains spaces")
1059 if c
not in string
.digits
+ string
.letters
+ "@":
1060 raise RuntimeError("marker contains invalid characters")
1061 if self
.actpage
.markers
.has_key(args
[0]):
1062 raise RuntimeError("marker name occurred several times")
1063 self
.actpage
.markers
[args
[0]] = x
* unit
.t_pt
, y
* unit
.t_pt
1065 raise RuntimeError("unknown PyX special '%s', aborting" % command
)
1068 # routines for pushing and popping different dvi chunks on the reader
1070 def _push_dvistring(self
, dvi
, fonts
, afterpos
, fontsize
):
1071 """ push dvi string with defined fonts on top of reader
1072 stack. Every positions gets scaled relatively by the factor
1073 scale. After the interpreting of the dvi chunk has been finished,
1074 continue with self.pos=afterpos. The designsize of the virtual
1075 font is passed as a fix_word
1080 print "executing new dvi chunk"
1081 self
.statestack
.append((self
.file, self
.fonts
, self
.activefont
, afterpos
, self
.stack
, self
.pyxconv
, self
.tfmconv
))
1083 # units in vf files are relative to the size of the font and given as fix_words
1084 # which can be converted to floats by diving by 2**20
1085 oldpyxconv
= self
.pyxconv
1086 self
.pyxconv
= fontsize
/2**20
1087 rescale
= self
.pyxconv
/oldpyxconv
1089 self
.file = stringbinfile(dvi
)
1094 # rescale self.pos in order to be consistent with the new scaling
1095 self
.pos
= map(lambda x
, rescale
=rescale
:1.0*x
/rescale
, self
.pos
)
1097 # since tfmconv converts from tfm units to dvi units, rescale it as well
1098 self
.tfmconv
/= rescale
1102 def _pop_dvistring(self
):
1105 print "finished executing dvi chunk"
1107 self
.file, self
.fonts
, self
.activefont
, self
.pos
, self
.stack
, self
.pyxconv
, self
.tfmconv
= self
.statestack
.pop()
1109 # routines corresponding to the different reader states of the dvi maschine
1111 def _read_pre(self
):
1114 self
.filepos
= afile
.tell()
1115 cmd
= afile
.readuchar()
1118 elif cmd
== _DVI_PRE
:
1119 if afile
.readuchar() != _DVI_VERSION
: raise DVIError
1120 num
= afile
.readuint32()
1121 den
= afile
.readuint32()
1122 self
.mag
= afile
.readuint32()
1124 # For the interpretation of the lengths in dvi and tfm files,
1125 # three conversion factors are relevant:
1126 # - self.tfmconv: tfm units -> dvi units
1127 # - self.pyxconv: dvi units -> (PostScript) points
1128 # - self.conv: dvi units -> pixels
1129 self
.tfmconv
= (25400000.0/num
)*(den
/473628672.0)/16.0
1131 # calculate conv as described in the DVIType docu using
1132 # a given resolution in dpi
1133 self
.resolution
= 300.0
1134 self
.conv
= (num
/254000.0)*(self
.resolution
/den
)
1136 # self.pyxconv is the conversion factor from the dvi units
1137 # to (PostScript) points. It consists of
1138 # - self.mag/1000.0: magstep scaling
1139 # - self.conv: conversion from dvi units to pixels
1140 # - 1/self.resolution: conversion from pixels to inch
1141 # - 72 : conversion from inch to points
1142 self
.pyxconv
= self
.mag
/1000.0*self
.conv
/self
.resolution
*72
1144 comment
= afile
.read(afile
.readuchar())
1149 def readpage(self
, pageid
=None):
1150 """ reads a page from the dvi file
1152 This routine reads a page from the dvi file which is
1153 returned as a canvas. When there is no page left in the
1154 dvifile, None is returned and the file is closed properly."""
1158 self
.filepos
= self
.file.tell()
1159 cmd
= self
.file.readuchar()
1162 elif cmd
== _DVI_BOP
:
1164 ispageid
= [self
.file.readuint32() for i
in range(10)]
1165 #if ispageid[:3] != [ord("P"), ord("y"), ord("X")] or ispageid[4:] != [0, 0, 0, 0, 0, 0]:
1166 if pageid
is not None and ispageid
!= pageid
:
1167 raise DVIError("invalid pageid")
1169 print "%d: beginning of page %i" % (self
.filepos
, ispageid
[0])
1170 self
.file.readuint32()
1172 elif cmd
== _DVI_POST
:
1174 return None # nothing left
1178 actpage
= canvas
.canvas()
1179 self
.actpage
= actpage
# XXX should be removed ...
1180 self
.actpage
.markers
= {}
1181 self
.pos
= [0, 0, 0, 0, 0, 0]
1182 self
.activetype1font
= None
1184 # Since we do not know which dvi pages the actual PS file contains later on,
1185 # we have to keep track of used char informations separately for each dvi page.
1186 # In order to do so, the already defined fonts have to be copied and their
1187 # used char informations have to be reset
1188 for nr
in self
.fonts
.keys():
1189 self
.fonts
[nr
] = copy
.copy(self
.fonts
[nr
])
1190 self
.fonts
[nr
].clearusedchars()
1194 self
.filepos
= afile
.tell()
1196 cmd
= afile
.readuchar()
1197 except struct
.error
:
1198 # we most probably (if the dvi file is not corrupt) hit the end of a dvi chunk,
1199 # so we have to continue with the rest of the dvi file
1200 self
._pop
_dvistring
()
1204 if cmd
>= _DVI_CHARMIN
and cmd
<= _DVI_CHARMAX
:
1206 elif cmd
>= _DVI_SET1234
and cmd
< _DVI_SET1234
+ 4:
1207 self
.putchar(afile
.readint(cmd
- _DVI_SET1234
+ 1))
1208 elif cmd
== _DVI_SETRULE
:
1209 self
.putrule(afile
.readint32(), afile
.readint32())
1210 elif cmd
>= _DVI_PUT1234
and cmd
< _DVI_PUT1234
+ 4:
1211 self
.putchar(afile
.readint(cmd
- _DVI_PUT1234
+ 1), advancepos
=0)
1212 elif cmd
== _DVI_PUTRULE
:
1213 self
.putrule(afile
.readint32(), afile
.readint32(), 0)
1214 elif cmd
== _DVI_EOP
:
1217 print "%d: eop" % self
.filepos
1220 elif cmd
== _DVI_PUSH
:
1221 self
.stack
.append(list(self
.pos
))
1223 print "%d: push" % self
.filepos
1224 print ("level %d:(h=%d,v=%d,w=%d,x=%d,y=%d,z=%d,hh=,vv=)" %
1225 (( len(self
.stack
)-1,)+tuple(self
.pos
)))
1226 elif cmd
== _DVI_POP
:
1228 self
.pos
= self
.stack
.pop()
1230 print "%d: pop" % self
.filepos
1231 print ("level %d:(h=%d,v=%d,w=%d,x=%d,y=%d,z=%d,hh=,vv=)" %
1232 (( len(self
.stack
),)+tuple(self
.pos
)))
1233 elif cmd
>= _DVI_RIGHT1234
and cmd
< _DVI_RIGHT1234
+ 4:
1235 dh
= afile
.readint(cmd
- _DVI_RIGHT1234
+ 1, 1)
1237 print ("%d: right%d %d h:=%d%+d=%d, hh:=" %
1239 cmd
- _DVI_RIGHT1234
+ 1,
1243 self
.pos
[_POS_H
]+dh
))
1244 self
.pos
[_POS_H
] += dh
1245 elif cmd
== _DVI_W0
:
1248 print ("%d: w0 %d h:=%d%+d=%d, hh:=" %
1253 self
.pos
[_POS_H
]+self
.pos
[_POS_W
]))
1254 self
.pos
[_POS_H
] += self
.pos
[_POS_W
]
1255 elif cmd
>= _DVI_W1234
and cmd
< _DVI_W1234
+ 4:
1257 self
.pos
[_POS_W
] = afile
.readint(cmd
- _DVI_W1234
+ 1, 1)
1259 print ("%d: w%d %d h:=%d%+d=%d, hh:=" %
1261 cmd
- _DVI_W1234
+ 1,
1265 self
.pos
[_POS_H
]+self
.pos
[_POS_W
]))
1266 self
.pos
[_POS_H
] += self
.pos
[_POS_W
]
1267 elif cmd
== _DVI_X0
:
1269 self
.pos
[_POS_H
] += self
.pos
[_POS_X
]
1270 elif cmd
>= _DVI_X1234
and cmd
< _DVI_X1234
+ 4:
1272 self
.pos
[_POS_X
] = afile
.readint(cmd
- _DVI_X1234
+ 1, 1)
1273 self
.pos
[_POS_H
] += self
.pos
[_POS_X
]
1274 elif cmd
>= _DVI_DOWN1234
and cmd
< _DVI_DOWN1234
+ 4:
1276 dv
= afile
.readint(cmd
- _DVI_DOWN1234
+ 1, 1)
1278 print ("%d: down%d %d v:=%d%+d=%d, vv:=" %
1280 cmd
- _DVI_DOWN1234
+ 1,
1284 self
.pos
[_POS_V
]+dv
))
1285 self
.pos
[_POS_V
] += dv
1286 elif cmd
== _DVI_Y0
:
1289 print ("%d: y0 %d v:=%d%+d=%d, vv:=" %
1294 self
.pos
[_POS_V
]+self
.pos
[_POS_Y
]))
1295 self
.pos
[_POS_V
] += self
.pos
[_POS_Y
]
1296 elif cmd
>= _DVI_Y1234
and cmd
< _DVI_Y1234
+ 4:
1298 self
.pos
[_POS_Y
] = afile
.readint(cmd
- _DVI_Y1234
+ 1, 1)
1300 print ("%d: y%d %d v:=%d%+d=%d, vv:=" %
1302 cmd
- _DVI_Y1234
+ 1,
1306 self
.pos
[_POS_V
]+self
.pos
[_POS_Y
]))
1307 self
.pos
[_POS_V
] += self
.pos
[_POS_Y
]
1308 elif cmd
== _DVI_Z0
:
1310 self
.pos
[_POS_V
] += self
.pos
[_POS_Z
]
1311 elif cmd
>= _DVI_Z1234
and cmd
< _DVI_Z1234
+ 4:
1313 self
.pos
[_POS_Z
] = afile
.readint(cmd
- _DVI_Z1234
+ 1, 1)
1314 self
.pos
[_POS_V
] += self
.pos
[_POS_Z
]
1315 elif cmd
>= _DVI_FNTNUMMIN
and cmd
<= _DVI_FNTNUMMAX
:
1316 self
.usefont(cmd
- _DVI_FNTNUMMIN
)
1317 elif cmd
>= _DVI_FNT1234
and cmd
< _DVI_FNT1234
+ 4:
1318 # note that according to the DVI docs, for four byte font numbers,
1319 # the font number is signed. Don't ask why!
1320 self
.usefont(afile
.readint(cmd
- _DVI_FNT1234
+ 1, cmd
== _DVI_FNT1234
+ 3))
1321 elif cmd
>= _DVI_SPECIAL1234
and cmd
< _DVI_SPECIAL1234
+ 4:
1322 self
.special(afile
.read(afile
.readint(cmd
- _DVI_SPECIAL1234
+ 1)))
1323 elif cmd
>= _DVI_FNTDEF1234
and cmd
< _DVI_FNTDEF1234
+ 4:
1324 if cmd
== _DVI_FNTDEF1234
:
1325 num
= afile
.readuchar()
1326 elif cmd
== _DVI_FNTDEF1234
+1:
1327 num
= afile
.readuint16()
1328 elif cmd
== _DVI_FNTDEF1234
+2:
1329 num
= afile
.readuint24()
1330 elif cmd
== _DVI_FNTDEF1234
+3:
1331 # Cool, here we have according to docu a signed int. Why?
1332 num
= afile
.readint32()
1333 self
.definefont(cmd
-_DVI_FNTDEF1234
+1,
1338 afile
.read(afile
.readuchar()+afile
.readuchar()))
1343 ##############################################################################
1345 ##############################################################################
1347 _VF_LONG_CHAR
= 242 # character packet (long version)
1348 _VF_FNTDEF1234
= _DVI_FNTDEF1234
# font definition
1349 _VF_PRE
= _DVI_PRE
# preamble
1350 _VF_POST
= _DVI_POST
# postamble
1352 _VF_ID
= 202 # VF id byte
1354 class VFError(exceptions
.Exception): pass
1357 def __init__(self
, filename
, scale
, tfmconv
, pyxconv
, fontmap
, debug
=0):
1358 self
.filename
= filename
1360 self
.tfmconv
= tfmconv
1361 self
.pyxconv
= pyxconv
1362 self
.fontmap
= fontmap
1364 self
.fonts
= {} # used fonts
1365 self
.widths
= {} # widths of defined chars
1366 self
.chardefs
= {} # dvi chunks for defined chars
1368 afile
= binfile(self
.filename
, "rb")
1370 cmd
= afile
.readuchar()
1372 if afile
.readuchar() != _VF_ID
: raise VFError
1373 comment
= afile
.read(afile
.readuchar())
1374 self
.cs
= afile
.readuint32()
1375 self
.ds
= afile
.readuint32()
1380 cmd
= afile
.readuchar()
1381 if cmd
>= _VF_FNTDEF1234
and cmd
< _VF_FNTDEF1234
+ 4:
1383 if cmd
== _VF_FNTDEF1234
:
1384 num
= afile
.readuchar()
1385 elif cmd
== _VF_FNTDEF1234
+1:
1386 num
= afile
.readuint16()
1387 elif cmd
== _VF_FNTDEF1234
+2:
1388 num
= afile
.readuint24()
1389 elif cmd
== _VF_FNTDEF1234
+3:
1390 num
= afile
.readint32()
1391 c
= afile
.readint32()
1392 s
= afile
.readint32() # relative scaling used for font (fix_word)
1393 d
= afile
.readint32() # design size of font
1394 fontname
= afile
.read(afile
.readuchar()+afile
.readuchar())
1396 # rescaled size of font: s is relative to the scaling
1397 # of the virtual font itself. Note that realscale has
1398 # to be a fix_word (like s)
1399 reals
= int(self
.scale
* float(fix_word(self
.ds
))*s
)
1401 # print ("defining font %s -- VF scale: %g, VF design size: %g, relative font size: %g => real size: %g" %
1402 # (fontname, self.scale, fix_word(self.ds), fix_word(s), fix_word(reals))
1406 # XXX allow for virtual fonts here too
1407 self
.fonts
[num
] = type1font(fontname
, c
, reals
, d
, self
.tfmconv
, self
.pyxconv
, self
.fontmap
, self
.debug
> 1)
1408 elif cmd
== _VF_LONG_CHAR
:
1409 # character packet (long form)
1410 pl
= afile
.readuint32() # packet length
1411 cc
= afile
.readuint32() # char code (assumed unsigned, but anyhow only 0 <= cc < 255 is actually used)
1412 tfm
= afile
.readuint24() # character width
1413 dvi
= afile
.read(pl
) # dvi code of character
1414 self
.widths
[cc
] = tfm
1415 self
.chardefs
[cc
] = dvi
1416 elif cmd
< _VF_LONG_CHAR
:
1417 # character packet (short form)
1418 cc
= afile
.readuchar() # char code
1419 tfm
= afile
.readuint24() # character width
1420 dvi
= afile
.read(cmd
)
1421 self
.widths
[cc
] = tfm
1422 self
.chardefs
[cc
] = dvi
1423 elif cmd
== _VF_POST
:
1433 def getchar(self
, cc
):
1434 return self
.chardefs
[cc
]