5 # this code will be part of PyX 0.2 ... ;-)
7 _DVI_CHARMIN
= 0 # typeset a character and move right (range min)
8 _DVI_CHARMAX
= 127 # typeset a character and move right (range max)
9 _DVI_SET1234
= 128 # typeset a character and move right
10 _DVI_SETRULE
= 132 # typeset a rule and move right
11 _DVI_PUT1234
= 133 # typeset a character
12 _DVI_PUTRULE
= 137 # typeset a rule
13 _DVI_NOP
= 138 # no operation
14 _DVI_BOP
= 139 # beginning of page
15 _DVI_EOP
= 140 # ending of page
16 _DVI_PUSH
= 141 # save the current positions (h, v, w, x, y, z)
17 _DVI_POP
= 142 # restore positions (h, v, w, x, y, z)
18 _DVI_RIGHT1234
= 143 # move right
19 _DVI_W0
= 147 # move right by w
20 _DVI_W1234
= 148 # move right and set w
21 _DVI_X0
= 152 # move right by x
22 _DVI_X1234
= 153 # move right and set x
23 _DVI_DOWN1234
= 157 # move down
24 _DVI_Y0
= 161 # move down by y
25 _DVI_Y1234
= 162 # move down and set y
26 _DVI_Z0
= 166 # move down by z
27 _DVI_Z1234
= 167 # move down and set z
28 _DVI_FNTNUMMIN
= 171 # set current font (range min)
29 _DVI_FNTNUMMAX
= 234 # set current font (range max)
30 _DVI_FNT1234
= 235 # set current font
31 _DVI_SPECIAL1234
= 239 # special (dvi extention)
32 _DVI_FNTDEF1234
= 243 # define the meaning of a font number
33 _DVI_PRE
= 247 # preamble
34 _DVI_POST
= 248 # postamble beginning
35 _DVI_POSTPOST
= 249 # postamble ending
37 _DVI_VERSION
= 2 # dvi version
39 # position variable indices
56 def __init__(self
, word
):
62 self
.precomma
= abs(word
) >> 20
63 self
.postcomma
= abs(word
) & 0xFFFFF
66 return self
.sign
* (self
.precomma
+ 1.0*self
.postcomma
/0xFFFFF)
68 def __mul__(self
, other
):
72 result
.sign
= self
.sign
*other
.sign
73 c
= self
.postcomma
*other
.precomma
+ self
.precomma
*other
.postcomma
74 result
.precomma
= self
.precomma
*other
.precomma
+ (c
>> 20)
75 result
.postcomma
= c
& 0xFFFFF + ((self
.postcomma
*other
.postcomma
) >> 40)
80 def __init__(self
, word
):
81 self
.width_index
= (word
& 0xFF000000) >> 24
82 self
.height_index
= (word
& 0x00F00000) >> 20
83 self
.depth_index
= (word
& 0x000F0000) >> 16
84 self
.italic_index
= (word
& 0x0000FC00) >> 10
85 self
.tag
= (word
& 0x00000300) >> 8
86 self
.remainder
= (word
& 0x000000FF)
88 if self
.width_index
==0:
89 raise TFMError
, "width_index should not be zero"
93 def readint(self
, bytes
= 4, signed
= 0):
97 value
= ord(self
.read(1))
98 if first
and signed
and value
> 127:
101 result
= 256 * result
+ value
106 return struct
.unpack(">l", self
.read(4))[0]
108 def readuint32(self
):
109 return struct
.unpack(">L", self
.read(4))[0]
112 return struct
.unpack(">h", self
.read(2))[0]
114 def readuint16(self
):
115 return struct
.unpack(">H", self
.read(2))[0]
118 return struct
.unpack("b", self
.read(1))[0]
121 return struct
.unpack("B", self
.read(1))[0]
123 def readstring(self
, bytes
):
125 assert l
<bytes
-1, "inconsistency in file: string too long"
126 return self
.read(bytes
-1)[:l
]
128 class DVIError(Exception): pass
130 class TFMError(Exception): pass
133 def __init__(self
, name
):
134 self
.file = binfile(name
, "rb")
140 self
.lf
= self
.file.readint16()
141 self
.lh
= self
.file.readint16()
142 self
.bc
= self
.file.readint16()
143 self
.ec
= self
.file.readint16()
144 self
.nw
= self
.file.readint16()
145 self
.nh
= self
.file.readint16()
146 self
.nd
= self
.file.readint16()
147 self
.ni
= self
.file.readint16()
148 self
.nl
= self
.file.readint16()
149 self
.nk
= self
.file.readint16()
150 self
.ne
= self
.file.readint16()
151 self
.np
= self
.file.readint16()
153 if not (self
.bc
-1<=self
.ec
<=255 and
155 self
.lf
==6+self
.lh
+(self
.ec
-self
.bc
+1)+self
.nw
+self
.nh
+self
.nd
156 +self
.ni
+self
.nl
+self
.nk
+self
.ne
+self
.np
):
157 raise TFMError
, "error in TFM pre-header"
163 self
.checksum
= self
.file.readuint32()
164 self
.designsize
= fix_word(self
.file.readint32())
166 self
.charcoding
= self
.file.readstring(40)
168 self
.charcoding
= None
171 self
.fontfamily
= self
.file.readstring(20)
173 self
.fontfamily
= None
176 self
.sevenbitsave
= self
.file.readuchar()
177 # ignore the following two bytes
178 self
.file.readint16()
179 facechar
= self
.file.readuchar()
180 # decode ugly face specification into the Knuth suggested string
192 self
.face
= "L" + self
.face
195 self
.face
= "B" + self
.face
198 self
.face
= "M" + self
.face
201 self
.face
= self
.face
[0] + "I" + self
.face
[1]
203 self
.face
= self
.face
[0] + "R" + self
.face
[1]
208 self
.sevenbitsave
= self
.face
= None
211 # just ignore the rest
212 self
.file.read((self
.lh
-18)*4)
218 self
.char_info
= [None for charcode
in range(self
.ec
+1)]
220 for charcode
in range(self
.bc
, self
.ec
+1):
221 self
.char_info
[charcode
] = char_info_word(self
.file.readint32())
227 self
.width
= [None for width_index
in range(self
.nw
)]
228 for width_index
in range(self
.nw
):
229 self
.width
[width_index
] = fix_word(self
.file.readint32())
235 self
.height
= [None for height_index
in range(self
.nh
)]
236 for height_index
in range(self
.nh
):
237 self
.height
[height_index
] = fix_word(self
.file.readint32())
243 self
.depth
= [None for depth_index
in range(self
.nd
)]
244 for depth_index
in range(self
.nd
):
245 self
.depth
[depth_index
] = fix_word(self
.file.readint32())
251 self
.italic
= [None for italic_index
in range(self
.ni
)]
252 for italic_index
in range(self
.ni
):
253 self
.italic
[italic_index
] = fix_word(self
.file.readint32())
259 # XXX decode to lig_kern_command
261 self
.lig_kern
= [None for lig_kern_index
in range(self
.nl
)]
262 for lig_kern_index
in range(self
.nl
):
263 self
.lig_kern
[lig_kern_index
] = self
.file.readint32()
269 self
.kern
= [None for kern_index
in range(self
.nk
)]
270 for kern_index
in range(self
.nk
):
271 self
.kern
[kern_index
] = fix_word(self
.file.readint32())
277 # XXX decode to extensible_recipe
279 self
.exten
= [None for exten_index
in range(self
.ne
)]
280 for exten_index
in range(self
.ne
):
281 self
.exten
[exten_index
] = self
.file.readint32()
289 self
.param
= [None for param_index
in range(self
.np
)]
290 for param_index
in range(self
.np
):
291 self
.param
[param_index
] = self
.file.readint32()
295 def __init__(self
, name
):
296 path
= os
.popen("kpsewhich %s.tfm" % name
, "r").readline()[:-1]
297 self
.tfmfile
= TFMFile(path
)
299 def __getattr__(self
, attr
):
300 return self
.tfmfile
.__dict
__[attr
]
302 def getwidth(self
, charcode
):
303 return self
.tfmfile
.width
[self
.char_info
[charcode
].width_index
] * self
.designsize
305 def getheight(self
, charcode
):
306 return self
.tfmfile
.height
[self
.char_info
[charcode
].height_index
] * self
.designsize
308 def getdepth(self
, charcode
):
309 return self
.tfmfile
.depth
[self
.char_info
[charcode
].depth_index
] * self
.designsize
311 def getitalic(self
, charcode
):
312 return self
.tfmfile
.italic
[self
.char_info
[charcode
].italic_index
] * self
.designsize
317 def char(self
, char
, inch
=1):
318 x
= self
.pos
[_POS_H
] * self
.scale
* 1e-5
319 y
= self
.pos
[_POS_V
] * self
.scale
* 1e-5
320 ascii
= (char
> 32 and char
< 128) and "(%s)" % chr(char
) or "???"
321 print "type 0x%08x %s at (%.3f cm, %.3f cm)" % (char
, ascii
, x
, y
)
323 # XXX what about scaling
324 self
.pos
[_POS_H
] += float(self
.fonts
[self
.activefont
].getwidth(char
))
326 def rule(self
, height
, width
, inch
=1):
327 if height
> 0 and width
> 0:
328 x1
= self
.pos
[_POS_H
] * self
.scale
* 1e-5
329 y1
= self
.pos
[_POS_V
] * self
.scale
* 1e-5
330 x2
= (self
.pos
[_POS_H
] + width
) * self
.scale
* 1e-5
331 y2
= (self
.pos
[_POS_V
] + height
) * self
.scale
* 1e-5
332 print "rule ((%.3f..%.3f cm), (%.3f..%.3f cm))" % (x1
, x2
, y1
, y2
)
334 pass # TODO: increment h
336 def __init__(self
, name
):
338 file = binfile(name
, "rb")
342 # XXX max number of fonts
343 self
.fonts
= [None for i
in range(64)]
344 self
.activefont
= None
346 while state
!= _READ_DONE
:
347 cmd
= file.readuchar()
348 if cmd
== _DVI_NOP
: pass
350 elif state
== _READ_PRE
:
352 if file.readuchar() != _DVI_VERSION
: raise DVIError
353 num
= file.readuint32()
354 den
= file.readuint32()
355 mag
= file.readuint32()
356 self
.scale
= num
*mag
/1000.0/den
357 comment
= file.read(file.readuchar())
361 elif state
== _READ_NOPAGE
:
364 for i
in range(10): print file.readuint32(),
367 self
.pos
= [0, 0, 0, 0, 0, 0]
369 elif cmd
== _DVI_POST
:
370 state
= _READ_DONE
# we skip the rest
373 elif state
== _READ_PAGE
:
374 if cmd
>= _DVI_CHARMIN
and cmd
<= _DVI_CHARMAX
:
376 elif cmd
>= _DVI_SET1234
and cmd
< _DVI_SET1234
+ 4:
377 self
.char(file.readint(cmd
- _DVI_SET1234
+ 1))
378 elif cmd
== _DVI_SETRULE
:
379 self
.rule(file.readint32(), file.readint32())
380 elif cmd
>= _DVI_PUT1234
and cmd
< _DVI_PUT1234
+ 4:
381 self
.char(file.readint(cmd
- _DVI_PUT1234
+ 1))
382 elif cmd
== _DVI_PUTRULE
:
383 self
.rule(file.readint32(), file.readint32(), 0)
384 elif cmd
== _DVI_EOP
:
386 elif cmd
== _DVI_PUSH
:
387 stack
.append(tuple(self
.pos
))
388 elif cmd
== _DVI_POP
:
389 self
.pos
= list(stack
[-1])
391 elif cmd
>= _DVI_RIGHT1234
and cmd
< _DVI_RIGHT1234
+ 4:
392 self
.pos
[_POS_H
] += file.readint(cmd
- _DVI_RIGHT1234
+ 1, 1)
394 self
.pos
[_POS_H
] += self
.pos
[_POS_W
]
395 elif cmd
>= _DVI_W1234
and cmd
< _DVI_W1234
+ 4:
396 self
.pos
[_POS_W
] = file.readint(cmd
- _DVI_W1234
+ 1, 1)
397 self
.pos
[_POS_H
] += self
.pos
[_POS_W
]
399 self
.pos
[_POS_H
] += self
.pos
[_POS_X
]
400 elif cmd
>= _DVI_X1234
and cmd
< _DVI_X1234
+ 4:
401 self
.pos
[_POS_X
] = file.readint(cmd
- _DVI_X1234
+ 1, 1)
402 self
.pos
[_POS_H
] += self
.pos
[_POS_X
]
403 elif cmd
>= _DVI_DOWN1234
and cmd
< _DVI_DOWN1234
+ 4:
404 self
.pos
[_POS_V
] += file.readint(cmd
- _DVI_DOWN1234
+ 1, 1)
406 self
.pos
[_POS_V
] += self
.pos
[_POS_Y
]
407 elif cmd
>= _DVI_Y1234
and cmd
< _DVI_Y1234
+ 4:
408 self
.pos
[_POS_Y
] = file.readint(cmd
- _DVI_Y1234
+ 1, 1)
409 self
.pos
[_POS_V
] += self
.pos
[_POS_Y
]
411 self
.pos
[_POS_V
] += self
.pos
[_POS_Z
]
412 elif cmd
>= _DVI_Z1234
and cmd
< _DVI_Z1234
+ 4:
413 self
.pos
[_POS_Z
] = file.readint(cmd
- _DVI_Z1234
+ 1, 1)
414 self
.pos
[_POS_V
] += self
.pos
[_POS_Z
]
415 elif cmd
>= _DVI_FNTNUMMIN
and cmd
<= _DVI_FNTNUMMAX
:
416 self
.activefont
= cmd
- _DVI_FNTNUMMIN
417 print "use font %i" % self
.activefont
418 elif cmd
>= _DVI_FNT1234
and cmd
< _DVI_FNT1234
+ 4:
419 self
.activefont
= file.readint(cmd
- _DVI_FNT1234
+ 1, 1)
420 print "use font %i" % self
.activefont
421 elif cmd
>= _DVI_SPECIAL1234
and cmd
< _DVI_SPECIAL1234
+ 4:
422 print "special %s" % file.read(file.readint(cmd
- _DVI_SPECIAL1234
+ 1))
423 elif cmd
>= _DVI_FNTDEF1234
and cmd
< _DVI_FNTDEF1234
+ 4:
424 num
= file.readint(cmd
- _DVI_FNTDEF1234
+ 1)
428 fontname
= file.read(file.readuchar()+file.readuchar())
429 self
.fonts
[num
] = Font(fontname
)
430 print "font %i is %s" % (num
, fontname
)
434 else: raise DVIError
# unexpected reader state
436 if __name__
=="__main__":
437 cmr10
= Font("cmr10")
438 print cmr10
.charcoding
439 print cmr10
.fontfamily
441 for charcode
in range(cmr10
.bc
, cmr10
.ec
+1):
442 print "%d\th=%f\tw=%f\td=%f\ti=%f" % (
444 cmr10
.getwidth(charcode
),
445 cmr10
.getheight(charcode
),
446 cmr10
.getdepth(charcode
),
447 cmr10
.getitalic(charcode
))