3d function plots
[PyX/mjg.git] / pyx / dvifile.py
blob06248c8959878564583bffe994ebace84374218f
1 # -*- coding: ISO-8859-1 -*-
4 # Copyright (C) 2002-2006 Jörg Lehmann <joergl@users.sourceforge.net>
5 # Copyright (C) 2003-2004,2006,2007 Michael Schindler <m-schindler@users.sourceforge.net>
6 # Copyright (C) 2002-2006 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("Cannot tokenize string '%s'" % s)
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("Unknown token '%s'" % token)
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, "rU")
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, mappath, 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 fontslant = fontmapinfo.slantfont
767 if fontslant is not None:
768 fontslant = float(fontslant)
770 # XXX we currently misuse use self.activefont as metric
771 font = type1font.font(fontbasefontname, fontfilename, fontencoding, fontslant, self.activefont)
773 self.activetext = type1font.text_pt(self.pos[_POS_H] * self.pyxconv, -self.pos[_POS_V] * self.pyxconv, font)
774 self.actpage.insert(self.activetext)
775 self.activetext.addchar(char)
776 self.pos[_POS_H] += dx
778 if not advancepos:
779 self.flushtext()
781 def usefont(self, fontnum, id1234=0):
782 self.flushtext()
783 self.activefont = self.fonts[fontnum]
784 if self.debug:
785 self.debugfile.write("%d: fnt%s%i current font is %s\n" %
786 (self.filepos,
787 id1234 and "%i " % id1234 or "num",
788 fontnum,
789 self.fonts[fontnum].name))
792 def definefont(self, cmdnr, num, c, q, d, fontname):
793 # cmdnr: type of fontdef command (only used for debugging output)
794 # c: checksum
795 # q: scaling factor (fix_word)
796 # Note that q is actually s in large parts of the documentation.
797 # d: design size (fix_word)
799 try:
800 afont = virtualfont(fontname, c, q/self.tfmconv, d/self.tfmconv, self.tfmconv, self.pyxconv, self.fontmap, self.debug > 1)
801 except (TypeError, RuntimeError):
802 afont = font(fontname, c, q/self.tfmconv, d/self.tfmconv, self.tfmconv, self.pyxconv, self.fontmap, self.debug > 1)
804 self.fonts[num] = afont
806 if self.debug:
807 self.debugfile.write("%d: fntdef%d %i: %s\n" % (self.filepos, cmdnr, num, fontname))
809 # scale = round((1000.0*self.conv*q)/(self.trueconv*d))
810 # m = 1.0*q/d
811 # scalestring = scale!=1000 and " scaled %d" % scale or ""
812 # print ("Font %i: %s%s---loaded at size %d DVI units" %
813 # (num, fontname, scalestring, q))
814 # if scale!=1000:
815 # print " (this font is magnified %d%%)" % round(scale/10)
817 def special(self, s):
818 x = self.pos[_POS_H] * self.pyxconv
819 y = -self.pos[_POS_V] * self.pyxconv
820 if self.debug:
821 self.debugfile.write("%d: xxx '%s'\n" % (self.filepos, s))
822 if not s.startswith("PyX:"):
823 warnings.warn("ignoring special '%s'" % s)
824 return
826 # it is in general not safe to continue using the currently active font because
827 # the specials may involve some gsave/grestore operations
828 self.flushtext()
830 command, args = s[4:].split()[0], s[4:].split()[1:]
831 if command == "color_begin":
832 if args[0] == "cmyk":
833 c = color.cmyk(float(args[1]), float(args[2]), float(args[3]), float(args[4]))
834 elif args[0] == "gray":
835 c = color.gray(float(args[1]))
836 elif args[0] == "hsb":
837 c = color.hsb(float(args[1]), float(args[2]), float(args[3]))
838 elif args[0] == "rgb":
839 c = color.rgb(float(args[1]), float(args[2]), float(args[3]))
840 elif args[0] == "RGB":
841 c = color.rgb(int(args[1])/255.0, int(args[2])/255.0, int(args[3])/255.0)
842 elif args[0] == "texnamed":
843 try:
844 c = getattr(color.cmyk, args[1])
845 except AttributeError:
846 raise RuntimeError("unknown TeX color '%s', aborting" % args[1])
847 elif args[0] == "pyxcolor":
848 # pyx.color.cmyk.PineGreen or
849 # pyx.color.cmyk(0,0,0,0.0)
850 pat = re.compile(r"(pyx\.)?(color\.)?(?P<model>(cmyk)|(rgb)|(grey)|(gray)|(hsb))[\.]?(?P<arg>.*)")
851 sd = pat.match(" ".join(args[1:]))
852 if sd:
853 sd = sd.groupdict()
854 if sd["arg"][0] == "(":
855 numpat = re.compile(r"[+-]?((\d+\.\d*)|(\d*\.\d+)|(\d+))([eE][+-]\d+)?")
856 arg = tuple([float(x[0]) for x in numpat.findall(sd["arg"])])
857 try:
858 c = getattr(color, sd["model"])(*arg)
859 except TypeError or AttributeError:
860 raise RuntimeError("cannot access PyX color '%s' in TeX, aborting" % " ".join(args[1:]))
861 else:
862 try:
863 c = getattr(getattr(color, sd["model"]), sd["arg"])
864 except AttributeError:
865 raise RuntimeError("cannot access PyX color '%s' in TeX, aborting" % " ".join(args[1:]))
866 else:
867 raise RuntimeError("cannot access PyX color '%s' in TeX, aborting" % " ".join(args[1:]))
868 else:
869 raise RuntimeError("color model '%s' cannot be handled by PyX, aborting" % args[0])
870 self.actpage.insert(_savecolor())
871 self.actpage.insert(c)
872 elif command == "color_end":
873 self.actpage.insert(_restorecolor())
874 elif command == "rotate_begin":
875 self.actpage.insert(_savetrafo())
876 self.actpage.insert(trafo.rotate_pt(float(args[0]), x, y))
877 elif command == "rotate_end":
878 self.actpage.insert(_restoretrafo())
879 elif command == "scale_begin":
880 self.actpage.insert(_savetrafo())
881 self.actpage.insert(trafo.scale_pt(float(args[0]), float(args[1]), x, y))
882 elif command == "scale_end":
883 self.actpage.insert(_restoretrafo())
884 elif command == "epsinclude":
885 # parse arguments
886 argdict = {}
887 for arg in args:
888 name, value = arg.split("=")
889 argdict[name] = value
891 # construct kwargs for epsfile constructor
892 epskwargs = {}
893 epskwargs["filename"] = argdict["file"]
894 epskwargs["bbox"] = bbox.bbox_pt(float(argdict["llx"]), float(argdict["lly"]),
895 float(argdict["urx"]), float(argdict["ury"]))
896 if argdict.has_key("width"):
897 epskwargs["width"] = float(argdict["width"]) * unit.t_pt
898 if argdict.has_key("height"):
899 epskwargs["height"] = float(argdict["height"]) * unit.t_pt
900 if argdict.has_key("clip"):
901 epskwargs["clip"] = int(argdict["clip"])
902 self.actpage.insert(epsfile.epsfile(x * unit.t_pt, y * unit.t_pt, **epskwargs))
903 elif command == "marker":
904 if len(args) != 1:
905 raise RuntimeError("marker contains spaces")
906 for c in args[0]:
907 if c not in string.digits + string.letters + "@":
908 raise RuntimeError("marker contains invalid characters")
909 if self.actpage.markers.has_key(args[0]):
910 raise RuntimeError("marker name occurred several times")
911 self.actpage.markers[args[0]] = x * unit.t_pt, y * unit.t_pt
912 else:
913 raise RuntimeError("unknown PyX special '%s', aborting" % command)
915 # routines for pushing and popping different dvi chunks on the reader
917 def _push_dvistring(self, dvi, fonts, afterpos, fontsize):
918 """ push dvi string with defined fonts on top of reader
919 stack. Every positions gets scaled relatively by the factor
920 scale. After the interpreting of the dvi chunk has been finished,
921 continue with self.pos=afterpos. The designsize of the virtual
922 font is passed as a fix_word
926 #if self.debug:
927 # self.debugfile.write("executing new dvi chunk\n")
928 self.debugstack.append(self.debug)
929 self.debug = 0
931 self.statestack.append((self.file, self.fonts, self.activefont, afterpos, self.stack, self.pyxconv, self.tfmconv))
933 # units in vf files are relative to the size of the font and given as fix_words
934 # which can be converted to floats by diving by 2**20
935 oldpyxconv = self.pyxconv
936 self.pyxconv = fontsize/2**20
937 rescale = self.pyxconv/oldpyxconv
939 self.file = stringbinfile(dvi)
940 self.fonts = fonts
941 self.stack = []
942 self.filepos = 0
944 # rescale self.pos in order to be consistent with the new scaling
945 self.pos = map(lambda x, rescale=rescale:1.0*x/rescale, self.pos)
947 # since tfmconv converts from tfm units to dvi units, rescale it as well
948 self.tfmconv /= rescale
950 self.usefont(0)
952 def _pop_dvistring(self):
953 self.flushtext()
954 #if self.debug:
955 # self.debugfile.write("finished executing dvi chunk\n")
956 self.debug = self.debugstack.pop()
958 self.file.close()
959 self.file, self.fonts, self.activefont, self.pos, self.stack, self.pyxconv, self.tfmconv = self.statestack.pop()
961 # routines corresponding to the different reader states of the dvi maschine
963 def _read_pre(self):
964 afile = self.file
965 while 1:
966 self.filepos = afile.tell()
967 cmd = afile.readuchar()
968 if cmd == _DVI_NOP:
969 pass
970 elif cmd == _DVI_PRE:
971 if afile.readuchar() != _DVI_VERSION: raise DVIError
972 num = afile.readuint32()
973 den = afile.readuint32()
974 self.mag = afile.readuint32()
976 # For the interpretation of the lengths in dvi and tfm files,
977 # three conversion factors are relevant:
978 # - self.tfmconv: tfm units -> dvi units
979 # - self.pyxconv: dvi units -> (PostScript) points
980 # - self.conv: dvi units -> pixels
981 self.tfmconv = (25400000.0/num)*(den/473628672.0)/16.0
983 # calculate conv as described in the DVIType docu using
984 # a given resolution in dpi
985 self.resolution = 300.0
986 self.conv = (num/254000.0)*(self.resolution/den)
988 # self.pyxconv is the conversion factor from the dvi units
989 # to (PostScript) points. It consists of
990 # - self.mag/1000.0: magstep scaling
991 # - self.conv: conversion from dvi units to pixels
992 # - 1/self.resolution: conversion from pixels to inch
993 # - 72 : conversion from inch to points
994 self.pyxconv = self.mag/1000.0*self.conv/self.resolution*72
996 comment = afile.read(afile.readuchar())
997 return
998 else:
999 raise DVIError
1001 def readpage(self, pageid=None):
1002 """ reads a page from the dvi file
1004 This routine reads a page from the dvi file which is
1005 returned as a canvas. When there is no page left in the
1006 dvifile, None is returned and the file is closed properly."""
1008 while 1:
1009 self.filepos = self.file.tell()
1010 cmd = self.file.readuchar()
1011 if cmd == _DVI_NOP:
1012 pass
1013 elif cmd == _DVI_BOP:
1014 ispageid = [self.file.readuint32() for i in range(10)]
1015 if pageid is not None and ispageid != pageid:
1016 raise DVIError("invalid pageid")
1017 if self.debug:
1018 self.debugfile.write("%d: beginning of page %i\n" % (self.filepos, ispageid[0]))
1019 self.file.readuint32()
1020 break
1021 elif cmd == _DVI_POST:
1022 self.file.close()
1023 return None # nothing left
1024 else:
1025 raise DVIError
1027 self.actpage = canvas.canvas()
1028 self.actpage.markers = {}
1029 self.pos = [0, 0, 0, 0, 0, 0]
1031 # currently active output: text instance currently used
1032 self.activetext = None
1034 while 1:
1035 afile = self.file
1036 self.filepos = afile.tell()
1037 try:
1038 cmd = afile.readuchar()
1039 except struct.error:
1040 # we most probably (if the dvi file is not corrupt) hit the end of a dvi chunk,
1041 # so we have to continue with the rest of the dvi file
1042 self._pop_dvistring()
1043 continue
1044 if cmd == _DVI_NOP:
1045 pass
1046 if cmd >= _DVI_CHARMIN and cmd <= _DVI_CHARMAX:
1047 self.putchar(cmd)
1048 elif cmd >= _DVI_SET1234 and cmd < _DVI_SET1234 + 4:
1049 self.putchar(afile.readint(cmd - _DVI_SET1234 + 1), id1234=cmd-_DVI_SET1234+1)
1050 elif cmd == _DVI_SETRULE:
1051 self.putrule(afile.readint32(), afile.readint32())
1052 elif cmd >= _DVI_PUT1234 and cmd < _DVI_PUT1234 + 4:
1053 self.putchar(afile.readint(cmd - _DVI_PUT1234 + 1), advancepos=0, id1234=cmd-_DVI_SET1234+1)
1054 elif cmd == _DVI_PUTRULE:
1055 self.putrule(afile.readint32(), afile.readint32(), 0)
1056 elif cmd == _DVI_EOP:
1057 self.flushtext()
1058 if self.debug:
1059 self.debugfile.write("%d: eop\n \n" % self.filepos)
1060 return self.actpage
1061 elif cmd == _DVI_PUSH:
1062 self.stack.append(list(self.pos))
1063 if self.debug:
1064 self.debugfile.write("%s: push\n"
1065 "level %d:(h=%d,v=%d,w=%d,x=%d,y=%d,z=%d,hh=???,vv=???)\n" %
1066 ((self.filepos, len(self.stack)-1) + tuple(self.pos)))
1067 elif cmd == _DVI_POP:
1068 self.flushtext()
1069 self.pos = self.stack.pop()
1070 if self.debug:
1071 self.debugfile.write("%s: pop\n"
1072 "level %d:(h=%d,v=%d,w=%d,x=%d,y=%d,z=%d,hh=???,vv=???)\n" %
1073 ((self.filepos, len(self.stack)) + tuple(self.pos)))
1074 elif cmd >= _DVI_RIGHT1234 and cmd < _DVI_RIGHT1234 + 4:
1075 self.flushtext()
1076 dh = afile.readint(cmd - _DVI_RIGHT1234 + 1, 1)
1077 if self.debug:
1078 self.debugfile.write("%d: right%d %d h:=%d%+d=%d, hh:=???\n" %
1079 (self.filepos,
1080 cmd - _DVI_RIGHT1234 + 1,
1082 self.pos[_POS_H],
1084 self.pos[_POS_H]+dh))
1085 self.pos[_POS_H] += dh
1086 elif cmd == _DVI_W0:
1087 self.flushtext()
1088 if self.debug:
1089 self.debugfile.write("%d: w0 %d h:=%d%+d=%d, hh:=???\n" %
1090 (self.filepos,
1091 self.pos[_POS_W],
1092 self.pos[_POS_H],
1093 self.pos[_POS_W],
1094 self.pos[_POS_H]+self.pos[_POS_W]))
1095 self.pos[_POS_H] += self.pos[_POS_W]
1096 elif cmd >= _DVI_W1234 and cmd < _DVI_W1234 + 4:
1097 self.flushtext()
1098 self.pos[_POS_W] = afile.readint(cmd - _DVI_W1234 + 1, 1)
1099 if self.debug:
1100 self.debugfile.write("%d: w%d %d h:=%d%+d=%d, hh:=???\n" %
1101 (self.filepos,
1102 cmd - _DVI_W1234 + 1,
1103 self.pos[_POS_W],
1104 self.pos[_POS_H],
1105 self.pos[_POS_W],
1106 self.pos[_POS_H]+self.pos[_POS_W]))
1107 self.pos[_POS_H] += self.pos[_POS_W]
1108 elif cmd == _DVI_X0:
1109 self.flushtext()
1110 if self.debug:
1111 self.debugfile.write("%d: x0 %d h:=%d%+d=%d, hh:=???\n" %
1112 (self.filepos,
1113 self.pos[_POS_X],
1114 self.pos[_POS_H],
1115 self.pos[_POS_X],
1116 self.pos[_POS_H]+self.pos[_POS_X]))
1117 self.pos[_POS_H] += self.pos[_POS_X]
1118 elif cmd >= _DVI_X1234 and cmd < _DVI_X1234 + 4:
1119 self.flushtext()
1120 self.pos[_POS_X] = afile.readint(cmd - _DVI_X1234 + 1, 1)
1121 if self.debug:
1122 self.debugfile.write("%d: x%d %d h:=%d%+d=%d, hh:=???\n" %
1123 (self.filepos,
1124 cmd - _DVI_X1234 + 1,
1125 self.pos[_POS_X],
1126 self.pos[_POS_H],
1127 self.pos[_POS_X],
1128 self.pos[_POS_H]+self.pos[_POS_X]))
1129 self.pos[_POS_H] += self.pos[_POS_X]
1130 elif cmd >= _DVI_DOWN1234 and cmd < _DVI_DOWN1234 + 4:
1131 self.flushtext()
1132 dv = afile.readint(cmd - _DVI_DOWN1234 + 1, 1)
1133 if self.debug:
1134 self.debugfile.write("%d: down%d %d v:=%d%+d=%d, vv:=???\n" %
1135 (self.filepos,
1136 cmd - _DVI_DOWN1234 + 1,
1138 self.pos[_POS_V],
1140 self.pos[_POS_V]+dv))
1141 self.pos[_POS_V] += dv
1142 elif cmd == _DVI_Y0:
1143 self.flushtext()
1144 if self.debug:
1145 self.debugfile.write("%d: y0 %d v:=%d%+d=%d, vv:=???\n" %
1146 (self.filepos,
1147 self.pos[_POS_Y],
1148 self.pos[_POS_V],
1149 self.pos[_POS_Y],
1150 self.pos[_POS_V]+self.pos[_POS_Y]))
1151 self.pos[_POS_V] += self.pos[_POS_Y]
1152 elif cmd >= _DVI_Y1234 and cmd < _DVI_Y1234 + 4:
1153 self.flushtext()
1154 self.pos[_POS_Y] = afile.readint(cmd - _DVI_Y1234 + 1, 1)
1155 if self.debug:
1156 self.debugfile.write("%d: y%d %d v:=%d%+d=%d, vv:=???\n" %
1157 (self.filepos,
1158 cmd - _DVI_Y1234 + 1,
1159 self.pos[_POS_Y],
1160 self.pos[_POS_V],
1161 self.pos[_POS_Y],
1162 self.pos[_POS_V]+self.pos[_POS_Y]))
1163 self.pos[_POS_V] += self.pos[_POS_Y]
1164 elif cmd == _DVI_Z0:
1165 self.flushtext()
1166 if self.debug:
1167 self.debugfile.write("%d: z0 %d v:=%d%+d=%d, vv:=???\n" %
1168 (self.filepos,
1169 self.pos[_POS_Z],
1170 self.pos[_POS_V],
1171 self.pos[_POS_Z],
1172 self.pos[_POS_V]+self.pos[_POS_Z]))
1173 self.pos[_POS_V] += self.pos[_POS_Z]
1174 elif cmd >= _DVI_Z1234 and cmd < _DVI_Z1234 + 4:
1175 self.flushtext()
1176 self.pos[_POS_Z] = afile.readint(cmd - _DVI_Z1234 + 1, 1)
1177 if self.debug:
1178 self.debugfile.write("%d: z%d %d v:=%d%+d=%d, vv:=???\n" %
1179 (self.filepos,
1180 cmd - _DVI_Z1234 + 1,
1181 self.pos[_POS_Z],
1182 self.pos[_POS_V],
1183 self.pos[_POS_Z],
1184 self.pos[_POS_V]+self.pos[_POS_Z]))
1185 self.pos[_POS_V] += self.pos[_POS_Z]
1186 elif cmd >= _DVI_FNTNUMMIN and cmd <= _DVI_FNTNUMMAX:
1187 self.usefont(cmd - _DVI_FNTNUMMIN, 0)
1188 elif cmd >= _DVI_FNT1234 and cmd < _DVI_FNT1234 + 4:
1189 # note that according to the DVI docs, for four byte font numbers,
1190 # the font number is signed. Don't ask why!
1191 fntnum = afile.readint(cmd - _DVI_FNT1234 + 1, cmd == _DVI_FNT1234 + 3)
1192 self.usefont(fntnum, id1234=cmd-_DVI_FNT1234+1)
1193 elif cmd >= _DVI_SPECIAL1234 and cmd < _DVI_SPECIAL1234 + 4:
1194 self.special(afile.read(afile.readint(cmd - _DVI_SPECIAL1234 + 1)))
1195 elif cmd >= _DVI_FNTDEF1234 and cmd < _DVI_FNTDEF1234 + 4:
1196 if cmd == _DVI_FNTDEF1234:
1197 num = afile.readuchar()
1198 elif cmd == _DVI_FNTDEF1234+1:
1199 num = afile.readuint16()
1200 elif cmd == _DVI_FNTDEF1234+2:
1201 num = afile.readuint24()
1202 elif cmd == _DVI_FNTDEF1234+3:
1203 # Cool, here we have according to docu a signed int. Why?
1204 num = afile.readint32()
1205 self.definefont(cmd-_DVI_FNTDEF1234+1,
1206 num,
1207 afile.readint32(),
1208 afile.readint32(),
1209 afile.readint32(),
1210 afile.read(afile.readuchar()+afile.readuchar()))
1211 else:
1212 raise DVIError
1215 ##############################################################################
1216 # VF file handling
1217 ##############################################################################
1219 _VF_LONG_CHAR = 242 # character packet (long version)
1220 _VF_FNTDEF1234 = _DVI_FNTDEF1234 # font definition
1221 _VF_PRE = _DVI_PRE # preamble
1222 _VF_POST = _DVI_POST # postamble
1224 _VF_ID = 202 # VF id byte
1226 class VFError(exceptions.Exception): pass
1228 class vffile:
1229 def __init__(self, filename, scale, tfmconv, pyxconv, fontmap, debug=0):
1230 self.filename = filename
1231 self.scale = scale
1232 self.tfmconv = tfmconv
1233 self.pyxconv = pyxconv
1234 self.fontmap = fontmap
1235 self.debug = debug
1236 self.fonts = {} # used fonts
1237 self.widths = {} # widths of defined chars
1238 self.chardefs = {} # dvi chunks for defined chars
1240 afile = binfile(self.filename, "rb")
1242 cmd = afile.readuchar()
1243 if cmd == _VF_PRE:
1244 if afile.readuchar() != _VF_ID: raise VFError
1245 comment = afile.read(afile.readuchar())
1246 self.cs = afile.readuint32()
1247 self.ds = afile.readuint32()
1248 else:
1249 raise VFError
1251 while 1:
1252 cmd = afile.readuchar()
1253 if cmd >= _VF_FNTDEF1234 and cmd < _VF_FNTDEF1234 + 4:
1254 # font definition
1255 if cmd == _VF_FNTDEF1234:
1256 num = afile.readuchar()
1257 elif cmd == _VF_FNTDEF1234+1:
1258 num = afile.readuint16()
1259 elif cmd == _VF_FNTDEF1234+2:
1260 num = afile.readuint24()
1261 elif cmd == _VF_FNTDEF1234+3:
1262 num = afile.readint32()
1263 c = afile.readint32()
1264 s = afile.readint32() # relative scaling used for font (fix_word)
1265 d = afile.readint32() # design size of font
1266 fontname = afile.read(afile.readuchar()+afile.readuchar())
1268 # rescaled size of font: s is relative to the scaling
1269 # of the virtual font itself. Note that realscale has
1270 # to be a fix_word (like s)
1271 # XXX: check rounding
1272 reals = int(round(self.scale * (16*self.ds/16777216L) * s))
1274 # print ("defining font %s -- VF scale: %g, VF design size: %d, relative font size: %d => real size: %d" %
1275 # (fontname, self.scale, self.ds, s, reals)
1278 # XXX allow for virtual fonts here too
1279 self.fonts[num] = font(fontname, c, reals, d, self.tfmconv, self.pyxconv, self.fontmap, self.debug > 1)
1280 elif cmd == _VF_LONG_CHAR:
1281 # character packet (long form)
1282 pl = afile.readuint32() # packet length
1283 cc = afile.readuint32() # char code (assumed unsigned, but anyhow only 0 <= cc < 255 is actually used)
1284 tfm = afile.readuint24() # character width
1285 dvi = afile.read(pl) # dvi code of character
1286 self.widths[cc] = tfm
1287 self.chardefs[cc] = dvi
1288 elif cmd < _VF_LONG_CHAR:
1289 # character packet (short form)
1290 cc = afile.readuchar() # char code
1291 tfm = afile.readuint24() # character width
1292 dvi = afile.read(cmd)
1293 self.widths[cc] = tfm
1294 self.chardefs[cc] = dvi
1295 elif cmd == _VF_POST:
1296 break
1297 else:
1298 raise VFError
1300 afile.close()
1302 def getfonts(self):
1303 return self.fonts
1305 def getchar(self, cc):
1306 return self.chardefs[cc]