remove shebang -- see comment 3 on https://bugzilla.redhat.com/bugzilla/show_bug...
[PyX/mjg.git] / pyx / dvifile.py
blob96941ee9f122496f71a68620c8ee9ab85c26843b
1 # -*- coding: ISO-8859-1 -*-
4 # Copyright (C) 2002-2005 Jörg Lehmann <joergl@users.sourceforge.net>
5 # Copyright (C) 2003-2004,2006 Michael Schindler <m-schindler@users.sourceforge.net>
6 # Copyright (C) 2002-2005 André Wobst <wobsta@users.sourceforge.net>
8 # This file is part of PyX (http://pyx.sourceforge.net/).
10 # PyX is free software; you can redistribute it and/or modify
11 # it under the terms of the GNU General Public License as published by
12 # the Free Software Foundation; either version 2 of the License, or
13 # (at your option) any later version.
15 # PyX is distributed in the hope that it will be useful,
16 # but WITHOUT ANY WARRANTY; without even the implied warranty of
17 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 # GNU General Public License for more details.
20 # You should have received a copy of the GNU General Public License
21 # along with PyX; if not, write to the Free Software
22 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
24 import cStringIO, exceptions, re, struct, string, sys, warnings, math
25 import unit, epsfile, bbox, canvas, color, trafo, path, pykpathsea, type1font
28 class binfile:
30 def __init__(self, filename, mode="r"):
31 self.file = open(filename, mode)
33 def close(self):
34 self.file.close()
36 def tell(self):
37 return self.file.tell()
39 def eof(self):
40 return self.file.eof()
42 def read(self, bytes):
43 return self.file.read(bytes)
45 def readint(self, bytes=4, signed=0):
46 first = 1
47 result = 0
48 while bytes:
49 value = ord(self.file.read(1))
50 if first and signed and value > 127:
51 value -= 256
52 first = 0
53 result = 256 * result + value
54 bytes -= 1
55 return result
57 def readint32(self):
58 return struct.unpack(">l", self.file.read(4))[0]
60 def readuint32(self):
61 return struct.unpack(">L", self.file.read(4))[0]
63 def readint24(self):
64 # XXX: checkme
65 return struct.unpack(">l", "\0"+self.file.read(3))[0]
67 def readuint24(self):
68 # XXX: checkme
69 return struct.unpack(">L", "\0"+self.file.read(3))[0]
71 def readint16(self):
72 return struct.unpack(">h", self.file.read(2))[0]
74 def readuint16(self):
75 return struct.unpack(">H", self.file.read(2))[0]
77 def readchar(self):
78 return struct.unpack("b", self.file.read(1))[0]
80 def readuchar(self):
81 return struct.unpack("B", self.file.read(1))[0]
83 def readstring(self, bytes):
84 l = self.readuchar()
85 assert l <= bytes-1, "inconsistency in file: string too long"
86 return self.file.read(bytes-1)[:l]
88 class stringbinfile(binfile):
90 def __init__(self, s):
91 self.file = cStringIO.StringIO(s)
96 ##############################################################################
97 # TFM file handling
98 ##############################################################################
100 class TFMError(exceptions.Exception): pass
103 class char_info_word:
104 def __init__(self, word):
105 self.width_index = int((word & 0xFF000000L) >> 24) #make sign-safe
106 self.height_index = (word & 0x00F00000) >> 20
107 self.depth_index = (word & 0x000F0000) >> 16
108 self.italic_index = (word & 0x0000FC00) >> 10
109 self.tag = (word & 0x00000300) >> 8
110 self.remainder = (word & 0x000000FF)
113 class tfmfile:
114 def __init__(self, name, debug=0):
115 self.file = binfile(name, "rb")
118 # read pre header
121 self.lf = self.file.readint16()
122 self.lh = self.file.readint16()
123 self.bc = self.file.readint16()
124 self.ec = self.file.readint16()
125 self.nw = self.file.readint16()
126 self.nh = self.file.readint16()
127 self.nd = self.file.readint16()
128 self.ni = self.file.readint16()
129 self.nl = self.file.readint16()
130 self.nk = self.file.readint16()
131 self.ne = self.file.readint16()
132 self.np = self.file.readint16()
134 if not (self.bc-1 <= self.ec <= 255 and
135 self.ne <= 256 and
136 self.lf == 6+self.lh+(self.ec-self.bc+1)+self.nw+self.nh+self.nd
137 +self.ni+self.nl+self.nk+self.ne+self.np):
138 raise TFMError, "error in TFM pre-header"
140 if debug:
141 print "lh=%d" % self.lh
144 # read header
147 self.checksum = self.file.readint32()
148 self.designsize = self.file.readint32()
149 assert self.designsize > 0, "invald design size"
150 if self.lh > 2:
151 assert self.lh > 11, "inconsistency in TFM file: incomplete field"
152 self.charcoding = self.file.readstring(40)
153 else:
154 self.charcoding = None
156 if self.lh > 12:
157 assert self.lh > 16, "inconsistency in TFM file: incomplete field"
158 self.fontfamily = self.file.readstring(20)
159 else:
160 self.fontfamily = None
162 if debug:
163 print "(FAMILY %s)" % self.fontfamily
164 print "(CODINGSCHEME %s)" % self.charcoding
165 print "(DESINGSIZE R %f)" % 16.0*self.designsize/16777216L
167 if self.lh > 17:
168 self.sevenbitsave = self.file.readuchar()
169 # ignore the following two bytes
170 self.file.readint16()
171 facechar = self.file.readuchar()
172 # decode ugly face specification into the Knuth suggested string
173 if facechar < 18:
174 if facechar >= 12:
175 self.face = "E"
176 facechar -= 12
177 elif facechar >= 6:
178 self.face = "C"
179 facechar -= 6
180 else:
181 self.face = "R"
183 if facechar >= 4:
184 self.face = "L" + self.face
185 facechar -= 4
186 elif facechar >= 2:
187 self.face = "B" + self.face
188 facechar -= 2
189 else:
190 self.face = "M" + self.face
192 if facechar == 1:
193 self.face = self.face[0] + "I" + self.face[1]
194 else:
195 self.face = self.face[0] + "R" + self.face[1]
197 else:
198 self.face = None
199 else:
200 self.sevenbitsave = self.face = None
202 if self.lh > 18:
203 # just ignore the rest
204 print self.file.read((self.lh-18)*4)
207 # read char_info
210 self.char_info = [None]*(self.ec+1)
211 for charcode in range(self.bc, self.ec+1):
212 self.char_info[charcode] = char_info_word(self.file.readint32())
213 if self.char_info[charcode].width_index == 0:
214 # disable character if width_index is zero
215 self.char_info[charcode] = None
218 # read widths
221 self.width = [None for width_index in range(self.nw)]
222 for width_index in range(self.nw):
223 self.width[width_index] = self.file.readint32()
226 # read heights
229 self.height = [None for height_index in range(self.nh)]
230 for height_index in range(self.nh):
231 self.height[height_index] = self.file.readint32()
234 # read depths
237 self.depth = [None for depth_index in range(self.nd)]
238 for depth_index in range(self.nd):
239 self.depth[depth_index] = self.file.readint32()
242 # read italic
245 self.italic = [None for italic_index in range(self.ni)]
246 for italic_index in range(self.ni):
247 self.italic[italic_index] = self.file.readint32()
250 # read lig_kern
253 # XXX decode to lig_kern_command
255 self.lig_kern = [None for lig_kern_index in range(self.nl)]
256 for lig_kern_index in range(self.nl):
257 self.lig_kern[lig_kern_index] = self.file.readint32()
260 # read kern
263 self.kern = [None for kern_index in range(self.nk)]
264 for kern_index in range(self.nk):
265 self.kern[kern_index] = self.file.readint32()
268 # read exten
271 # XXX decode to extensible_recipe
273 self.exten = [None for exten_index in range(self.ne)]
274 for exten_index in range(self.ne):
275 self.exten[exten_index] = self.file.readint32()
278 # read param
281 # XXX decode
283 self.param = [None for param_index in range(self.np)]
284 for param_index in range(self.np):
285 self.param[param_index] = self.file.readint32()
287 self.file.close()
291 ##############################################################################
292 # Font handling
293 ##############################################################################
296 # PostScript font selection and output primitives
299 class UnsupportedFontFormat(Exception):
300 pass
302 class UnsupportedPSFragment(Exception):
303 pass
305 class fontmapping:
307 tokenpattern = re.compile(r'"(.*?)("\s+|"$|$)|(.*?)(\s+|$)')
309 def __init__(self, s):
310 """ construct font mapping from line s of font mapping file """
311 self.texname = self.basepsname = self.fontfile = None
313 # standard encoding
314 self.encodingfile = None
316 # supported postscript fragments occuring in psfonts.map
317 self.reencodefont = self.extendfont = self.slantfont = None
319 tokens = []
320 while len(s):
321 match = self.tokenpattern.match(s)
322 if match:
323 if match.groups()[0] is not None:
324 tokens.append('"%s"' % match.groups()[0])
325 else:
326 tokens.append(match.groups()[2])
327 s = s[match.end():]
328 else:
329 raise RuntimeError("wrong syntax")
331 for token in tokens:
332 if token.startswith("<"):
333 if token.startswith("<<"):
334 # XXX: support non-partial download here
335 self.fontfile = token[2:]
336 elif token.startswith("<["):
337 self.encodingfile = token[2:]
338 elif token.endswith(".pfa") or token.endswith(".pfb"):
339 self.fontfile = token[1:]
340 elif token.endswith(".enc"):
341 self.encodingfile = token[1:]
342 elif token.endswith(".ttf"):
343 raise UnsupportedFontFormat("TrueType font")
344 else:
345 raise RuntimeError("wrong syntax")
346 elif token.startswith('"'):
347 pscode = token[1:-1].split()
348 # parse standard postscript code fragments
349 while pscode:
350 try:
351 arg, cmd = pscode[:2]
352 except:
353 raise UnsupportedPSFragment("Unsupported Postscript fragment '%s'" % pscode)
354 pscode = pscode[2:]
355 if cmd == "ReEncodeFont":
356 self.reencodefont = arg
357 elif cmd == "ExtendFont":
358 self.extendfont = arg
359 elif cmd == "SlantFont":
360 self.slantfont = arg
361 else:
362 raise UnsupportedPSFragment("Unsupported Postscript fragment '%s %s'" % (arg, cmd))
363 else:
364 if self.texname is None:
365 self.texname = token
366 else:
367 self.basepsname = token
368 if self.basepsname is None:
369 self.basepsname = self.texname
371 def __str__(self):
372 return ("'%s' is '%s' read from '%s' encoded as '%s'" %
373 (self.texname, self.basepsname, self.fontfile, repr(self.encodingfile)))
375 # generate fontmap
377 def readfontmap(filenames):
378 """ read font map from filename (without path) """
379 fontmap = {}
380 for filename in filenames:
381 mappath = pykpathsea.find_file(filename, pykpathsea.kpse_fontmap_format)
382 # try also the oft-used registration as dvips config file
383 if not mappath:
384 mappath = pykpathsea.find_file(filename, pykpathsea.kpse_dvips_config_format)
385 if not mappath:
386 raise RuntimeError("cannot find font mapping file '%s'" % filename)
387 mapfile = open(mappath, "r")
388 lineno = 0
389 for line in mapfile.readlines():
390 lineno += 1
391 line = line.rstrip()
392 if not (line=="" or line[0] in (" ", "%", "*", ";" , "#")):
393 try:
394 fm = fontmapping(line)
395 except (RuntimeError, UnsupportedPSFragment), e:
396 warnings.warn("Ignoring line %i in mapping file '%s': %s" % (lineno, filename, e))
397 except UnsupportedFontFormat, e:
398 pass
399 else:
400 fontmap[fm.texname] = fm
401 mapfile.close()
402 return fontmap
405 class font:
407 def __init__(self, name, c, q, d, tfmconv, pyxconv, fontmap, debug=0):
408 self.name = name
409 self.q = q # desired size of font (fix_word) in TeX points
410 self.d = d # design size of font (fix_word) in TeX points
411 self.tfmconv = tfmconv # conversion factor from tfm units to dvi units
412 self.pyxconv = pyxconv # conversion factor from dvi units to PostScript points
413 self.fontmap = fontmap
414 tfmpath = pykpathsea.find_file("%s.tfm" % self.name, pykpathsea.kpse_tfm_format)
415 if not tfmpath:
416 raise TFMError("cannot find %s.tfm" % self.name)
417 self.tfmfile = tfmfile(tfmpath, debug)
419 # We only check for equality of font checksums if none of them
420 # is zero. The case c == 0 happend in some VF files and
421 # according to the VFtoVP documentation, paragraph 40, a check
422 # is only performed if tfmfile.checksum > 0. Anyhow, being
423 # more generous here seems to be reasonable
424 if self.tfmfile.checksum != c and self.tfmfile.checksum != 0 and c != 0:
425 raise DVIError("check sums do not agree: %d vs. %d" %
426 (self.tfmfile.checksum, c))
428 # Check whether the given design size matches the one defined in the tfm file
429 if abs(self.tfmfile.designsize - d) > 2:
430 raise DVIError("design sizes do not agree: %d vs. %d" % (self.tfmfile.designsize, d))
431 #if q < 0 or q > 134217728:
432 # raise DVIError("font '%s' not loaded: bad scale" % self.name)
433 if d < 0 or d > 134217728:
434 raise DVIError("font '%s' not loaded: bad design size" % self.name)
436 self.scale = 1.0*q/d
438 def fontinfo(self):
439 class fontinfo:
440 pass
442 # The following code is a very crude way to obtain the information
443 # required for the PDF font descritor. (TODO: The correct way would
444 # be to read the information from the AFM file.)
445 fontinfo = fontinfo()
446 try:
447 fontinfo.fontbbox = (0,
448 -self.getdepth_ds(ord("y")),
449 self.getwidth_ds(ord("W")),
450 self.getheight_ds(ord("H")))
451 except:
452 fontinfo.fontbbox = (0, -10, 100, 100)
453 try:
454 fontinfo.italicangle = -180/math.pi*math.atan(self.tfmfile.param[0]/65536.0)
455 except IndexError:
456 fontinfo.italicangle = 0
457 fontinfo.ascent = fontinfo.fontbbox[3]
458 fontinfo.descent = fontinfo.fontbbox[1]
459 try:
460 fontinfo.capheight = self.getheight_ds(ord("h"))
461 except:
462 fontinfo.capheight = 100
463 try:
464 fontinfo.vstem = self.getwidth_ds(ord("."))/3
465 except:
466 fontinfo.vstem = 5
467 return fontinfo
469 def __str__(self):
470 return "font %s designed at %g TeX pts used at %g TeX pts" % (self.name,
471 16.0*self.d/16777216L,
472 16.0*self.q/16777216L)
473 __repr__ = __str__
475 def getsize_pt(self):
476 """ return size of font in (PS) points """
477 # The factor 16L/16777216L=2**(-20) converts a fix_word (here self.q)
478 # to the corresponding float. Furthermore, we have to convert from TeX
479 # points to points, hence the factor 72/72.27.
480 return 16L*self.q/16777216L*72/72.27
482 def _convert_tfm_to_dvi(self, length):
483 # doing the integer math with long integers will lead to different roundings
484 # return 16*length*int(round(self.q*self.tfmconv))/16777216
486 # Knuth instead suggests the following algorithm based on 4 byte integer logic only
487 # z = int(round(self.q*self.tfmconv))
488 # b0, b1, b2, b3 = [ord(c) for c in struct.pack(">L", length)]
489 # assert b0 == 0 or b0 == 255
490 # shift = 4
491 # while z >= 8388608:
492 # z >>= 1
493 # shift -= 1
494 # assert shift >= 0
495 # result = ( ( ( ( ( b3 * z ) >> 8 ) + ( b2 * z ) ) >> 8 ) + ( b1 * z ) ) >> shift
496 # if b0 == 255:
497 # result = result - (z << (8-shift))
499 # however, we can simplify this using a single long integer multiplication,
500 # but take into account the transformation of z
501 z = int(round(self.q*self.tfmconv))
502 assert -16777216 <= length < 16777216 # -(1 << 24) <= length < (1 << 24)
503 assert z < 134217728 # 1 << 27
504 shift = 20 # 1 << 20
505 while z >= 8388608: # 1 << 23
506 z >>= 1
507 shift -= 1
508 # length*z is a long integer, but the result will be a regular integer
509 return int(length*long(z) >> shift)
511 def _convert_tfm_to_ds(self, length):
512 return (16*long(round(length*float(self.q)*self.tfmconv))/16777216) * self.pyxconv * 1000 / self.getsize_pt()
514 def _convert_tfm_to_pt(self, length):
515 return (16*long(round(length*float(self.q)*self.tfmconv))/16777216) * self.pyxconv
517 # routines returning lengths as integers in dvi units
519 def getwidth_dvi(self, charcode):
520 return self._convert_tfm_to_dvi(self.tfmfile.width[self.tfmfile.char_info[charcode].width_index])
522 def getheight_dvi(self, charcode):
523 return self._convert_tfm_to_dvi(self.tfmfile.height[self.tfmfile.char_info[charcode].height_index])
525 def getdepth_dvi(self, charcode):
526 return self._convert_tfm_to_dvi(self.tfmfile.depth[self.tfmfile.char_info[charcode].depth_index])
528 def getitalic_dvi(self, charcode):
529 return self._convert_tfm_to_dvi(self.tfmfile.italic[self.tfmfile.char_info[charcode].italic_index])
531 # routines returning lengths as integers in design size (AFM) units
533 def getwidth_ds(self, charcode):
534 return self._convert_tfm_to_ds(self.tfmfile.width[self.tfmfile.char_info[charcode].width_index])
536 def getheight_ds(self, charcode):
537 return self._convert_tfm_to_ds(self.tfmfile.height[self.tfmfile.char_info[charcode].height_index])
539 def getdepth_ds(self, charcode):
540 return self._convert_tfm_to_ds(self.tfmfile.depth[self.tfmfile.char_info[charcode].depth_index])
542 def getitalic_ds(self, charcode):
543 return self._convert_tfm_to_ds(self.tfmfile.italic[self.tfmfile.char_info[charcode].italic_index])
545 # routines returning lengths as floats in PostScript points
547 def getwidth_pt(self, charcode):
548 return self._convert_tfm_to_pt(self.tfmfile.width[self.tfmfile.char_info[charcode].width_index])
550 def getheight_pt(self, charcode):
551 return self._convert_tfm_to_pt(self.tfmfile.height[self.tfmfile.char_info[charcode].height_index])
553 def getdepth_pt(self, charcode):
554 return self._convert_tfm_to_pt(self.tfmfile.depth[self.tfmfile.char_info[charcode].depth_index])
556 def getitalic_pt(self, charcode):
557 return self._convert_tfm_to_pt(self.tfmfile.italic[self.tfmfile.char_info[charcode].italic_index])
560 class virtualfont(font):
561 def __init__(self, name, c, q, d, tfmconv, pyxconv, fontmap, debug=0):
562 fontpath = pykpathsea.find_file(name, pykpathsea.kpse_vf_format)
563 if fontpath is None or not len(fontpath):
564 raise RuntimeError
565 font.__init__(self, name, c, q, d, tfmconv, pyxconv, fontmap, debug)
566 self.vffile = vffile(fontpath, self.scale, tfmconv, pyxconv, fontmap, debug > 1)
568 def getfonts(self):
569 """ return fonts used in virtual font itself """
570 return self.vffile.getfonts()
572 def getchar(self, cc):
573 """ return dvi chunk corresponding to char code cc """
574 return self.vffile.getchar(cc)
577 ##############################################################################
578 # DVI file handling
579 ##############################################################################
581 _DVI_CHARMIN = 0 # typeset a character and move right (range min)
582 _DVI_CHARMAX = 127 # typeset a character and move right (range max)
583 _DVI_SET1234 = 128 # typeset a character and move right
584 _DVI_SETRULE = 132 # typeset a rule and move right
585 _DVI_PUT1234 = 133 # typeset a character
586 _DVI_PUTRULE = 137 # typeset a rule
587 _DVI_NOP = 138 # no operation
588 _DVI_BOP = 139 # beginning of page
589 _DVI_EOP = 140 # ending of page
590 _DVI_PUSH = 141 # save the current positions (h, v, w, x, y, z)
591 _DVI_POP = 142 # restore positions (h, v, w, x, y, z)
592 _DVI_RIGHT1234 = 143 # move right
593 _DVI_W0 = 147 # move right by w
594 _DVI_W1234 = 148 # move right and set w
595 _DVI_X0 = 152 # move right by x
596 _DVI_X1234 = 153 # move right and set x
597 _DVI_DOWN1234 = 157 # move down
598 _DVI_Y0 = 161 # move down by y
599 _DVI_Y1234 = 162 # move down and set y
600 _DVI_Z0 = 166 # move down by z
601 _DVI_Z1234 = 167 # move down and set z
602 _DVI_FNTNUMMIN = 171 # set current font (range min)
603 _DVI_FNTNUMMAX = 234 # set current font (range max)
604 _DVI_FNT1234 = 235 # set current font
605 _DVI_SPECIAL1234 = 239 # special (dvi extention)
606 _DVI_FNTDEF1234 = 243 # define the meaning of a font number
607 _DVI_PRE = 247 # preamble
608 _DVI_POST = 248 # postamble beginning
609 _DVI_POSTPOST = 249 # postamble ending
611 _DVI_VERSION = 2 # dvi version
613 # position variable indices
614 _POS_H = 0
615 _POS_V = 1
616 _POS_W = 2
617 _POS_X = 3
618 _POS_Y = 4
619 _POS_Z = 5
621 # reader states
622 _READ_PRE = 1
623 _READ_NOPAGE = 2
624 _READ_PAGE = 3
625 _READ_POST = 4 # XXX not used
626 _READ_POSTPOST = 5 # XXX not used
627 _READ_DONE = 6
630 class DVIError(exceptions.Exception): pass
632 # save and restore colors
634 class _savecolor(canvas.canvasitem):
635 def processPS(self, file, writer, context, registry, bbox):
636 file.write("currentcolor currentcolorspace\n")
638 def processPDF(self, file, writer, context, registry, bbox):
639 file.write("q\n")
642 class _restorecolor(canvas.canvasitem):
643 def processPS(self, file, writer, context, registry, bbox):
644 file.write("setcolorspace setcolor\n")
646 def processPDF(self, file, writer, context, registry, bbox):
647 file.write("Q\n")
649 class _savetrafo(canvas.canvasitem):
650 def processPS(self, file, writer, context, registry, bbox):
651 file.write("matrix currentmatrix\n")
653 def processPDF(self, file, writer, context, registry, bbox):
654 file.write("q\n")
657 class _restoretrafo(canvas.canvasitem):
658 def processPS(self, file, writer, context, registry, bbox):
659 file.write("setmatrix\n")
661 def processPDF(self, file, writer, context, registry, bbox):
662 file.write("Q\n")
665 class dvifile:
667 def __init__(self, filename, fontmap, debug=0, debugfile=sys.stdout):
668 """ opens the dvi file and reads the preamble """
669 self.filename = filename
670 self.fontmap = fontmap
671 self.debug = debug
672 self.debugfile = debugfile
673 self.debugstack = []
675 self.fonts = {}
676 self.activefont = None
678 # stack of fonts and fontscale currently used (used for VFs)
679 self.fontstack = []
680 self.stack = []
682 # pointer to currently active page
683 self.actpage = None
685 # stack for self.file, self.fonts and self.stack, needed for VF inclusion
686 self.statestack = []
688 self.file = binfile(self.filename, "rb")
690 # currently read byte in file (for debugging output)
691 self.filepos = None
693 self._read_pre()
695 # helper routines
697 def flushtext(self):
698 """ finish currently active text object """
699 if self.debug and self.activetext:
700 self.debugfile.write("[%s]\n" % "".join([chr(char) for char in self.activetext.chars]))
701 self.activetext = None
703 def putrule(self, height, width, advancepos=1):
704 self.flushtext()
705 x1 = self.pos[_POS_H] * self.pyxconv
706 y1 = -self.pos[_POS_V] * self.pyxconv
707 w = width * self.pyxconv
708 h = height * self.pyxconv
710 if height > 0 and width > 0:
711 if self.debug:
712 self.debugfile.write("%d: %srule height %d, width %d (???x??? pixels)\n" %
713 (self.filepos, advancepos and "set" or "put", height, width))
714 self.actpage.fill(path.rect_pt(x1, y1, w, h))
715 else:
716 if self.debug:
717 self.debugfile.write("%d: %srule height %d, width %d (invisible)\n" %
718 (self.filepos, advancepos and "set" or "put", height, width))
720 if advancepos:
721 if self.debug:
722 self.debugfile.write(" h:=%d+%d=%d, hh:=???\n" %
723 (self.pos[_POS_H], width, self.pos[_POS_H]+width))
724 self.pos[_POS_H] += width
726 def putchar(self, char, advancepos=1, id1234=0):
727 dx = advancepos and self.activefont.getwidth_dvi(char) or 0
729 if self.debug:
730 self.debugfile.write("%d: %s%s%d h:=%d+%d=%d, hh:=???\n" %
731 (self.filepos,
732 advancepos and "set" or "put",
733 id1234 and "%i " % id1234 or "char",
734 char,
735 self.pos[_POS_H], dx, self.pos[_POS_H]+dx))
737 if isinstance(self.activefont, virtualfont):
738 # virtual font handling
739 afterpos = list(self.pos)
740 afterpos[_POS_H] += dx
741 self._push_dvistring(self.activefont.getchar(char), self.activefont.getfonts(), afterpos,
742 self.activefont.getsize_pt())
743 else:
744 if self.activetext is None:
745 if not self.fontmap.has_key(self.activefont.name):
746 raise RuntimeError("missing font information for '%s'; check fontmapping file(s)" % self.activefont.name)
747 fontmapinfo = self.fontmap[self.activefont.name]
749 encodingname = fontmapinfo.reencodefont
750 if encodingname is not None:
751 encodingfilename = pykpathsea.find_file(fontmapinfo.encodingfile, pykpathsea.kpse_tex_ps_header_format)
752 if not encodingfilename:
753 raise RuntimeError("cannot find font encoding file %s" % fontmapinfo.encodingfile)
754 fontencoding = type1font.encoding(encodingname, encodingfilename)
755 else:
756 fontencoding = None
758 fontbasefontname = fontmapinfo.basepsname
759 if fontmapinfo.fontfile is not None:
760 fontfilename = pykpathsea.find_file(fontmapinfo.fontfile, pykpathsea.kpse_type1_format)
761 if not fontfilename:
762 raise RuntimeError("cannot find type 1 font %s" % fontmapinfo.fontfile)
763 else:
764 fontfilename = None
766 # XXX we currently misuse use self.activefont as metric
767 font = type1font.font(fontbasefontname, fontfilename, fontencoding, self.activefont)
769 self.activetext = type1font.text_pt(self.pos[_POS_H] * self.pyxconv, -self.pos[_POS_V] * self.pyxconv, font)
770 self.actpage.insert(self.activetext)
771 self.activetext.addchar(char)
772 self.pos[_POS_H] += dx
774 if not advancepos:
775 self.flushtext()
777 def usefont(self, fontnum, id1234=0):
778 self.flushtext()
779 self.activefont = self.fonts[fontnum]
780 if self.debug:
781 self.debugfile.write("%d: fnt%s%i current font is %s\n" %
782 (self.filepos,
783 id1234 and "%i " % id1234 or "num",
784 fontnum,
785 self.fonts[fontnum].name))
788 def definefont(self, cmdnr, num, c, q, d, fontname):
789 # cmdnr: type of fontdef command (only used for debugging output)
790 # c: checksum
791 # q: scaling factor (fix_word)
792 # Note that q is actually s in large parts of the documentation.
793 # d: design size (fix_word)
795 try:
796 afont = virtualfont(fontname, c, q/self.tfmconv, d/self.tfmconv, self.tfmconv, self.pyxconv, self.fontmap, self.debug > 1)
797 except (TypeError, RuntimeError):
798 afont = font(fontname, c, q/self.tfmconv, d/self.tfmconv, self.tfmconv, self.pyxconv, self.fontmap, self.debug > 1)
800 self.fonts[num] = afont
802 if self.debug:
803 self.debugfile.write("%d: fntdef%d %i: %s\n" % (self.filepos, cmdnr, num, fontname))
805 # scale = round((1000.0*self.conv*q)/(self.trueconv*d))
806 # m = 1.0*q/d
807 # scalestring = scale!=1000 and " scaled %d" % scale or ""
808 # print ("Font %i: %s%s---loaded at size %d DVI units" %
809 # (num, fontname, scalestring, q))
810 # if scale!=1000:
811 # print " (this font is magnified %d%%)" % round(scale/10)
813 def special(self, s):
814 x = self.pos[_POS_H] * self.pyxconv
815 y = -self.pos[_POS_V] * self.pyxconv
816 if self.debug:
817 self.debugfile.write("%d: xxx '%s'\n" % (self.filepos, s))
818 if not s.startswith("PyX:"):
819 warnings.warn("ignoring special '%s'" % s)
820 return
822 # it is in general not safe to continue using the currently active font because
823 # the specials may involve some gsave/grestore operations
824 self.flushtext()
826 command, args = s[4:].split()[0], s[4:].split()[1:]
827 if command == "color_begin":
828 if args[0] == "cmyk":
829 c = color.cmyk(float(args[1]), float(args[2]), float(args[3]), float(args[4]))
830 elif args[0] == "gray":
831 c = color.gray(float(args[1]))
832 elif args[0] == "hsb":
833 c = color.hsb(float(args[1]), float(args[2]), float(args[3]))
834 elif args[0] == "rgb":
835 c = color.rgb(float(args[1]), float(args[2]), float(args[3]))
836 elif args[0] == "RGB":
837 c = color.rgb(int(args[1])/255.0, int(args[2])/255.0, int(args[3])/255.0)
838 elif args[0] == "texnamed":
839 try:
840 c = getattr(color.cmyk, args[1])
841 except AttributeError:
842 raise RuntimeError("unknown TeX color '%s', aborting" % args[1])
843 elif args[0] == "pyxcolor":
844 # pyx.color.cmyk.PineGreen or
845 # pyx.color.cmyk(0,0,0,0.0)
846 pat = re.compile(r"(pyx\.)?(color\.)?(?P<model>(cmyk)|(rgb)|(grey)|(gray)|(hsb))[\.]?(?P<arg>.*)")
847 sd = pat.match(" ".join(args[1:]))
848 if sd:
849 sd = sd.groupdict()
850 if sd["arg"][0] == "(":
851 numpat = re.compile(r"[+-]?((\d+\.\d*)|(\d*\.\d+)|(\d+))([eE][+-]\d+)?")
852 arg = tuple([float(x[0]) for x in numpat.findall(sd["arg"])])
853 try:
854 c = getattr(color, sd["model"])(*arg)
855 except TypeError or AttributeError:
856 raise RuntimeError("cannot access PyX color '%s' in TeX, aborting" % " ".join(args[1:]))
857 else:
858 try:
859 c = getattr(getattr(color, sd["model"]), sd["arg"])
860 except AttributeError:
861 raise RuntimeError("cannot access PyX color '%s' in TeX, aborting" % " ".join(args[1:]))
862 else:
863 raise RuntimeError("cannot access PyX color '%s' in TeX, aborting" % " ".join(args[1:]))
864 else:
865 raise RuntimeError("color model '%s' cannot be handled by PyX, aborting" % args[0])
866 self.actpage.insert(_savecolor())
867 self.actpage.insert(c)
868 elif command == "color_end":
869 self.actpage.insert(_restorecolor())
870 elif command == "rotate_begin":
871 self.actpage.insert(_savetrafo())
872 self.actpage.insert(trafo.rotate_pt(float(args[0]), x, y))
873 elif command == "rotate_end":
874 self.actpage.insert(_restoretrafo())
875 elif command == "scale_begin":
876 self.actpage.insert(_savetrafo())
877 self.actpage.insert(trafo.scale_pt(float(args[0]), float(args[1]), x, y))
878 elif command == "scale_end":
879 self.actpage.insert(_restoretrafo())
880 elif command == "epsinclude":
881 # parse arguments
882 argdict = {}
883 for arg in args:
884 name, value = arg.split("=")
885 argdict[name] = value
887 # construct kwargs for epsfile constructor
888 epskwargs = {}
889 epskwargs["filename"] = argdict["file"]
890 epskwargs["bbox"] = bbox.bbox_pt(float(argdict["llx"]), float(argdict["lly"]),
891 float(argdict["urx"]), float(argdict["ury"]))
892 if argdict.has_key("width"):
893 epskwargs["width"] = float(argdict["width"]) * unit.t_pt
894 if argdict.has_key("height"):
895 epskwargs["height"] = float(argdict["height"]) * unit.t_pt
896 if argdict.has_key("clip"):
897 epskwargs["clip"] = int(argdict["clip"])
898 self.actpage.insert(epsfile.epsfile(x * unit.t_pt, y * unit.t_pt, **epskwargs))
899 elif command == "marker":
900 if len(args) != 1:
901 raise RuntimeError("marker contains spaces")
902 for c in args[0]:
903 if c not in string.digits + string.letters + "@":
904 raise RuntimeError("marker contains invalid characters")
905 if self.actpage.markers.has_key(args[0]):
906 raise RuntimeError("marker name occurred several times")
907 self.actpage.markers[args[0]] = x * unit.t_pt, y * unit.t_pt
908 else:
909 raise RuntimeError("unknown PyX special '%s', aborting" % command)
911 # routines for pushing and popping different dvi chunks on the reader
913 def _push_dvistring(self, dvi, fonts, afterpos, fontsize):
914 """ push dvi string with defined fonts on top of reader
915 stack. Every positions gets scaled relatively by the factor
916 scale. After the interpreting of the dvi chunk has been finished,
917 continue with self.pos=afterpos. The designsize of the virtual
918 font is passed as a fix_word
922 #if self.debug:
923 # self.debugfile.write("executing new dvi chunk\n")
924 self.debugstack.append(self.debug)
925 self.debug = 0
927 self.statestack.append((self.file, self.fonts, self.activefont, afterpos, self.stack, self.pyxconv, self.tfmconv))
929 # units in vf files are relative to the size of the font and given as fix_words
930 # which can be converted to floats by diving by 2**20
931 oldpyxconv = self.pyxconv
932 self.pyxconv = fontsize/2**20
933 rescale = self.pyxconv/oldpyxconv
935 self.file = stringbinfile(dvi)
936 self.fonts = fonts
937 self.stack = []
938 self.filepos = 0
940 # rescale self.pos in order to be consistent with the new scaling
941 self.pos = map(lambda x, rescale=rescale:1.0*x/rescale, self.pos)
943 # since tfmconv converts from tfm units to dvi units, rescale it as well
944 self.tfmconv /= rescale
946 self.usefont(0)
948 def _pop_dvistring(self):
949 self.flushtext()
950 #if self.debug:
951 # self.debugfile.write("finished executing dvi chunk\n")
952 self.debug = self.debugstack.pop()
954 self.file.close()
955 self.file, self.fonts, self.activefont, self.pos, self.stack, self.pyxconv, self.tfmconv = self.statestack.pop()
957 # routines corresponding to the different reader states of the dvi maschine
959 def _read_pre(self):
960 afile = self.file
961 while 1:
962 self.filepos = afile.tell()
963 cmd = afile.readuchar()
964 if cmd == _DVI_NOP:
965 pass
966 elif cmd == _DVI_PRE:
967 if afile.readuchar() != _DVI_VERSION: raise DVIError
968 num = afile.readuint32()
969 den = afile.readuint32()
970 self.mag = afile.readuint32()
972 # For the interpretation of the lengths in dvi and tfm files,
973 # three conversion factors are relevant:
974 # - self.tfmconv: tfm units -> dvi units
975 # - self.pyxconv: dvi units -> (PostScript) points
976 # - self.conv: dvi units -> pixels
977 self.tfmconv = (25400000.0/num)*(den/473628672.0)/16.0
979 # calculate conv as described in the DVIType docu using
980 # a given resolution in dpi
981 self.resolution = 300.0
982 self.conv = (num/254000.0)*(self.resolution/den)
984 # self.pyxconv is the conversion factor from the dvi units
985 # to (PostScript) points. It consists of
986 # - self.mag/1000.0: magstep scaling
987 # - self.conv: conversion from dvi units to pixels
988 # - 1/self.resolution: conversion from pixels to inch
989 # - 72 : conversion from inch to points
990 self.pyxconv = self.mag/1000.0*self.conv/self.resolution*72
992 comment = afile.read(afile.readuchar())
993 return
994 else:
995 raise DVIError
997 def readpage(self, pageid=None):
998 """ reads a page from the dvi file
1000 This routine reads a page from the dvi file which is
1001 returned as a canvas. When there is no page left in the
1002 dvifile, None is returned and the file is closed properly."""
1004 while 1:
1005 self.filepos = self.file.tell()
1006 cmd = self.file.readuchar()
1007 if cmd == _DVI_NOP:
1008 pass
1009 elif cmd == _DVI_BOP:
1010 ispageid = [self.file.readuint32() for i in range(10)]
1011 if pageid is not None and ispageid != pageid:
1012 raise DVIError("invalid pageid")
1013 if self.debug:
1014 self.debugfile.write("%d: beginning of page %i\n" % (self.filepos, ispageid[0]))
1015 self.file.readuint32()
1016 break
1017 elif cmd == _DVI_POST:
1018 self.file.close()
1019 return None # nothing left
1020 else:
1021 raise DVIError
1023 self.actpage = canvas.canvas()
1024 self.actpage.markers = {}
1025 self.pos = [0, 0, 0, 0, 0, 0]
1027 # currently active output: text instance currently used
1028 self.activetext = None
1030 while 1:
1031 afile = self.file
1032 self.filepos = afile.tell()
1033 try:
1034 cmd = afile.readuchar()
1035 except struct.error:
1036 # we most probably (if the dvi file is not corrupt) hit the end of a dvi chunk,
1037 # so we have to continue with the rest of the dvi file
1038 self._pop_dvistring()
1039 continue
1040 if cmd == _DVI_NOP:
1041 pass
1042 if cmd >= _DVI_CHARMIN and cmd <= _DVI_CHARMAX:
1043 self.putchar(cmd)
1044 elif cmd >= _DVI_SET1234 and cmd < _DVI_SET1234 + 4:
1045 self.putchar(afile.readint(cmd - _DVI_SET1234 + 1), id1234=cmd-_DVI_SET1234+1)
1046 elif cmd == _DVI_SETRULE:
1047 self.putrule(afile.readint32(), afile.readint32())
1048 elif cmd >= _DVI_PUT1234 and cmd < _DVI_PUT1234 + 4:
1049 self.putchar(afile.readint(cmd - _DVI_PUT1234 + 1), advancepos=0, id1234=cmd-_DVI_SET1234+1)
1050 elif cmd == _DVI_PUTRULE:
1051 self.putrule(afile.readint32(), afile.readint32(), 0)
1052 elif cmd == _DVI_EOP:
1053 self.flushtext()
1054 if self.debug:
1055 self.debugfile.write("%d: eop\n \n" % self.filepos)
1056 return self.actpage
1057 elif cmd == _DVI_PUSH:
1058 self.stack.append(list(self.pos))
1059 if self.debug:
1060 self.debugfile.write("%s: push\n"
1061 "level %d:(h=%d,v=%d,w=%d,x=%d,y=%d,z=%d,hh=???,vv=???)\n" %
1062 ((self.filepos, len(self.stack)-1) + tuple(self.pos)))
1063 elif cmd == _DVI_POP:
1064 self.flushtext()
1065 self.pos = self.stack.pop()
1066 if self.debug:
1067 self.debugfile.write("%s: pop\n"
1068 "level %d:(h=%d,v=%d,w=%d,x=%d,y=%d,z=%d,hh=???,vv=???)\n" %
1069 ((self.filepos, len(self.stack)) + tuple(self.pos)))
1070 elif cmd >= _DVI_RIGHT1234 and cmd < _DVI_RIGHT1234 + 4:
1071 self.flushtext()
1072 dh = afile.readint(cmd - _DVI_RIGHT1234 + 1, 1)
1073 if self.debug:
1074 self.debugfile.write("%d: right%d %d h:=%d%+d=%d, hh:=???\n" %
1075 (self.filepos,
1076 cmd - _DVI_RIGHT1234 + 1,
1078 self.pos[_POS_H],
1080 self.pos[_POS_H]+dh))
1081 self.pos[_POS_H] += dh
1082 elif cmd == _DVI_W0:
1083 self.flushtext()
1084 if self.debug:
1085 self.debugfile.write("%d: w0 %d h:=%d%+d=%d, hh:=???\n" %
1086 (self.filepos,
1087 self.pos[_POS_W],
1088 self.pos[_POS_H],
1089 self.pos[_POS_W],
1090 self.pos[_POS_H]+self.pos[_POS_W]))
1091 self.pos[_POS_H] += self.pos[_POS_W]
1092 elif cmd >= _DVI_W1234 and cmd < _DVI_W1234 + 4:
1093 self.flushtext()
1094 self.pos[_POS_W] = afile.readint(cmd - _DVI_W1234 + 1, 1)
1095 if self.debug:
1096 self.debugfile.write("%d: w%d %d h:=%d%+d=%d, hh:=???\n" %
1097 (self.filepos,
1098 cmd - _DVI_W1234 + 1,
1099 self.pos[_POS_W],
1100 self.pos[_POS_H],
1101 self.pos[_POS_W],
1102 self.pos[_POS_H]+self.pos[_POS_W]))
1103 self.pos[_POS_H] += self.pos[_POS_W]
1104 elif cmd == _DVI_X0:
1105 self.flushtext()
1106 if self.debug:
1107 self.debugfile.write("%d: x0 %d h:=%d%+d=%d, hh:=???\n" %
1108 (self.filepos,
1109 self.pos[_POS_X],
1110 self.pos[_POS_H],
1111 self.pos[_POS_X],
1112 self.pos[_POS_H]+self.pos[_POS_X]))
1113 self.pos[_POS_H] += self.pos[_POS_X]
1114 elif cmd >= _DVI_X1234 and cmd < _DVI_X1234 + 4:
1115 self.flushtext()
1116 self.pos[_POS_X] = afile.readint(cmd - _DVI_X1234 + 1, 1)
1117 if self.debug:
1118 self.debugfile.write("%d: x%d %d h:=%d%+d=%d, hh:=???\n" %
1119 (self.filepos,
1120 cmd - _DVI_X1234 + 1,
1121 self.pos[_POS_X],
1122 self.pos[_POS_H],
1123 self.pos[_POS_X],
1124 self.pos[_POS_H]+self.pos[_POS_X]))
1125 self.pos[_POS_H] += self.pos[_POS_X]
1126 elif cmd >= _DVI_DOWN1234 and cmd < _DVI_DOWN1234 + 4:
1127 self.flushtext()
1128 dv = afile.readint(cmd - _DVI_DOWN1234 + 1, 1)
1129 if self.debug:
1130 self.debugfile.write("%d: down%d %d v:=%d%+d=%d, vv:=???\n" %
1131 (self.filepos,
1132 cmd - _DVI_DOWN1234 + 1,
1134 self.pos[_POS_V],
1136 self.pos[_POS_V]+dv))
1137 self.pos[_POS_V] += dv
1138 elif cmd == _DVI_Y0:
1139 self.flushtext()
1140 if self.debug:
1141 self.debugfile.write("%d: y0 %d v:=%d%+d=%d, vv:=???\n" %
1142 (self.filepos,
1143 self.pos[_POS_Y],
1144 self.pos[_POS_V],
1145 self.pos[_POS_Y],
1146 self.pos[_POS_V]+self.pos[_POS_Y]))
1147 self.pos[_POS_V] += self.pos[_POS_Y]
1148 elif cmd >= _DVI_Y1234 and cmd < _DVI_Y1234 + 4:
1149 self.flushtext()
1150 self.pos[_POS_Y] = afile.readint(cmd - _DVI_Y1234 + 1, 1)
1151 if self.debug:
1152 self.debugfile.write("%d: y%d %d v:=%d%+d=%d, vv:=???\n" %
1153 (self.filepos,
1154 cmd - _DVI_Y1234 + 1,
1155 self.pos[_POS_Y],
1156 self.pos[_POS_V],
1157 self.pos[_POS_Y],
1158 self.pos[_POS_V]+self.pos[_POS_Y]))
1159 self.pos[_POS_V] += self.pos[_POS_Y]
1160 elif cmd == _DVI_Z0:
1161 self.flushtext()
1162 if self.debug:
1163 self.debugfile.write("%d: z0 %d v:=%d%+d=%d, vv:=???\n" %
1164 (self.filepos,
1165 self.pos[_POS_Z],
1166 self.pos[_POS_V],
1167 self.pos[_POS_Z],
1168 self.pos[_POS_V]+self.pos[_POS_Z]))
1169 self.pos[_POS_V] += self.pos[_POS_Z]
1170 elif cmd >= _DVI_Z1234 and cmd < _DVI_Z1234 + 4:
1171 self.flushtext()
1172 self.pos[_POS_Z] = afile.readint(cmd - _DVI_Z1234 + 1, 1)
1173 if self.debug:
1174 self.debugfile.write("%d: z%d %d v:=%d%+d=%d, vv:=???\n" %
1175 (self.filepos,
1176 cmd - _DVI_Z1234 + 1,
1177 self.pos[_POS_Z],
1178 self.pos[_POS_V],
1179 self.pos[_POS_Z],
1180 self.pos[_POS_V]+self.pos[_POS_Z]))
1181 self.pos[_POS_V] += self.pos[_POS_Z]
1182 elif cmd >= _DVI_FNTNUMMIN and cmd <= _DVI_FNTNUMMAX:
1183 self.usefont(cmd - _DVI_FNTNUMMIN, 0)
1184 elif cmd >= _DVI_FNT1234 and cmd < _DVI_FNT1234 + 4:
1185 # note that according to the DVI docs, for four byte font numbers,
1186 # the font number is signed. Don't ask why!
1187 fntnum = afile.readint(cmd - _DVI_FNT1234 + 1, cmd == _DVI_FNT1234 + 3)
1188 self.usefont(fntnum, id1234=cmd-_DVI_FNT1234+1)
1189 elif cmd >= _DVI_SPECIAL1234 and cmd < _DVI_SPECIAL1234 + 4:
1190 self.special(afile.read(afile.readint(cmd - _DVI_SPECIAL1234 + 1)))
1191 elif cmd >= _DVI_FNTDEF1234 and cmd < _DVI_FNTDEF1234 + 4:
1192 if cmd == _DVI_FNTDEF1234:
1193 num = afile.readuchar()
1194 elif cmd == _DVI_FNTDEF1234+1:
1195 num = afile.readuint16()
1196 elif cmd == _DVI_FNTDEF1234+2:
1197 num = afile.readuint24()
1198 elif cmd == _DVI_FNTDEF1234+3:
1199 # Cool, here we have according to docu a signed int. Why?
1200 num = afile.readint32()
1201 self.definefont(cmd-_DVI_FNTDEF1234+1,
1202 num,
1203 afile.readint32(),
1204 afile.readint32(),
1205 afile.readint32(),
1206 afile.read(afile.readuchar()+afile.readuchar()))
1207 else:
1208 raise DVIError
1211 ##############################################################################
1212 # VF file handling
1213 ##############################################################################
1215 _VF_LONG_CHAR = 242 # character packet (long version)
1216 _VF_FNTDEF1234 = _DVI_FNTDEF1234 # font definition
1217 _VF_PRE = _DVI_PRE # preamble
1218 _VF_POST = _DVI_POST # postamble
1220 _VF_ID = 202 # VF id byte
1222 class VFError(exceptions.Exception): pass
1224 class vffile:
1225 def __init__(self, filename, scale, tfmconv, pyxconv, fontmap, debug=0):
1226 self.filename = filename
1227 self.scale = scale
1228 self.tfmconv = tfmconv
1229 self.pyxconv = pyxconv
1230 self.fontmap = fontmap
1231 self.debug = debug
1232 self.fonts = {} # used fonts
1233 self.widths = {} # widths of defined chars
1234 self.chardefs = {} # dvi chunks for defined chars
1236 afile = binfile(self.filename, "rb")
1238 cmd = afile.readuchar()
1239 if cmd == _VF_PRE:
1240 if afile.readuchar() != _VF_ID: raise VFError
1241 comment = afile.read(afile.readuchar())
1242 self.cs = afile.readuint32()
1243 self.ds = afile.readuint32()
1244 else:
1245 raise VFError
1247 while 1:
1248 cmd = afile.readuchar()
1249 if cmd >= _VF_FNTDEF1234 and cmd < _VF_FNTDEF1234 + 4:
1250 # font definition
1251 if cmd == _VF_FNTDEF1234:
1252 num = afile.readuchar()
1253 elif cmd == _VF_FNTDEF1234+1:
1254 num = afile.readuint16()
1255 elif cmd == _VF_FNTDEF1234+2:
1256 num = afile.readuint24()
1257 elif cmd == _VF_FNTDEF1234+3:
1258 num = afile.readint32()
1259 c = afile.readint32()
1260 s = afile.readint32() # relative scaling used for font (fix_word)
1261 d = afile.readint32() # design size of font
1262 fontname = afile.read(afile.readuchar()+afile.readuchar())
1264 # rescaled size of font: s is relative to the scaling
1265 # of the virtual font itself. Note that realscale has
1266 # to be a fix_word (like s)
1267 # XXX: check rounding
1268 reals = int(round(self.scale * (16*self.ds/16777216L) * s))
1270 # print ("defining font %s -- VF scale: %g, VF design size: %d, relative font size: %d => real size: %d" %
1271 # (fontname, self.scale, self.ds, s, reals)
1274 # XXX allow for virtual fonts here too
1275 self.fonts[num] = font(fontname, c, reals, d, self.tfmconv, self.pyxconv, self.fontmap, self.debug > 1)
1276 elif cmd == _VF_LONG_CHAR:
1277 # character packet (long form)
1278 pl = afile.readuint32() # packet length
1279 cc = afile.readuint32() # char code (assumed unsigned, but anyhow only 0 <= cc < 255 is actually used)
1280 tfm = afile.readuint24() # character width
1281 dvi = afile.read(pl) # dvi code of character
1282 self.widths[cc] = tfm
1283 self.chardefs[cc] = dvi
1284 elif cmd < _VF_LONG_CHAR:
1285 # character packet (short form)
1286 cc = afile.readuchar() # char code
1287 tfm = afile.readuint24() # character width
1288 dvi = afile.read(cmd)
1289 self.widths[cc] = tfm
1290 self.chardefs[cc] = dvi
1291 elif cmd == _VF_POST:
1292 break
1293 else:
1294 raise VFError
1296 afile.close()
1298 def getfonts(self):
1299 return self.fonts
1301 def getchar(self, cc):
1302 return self.chardefs[cc]