- split dvifile into several files organized in new dvi directory
[PyX.git] / pyx / dvi / dvifile.py
blobd732c3f9f0af5b34ff0252e0d6281d45cb6e0c6d
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 from pyx import unit, epsfile, bbox, canvas, color, trafo, path, pykpathsea, reader, type1font
26 import tfmfile, texfont
29 _DVI_CHARMIN = 0 # typeset a character and move right (range min)
30 _DVI_CHARMAX = 127 # typeset a character and move right (range max)
31 _DVI_SET1234 = 128 # typeset a character and move right
32 _DVI_SETRULE = 132 # typeset a rule and move right
33 _DVI_PUT1234 = 133 # typeset a character
34 _DVI_PUTRULE = 137 # typeset a rule
35 _DVI_NOP = 138 # no operation
36 _DVI_BOP = 139 # beginning of page
37 _DVI_EOP = 140 # ending of page
38 _DVI_PUSH = 141 # save the current positions (h, v, w, x, y, z)
39 _DVI_POP = 142 # restore positions (h, v, w, x, y, z)
40 _DVI_RIGHT1234 = 143 # move right
41 _DVI_W0 = 147 # move right by w
42 _DVI_W1234 = 148 # move right and set w
43 _DVI_X0 = 152 # move right by x
44 _DVI_X1234 = 153 # move right and set x
45 _DVI_DOWN1234 = 157 # move down
46 _DVI_Y0 = 161 # move down by y
47 _DVI_Y1234 = 162 # move down and set y
48 _DVI_Z0 = 166 # move down by z
49 _DVI_Z1234 = 167 # move down and set z
50 _DVI_FNTNUMMIN = 171 # set current font (range min)
51 _DVI_FNTNUMMAX = 234 # set current font (range max)
52 _DVI_FNT1234 = 235 # set current font
53 _DVI_SPECIAL1234 = 239 # special (dvi extention)
54 _DVI_FNTDEF1234 = 243 # define the meaning of a font number
55 _DVI_PRE = 247 # preamble
56 _DVI_POST = 248 # postamble beginning
57 _DVI_POSTPOST = 249 # postamble ending
59 _DVI_VERSION = 2 # dvi version
61 # position variable indices
62 _POS_H = 0
63 _POS_V = 1
64 _POS_W = 2
65 _POS_X = 3
66 _POS_Y = 4
67 _POS_Z = 5
69 # reader states
70 _READ_PRE = 1
71 _READ_NOPAGE = 2
72 _READ_PAGE = 3
73 _READ_POST = 4 # XXX not used
74 _READ_POSTPOST = 5 # XXX not used
75 _READ_DONE = 6
78 class DVIError(exceptions.Exception): pass
80 # save and restore colors
82 class _savecolor(canvas.canvasitem):
83 def processPS(self, file, writer, context, registry, bbox):
84 file.write("currentcolor currentcolorspace\n")
86 def processPDF(self, file, writer, context, registry, bbox):
87 file.write("q\n")
90 class _restorecolor(canvas.canvasitem):
91 def processPS(self, file, writer, context, registry, bbox):
92 file.write("setcolorspace setcolor\n")
94 def processPDF(self, file, writer, context, registry, bbox):
95 file.write("Q\n")
97 class _savetrafo(canvas.canvasitem):
98 def processPS(self, file, writer, context, registry, bbox):
99 file.write("matrix currentmatrix\n")
101 def processPDF(self, file, writer, context, registry, bbox):
102 file.write("q\n")
105 class _restoretrafo(canvas.canvasitem):
106 def processPS(self, file, writer, context, registry, bbox):
107 file.write("setmatrix\n")
109 def processPDF(self, file, writer, context, registry, bbox):
110 file.write("Q\n")
113 class DVIfile:
115 def __init__(self, filename, debug=0, debugfile=sys.stdout):
116 """ opens the dvi file and reads the preamble """
117 self.filename = filename
118 self.debug = debug
119 self.debugfile = debugfile
120 self.debugstack = []
122 self.fonts = {}
123 self.activefont = None
125 # stack of fonts and fontscale currently used (used for VFs)
126 self.fontstack = []
127 self.stack = []
129 # pointer to currently active page
130 self.actpage = None
132 # stack for self.file, self.fonts and self.stack, needed for VF inclusion
133 self.statestack = []
135 self.file = reader.reader(self.filename)
137 # currently read byte in file (for debugging output)
138 self.filepos = None
140 self._read_pre()
142 # helper routines
144 def flushtext(self):
145 """ finish currently active text object """
146 if self.activetext:
147 # self.actpage.insert(self.activefont.text_pt(self.pos[_POS_H] * self.pyxconv, -self.pos[_POS_V] * self.pyxconv, font))
148 self.actpage.insert(self.activefont.text(self.pos[_POS_H], self.pos[_POS_V], self.activetext))
150 if self.debug and self.activetext:
151 self.debugfile.write("[%s]\n" % "".join([chr(char) for char in self.activetext]))
153 self.activetext = []
155 def putrule(self, height, width, advancepos=1):
156 self.flushtext()
157 x1 = self.pos[_POS_H] * self.pyxconv
158 y1 = -self.pos[_POS_V] * self.pyxconv
159 w = width * self.pyxconv
160 h = height * self.pyxconv
162 if height > 0 and width > 0:
163 if self.debug:
164 self.debugfile.write("%d: %srule height %d, width %d (???x??? pixels)\n" %
165 (self.filepos, advancepos and "set" or "put", height, width))
166 self.actpage.fill(path.rect_pt(x1, y1, w, h))
167 else:
168 if self.debug:
169 self.debugfile.write("%d: %srule height %d, width %d (invisible)\n" %
170 (self.filepos, advancepos and "set" or "put", height, width))
172 if advancepos:
173 if self.debug:
174 self.debugfile.write(" h:=%d+%d=%d, hh:=???\n" %
175 (self.pos[_POS_H], width, self.pos[_POS_H]+width))
176 self.pos[_POS_H] += width
178 def putchar(self, char, advancepos=1, id1234=0):
179 dx = advancepos and self.activefont.getwidth_dvi(char) or 0
181 if self.debug:
182 self.debugfile.write("%d: %s%s%d h:=%d+%d=%d, hh:=???\n" %
183 (self.filepos,
184 advancepos and "set" or "put",
185 id1234 and "%i " % id1234 or "char",
186 char,
187 self.pos[_POS_H], dx, self.pos[_POS_H]+dx))
189 if isinstance(self.activefont, texfont.virtualfont):
190 # virtual font handling
191 afterpos = list(self.pos)
192 afterpos[_POS_H] += dx
193 self._push_dvistring(self.activefont.getchar(char), self.activefont.getfonts(), afterpos,
194 self.activefont.getsize_pt())
195 else:
196 self.activetext.append(char)
197 self.pos[_POS_H] += dx
199 if not advancepos:
200 self.flushtext()
202 def usefont(self, fontnum, id1234=0):
203 self.flushtext()
204 self.activefont = self.fonts[fontnum]
205 if self.debug:
206 self.debugfile.write("%d: fnt%s%i current font is %s\n" %
207 (self.filepos,
208 id1234 and "%i " % id1234 or "num",
209 fontnum,
210 self.fonts[fontnum].name))
213 def definefont(self, cmdnr, num, c, q, d, fontname):
214 # cmdnr: type of fontdef command (only used for debugging output)
215 # c: checksum
216 # q: scaling factor (fix_word)
217 # Note that q is actually s in large parts of the documentation.
218 # d: design size (fix_word)
220 try:
221 afont = texfont.virtualfont(fontname, c, q/self.tfmconv, d/self.tfmconv, self.tfmconv, self.pyxconv, self.debug > 1)
222 except (TypeError, RuntimeError):
223 afont = texfont.TeXfont(fontname, c, q/self.tfmconv, d/self.tfmconv, self.tfmconv, self.pyxconv, self.debug > 1)
225 self.fonts[num] = afont
227 if self.debug:
228 self.debugfile.write("%d: fntdef%d %i: %s\n" % (self.filepos, cmdnr, num, fontname))
230 # scale = round((1000.0*self.conv*q)/(self.trueconv*d))
231 # m = 1.0*q/d
232 # scalestring = scale!=1000 and " scaled %d" % scale or ""
233 # print ("Font %i: %s%s---loaded at size %d DVI units" %
234 # (num, fontname, scalestring, q))
235 # if scale!=1000:
236 # print " (this font is magnified %d%%)" % round(scale/10)
238 def special(self, s):
239 x = self.pos[_POS_H] * self.pyxconv
240 y = -self.pos[_POS_V] * self.pyxconv
241 if self.debug:
242 self.debugfile.write("%d: xxx '%s'\n" % (self.filepos, s))
243 if not s.startswith("PyX:"):
244 warnings.warn("ignoring special '%s'" % s)
245 return
247 # it is in general not safe to continue using the currently active font because
248 # the specials may involve some gsave/grestore operations
249 self.flushtext()
251 command, args = s[4:].split()[0], s[4:].split()[1:]
252 if command == "color_begin":
253 if args[0] == "cmyk":
254 c = color.cmyk(float(args[1]), float(args[2]), float(args[3]), float(args[4]))
255 elif args[0] == "gray":
256 c = color.gray(float(args[1]))
257 elif args[0] == "hsb":
258 c = color.hsb(float(args[1]), float(args[2]), float(args[3]))
259 elif args[0] == "rgb":
260 c = color.rgb(float(args[1]), float(args[2]), float(args[3]))
261 elif args[0] == "RGB":
262 c = color.rgb(int(args[1])/255.0, int(args[2])/255.0, int(args[3])/255.0)
263 elif args[0] == "texnamed":
264 try:
265 c = getattr(color.cmyk, args[1])
266 except AttributeError:
267 raise RuntimeError("unknown TeX color '%s', aborting" % args[1])
268 elif args[0] == "pyxcolor":
269 # pyx.color.cmyk.PineGreen or
270 # pyx.color.cmyk(0,0,0,0.0)
271 pat = re.compile(r"(pyx\.)?(color\.)?(?P<model>(cmyk)|(rgb)|(grey)|(gray)|(hsb))[\.]?(?P<arg>.*)")
272 sd = pat.match(" ".join(args[1:]))
273 if sd:
274 sd = sd.groupdict()
275 if sd["arg"][0] == "(":
276 numpat = re.compile(r"[+-]?((\d+\.\d*)|(\d*\.\d+)|(\d+))([eE][+-]\d+)?")
277 arg = tuple([float(x[0]) for x in numpat.findall(sd["arg"])])
278 try:
279 c = getattr(color, sd["model"])(*arg)
280 except TypeError or AttributeError:
281 raise RuntimeError("cannot access PyX color '%s' in TeX, aborting" % " ".join(args[1:]))
282 else:
283 try:
284 c = getattr(getattr(color, sd["model"]), sd["arg"])
285 except AttributeError:
286 raise RuntimeError("cannot access PyX color '%s' in TeX, aborting" % " ".join(args[1:]))
287 else:
288 raise RuntimeError("cannot access PyX color '%s' in TeX, aborting" % " ".join(args[1:]))
289 else:
290 raise RuntimeError("color model '%s' cannot be handled by PyX, aborting" % args[0])
291 self.actpage.insert(_savecolor())
292 self.actpage.insert(c)
293 elif command == "color_end":
294 self.actpage.insert(_restorecolor())
295 elif command == "rotate_begin":
296 self.actpage.insert(_savetrafo())
297 self.actpage.insert(trafo.rotate_pt(float(args[0]), x, y))
298 elif command == "rotate_end":
299 self.actpage.insert(_restoretrafo())
300 elif command == "scale_begin":
301 self.actpage.insert(_savetrafo())
302 self.actpage.insert(trafo.scale_pt(float(args[0]), float(args[1]), x, y))
303 elif command == "scale_end":
304 self.actpage.insert(_restoretrafo())
305 elif command == "epsinclude":
306 # parse arguments
307 argdict = {}
308 for arg in args:
309 name, value = arg.split("=")
310 argdict[name] = value
312 # construct kwargs for epsfile constructor
313 epskwargs = {}
314 epskwargs["filename"] = argdict["file"]
315 epskwargs["bbox"] = bbox.bbox_pt(float(argdict["llx"]), float(argdict["lly"]),
316 float(argdict["urx"]), float(argdict["ury"]))
317 if argdict.has_key("width"):
318 epskwargs["width"] = float(argdict["width"]) * unit.t_pt
319 if argdict.has_key("height"):
320 epskwargs["height"] = float(argdict["height"]) * unit.t_pt
321 if argdict.has_key("clip"):
322 epskwargs["clip"] = int(argdict["clip"])
323 self.actpage.insert(epsfile.epsfile(x * unit.t_pt, y * unit.t_pt, **epskwargs))
324 elif command == "marker":
325 if len(args) != 1:
326 raise RuntimeError("marker contains spaces")
327 for c in args[0]:
328 if c not in string.digits + string.letters + "@":
329 raise RuntimeError("marker contains invalid characters")
330 if self.actpage.markers.has_key(args[0]):
331 raise RuntimeError("marker name occurred several times")
332 self.actpage.markers[args[0]] = x * unit.t_pt, y * unit.t_pt
333 else:
334 raise RuntimeError("unknown PyX special '%s', aborting" % command)
336 # routines for pushing and popping different dvi chunks on the reader
338 def _push_dvistring(self, dvi, fonts, afterpos, fontsize):
339 """ push dvi string with defined fonts on top of reader
340 stack. Every positions gets scaled relatively by the factor
341 scale. After the interpreting of the dvi chunk has been finished,
342 continue with self.pos=afterpos. The designsize of the virtual
343 font is passed as a fix_word
347 #if self.debug:
348 # self.debugfile.write("executing new dvi chunk\n")
349 self.debugstack.append(self.debug)
350 self.debug = 0
352 self.statestack.append((self.file, self.fonts, self.activefont, afterpos, self.stack, self.pyxconv, self.tfmconv))
354 # units in vf files are relative to the size of the font and given as fix_words
355 # which can be converted to floats by diving by 2**20
356 oldpyxconv = self.pyxconv
357 self.pyxconv = fontsize/2**20
358 rescale = self.pyxconv/oldpyxconv
360 self.file = stringbinfile(dvi)
361 self.fonts = fonts
362 self.stack = []
363 self.filepos = 0
365 # rescale self.pos in order to be consistent with the new scaling
366 self.pos = map(lambda x, rescale=rescale:1.0*x/rescale, self.pos)
368 # since tfmconv converts from tfm units to dvi units, rescale it as well
369 self.tfmconv /= rescale
371 self.usefont(0)
373 def _pop_dvistring(self):
374 self.flushtext()
375 #if self.debug:
376 # self.debugfile.write("finished executing dvi chunk\n")
377 self.debug = self.debugstack.pop()
379 self.file.close()
380 self.file, self.fonts, self.activefont, self.pos, self.stack, self.pyxconv, self.tfmconv = self.statestack.pop()
382 # routines corresponding to the different reader states of the dvi maschine
384 def _read_pre(self):
385 afile = self.file
386 while 1:
387 self.filepos = afile.tell()
388 cmd = afile.readuchar()
389 if cmd == _DVI_NOP:
390 pass
391 elif cmd == _DVI_PRE:
392 if afile.readuchar() != _DVI_VERSION: raise DVIError
393 num = afile.readuint32()
394 den = afile.readuint32()
395 self.mag = afile.readuint32()
397 # For the interpretation of the lengths in dvi and tfm files,
398 # three conversion factors are relevant:
399 # - self.tfmconv: tfm units -> dvi units
400 # - self.pyxconv: dvi units -> (PostScript) points
401 # - self.conv: dvi units -> pixels
402 self.tfmconv = (25400000.0/num)*(den/473628672.0)/16.0
404 # calculate conv as described in the DVIType docu using
405 # a given resolution in dpi
406 self.resolution = 300.0
407 self.conv = (num/254000.0)*(self.resolution/den)
409 # self.pyxconv is the conversion factor from the dvi units
410 # to (PostScript) points. It consists of
411 # - self.mag/1000.0: magstep scaling
412 # - self.conv: conversion from dvi units to pixels
413 # - 1/self.resolution: conversion from pixels to inch
414 # - 72 : conversion from inch to points
415 self.pyxconv = self.mag/1000.0*self.conv/self.resolution*72
417 comment = afile.read(afile.readuchar())
418 return
419 else:
420 raise DVIError
422 def readpage(self, pageid=None):
423 """ reads a page from the dvi file
425 This routine reads a page from the dvi file which is
426 returned as a canvas. When there is no page left in the
427 dvifile, None is returned and the file is closed properly."""
429 while 1:
430 self.filepos = self.file.tell()
431 cmd = self.file.readuchar()
432 if cmd == _DVI_NOP:
433 pass
434 elif cmd == _DVI_BOP:
435 ispageid = [self.file.readuint32() for i in range(10)]
436 if pageid is not None and ispageid != pageid:
437 raise DVIError("invalid pageid")
438 if self.debug:
439 self.debugfile.write("%d: beginning of page %i\n" % (self.filepos, ispageid[0]))
440 self.file.readuint32()
441 break
442 elif cmd == _DVI_POST:
443 self.file.close()
444 return None # nothing left
445 else:
446 raise DVIError
448 self.actpage = canvas.canvas()
449 self.actpage.markers = {}
450 self.pos = [0, 0, 0, 0, 0, 0]
452 # list of codepoints to be output
453 self.activetext = []
455 while 1:
456 afile = self.file
457 self.filepos = afile.tell()
458 try:
459 cmd = afile.readuchar()
460 except struct.error:
461 # we most probably (if the dvi file is not corrupt) hit the end of a dvi chunk,
462 # so we have to continue with the rest of the dvi file
463 self._pop_dvistring()
464 continue
465 if cmd == _DVI_NOP:
466 pass
467 if cmd >= _DVI_CHARMIN and cmd <= _DVI_CHARMAX:
468 self.putchar(cmd)
469 elif cmd >= _DVI_SET1234 and cmd < _DVI_SET1234 + 4:
470 self.putchar(afile.readint(cmd - _DVI_SET1234 + 1), id1234=cmd-_DVI_SET1234+1)
471 elif cmd == _DVI_SETRULE:
472 self.putrule(afile.readint32(), afile.readint32())
473 elif cmd >= _DVI_PUT1234 and cmd < _DVI_PUT1234 + 4:
474 self.putchar(afile.readint(cmd - _DVI_PUT1234 + 1), advancepos=0, id1234=cmd-_DVI_SET1234+1)
475 elif cmd == _DVI_PUTRULE:
476 self.putrule(afile.readint32(), afile.readint32(), 0)
477 elif cmd == _DVI_EOP:
478 self.flushtext()
479 if self.debug:
480 self.debugfile.write("%d: eop\n \n" % self.filepos)
481 return self.actpage
482 elif cmd == _DVI_PUSH:
483 self.stack.append(list(self.pos))
484 if self.debug:
485 self.debugfile.write("%s: push\n"
486 "level %d:(h=%d,v=%d,w=%d,x=%d,y=%d,z=%d,hh=???,vv=???)\n" %
487 ((self.filepos, len(self.stack)-1) + tuple(self.pos)))
488 elif cmd == _DVI_POP:
489 self.flushtext()
490 self.pos = self.stack.pop()
491 if self.debug:
492 self.debugfile.write("%s: pop\n"
493 "level %d:(h=%d,v=%d,w=%d,x=%d,y=%d,z=%d,hh=???,vv=???)\n" %
494 ((self.filepos, len(self.stack)) + tuple(self.pos)))
495 elif cmd >= _DVI_RIGHT1234 and cmd < _DVI_RIGHT1234 + 4:
496 self.flushtext()
497 dh = afile.readint(cmd - _DVI_RIGHT1234 + 1, 1)
498 if self.debug:
499 self.debugfile.write("%d: right%d %d h:=%d%+d=%d, hh:=???\n" %
500 (self.filepos,
501 cmd - _DVI_RIGHT1234 + 1,
503 self.pos[_POS_H],
505 self.pos[_POS_H]+dh))
506 self.pos[_POS_H] += dh
507 elif cmd == _DVI_W0:
508 self.flushtext()
509 if self.debug:
510 self.debugfile.write("%d: w0 %d h:=%d%+d=%d, hh:=???\n" %
511 (self.filepos,
512 self.pos[_POS_W],
513 self.pos[_POS_H],
514 self.pos[_POS_W],
515 self.pos[_POS_H]+self.pos[_POS_W]))
516 self.pos[_POS_H] += self.pos[_POS_W]
517 elif cmd >= _DVI_W1234 and cmd < _DVI_W1234 + 4:
518 self.flushtext()
519 self.pos[_POS_W] = afile.readint(cmd - _DVI_W1234 + 1, 1)
520 if self.debug:
521 self.debugfile.write("%d: w%d %d h:=%d%+d=%d, hh:=???\n" %
522 (self.filepos,
523 cmd - _DVI_W1234 + 1,
524 self.pos[_POS_W],
525 self.pos[_POS_H],
526 self.pos[_POS_W],
527 self.pos[_POS_H]+self.pos[_POS_W]))
528 self.pos[_POS_H] += self.pos[_POS_W]
529 elif cmd == _DVI_X0:
530 self.flushtext()
531 if self.debug:
532 self.debugfile.write("%d: x0 %d h:=%d%+d=%d, hh:=???\n" %
533 (self.filepos,
534 self.pos[_POS_X],
535 self.pos[_POS_H],
536 self.pos[_POS_X],
537 self.pos[_POS_H]+self.pos[_POS_X]))
538 self.pos[_POS_H] += self.pos[_POS_X]
539 elif cmd >= _DVI_X1234 and cmd < _DVI_X1234 + 4:
540 self.flushtext()
541 self.pos[_POS_X] = afile.readint(cmd - _DVI_X1234 + 1, 1)
542 if self.debug:
543 self.debugfile.write("%d: x%d %d h:=%d%+d=%d, hh:=???\n" %
544 (self.filepos,
545 cmd - _DVI_X1234 + 1,
546 self.pos[_POS_X],
547 self.pos[_POS_H],
548 self.pos[_POS_X],
549 self.pos[_POS_H]+self.pos[_POS_X]))
550 self.pos[_POS_H] += self.pos[_POS_X]
551 elif cmd >= _DVI_DOWN1234 and cmd < _DVI_DOWN1234 + 4:
552 self.flushtext()
553 dv = afile.readint(cmd - _DVI_DOWN1234 + 1, 1)
554 if self.debug:
555 self.debugfile.write("%d: down%d %d v:=%d%+d=%d, vv:=???\n" %
556 (self.filepos,
557 cmd - _DVI_DOWN1234 + 1,
559 self.pos[_POS_V],
561 self.pos[_POS_V]+dv))
562 self.pos[_POS_V] += dv
563 elif cmd == _DVI_Y0:
564 self.flushtext()
565 if self.debug:
566 self.debugfile.write("%d: y0 %d v:=%d%+d=%d, vv:=???\n" %
567 (self.filepos,
568 self.pos[_POS_Y],
569 self.pos[_POS_V],
570 self.pos[_POS_Y],
571 self.pos[_POS_V]+self.pos[_POS_Y]))
572 self.pos[_POS_V] += self.pos[_POS_Y]
573 elif cmd >= _DVI_Y1234 and cmd < _DVI_Y1234 + 4:
574 self.flushtext()
575 self.pos[_POS_Y] = afile.readint(cmd - _DVI_Y1234 + 1, 1)
576 if self.debug:
577 self.debugfile.write("%d: y%d %d v:=%d%+d=%d, vv:=???\n" %
578 (self.filepos,
579 cmd - _DVI_Y1234 + 1,
580 self.pos[_POS_Y],
581 self.pos[_POS_V],
582 self.pos[_POS_Y],
583 self.pos[_POS_V]+self.pos[_POS_Y]))
584 self.pos[_POS_V] += self.pos[_POS_Y]
585 elif cmd == _DVI_Z0:
586 self.flushtext()
587 if self.debug:
588 self.debugfile.write("%d: z0 %d v:=%d%+d=%d, vv:=???\n" %
589 (self.filepos,
590 self.pos[_POS_Z],
591 self.pos[_POS_V],
592 self.pos[_POS_Z],
593 self.pos[_POS_V]+self.pos[_POS_Z]))
594 self.pos[_POS_V] += self.pos[_POS_Z]
595 elif cmd >= _DVI_Z1234 and cmd < _DVI_Z1234 + 4:
596 self.flushtext()
597 self.pos[_POS_Z] = afile.readint(cmd - _DVI_Z1234 + 1, 1)
598 if self.debug:
599 self.debugfile.write("%d: z%d %d v:=%d%+d=%d, vv:=???\n" %
600 (self.filepos,
601 cmd - _DVI_Z1234 + 1,
602 self.pos[_POS_Z],
603 self.pos[_POS_V],
604 self.pos[_POS_Z],
605 self.pos[_POS_V]+self.pos[_POS_Z]))
606 self.pos[_POS_V] += self.pos[_POS_Z]
607 elif cmd >= _DVI_FNTNUMMIN and cmd <= _DVI_FNTNUMMAX:
608 self.usefont(cmd - _DVI_FNTNUMMIN, 0)
609 elif cmd >= _DVI_FNT1234 and cmd < _DVI_FNT1234 + 4:
610 # note that according to the DVI docs, for four byte font numbers,
611 # the font number is signed. Don't ask why!
612 fntnum = afile.readint(cmd - _DVI_FNT1234 + 1, cmd == _DVI_FNT1234 + 3)
613 self.usefont(fntnum, id1234=cmd-_DVI_FNT1234+1)
614 elif cmd >= _DVI_SPECIAL1234 and cmd < _DVI_SPECIAL1234 + 4:
615 self.special(afile.read(afile.readint(cmd - _DVI_SPECIAL1234 + 1)))
616 elif cmd >= _DVI_FNTDEF1234 and cmd < _DVI_FNTDEF1234 + 4:
617 if cmd == _DVI_FNTDEF1234:
618 num = afile.readuchar()
619 elif cmd == _DVI_FNTDEF1234+1:
620 num = afile.readuint16()
621 elif cmd == _DVI_FNTDEF1234+2:
622 num = afile.readuint24()
623 elif cmd == _DVI_FNTDEF1234+3:
624 # Cool, here we have according to docu a signed int. Why?
625 num = afile.readint32()
626 self.definefont(cmd-_DVI_FNTDEF1234+1,
627 num,
628 afile.readint32(),
629 afile.readint32(),
630 afile.readint32(),
631 afile.read(afile.readuchar()+afile.readuchar()))
632 else:
633 raise DVIError
636 ##############################################################################
637 # VF file handling
638 ##############################################################################
640 _VF_LONG_CHAR = 242 # character packet (long version)
641 _VF_FNTDEF1234 = _DVI_FNTDEF1234 # font definition
642 _VF_PRE = _DVI_PRE # preamble
643 _VF_POST = _DVI_POST # postamble
645 _VF_ID = 202 # VF id byte
647 class VFError(exceptions.Exception): pass
649 class vffile:
650 def __init__(self, filename, scale, tfmconv, pyxconv, fontmap, debug=0):
651 self.filename = filename
652 self.scale = scale
653 self.tfmconv = tfmconv
654 self.pyxconv = pyxconv
655 self.fontmap = fontmap
656 self.debug = debug
657 self.fonts = {} # used fonts
658 self.widths = {} # widths of defined chars
659 self.chardefs = {} # dvi chunks for defined chars
661 afile = binfile(self.filename, "rb")
663 cmd = afile.readuchar()
664 if cmd == _VF_PRE:
665 if afile.readuchar() != _VF_ID: raise VFError
666 comment = afile.read(afile.readuchar())
667 self.cs = afile.readuint32()
668 self.ds = afile.readuint32()
669 else:
670 raise VFError
672 while 1:
673 cmd = afile.readuchar()
674 if cmd >= _VF_FNTDEF1234 and cmd < _VF_FNTDEF1234 + 4:
675 # font definition
676 if cmd == _VF_FNTDEF1234:
677 num = afile.readuchar()
678 elif cmd == _VF_FNTDEF1234+1:
679 num = afile.readuint16()
680 elif cmd == _VF_FNTDEF1234+2:
681 num = afile.readuint24()
682 elif cmd == _VF_FNTDEF1234+3:
683 num = afile.readint32()
684 c = afile.readint32()
685 s = afile.readint32() # relative scaling used for font (fix_word)
686 d = afile.readint32() # design size of font
687 fontname = afile.read(afile.readuchar()+afile.readuchar())
689 # rescaled size of font: s is relative to the scaling
690 # of the virtual font itself. Note that realscale has
691 # to be a fix_word (like s)
692 # XXX: check rounding
693 reals = int(round(self.scale * (16*self.ds/16777216L) * s))
695 # print ("defining font %s -- VF scale: %g, VF design size: %d, relative font size: %d => real size: %d" %
696 # (fontname, self.scale, self.ds, s, reals)
699 # XXX allow for virtual fonts here too
700 self.fonts[num] = font(fontname, c, reals, d, self.tfmconv, self.pyxconv, self.fontmap, self.debug > 1)
701 elif cmd == _VF_LONG_CHAR:
702 # character packet (long form)
703 pl = afile.readuint32() # packet length
704 cc = afile.readuint32() # char code (assumed unsigned, but anyhow only 0 <= cc < 255 is actually used)
705 tfm = afile.readuint24() # character width
706 dvi = afile.read(pl) # dvi code of character
707 self.widths[cc] = tfm
708 self.chardefs[cc] = dvi
709 elif cmd < _VF_LONG_CHAR:
710 # character packet (short form)
711 cc = afile.readuchar() # char code
712 tfm = afile.readuint24() # character width
713 dvi = afile.read(cmd)
714 self.widths[cc] = tfm
715 self.chardefs[cc] = dvi
716 elif cmd == _VF_POST:
717 break
718 else:
719 raise VFError
721 afile.close()
723 def getfonts(self):
724 return self.fonts
726 def getchar(self, cc):
727 return self.chardefs[cc]