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
, pswriter
, pdfwriter
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()
392 def registerPS(self
, registry
):
393 # note that we don't register PSfont as it is just a helper resource
394 # which registers the needed components
395 pswriter
.PSfont(self
.font
, registry
)
397 def registerPDF(self
, registry
):
398 registry
.add(pdfwriter
.PDFfont(self
.font
, registry
))
400 def outputPS(self
, file):
401 file.write("/%s %f selectfont\n" % (self
.font
.getpsname(), self
.size
))
403 def outputPDF(self
, file, writer
, context
):
404 file.write("/%s %f Tf\n" % (self
.font
.getpsname(), self
.size
))
407 class _show(canvas
.canvasitem
):
409 def __init__(self
, x
, y
):
417 def addchar(self
, width
, height
, depth
, char
):
419 if height
> self
.height
:
421 if depth
> self
.depth
:
423 self
.chars
.append(char
)
426 return bbox
.bbox_pt(self
.x
, self
.y
-self
.depth
, self
.x
+self
.width
, self
.y
+self
.height
)
428 def outputPS(self
, file):
430 for char
in self
.chars
:
431 if char
> 32 and char
< 127 and chr(char
) not in "()[]<>\\":
432 ascii
= "%s" % chr(char
)
434 ascii
= "\\%03o" % char
436 file.write("%g %g moveto (%s) show\n" % (self
.x
, self
.y
, outstring
))
438 def outputPDF(self
, file, writer
, context
):
440 for char
in self
.chars
:
441 if char
> 32 and char
< 127 and chr(char
) not in "()[]<>\\":
442 ascii
= "%s" % chr(char
)
444 ascii
= "\\%03o" % char
446 # file.write("%f %f Td (%s) Tj\n" % (self.x, self.y, outstring))
447 file.write("1 0 0 1 %f %f Tm (%s) Tj\n" % (self
.x
, self
.y
, outstring
))
452 tokenpattern
= re
.compile(r
'"(.*?)("\s+|"$|$)|(.*?)(\s+|$)')
454 def __init__(self
, s
):
455 """ construct font mapping from line s of font mapping file """
456 self
.texname
= self
.basepsname
= self
.fontfile
= None
459 self
.encodingfile
= None
461 # supported postscript fragments occuring in psfonts.map
462 self
.reencodefont
= self
.extendfont
= self
.slantfont
= None
466 match
= self
.tokenpattern
.match(s
)
468 if match
.groups()[0] is not None:
469 tokens
.append('"%s"' % match
.groups()[0])
471 tokens
.append(match
.groups()[2])
474 raise RuntimeError("wrong syntax")
477 if token
.startswith("<"):
478 if token
.startswith("<<"):
479 # XXX: support non-partial download here
480 self
.fontfile
= token
[2:]
481 elif token
.startswith("<["):
482 self
.encodingfile
= token
[2:]
483 elif token
.endswith(".pfa") or token
.endswith(".pfb"):
484 self
.fontfile
= token
[1:]
485 elif token
.endswith(".enc"):
486 self
.encodingfile
= token
[1:]
488 raise RuntimeError("wrong syntax")
489 elif token
.startswith('"'):
490 pscode
= token
[1:-1].split()
491 # parse standard postscript code fragments
494 arg
, cmd
= pscode
[:2]
496 raise RuntimeError("Unsupported Postscript fragment '%s'" % pscode
)
498 if cmd
== "ReEncodeFont":
499 self
.reencodefont
= arg
500 elif cmd
== "ExtendFont":
501 self
.extendfont
= arg
502 elif cmd
== "SlantFont":
505 raise RuntimeError("Unsupported Postscript fragment '%s %s'" % (arg
, cmd
))
507 if self
.texname
is None:
510 self
.basepsname
= token
511 if self
.basepsname
is None:
512 self
.basepsname
= self
.texname
515 return ("'%s' is '%s' read from '%s' encoded as '%s'" %
516 (self
.texname
, self
.basepsname
, self
.fontfile
, repr(self
.encodingfile
)))
520 def readfontmap(filenames
):
521 """ read font map from filename (without path) """
523 for filename
in filenames
:
524 mappath
= pykpathsea
.find_file(filename
, pykpathsea
.kpse_fontmap_format
)
525 # try also the oft-used registration as dvips config file
527 mappath
= pykpathsea
.find_file(filename
, pykpathsea
.kpse_dvips_config_format
)
529 raise RuntimeError("cannot find font mapping file '%s'" % filename
)
530 mapfile
= open(mappath
, "r")
532 for line
in mapfile
.readlines():
535 if not (line
=="" or line
[0] in (" ", "%", "*", ";" , "#")):
537 fm
= fontmapping(line
)
538 except RuntimeError, e
:
539 warnings
.warn("Ignoring line %i in mapping file '%s': %s" % (lineno
, filename
, e
))
541 fontmap
[fm
.texname
] = fm
547 def __init__(self
, name
, c
, q
, d
, tfmconv
, pyxconv
, debug
=0):
549 self
.q
= q
# desired size of font (fix_word) in TeX points
550 self
.d
= d
# design size of font (fix_word) in TeX points
551 self
.tfmconv
= tfmconv
# conversion factor from tfm units to dvi units
552 self
.pyxconv
= pyxconv
# conversion factor from dvi units to PostScript points
553 tfmpath
= pykpathsea
.find_file("%s.tfm" % self
.name
, pykpathsea
.kpse_tfm_format
)
555 raise TFMError("cannot find %s.tfm" % self
.name
)
556 self
.tfmfile
= tfmfile(tfmpath
, debug
)
558 # We only check for equality of font checksums if none of them
559 # is zero. The case c == 0 happend in some VF files and
560 # according to the VFtoVP documentation, paragraph 40, a check
561 # is only performed if tfmfile.checksum > 0. Anyhow, being
562 # more generous here seems to be reasonable
563 if self
.tfmfile
.checksum
!= c
and self
.tfmfile
.checksum
!= 0 and c
!= 0:
564 raise DVIError("check sums do not agree: %d vs. %d" %
565 (self
.tfmfile
.checksum
, c
))
567 # Check whether the given design size matches the one defined in the tfm file
568 if abs(self
.tfmfile
.designsize
- d
) > 2:
569 raise DVIError("design sizes do not agree: %d vs. %d" % (self
.tfmfile
.designsize
, d
))
570 #if q < 0 or q > 134217728:
571 # raise DVIError("font '%s' not loaded: bad scale" % self.name)
572 if d
< 0 or d
> 134217728:
573 raise DVIError("font '%s' not loaded: bad design size" % self
.name
)
577 # for bookkeeping of used characters
578 self
.usedchars
= [0] * 256
581 return "font %s designed at %g TeX pts used at %g TeX pts" % (self
.name
,
582 16.0*self
.d
/16777216L,
583 16.0*self
.q
/16777216L)
588 def getsize_pt(self
):
589 """ return size of font in (PS) points """
590 # The factor 16L/16777216L=2**(-20) converts a fix_word (here self.q)
591 # to the corresponding float. Furthermore, we have to convert from TeX
592 # points to points, hence the factor 72/72.27.
593 return 16L*self
.q
/16777216L*72/72.27
595 def _convert_tfm_to_dvi(self
, length
):
596 # doing the integer math with long integers will lead to different roundings
597 # return 16*length*int(round(self.q*self.tfmconv))/16777216
599 # Knuth instead suggests the following algorithm based on 4 byte integer logic only
600 # z = int(round(self.q*self.tfmconv))
601 # b0, b1, b2, b3 = [ord(c) for c in struct.pack(">L", length)]
602 # assert b0 == 0 or b0 == 255
604 # while z >= 8388608:
608 # result = ( ( ( ( ( b3 * z ) >> 8 ) + ( b2 * z ) ) >> 8 ) + ( b1 * z ) ) >> shift
610 # result = result - (z << (8-shift))
612 # however, we can simplify this using a single long integer multiplication,
613 # but take into account the transformation of z
614 z
= int(round(self
.q
*self
.tfmconv
))
615 assert -16777216 <= length
< 16777216 # -(1 << 24) <= length < (1 << 24)
616 assert z
< 134217728 # 1 << 27
618 while z
>= 8388608: # 1 << 23
621 # length*z is a long integer, but the result will be a regular integer
622 return int(length
*long(z
) >> shift
)
624 # we do not need that ...
625 def _convert_tfm_to_pt(self
, length
):
626 return (16*long(round(length
*self
.q
*self
.tfmconv
))/16777216) * self
.pyxconv
628 # routines returning lengths as integers in dvi units
630 def getwidth_dvi(self
, charcode
):
631 return self
._convert
_tfm
_to
_dvi
(self
.tfmfile
.width
[self
.tfmfile
.char_info
[charcode
].width_index
])
633 def getheight_dvi(self
, charcode
):
634 return self
._convert
_tfm
_to
_dvi
(self
.tfmfile
.height
[self
.tfmfile
.char_info
[charcode
].height_index
])
636 def getdepth_dvi(self
, charcode
):
637 return self
._convert
_tfm
_to
_dvi
(self
.tfmfile
.depth
[self
.tfmfile
.char_info
[charcode
].depth_index
])
639 def getitalic_dvi(self
, charcode
):
640 return self
._convert
_tfm
_to
_dvi
(self
.tfmfile
.italic
[self
.tfmfile
.char_info
[charcode
].italic_index
])
642 # routines returning lengths as floats in PostScript points
644 def getwidth_pt(self
, charcode
):
645 return self
._convert
_tfm
_to
_pt
(self
.tfmfile
.width
[self
.tfmfile
.char_info
[charcode
].width_index
])
647 # def getheight_pt(self, charcode):
648 # return self._convert_tfm_to_pt(self.tfmfile.height[self.tfmfile.char_info[charcode].height_index])
650 # def getdepth_pt(self, charcode):
651 # return self._convert_tfm_to_pt(self.tfmfile.depth[self.tfmfile.char_info[charcode].depth_index])
653 # def getitalic_pt(self, charcode):
654 # return self._convert_tfm_to_pt(self.tfmfile.italic[self.tfmfile.char_info[charcode].italic_index])
656 def markcharused(self
, charcode
):
657 self
.usedchars
[charcode
] = 1
659 def mergeusedchars(self
, otherfont
):
660 for i
in range(len(self
.usedchars
)):
661 self
.usedchars
[i
] = self
.usedchars
[i
] or otherfont
.usedchars
[i
]
663 def clearusedchars(self
):
664 self
.usedchars
= [0] * 256
667 class type1font(font
):
668 def __init__(self
, name
, c
, q
, d
, tfmconv
, pyxconv
, fontmap
, debug
=0):
669 font
.__init
__(self
, name
, c
, q
, d
, tfmconv
, pyxconv
, debug
)
670 self
.fontmapping
= fontmap
.get(name
)
671 if self
.fontmapping
is None:
672 raise RuntimeError("no information for font '%s' found in font mapping file, aborting" % name
)
674 def getbasepsname(self
):
675 return self
.fontmapping
.basepsname
678 if self
.fontmapping
.reencodefont
:
679 return "%s-%s" % (self
.fontmapping
.basepsname
, self
.fontmapping
.reencodefont
)
681 return self
.fontmapping
.basepsname
683 def getfontfile(self
):
684 if self
.fontmapping
.fontfile
is None:
687 return pykpathsea
.find_file(self
.fontmapping
.fontfile
, pykpathsea
.kpse_type1_format
)
689 def getencoding(self
):
690 return self
.fontmapping
.reencodefont
692 def getencodingfile(self
):
693 return self
.fontmapping
.encodingfile
696 class virtualfont(font
):
697 def __init__(self
, name
, c
, q
, d
, tfmconv
, pyxconv
, fontmap
, debug
=0):
698 font
.__init
__(self
, name
, c
, q
, d
, tfmconv
, pyxconv
, debug
)
699 fontpath
= pykpathsea
.find_file(name
, pykpathsea
.kpse_vf_format
)
700 if fontpath
is None or not len(fontpath
):
702 self
.vffile
= vffile(fontpath
, self
.scale
, tfmconv
, pyxconv
, fontmap
, debug
> 1)
705 """ return fonts used in virtual font itself """
706 return self
.vffile
.getfonts()
708 def getchar(self
, cc
):
709 """ return dvi chunk corresponding to char code cc """
710 return self
.vffile
.getchar(cc
)
713 ##############################################################################
715 ##############################################################################
717 _DVI_CHARMIN
= 0 # typeset a character and move right (range min)
718 _DVI_CHARMAX
= 127 # typeset a character and move right (range max)
719 _DVI_SET1234
= 128 # typeset a character and move right
720 _DVI_SETRULE
= 132 # typeset a rule and move right
721 _DVI_PUT1234
= 133 # typeset a character
722 _DVI_PUTRULE
= 137 # typeset a rule
723 _DVI_NOP
= 138 # no operation
724 _DVI_BOP
= 139 # beginning of page
725 _DVI_EOP
= 140 # ending of page
726 _DVI_PUSH
= 141 # save the current positions (h, v, w, x, y, z)
727 _DVI_POP
= 142 # restore positions (h, v, w, x, y, z)
728 _DVI_RIGHT1234
= 143 # move right
729 _DVI_W0
= 147 # move right by w
730 _DVI_W1234
= 148 # move right and set w
731 _DVI_X0
= 152 # move right by x
732 _DVI_X1234
= 153 # move right and set x
733 _DVI_DOWN1234
= 157 # move down
734 _DVI_Y0
= 161 # move down by y
735 _DVI_Y1234
= 162 # move down and set y
736 _DVI_Z0
= 166 # move down by z
737 _DVI_Z1234
= 167 # move down and set z
738 _DVI_FNTNUMMIN
= 171 # set current font (range min)
739 _DVI_FNTNUMMAX
= 234 # set current font (range max)
740 _DVI_FNT1234
= 235 # set current font
741 _DVI_SPECIAL1234
= 239 # special (dvi extention)
742 _DVI_FNTDEF1234
= 243 # define the meaning of a font number
743 _DVI_PRE
= 247 # preamble
744 _DVI_POST
= 248 # postamble beginning
745 _DVI_POSTPOST
= 249 # postamble ending
747 _DVI_VERSION
= 2 # dvi version
749 # position variable indices
761 _READ_POST
= 4 # XXX not used
762 _READ_POSTPOST
= 5 # XXX not used
766 class DVIError(exceptions
.Exception): pass
768 # save and restore colors
770 class _savecolor(canvas
.canvasitem
):
771 def outputPS(self
, file):
772 file.write("currentcolor currentcolorspace\n")
774 def outputPDF(self
, file, writer
, context
):
778 class _restorecolor(canvas
.canvasitem
):
779 def outputPS(self
, file):
780 file.write("setcolorspace setcolor\n")
782 def outputPDF(self
, file, writer
, context
):
785 class _savetrafo(canvas
.canvasitem
):
786 def outputPS(self
, file):
787 file.write("matrix currentmatrix\n")
789 def outputPDF(self
, file, writer
, context
):
793 class _restoretrafo(canvas
.canvasitem
):
794 def outputPS(self
, file):
795 file.write("setmatrix\n")
797 def outputPDF(self
, file, writer
, context
):
803 def __init__(self
, filename
, fontmap
, debug
=0, debugfile
=sys
.stdout
):
804 """ opens the dvi file and reads the preamble """
805 self
.filename
= filename
806 self
.fontmap
= fontmap
808 self
.debugfile
= debugfile
812 self
.activefont
= None
814 # stack of fonts and fontscale currently used (used for VFs)
818 # pointer to currently active page
821 # currently active output: show instance currently used and
822 # the corresponding type 1 font
823 self
.activeshow
= None
824 self
.activetype1font
= None
826 # stack for self.file, self.fonts and self.stack, needed for VF inclusion
829 self
.file = binfile(self
.filename
, "rb")
831 # currently read byte in file (for debugging output)
839 """ flush currently active string """
840 if self
.activeshow
is not None:
842 self
.debugfile
.write("[%s]\n" % "".join([chr(char
) for char
in self
.activeshow
.chars
]))
843 self
.actpage
.insert(self
.activeshow
)
844 self
.activeshow
= None
847 """ activate the font if is not yet active, closing a currently active
848 text object and flushing the output"""
849 if isinstance(self
.activefont
, type1font
):
851 if self
.activetype1font
!= self
.activefont
and self
.activefont
:
852 self
.actpage
.insert(_begintextobject())
853 self
.actpage
.insert(selectfont(self
.activefont
))
854 self
.activetype1font
= self
.activefont
858 if self
.activetype1font
:
859 self
.actpage
.insert(_endtextobject())
860 self
.activetype1font
= None
862 def putrule(self
, height
, width
, advancepos
=1):
864 x1
= self
.pos
[_POS_H
] * self
.pyxconv
865 y1
= -self
.pos
[_POS_V
] * self
.pyxconv
866 w
= width
* self
.pyxconv
867 h
= height
* self
.pyxconv
869 if height
> 0 and width
> 0:
871 self
.debugfile
.write("%d: %srule height %d, width %d (???x??? pixels)\n" %
872 (self
.filepos
, advancepos
and "set" or "put", height
, width
))
873 self
.actpage
.fill(path
.rect_pt(x1
, y1
, w
, h
))
876 self
.debugfile
.write("%d: %srule height %d, width %d (invisible)\n" %
877 (self
.filepos
, advancepos
and "set" or "put", height
, width
))
881 self
.debugfile
.write(" h:=%d+%d=%d, hh:=???\n" %
882 (self
.pos
[_POS_H
], width
, self
.pos
[_POS_H
]+width
))
883 self
.pos
[_POS_H
] += width
885 def putchar(self
, char
, advancepos
=1, id1234
=0):
886 dx
= advancepos
and self
.activefont
.getwidth_dvi(char
) or 0
889 self
.debugfile
.write("%d: %s%s%d h:=%d+%d=%d, hh:=???\n" %
891 advancepos
and "set" or "put",
892 id1234
and "%i " % id1234
or "char",
894 self
.pos
[_POS_H
], dx
, self
.pos
[_POS_H
]+dx
))
896 if isinstance(self
.activefont
, type1font
):
897 if self
.activeshow
is None:
898 # XXX: begintext would lead to massive number of selectfonts being issued
899 # OTOH is it save to remove begintext here? I think so ...
901 self
.activeshow
= _show(self
.pos
[_POS_H
] * self
.pyxconv
, -self
.pos
[_POS_V
] * self
.pyxconv
)
902 width
= self
.activefont
.getwidth_dvi(char
) * self
.pyxconv
903 height
= self
.activefont
.getheight_dvi(char
) * self
.pyxconv
904 depth
= self
.activefont
.getdepth_dvi(char
) * self
.pyxconv
905 self
.activeshow
.addchar(width
, height
, depth
, char
)
907 self
.activefont
.markcharused(char
)
908 self
.pos
[_POS_H
] += dx
910 # virtual font handling
911 afterpos
= list(self
.pos
)
912 afterpos
[_POS_H
] += dx
913 self
._push
_dvistring
(self
.activefont
.getchar(char
), self
.activefont
.getfonts(), afterpos
,
914 self
.activefont
.getsize_pt())
921 def usefont(self
, fontnum
, id1234
=0):
922 self
.activefont
= self
.fonts
[fontnum
]
925 self
.debugfile
.write("%d: fnt%s%i current font is %s\n" %
927 id1234
and "%i " % id1234
or "num",
929 self
.fonts
[fontnum
].name
))
932 def definefont(self
, cmdnr
, num
, c
, q
, d
, fontname
):
933 # cmdnr: type of fontdef command (only used for debugging output)
935 # q: scaling factor (fix_word)
936 # Note that q is actually s in large parts of the documentation.
937 # d: design size (fix_word)
940 font
= virtualfont(fontname
, c
, q
/self
.tfmconv
, d
/self
.tfmconv
, self
.tfmconv
, self
.pyxconv
, self
.fontmap
, self
.debug
> 1)
941 except (TypeError, RuntimeError):
942 font
= type1font(fontname
, c
, q
/self
.tfmconv
, d
/self
.tfmconv
, self
.tfmconv
, self
.pyxconv
, self
.fontmap
, self
.debug
> 1)
944 self
.fonts
[num
] = font
947 self
.debugfile
.write("%d: fntdef%d %i: %s\n" % (self
.filepos
, cmdnr
, num
, fontname
))
949 # scale = round((1000.0*self.conv*q)/(self.trueconv*d))
951 # scalestring = scale!=1000 and " scaled %d" % scale or ""
952 # print ("Font %i: %s%s---loaded at size %d DVI units" %
953 # (num, fontname, scalestring, q))
955 # print " (this font is magnified %d%%)" % round(scale/10)
957 def special(self
, s
):
958 x
= self
.pos
[_POS_H
] * self
.pyxconv
959 y
= -self
.pos
[_POS_V
] * self
.pyxconv
961 self
.debugfile
.write("%d: xxx '%s'\n" % (self
.filepos
, s
))
962 if not s
.startswith("PyX:"):
963 warnings
.warn("ignoring special '%s'" % s
)
966 # it is in general not safe to continue using the currently active font because
967 # the specials may involve some gsave/grestore operations
970 command
, args
= s
[4:].split()[0], s
[4:].split()[1:]
971 if command
=="color_begin":
973 c
= color
.cmyk(float(args
[1]), float(args
[2]), float(args
[3]), float(args
[4]))
974 elif args
[0]=="gray":
975 c
= color
.gray(float(args
[1]))
977 c
= color
.hsb(float(args
[1]), float(args
[2]), float(args
[3]))
979 c
= color
.rgb(float(args
[1]), float(args
[2]), float(args
[3]))
981 c
= color
.rgb(int(args
[1])/255.0, int(args
[2])/255.0, int(args
[3])/255.0)
982 elif args
[0]=="texnamed":
984 c
= getattr(color
.cmyk
, args
[1])
985 except AttributeError:
986 raise RuntimeError("unknown TeX color '%s', aborting" % args
[1])
988 raise RuntimeError("color model '%s' cannot be handled by PyX, aborting" % args
[0])
989 self
.actpage
.insert(_savecolor())
990 self
.actpage
.insert(c
)
991 elif command
=="color_end":
992 self
.actpage
.insert(_restorecolor())
993 elif command
=="rotate_begin":
994 self
.actpage
.insert(_savetrafo())
995 self
.actpage
.insert(trafo
.rotate_pt(float(args
[0]), x
, y
))
996 elif command
=="rotate_end":
997 self
.actpage
.insert(_restoretrafo())
998 elif command
=="scale_begin":
999 self
.actpage
.insert(_savetrafo())
1000 self
.actpage
.insert(trafo
.scale_pt(float(args
[0]), float(args
[1]), x
, y
))
1001 elif command
=="scale_end":
1002 self
.actpage
.insert(_restoretrafo())
1003 elif command
=="epsinclude":
1007 name
, value
= arg
.split("=")
1008 argdict
[name
] = value
1010 # construct kwargs for epsfile constructor
1012 epskwargs
["filename"] = argdict
["file"]
1013 epskwargs
["bbox"] = bbox
.bbox_pt(float(argdict
["llx"]), float(argdict
["lly"]),
1014 float(argdict
["urx"]), float(argdict
["ury"]))
1015 if argdict
.has_key("width"):
1016 epskwargs
["width"] = float(argdict
["width"]) * unit
.t_pt
1017 if argdict
.has_key("height"):
1018 epskwargs
["height"] = float(argdict
["height"]) * unit
.t_pt
1019 if argdict
.has_key("clip"):
1020 epskwargs
["clip"] = int(argdict
["clip"])
1021 self
.actpage
.insert(epsfile
.epsfile(x
* unit
.t_pt
, y
* unit
.t_pt
, **epskwargs
))
1022 elif command
=="marker":
1024 raise RuntimeError("marker contains spaces")
1026 if c
not in string
.digits
+ string
.letters
+ "@":
1027 raise RuntimeError("marker contains invalid characters")
1028 if self
.actpage
.markers
.has_key(args
[0]):
1029 raise RuntimeError("marker name occurred several times")
1030 self
.actpage
.markers
[args
[0]] = x
* unit
.t_pt
, y
* unit
.t_pt
1032 raise RuntimeError("unknown PyX special '%s', aborting" % command
)
1035 # routines for pushing and popping different dvi chunks on the reader
1037 def _push_dvistring(self
, dvi
, fonts
, afterpos
, fontsize
):
1038 """ push dvi string with defined fonts on top of reader
1039 stack. Every positions gets scaled relatively by the factor
1040 scale. After the interpreting of the dvi chunk has been finished,
1041 continue with self.pos=afterpos. The designsize of the virtual
1042 font is passed as a fix_word
1047 # self.debugfile.write("executing new dvi chunk\n")
1048 self
.debugstack
.append(self
.debug
)
1051 self
.statestack
.append((self
.file, self
.fonts
, self
.activefont
, afterpos
, self
.stack
, self
.pyxconv
, self
.tfmconv
))
1053 # units in vf files are relative to the size of the font and given as fix_words
1054 # which can be converted to floats by diving by 2**20
1055 oldpyxconv
= self
.pyxconv
1056 self
.pyxconv
= fontsize
/2**20
1057 rescale
= self
.pyxconv
/oldpyxconv
1059 self
.file = stringbinfile(dvi
)
1064 # rescale self.pos in order to be consistent with the new scaling
1065 self
.pos
= map(lambda x
, rescale
=rescale
:1.0*x
/rescale
, self
.pos
)
1067 # since tfmconv converts from tfm units to dvi units, rescale it as well
1068 self
.tfmconv
/= rescale
1072 def _pop_dvistring(self
):
1075 # self.debugfile.write("finished executing dvi chunk\n")
1076 self
.debug
= self
.debugstack
.pop()
1079 self
.file, self
.fonts
, self
.activefont
, self
.pos
, self
.stack
, self
.pyxconv
, self
.tfmconv
= self
.statestack
.pop()
1081 # routines corresponding to the different reader states of the dvi maschine
1083 def _read_pre(self
):
1086 self
.filepos
= afile
.tell()
1087 cmd
= afile
.readuchar()
1090 elif cmd
== _DVI_PRE
:
1091 if afile
.readuchar() != _DVI_VERSION
: raise DVIError
1092 num
= afile
.readuint32()
1093 den
= afile
.readuint32()
1094 self
.mag
= afile
.readuint32()
1096 # For the interpretation of the lengths in dvi and tfm files,
1097 # three conversion factors are relevant:
1098 # - self.tfmconv: tfm units -> dvi units
1099 # - self.pyxconv: dvi units -> (PostScript) points
1100 # - self.conv: dvi units -> pixels
1101 self
.tfmconv
= (25400000.0/num
)*(den
/473628672.0)/16.0
1103 # calculate conv as described in the DVIType docu using
1104 # a given resolution in dpi
1105 self
.resolution
= 300.0
1106 self
.conv
= (num
/254000.0)*(self
.resolution
/den
)
1108 # self.pyxconv is the conversion factor from the dvi units
1109 # to (PostScript) points. It consists of
1110 # - self.mag/1000.0: magstep scaling
1111 # - self.conv: conversion from dvi units to pixels
1112 # - 1/self.resolution: conversion from pixels to inch
1113 # - 72 : conversion from inch to points
1114 self
.pyxconv
= self
.mag
/1000.0*self
.conv
/self
.resolution
*72
1116 comment
= afile
.read(afile
.readuchar())
1121 def readpage(self
, pageid
=None):
1122 """ reads a page from the dvi file
1124 This routine reads a page from the dvi file which is
1125 returned as a canvas. When there is no page left in the
1126 dvifile, None is returned and the file is closed properly."""
1129 self
.filepos
= self
.file.tell()
1130 cmd
= self
.file.readuchar()
1133 elif cmd
== _DVI_BOP
:
1135 ispageid
= [self
.file.readuint32() for i
in range(10)]
1136 if pageid
is not None and ispageid
!= pageid
:
1137 raise DVIError("invalid pageid")
1139 self
.debugfile
.write("%d: beginning of page %i\n" % (self
.filepos
, ispageid
[0]))
1140 self
.file.readuint32()
1142 elif cmd
== _DVI_POST
:
1144 return None # nothing left
1148 actpage
= canvas
.canvas()
1149 self
.actpage
= actpage
# XXX should be removed ...
1150 self
.actpage
.markers
= {}
1151 self
.pos
= [0, 0, 0, 0, 0, 0]
1152 self
.activetype1font
= None
1154 # Since we do not know which dvi pages the actual PS file contains later on,
1155 # we have to keep track of used char informations separately for each dvi page.
1156 # In order to do so, the already defined fonts have to be copied and their
1157 # used char informations have to be reset
1158 for nr
in self
.fonts
.keys():
1159 self
.fonts
[nr
] = copy
.copy(self
.fonts
[nr
])
1160 self
.fonts
[nr
].clearusedchars()
1164 self
.filepos
= afile
.tell()
1166 cmd
= afile
.readuchar()
1167 except struct
.error
:
1168 # we most probably (if the dvi file is not corrupt) hit the end of a dvi chunk,
1169 # so we have to continue with the rest of the dvi file
1170 self
._pop
_dvistring
()
1174 if cmd
>= _DVI_CHARMIN
and cmd
<= _DVI_CHARMAX
:
1176 elif cmd
>= _DVI_SET1234
and cmd
< _DVI_SET1234
+ 4:
1177 self
.putchar(afile
.readint(cmd
- _DVI_SET1234
+ 1), id1234
=cmd
-_DVI_SET1234
+1)
1178 elif cmd
== _DVI_SETRULE
:
1179 self
.putrule(afile
.readint32(), afile
.readint32())
1180 elif cmd
>= _DVI_PUT1234
and cmd
< _DVI_PUT1234
+ 4:
1181 self
.putchar(afile
.readint(cmd
- _DVI_PUT1234
+ 1), advancepos
=0, id1234
=cmd
-_DVI_SET1234
+1)
1182 elif cmd
== _DVI_PUTRULE
:
1183 self
.putrule(afile
.readint32(), afile
.readint32(), 0)
1184 elif cmd
== _DVI_EOP
:
1187 self
.debugfile
.write("%d: eop\n \n" % self
.filepos
)
1189 elif cmd
== _DVI_PUSH
:
1190 self
.stack
.append(list(self
.pos
))
1192 self
.debugfile
.write("%s: push\n"
1193 "level %d:(h=%d,v=%d,w=%d,x=%d,y=%d,z=%d,hh=???,vv=???)\n" %
1194 ((self
.filepos
, len(self
.stack
)-1) + tuple(self
.pos
)))
1195 elif cmd
== _DVI_POP
:
1197 self
.pos
= self
.stack
.pop()
1199 self
.debugfile
.write("%s: pop\n"
1200 "level %d:(h=%d,v=%d,w=%d,x=%d,y=%d,z=%d,hh=???,vv=???)\n" %
1201 ((self
.filepos
, len(self
.stack
)) + tuple(self
.pos
)))
1202 elif cmd
>= _DVI_RIGHT1234
and cmd
< _DVI_RIGHT1234
+ 4:
1204 dh
= afile
.readint(cmd
- _DVI_RIGHT1234
+ 1, 1)
1206 self
.debugfile
.write("%d: right%d %d h:=%d%+d=%d, hh:=???\n" %
1208 cmd
- _DVI_RIGHT1234
+ 1,
1212 self
.pos
[_POS_H
]+dh
))
1213 self
.pos
[_POS_H
] += dh
1214 elif cmd
== _DVI_W0
:
1217 self
.debugfile
.write("%d: w0 %d h:=%d%+d=%d, hh:=???\n" %
1222 self
.pos
[_POS_H
]+self
.pos
[_POS_W
]))
1223 self
.pos
[_POS_H
] += self
.pos
[_POS_W
]
1224 elif cmd
>= _DVI_W1234
and cmd
< _DVI_W1234
+ 4:
1226 self
.pos
[_POS_W
] = afile
.readint(cmd
- _DVI_W1234
+ 1, 1)
1228 self
.debugfile
.write("%d: w%d %d h:=%d%+d=%d, hh:=???\n" %
1230 cmd
- _DVI_W1234
+ 1,
1234 self
.pos
[_POS_H
]+self
.pos
[_POS_W
]))
1235 self
.pos
[_POS_H
] += self
.pos
[_POS_W
]
1236 elif cmd
== _DVI_X0
:
1239 self
.debugfile
.write("%d: x0 %d h:=%d%+d=%d, hh:=???\n" %
1244 self
.pos
[_POS_H
]+self
.pos
[_POS_X
]))
1245 self
.pos
[_POS_H
] += self
.pos
[_POS_X
]
1246 elif cmd
>= _DVI_X1234
and cmd
< _DVI_X1234
+ 4:
1248 self
.pos
[_POS_X
] = afile
.readint(cmd
- _DVI_X1234
+ 1, 1)
1250 self
.debugfile
.write("%d: x%d %d h:=%d%+d=%d, hh:=???\n" %
1252 cmd
- _DVI_X1234
+ 1,
1256 self
.pos
[_POS_H
]+self
.pos
[_POS_X
]))
1257 self
.pos
[_POS_H
] += self
.pos
[_POS_X
]
1258 elif cmd
>= _DVI_DOWN1234
and cmd
< _DVI_DOWN1234
+ 4:
1260 dv
= afile
.readint(cmd
- _DVI_DOWN1234
+ 1, 1)
1262 self
.debugfile
.write("%d: down%d %d v:=%d%+d=%d, vv:=???\n" %
1264 cmd
- _DVI_DOWN1234
+ 1,
1268 self
.pos
[_POS_V
]+dv
))
1269 self
.pos
[_POS_V
] += dv
1270 elif cmd
== _DVI_Y0
:
1273 self
.debugfile
.write("%d: y0 %d v:=%d%+d=%d, vv:=???\n" %
1278 self
.pos
[_POS_V
]+self
.pos
[_POS_Y
]))
1279 self
.pos
[_POS_V
] += self
.pos
[_POS_Y
]
1280 elif cmd
>= _DVI_Y1234
and cmd
< _DVI_Y1234
+ 4:
1282 self
.pos
[_POS_Y
] = afile
.readint(cmd
- _DVI_Y1234
+ 1, 1)
1284 self
.debugfile
.write("%d: y%d %d v:=%d%+d=%d, vv:=???\n" %
1286 cmd
- _DVI_Y1234
+ 1,
1290 self
.pos
[_POS_V
]+self
.pos
[_POS_Y
]))
1291 self
.pos
[_POS_V
] += self
.pos
[_POS_Y
]
1292 elif cmd
== _DVI_Z0
:
1295 self
.debugfile
.write("%d: z0 %d v:=%d%+d=%d, vv:=???\n" %
1300 self
.pos
[_POS_V
]+self
.pos
[_POS_Z
]))
1301 self
.pos
[_POS_V
] += self
.pos
[_POS_Z
]
1302 elif cmd
>= _DVI_Z1234
and cmd
< _DVI_Z1234
+ 4:
1304 self
.pos
[_POS_Z
] = afile
.readint(cmd
- _DVI_Z1234
+ 1, 1)
1306 self
.debugfile
.write("%d: z%d %d v:=%d%+d=%d, vv:=???\n" %
1308 cmd
- _DVI_Z1234
+ 1,
1312 self
.pos
[_POS_V
]+self
.pos
[_POS_Z
]))
1313 self
.pos
[_POS_V
] += self
.pos
[_POS_Z
]
1314 elif cmd
>= _DVI_FNTNUMMIN
and cmd
<= _DVI_FNTNUMMAX
:
1315 self
.usefont(cmd
- _DVI_FNTNUMMIN
, 0)
1316 elif cmd
>= _DVI_FNT1234
and cmd
< _DVI_FNT1234
+ 4:
1317 # note that according to the DVI docs, for four byte font numbers,
1318 # the font number is signed. Don't ask why!
1319 fntnum
= afile
.readint(cmd
- _DVI_FNT1234
+ 1, cmd
== _DVI_FNT1234
+ 3)
1320 self
.usefont(fntnum
, id1234
=cmd
-_DVI_FNT1234
+1)
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 # XXX: check rounding
1400 reals
= int(round(self
.scale
* (16*self
.ds
/16777216L) * s
))
1402 # print ("defining font %s -- VF scale: %g, VF design size: %d, relative font size: %d => real size: %d" %
1403 # (fontname, self.scale, self.ds, s, 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
]