mirror of https://github.com/VLSIDA/OpenRAM.git
1309 lines
52 KiB
Python
1309 lines
52 KiB
Python
# -*- coding: ISO-8859-1 -*-
|
|
#
|
|
#
|
|
# Copyright (C) 2002-2006 Jörg Lehmann <joergl@users.sourceforge.net>
|
|
# Copyright (C) 2003-2004,2006,2007 Michael Schindler <m-schindler@users.sourceforge.net>
|
|
# Copyright (C) 2002-2006 André Wobst <wobsta@users.sourceforge.net>
|
|
#
|
|
# This file is part of PyX (http://pyx.sourceforge.net/).
|
|
#
|
|
# PyX is free software; you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation; either version 2 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# PyX is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with PyX; if not, write to the Free Software
|
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
|
|
|
import cStringIO, exceptions, re, struct, string, sys, warnings, math
|
|
import unit, epsfile, bbox, canvas, color, trafo, path, pykpathsea, type1font
|
|
|
|
|
|
class binfile:
|
|
|
|
def __init__(self, filename, mode="r"):
|
|
self.file = open(filename, mode)
|
|
|
|
def close(self):
|
|
self.file.close()
|
|
|
|
def tell(self):
|
|
return self.file.tell()
|
|
|
|
def eof(self):
|
|
return self.file.eof()
|
|
|
|
def read(self, bytes):
|
|
return self.file.read(bytes)
|
|
|
|
def readint(self, bytes=4, signed=0):
|
|
first = 1
|
|
result = 0
|
|
while bytes:
|
|
value = ord(self.file.read(1))
|
|
if first and signed and value > 127:
|
|
value -= 256
|
|
first = 0
|
|
result = 256 * result + value
|
|
bytes -= 1
|
|
return result
|
|
|
|
def readint32(self):
|
|
return struct.unpack(">l", self.file.read(4))[0]
|
|
|
|
def readuint32(self):
|
|
return struct.unpack(">L", self.file.read(4))[0]
|
|
|
|
def readint24(self):
|
|
# XXX: checkme
|
|
return struct.unpack(">l", "\0"+self.file.read(3))[0]
|
|
|
|
def readuint24(self):
|
|
# XXX: checkme
|
|
return struct.unpack(">L", "\0"+self.file.read(3))[0]
|
|
|
|
def readint16(self):
|
|
return struct.unpack(">h", self.file.read(2))[0]
|
|
|
|
def readuint16(self):
|
|
return struct.unpack(">H", self.file.read(2))[0]
|
|
|
|
def readchar(self):
|
|
return struct.unpack("b", self.file.read(1))[0]
|
|
|
|
def readuchar(self):
|
|
return struct.unpack("B", self.file.read(1))[0]
|
|
|
|
def readstring(self, bytes):
|
|
l = self.readuchar()
|
|
assert l <= bytes-1, "inconsistency in file: string too long"
|
|
return self.file.read(bytes-1)[:l]
|
|
|
|
class stringbinfile(binfile):
|
|
|
|
def __init__(self, s):
|
|
self.file = cStringIO.StringIO(s)
|
|
|
|
|
|
|
|
|
|
##############################################################################
|
|
# TFM file handling
|
|
##############################################################################
|
|
|
|
class TFMError(exceptions.Exception): pass
|
|
|
|
|
|
class char_info_word:
|
|
def __init__(self, word):
|
|
self.width_index = int((word & 0xFF000000L) >> 24) #make sign-safe
|
|
self.height_index = (word & 0x00F00000) >> 20
|
|
self.depth_index = (word & 0x000F0000) >> 16
|
|
self.italic_index = (word & 0x0000FC00) >> 10
|
|
self.tag = (word & 0x00000300) >> 8
|
|
self.remainder = (word & 0x000000FF)
|
|
|
|
|
|
class tfmfile:
|
|
def __init__(self, name, debug=0):
|
|
self.file = binfile(name, "rb")
|
|
|
|
#
|
|
# read pre header
|
|
#
|
|
|
|
self.lf = self.file.readint16()
|
|
self.lh = self.file.readint16()
|
|
self.bc = self.file.readint16()
|
|
self.ec = self.file.readint16()
|
|
self.nw = self.file.readint16()
|
|
self.nh = self.file.readint16()
|
|
self.nd = self.file.readint16()
|
|
self.ni = self.file.readint16()
|
|
self.nl = self.file.readint16()
|
|
self.nk = self.file.readint16()
|
|
self.ne = self.file.readint16()
|
|
self.np = self.file.readint16()
|
|
|
|
if not (self.bc-1 <= self.ec <= 255 and
|
|
self.ne <= 256 and
|
|
self.lf == 6+self.lh+(self.ec-self.bc+1)+self.nw+self.nh+self.nd
|
|
+self.ni+self.nl+self.nk+self.ne+self.np):
|
|
raise TFMError, "error in TFM pre-header"
|
|
|
|
if debug:
|
|
print "lh=%d" % self.lh
|
|
|
|
#
|
|
# read header
|
|
#
|
|
|
|
self.checksum = self.file.readint32()
|
|
self.designsize = self.file.readint32()
|
|
assert self.designsize > 0, "invald design size"
|
|
if self.lh > 2:
|
|
assert self.lh > 11, "inconsistency in TFM file: incomplete field"
|
|
self.charcoding = self.file.readstring(40)
|
|
else:
|
|
self.charcoding = None
|
|
|
|
if self.lh > 12:
|
|
assert self.lh > 16, "inconsistency in TFM file: incomplete field"
|
|
self.fontfamily = self.file.readstring(20)
|
|
else:
|
|
self.fontfamily = None
|
|
|
|
if debug:
|
|
print "(FAMILY %s)" % self.fontfamily
|
|
print "(CODINGSCHEME %s)" % self.charcoding
|
|
print "(DESINGSIZE R %f)" % 16.0*self.designsize/16777216L
|
|
|
|
if self.lh > 17:
|
|
self.sevenbitsave = self.file.readuchar()
|
|
# ignore the following two bytes
|
|
self.file.readint16()
|
|
facechar = self.file.readuchar()
|
|
# decode ugly face specification into the Knuth suggested string
|
|
if facechar < 18:
|
|
if facechar >= 12:
|
|
self.face = "E"
|
|
facechar -= 12
|
|
elif facechar >= 6:
|
|
self.face = "C"
|
|
facechar -= 6
|
|
else:
|
|
self.face = "R"
|
|
|
|
if facechar >= 4:
|
|
self.face = "L" + self.face
|
|
facechar -= 4
|
|
elif facechar >= 2:
|
|
self.face = "B" + self.face
|
|
facechar -= 2
|
|
else:
|
|
self.face = "M" + self.face
|
|
|
|
if facechar == 1:
|
|
self.face = self.face[0] + "I" + self.face[1]
|
|
else:
|
|
self.face = self.face[0] + "R" + self.face[1]
|
|
|
|
else:
|
|
self.face = None
|
|
else:
|
|
self.sevenbitsave = self.face = None
|
|
|
|
if self.lh > 18:
|
|
# just ignore the rest
|
|
print self.file.read((self.lh-18)*4)
|
|
|
|
#
|
|
# read char_info
|
|
#
|
|
|
|
self.char_info = [None]*(self.ec+1)
|
|
for charcode in range(self.bc, self.ec+1):
|
|
self.char_info[charcode] = char_info_word(self.file.readint32())
|
|
if self.char_info[charcode].width_index == 0:
|
|
# disable character if width_index is zero
|
|
self.char_info[charcode] = None
|
|
|
|
#
|
|
# read widths
|
|
#
|
|
|
|
self.width = [None for width_index in range(self.nw)]
|
|
for width_index in range(self.nw):
|
|
self.width[width_index] = self.file.readint32()
|
|
|
|
#
|
|
# read heights
|
|
#
|
|
|
|
self.height = [None for height_index in range(self.nh)]
|
|
for height_index in range(self.nh):
|
|
self.height[height_index] = self.file.readint32()
|
|
|
|
#
|
|
# read depths
|
|
#
|
|
|
|
self.depth = [None for depth_index in range(self.nd)]
|
|
for depth_index in range(self.nd):
|
|
self.depth[depth_index] = self.file.readint32()
|
|
|
|
#
|
|
# read italic
|
|
#
|
|
|
|
self.italic = [None for italic_index in range(self.ni)]
|
|
for italic_index in range(self.ni):
|
|
self.italic[italic_index] = self.file.readint32()
|
|
|
|
#
|
|
# read lig_kern
|
|
#
|
|
|
|
# XXX decode to lig_kern_command
|
|
|
|
self.lig_kern = [None for lig_kern_index in range(self.nl)]
|
|
for lig_kern_index in range(self.nl):
|
|
self.lig_kern[lig_kern_index] = self.file.readint32()
|
|
|
|
#
|
|
# read kern
|
|
#
|
|
|
|
self.kern = [None for kern_index in range(self.nk)]
|
|
for kern_index in range(self.nk):
|
|
self.kern[kern_index] = self.file.readint32()
|
|
|
|
#
|
|
# read exten
|
|
#
|
|
|
|
# XXX decode to extensible_recipe
|
|
|
|
self.exten = [None for exten_index in range(self.ne)]
|
|
for exten_index in range(self.ne):
|
|
self.exten[exten_index] = self.file.readint32()
|
|
|
|
#
|
|
# read param
|
|
#
|
|
|
|
# XXX decode
|
|
|
|
self.param = [None for param_index in range(self.np)]
|
|
for param_index in range(self.np):
|
|
self.param[param_index] = self.file.readint32()
|
|
|
|
self.file.close()
|
|
|
|
|
|
|
|
##############################################################################
|
|
# Font handling
|
|
##############################################################################
|
|
|
|
#
|
|
# PostScript font selection and output primitives
|
|
#
|
|
|
|
class UnsupportedFontFormat(Exception):
|
|
pass
|
|
|
|
class UnsupportedPSFragment(Exception):
|
|
pass
|
|
|
|
class fontmapping:
|
|
|
|
tokenpattern = re.compile(r'"(.*?)("\s+|"$|$)|(.*?)(\s+|$)')
|
|
|
|
def __init__(self, s):
|
|
""" construct font mapping from line s of font mapping file """
|
|
self.texname = self.basepsname = self.fontfile = None
|
|
|
|
# standard encoding
|
|
self.encodingfile = None
|
|
|
|
# supported postscript fragments occuring in psfonts.map
|
|
self.reencodefont = self.extendfont = self.slantfont = None
|
|
|
|
tokens = []
|
|
while len(s):
|
|
match = self.tokenpattern.match(s)
|
|
if match:
|
|
if match.groups()[0] is not None:
|
|
tokens.append('"%s"' % match.groups()[0])
|
|
else:
|
|
tokens.append(match.groups()[2])
|
|
s = s[match.end():]
|
|
else:
|
|
raise RuntimeError("Cannot tokenize string '%s'" % s)
|
|
|
|
for token in tokens:
|
|
if token.startswith("<"):
|
|
if token.startswith("<<"):
|
|
# XXX: support non-partial download here
|
|
self.fontfile = token[2:]
|
|
elif token.startswith("<["):
|
|
self.encodingfile = token[2:]
|
|
elif token.endswith(".pfa") or token.endswith(".pfb"):
|
|
self.fontfile = token[1:]
|
|
elif token.endswith(".enc"):
|
|
self.encodingfile = token[1:]
|
|
elif token.endswith(".ttf"):
|
|
raise UnsupportedFontFormat("TrueType font")
|
|
else:
|
|
raise RuntimeError("Unknown token '%s'" % token)
|
|
elif token.startswith('"'):
|
|
pscode = token[1:-1].split()
|
|
# parse standard postscript code fragments
|
|
while pscode:
|
|
try:
|
|
arg, cmd = pscode[:2]
|
|
except:
|
|
raise UnsupportedPSFragment("Unsupported Postscript fragment '%s'" % pscode)
|
|
pscode = pscode[2:]
|
|
if cmd == "ReEncodeFont":
|
|
self.reencodefont = arg
|
|
elif cmd == "ExtendFont":
|
|
self.extendfont = arg
|
|
elif cmd == "SlantFont":
|
|
self.slantfont = arg
|
|
else:
|
|
raise UnsupportedPSFragment("Unsupported Postscript fragment '%s %s'" % (arg, cmd))
|
|
else:
|
|
if self.texname is None:
|
|
self.texname = token
|
|
else:
|
|
self.basepsname = token
|
|
if self.basepsname is None:
|
|
self.basepsname = self.texname
|
|
|
|
def __str__(self):
|
|
return ("'%s' is '%s' read from '%s' encoded as '%s'" %
|
|
(self.texname, self.basepsname, self.fontfile, repr(self.encodingfile)))
|
|
|
|
# generate fontmap
|
|
|
|
def readfontmap(filenames):
|
|
""" read font map from filename (without path) """
|
|
fontmap = {}
|
|
for filename in filenames:
|
|
mappath = pykpathsea.find_file(filename, pykpathsea.kpse_fontmap_format)
|
|
# try also the oft-used registration as dvips config file
|
|
if not mappath:
|
|
mappath = pykpathsea.find_file(filename, pykpathsea.kpse_dvips_config_format)
|
|
if not mappath:
|
|
raise RuntimeError("cannot find font mapping file '%s'" % filename)
|
|
mapfile = open(mappath, "rU")
|
|
lineno = 0
|
|
for line in mapfile.readlines():
|
|
lineno += 1
|
|
line = line.rstrip()
|
|
if not (line=="" or line[0] in (" ", "%", "*", ";" , "#")):
|
|
try:
|
|
fm = fontmapping(line)
|
|
except (RuntimeError, UnsupportedPSFragment), e:
|
|
warnings.warn("Ignoring line %i in mapping file '%s': %s" % (lineno, mappath, e))
|
|
except UnsupportedFontFormat, e:
|
|
pass
|
|
else:
|
|
fontmap[fm.texname] = fm
|
|
mapfile.close()
|
|
return fontmap
|
|
|
|
|
|
class font:
|
|
|
|
def __init__(self, name, c, q, d, tfmconv, pyxconv, fontmap, debug=0):
|
|
self.name = name
|
|
self.q = q # desired size of font (fix_word) in TeX points
|
|
self.d = d # design size of font (fix_word) in TeX points
|
|
self.tfmconv = tfmconv # conversion factor from tfm units to dvi units
|
|
self.pyxconv = pyxconv # conversion factor from dvi units to PostScript points
|
|
self.fontmap = fontmap
|
|
tfmpath = pykpathsea.find_file("%s.tfm" % self.name, pykpathsea.kpse_tfm_format)
|
|
if not tfmpath:
|
|
raise TFMError("cannot find %s.tfm" % self.name)
|
|
self.tfmfile = tfmfile(tfmpath, debug)
|
|
|
|
# We only check for equality of font checksums if none of them
|
|
# is zero. The case c == 0 happend in some VF files and
|
|
# according to the VFtoVP documentation, paragraph 40, a check
|
|
# is only performed if tfmfile.checksum > 0. Anyhow, being
|
|
# more generous here seems to be reasonable
|
|
if self.tfmfile.checksum != c and self.tfmfile.checksum != 0 and c != 0:
|
|
raise DVIError("check sums do not agree: %d vs. %d" %
|
|
(self.tfmfile.checksum, c))
|
|
|
|
# Check whether the given design size matches the one defined in the tfm file
|
|
if abs(self.tfmfile.designsize - d) > 2:
|
|
raise DVIError("design sizes do not agree: %d vs. %d" % (self.tfmfile.designsize, d))
|
|
#if q < 0 or q > 134217728:
|
|
# raise DVIError("font '%s' not loaded: bad scale" % self.name)
|
|
if d < 0 or d > 134217728:
|
|
raise DVIError("font '%s' not loaded: bad design size" % self.name)
|
|
|
|
self.scale = 1.0*q/d
|
|
|
|
def fontinfo(self):
|
|
class fontinfo:
|
|
pass
|
|
|
|
# The following code is a very crude way to obtain the information
|
|
# required for the PDF font descritor. (TODO: The correct way would
|
|
# be to read the information from the AFM file.)
|
|
fontinfo = fontinfo()
|
|
try:
|
|
fontinfo.fontbbox = (0,
|
|
-self.getdepth_ds(ord("y")),
|
|
self.getwidth_ds(ord("W")),
|
|
self.getheight_ds(ord("H")))
|
|
except:
|
|
fontinfo.fontbbox = (0, -10, 100, 100)
|
|
try:
|
|
fontinfo.italicangle = -180/math.pi*math.atan(self.tfmfile.param[0]/65536.0)
|
|
except IndexError:
|
|
fontinfo.italicangle = 0
|
|
fontinfo.ascent = fontinfo.fontbbox[3]
|
|
fontinfo.descent = fontinfo.fontbbox[1]
|
|
try:
|
|
fontinfo.capheight = self.getheight_ds(ord("h"))
|
|
except:
|
|
fontinfo.capheight = 100
|
|
try:
|
|
fontinfo.vstem = self.getwidth_ds(ord("."))/3
|
|
except:
|
|
fontinfo.vstem = 5
|
|
return fontinfo
|
|
|
|
def __str__(self):
|
|
return "font %s designed at %g TeX pts used at %g TeX pts" % (self.name,
|
|
16.0*self.d/16777216L,
|
|
16.0*self.q/16777216L)
|
|
__repr__ = __str__
|
|
|
|
def getsize_pt(self):
|
|
""" return size of font in (PS) points """
|
|
# The factor 16L/16777216L=2**(-20) converts a fix_word (here self.q)
|
|
# to the corresponding float. Furthermore, we have to convert from TeX
|
|
# points to points, hence the factor 72/72.27.
|
|
return 16L*self.q/16777216L*72/72.27
|
|
|
|
def _convert_tfm_to_dvi(self, length):
|
|
# doing the integer math with long integers will lead to different roundings
|
|
# return 16*length*int(round(self.q*self.tfmconv))/16777216
|
|
|
|
# Knuth instead suggests the following algorithm based on 4 byte integer logic only
|
|
# z = int(round(self.q*self.tfmconv))
|
|
# b0, b1, b2, b3 = [ord(c) for c in struct.pack(">L", length)]
|
|
# assert b0 == 0 or b0 == 255
|
|
# shift = 4
|
|
# while z >= 8388608:
|
|
# z >>= 1
|
|
# shift -= 1
|
|
# assert shift >= 0
|
|
# result = ( ( ( ( ( b3 * z ) >> 8 ) + ( b2 * z ) ) >> 8 ) + ( b1 * z ) ) >> shift
|
|
# if b0 == 255:
|
|
# result = result - (z << (8-shift))
|
|
|
|
# however, we can simplify this using a single long integer multiplication,
|
|
# but take into account the transformation of z
|
|
z = int(round(self.q*self.tfmconv))
|
|
assert -16777216 <= length < 16777216 # -(1 << 24) <= length < (1 << 24)
|
|
assert z < 134217728 # 1 << 27
|
|
shift = 20 # 1 << 20
|
|
while z >= 8388608: # 1 << 23
|
|
z >>= 1
|
|
shift -= 1
|
|
# length*z is a long integer, but the result will be a regular integer
|
|
return int(length*long(z) >> shift)
|
|
|
|
def _convert_tfm_to_ds(self, length):
|
|
return (16*long(round(length*float(self.q)*self.tfmconv))/16777216) * self.pyxconv * 1000 / self.getsize_pt()
|
|
|
|
def _convert_tfm_to_pt(self, length):
|
|
return (16*long(round(length*float(self.q)*self.tfmconv))/16777216) * self.pyxconv
|
|
|
|
# routines returning lengths as integers in dvi units
|
|
|
|
def getwidth_dvi(self, charcode):
|
|
return self._convert_tfm_to_dvi(self.tfmfile.width[self.tfmfile.char_info[charcode].width_index])
|
|
|
|
def getheight_dvi(self, charcode):
|
|
return self._convert_tfm_to_dvi(self.tfmfile.height[self.tfmfile.char_info[charcode].height_index])
|
|
|
|
def getdepth_dvi(self, charcode):
|
|
return self._convert_tfm_to_dvi(self.tfmfile.depth[self.tfmfile.char_info[charcode].depth_index])
|
|
|
|
def getitalic_dvi(self, charcode):
|
|
return self._convert_tfm_to_dvi(self.tfmfile.italic[self.tfmfile.char_info[charcode].italic_index])
|
|
|
|
# routines returning lengths as integers in design size (AFM) units
|
|
|
|
def getwidth_ds(self, charcode):
|
|
return self._convert_tfm_to_ds(self.tfmfile.width[self.tfmfile.char_info[charcode].width_index])
|
|
|
|
def getheight_ds(self, charcode):
|
|
return self._convert_tfm_to_ds(self.tfmfile.height[self.tfmfile.char_info[charcode].height_index])
|
|
|
|
def getdepth_ds(self, charcode):
|
|
return self._convert_tfm_to_ds(self.tfmfile.depth[self.tfmfile.char_info[charcode].depth_index])
|
|
|
|
def getitalic_ds(self, charcode):
|
|
return self._convert_tfm_to_ds(self.tfmfile.italic[self.tfmfile.char_info[charcode].italic_index])
|
|
|
|
# routines returning lengths as floats in PostScript points
|
|
|
|
def getwidth_pt(self, charcode):
|
|
return self._convert_tfm_to_pt(self.tfmfile.width[self.tfmfile.char_info[charcode].width_index])
|
|
|
|
def getheight_pt(self, charcode):
|
|
return self._convert_tfm_to_pt(self.tfmfile.height[self.tfmfile.char_info[charcode].height_index])
|
|
|
|
def getdepth_pt(self, charcode):
|
|
return self._convert_tfm_to_pt(self.tfmfile.depth[self.tfmfile.char_info[charcode].depth_index])
|
|
|
|
def getitalic_pt(self, charcode):
|
|
return self._convert_tfm_to_pt(self.tfmfile.italic[self.tfmfile.char_info[charcode].italic_index])
|
|
|
|
|
|
class virtualfont(font):
|
|
def __init__(self, name, c, q, d, tfmconv, pyxconv, fontmap, debug=0):
|
|
fontpath = pykpathsea.find_file(name, pykpathsea.kpse_vf_format)
|
|
if fontpath is None or not len(fontpath):
|
|
raise RuntimeError
|
|
font.__init__(self, name, c, q, d, tfmconv, pyxconv, fontmap, debug)
|
|
self.vffile = vffile(fontpath, self.scale, tfmconv, pyxconv, fontmap, debug > 1)
|
|
|
|
def getfonts(self):
|
|
""" return fonts used in virtual font itself """
|
|
return self.vffile.getfonts()
|
|
|
|
def getchar(self, cc):
|
|
""" return dvi chunk corresponding to char code cc """
|
|
return self.vffile.getchar(cc)
|
|
|
|
|
|
##############################################################################
|
|
# DVI file handling
|
|
##############################################################################
|
|
|
|
_DVI_CHARMIN = 0 # typeset a character and move right (range min)
|
|
_DVI_CHARMAX = 127 # typeset a character and move right (range max)
|
|
_DVI_SET1234 = 128 # typeset a character and move right
|
|
_DVI_SETRULE = 132 # typeset a rule and move right
|
|
_DVI_PUT1234 = 133 # typeset a character
|
|
_DVI_PUTRULE = 137 # typeset a rule
|
|
_DVI_NOP = 138 # no operation
|
|
_DVI_BOP = 139 # beginning of page
|
|
_DVI_EOP = 140 # ending of page
|
|
_DVI_PUSH = 141 # save the current positions (h, v, w, x, y, z)
|
|
_DVI_POP = 142 # restore positions (h, v, w, x, y, z)
|
|
_DVI_RIGHT1234 = 143 # move right
|
|
_DVI_W0 = 147 # move right by w
|
|
_DVI_W1234 = 148 # move right and set w
|
|
_DVI_X0 = 152 # move right by x
|
|
_DVI_X1234 = 153 # move right and set x
|
|
_DVI_DOWN1234 = 157 # move down
|
|
_DVI_Y0 = 161 # move down by y
|
|
_DVI_Y1234 = 162 # move down and set y
|
|
_DVI_Z0 = 166 # move down by z
|
|
_DVI_Z1234 = 167 # move down and set z
|
|
_DVI_FNTNUMMIN = 171 # set current font (range min)
|
|
_DVI_FNTNUMMAX = 234 # set current font (range max)
|
|
_DVI_FNT1234 = 235 # set current font
|
|
_DVI_SPECIAL1234 = 239 # special (dvi extention)
|
|
_DVI_FNTDEF1234 = 243 # define the meaning of a font number
|
|
_DVI_PRE = 247 # preamble
|
|
_DVI_POST = 248 # postamble beginning
|
|
_DVI_POSTPOST = 249 # postamble ending
|
|
|
|
_DVI_VERSION = 2 # dvi version
|
|
|
|
# position variable indices
|
|
_POS_H = 0
|
|
_POS_V = 1
|
|
_POS_W = 2
|
|
_POS_X = 3
|
|
_POS_Y = 4
|
|
_POS_Z = 5
|
|
|
|
# reader states
|
|
_READ_PRE = 1
|
|
_READ_NOPAGE = 2
|
|
_READ_PAGE = 3
|
|
_READ_POST = 4 # XXX not used
|
|
_READ_POSTPOST = 5 # XXX not used
|
|
_READ_DONE = 6
|
|
|
|
|
|
class DVIError(exceptions.Exception): pass
|
|
|
|
# save and restore colors
|
|
|
|
class _savecolor(canvas.canvasitem):
|
|
def processPS(self, file, writer, context, registry, bbox):
|
|
file.write("currentcolor currentcolorspace\n")
|
|
|
|
def processPDF(self, file, writer, context, registry, bbox):
|
|
file.write("q\n")
|
|
|
|
|
|
class _restorecolor(canvas.canvasitem):
|
|
def processPS(self, file, writer, context, registry, bbox):
|
|
file.write("setcolorspace setcolor\n")
|
|
|
|
def processPDF(self, file, writer, context, registry, bbox):
|
|
file.write("Q\n")
|
|
|
|
class _savetrafo(canvas.canvasitem):
|
|
def processPS(self, file, writer, context, registry, bbox):
|
|
file.write("matrix currentmatrix\n")
|
|
|
|
def processPDF(self, file, writer, context, registry, bbox):
|
|
file.write("q\n")
|
|
|
|
|
|
class _restoretrafo(canvas.canvasitem):
|
|
def processPS(self, file, writer, context, registry, bbox):
|
|
file.write("setmatrix\n")
|
|
|
|
def processPDF(self, file, writer, context, registry, bbox):
|
|
file.write("Q\n")
|
|
|
|
|
|
class dvifile:
|
|
|
|
def __init__(self, filename, fontmap, debug=0, debugfile=sys.stdout):
|
|
""" opens the dvi file and reads the preamble """
|
|
self.filename = filename
|
|
self.fontmap = fontmap
|
|
self.debug = debug
|
|
self.debugfile = debugfile
|
|
self.debugstack = []
|
|
|
|
self.fonts = {}
|
|
self.activefont = None
|
|
|
|
# stack of fonts and fontscale currently used (used for VFs)
|
|
self.fontstack = []
|
|
self.stack = []
|
|
|
|
# pointer to currently active page
|
|
self.actpage = None
|
|
|
|
# stack for self.file, self.fonts and self.stack, needed for VF inclusion
|
|
self.statestack = []
|
|
|
|
self.file = binfile(self.filename, "rb")
|
|
|
|
# currently read byte in file (for debugging output)
|
|
self.filepos = None
|
|
|
|
self._read_pre()
|
|
|
|
# helper routines
|
|
|
|
def flushtext(self):
|
|
""" finish currently active text object """
|
|
if self.debug and self.activetext:
|
|
self.debugfile.write("[%s]\n" % "".join([chr(char) for char in self.activetext.chars]))
|
|
self.activetext = None
|
|
|
|
def putrule(self, height, width, advancepos=1):
|
|
self.flushtext()
|
|
x1 = self.pos[_POS_H] * self.pyxconv
|
|
y1 = -self.pos[_POS_V] * self.pyxconv
|
|
w = width * self.pyxconv
|
|
h = height * self.pyxconv
|
|
|
|
if height > 0 and width > 0:
|
|
if self.debug:
|
|
self.debugfile.write("%d: %srule height %d, width %d (???x??? pixels)\n" %
|
|
(self.filepos, advancepos and "set" or "put", height, width))
|
|
self.actpage.fill(path.rect_pt(x1, y1, w, h))
|
|
else:
|
|
if self.debug:
|
|
self.debugfile.write("%d: %srule height %d, width %d (invisible)\n" %
|
|
(self.filepos, advancepos and "set" or "put", height, width))
|
|
|
|
if advancepos:
|
|
if self.debug:
|
|
self.debugfile.write(" h:=%d+%d=%d, hh:=???\n" %
|
|
(self.pos[_POS_H], width, self.pos[_POS_H]+width))
|
|
self.pos[_POS_H] += width
|
|
|
|
def putchar(self, char, advancepos=1, id1234=0):
|
|
dx = advancepos and self.activefont.getwidth_dvi(char) or 0
|
|
|
|
if self.debug:
|
|
self.debugfile.write("%d: %s%s%d h:=%d+%d=%d, hh:=???\n" %
|
|
(self.filepos,
|
|
advancepos and "set" or "put",
|
|
id1234 and "%i " % id1234 or "char",
|
|
char,
|
|
self.pos[_POS_H], dx, self.pos[_POS_H]+dx))
|
|
|
|
if isinstance(self.activefont, virtualfont):
|
|
# virtual font handling
|
|
afterpos = list(self.pos)
|
|
afterpos[_POS_H] += dx
|
|
self._push_dvistring(self.activefont.getchar(char), self.activefont.getfonts(), afterpos,
|
|
self.activefont.getsize_pt())
|
|
else:
|
|
if self.activetext is None:
|
|
if not self.fontmap.has_key(self.activefont.name):
|
|
raise RuntimeError("missing font information for '%s'; check fontmapping file(s)" % self.activefont.name)
|
|
fontmapinfo = self.fontmap[self.activefont.name]
|
|
|
|
encodingname = fontmapinfo.reencodefont
|
|
if encodingname is not None:
|
|
encodingfilename = pykpathsea.find_file(fontmapinfo.encodingfile, pykpathsea.kpse_tex_ps_header_format)
|
|
if not encodingfilename:
|
|
raise RuntimeError("cannot find font encoding file %s" % fontmapinfo.encodingfile)
|
|
fontencoding = type1font.encoding(encodingname, encodingfilename)
|
|
else:
|
|
fontencoding = None
|
|
|
|
fontbasefontname = fontmapinfo.basepsname
|
|
if fontmapinfo.fontfile is not None:
|
|
fontfilename = pykpathsea.find_file(fontmapinfo.fontfile, pykpathsea.kpse_type1_format)
|
|
if not fontfilename:
|
|
raise RuntimeError("cannot find type 1 font %s" % fontmapinfo.fontfile)
|
|
else:
|
|
fontfilename = None
|
|
|
|
fontslant = fontmapinfo.slantfont
|
|
if fontslant is not None:
|
|
fontslant = float(fontslant)
|
|
|
|
# XXX we currently misuse use self.activefont as metric
|
|
font = type1font.font(fontbasefontname, fontfilename, fontencoding, fontslant, self.activefont)
|
|
|
|
self.activetext = type1font.text_pt(self.pos[_POS_H] * self.pyxconv, -self.pos[_POS_V] * self.pyxconv, font)
|
|
self.actpage.insert(self.activetext)
|
|
self.activetext.addchar(char)
|
|
self.pos[_POS_H] += dx
|
|
|
|
if not advancepos:
|
|
self.flushtext()
|
|
|
|
def usefont(self, fontnum, id1234=0):
|
|
self.flushtext()
|
|
self.activefont = self.fonts[fontnum]
|
|
if self.debug:
|
|
self.debugfile.write("%d: fnt%s%i current font is %s\n" %
|
|
(self.filepos,
|
|
id1234 and "%i " % id1234 or "num",
|
|
fontnum,
|
|
self.fonts[fontnum].name))
|
|
|
|
|
|
def definefont(self, cmdnr, num, c, q, d, fontname):
|
|
# cmdnr: type of fontdef command (only used for debugging output)
|
|
# c: checksum
|
|
# q: scaling factor (fix_word)
|
|
# Note that q is actually s in large parts of the documentation.
|
|
# d: design size (fix_word)
|
|
|
|
try:
|
|
afont = virtualfont(fontname, c, q/self.tfmconv, d/self.tfmconv, self.tfmconv, self.pyxconv, self.fontmap, self.debug > 1)
|
|
except (TypeError, RuntimeError):
|
|
afont = font(fontname, c, q/self.tfmconv, d/self.tfmconv, self.tfmconv, self.pyxconv, self.fontmap, self.debug > 1)
|
|
|
|
self.fonts[num] = afont
|
|
|
|
if self.debug:
|
|
self.debugfile.write("%d: fntdef%d %i: %s\n" % (self.filepos, cmdnr, num, fontname))
|
|
|
|
# scale = round((1000.0*self.conv*q)/(self.trueconv*d))
|
|
# m = 1.0*q/d
|
|
# scalestring = scale!=1000 and " scaled %d" % scale or ""
|
|
# print ("Font %i: %s%s---loaded at size %d DVI units" %
|
|
# (num, fontname, scalestring, q))
|
|
# if scale!=1000:
|
|
# print " (this font is magnified %d%%)" % round(scale/10)
|
|
|
|
def special(self, s):
|
|
x = self.pos[_POS_H] * self.pyxconv
|
|
y = -self.pos[_POS_V] * self.pyxconv
|
|
if self.debug:
|
|
self.debugfile.write("%d: xxx '%s'\n" % (self.filepos, s))
|
|
if not s.startswith("PyX:"):
|
|
warnings.warn("ignoring special '%s'" % s)
|
|
return
|
|
|
|
# it is in general not safe to continue using the currently active font because
|
|
# the specials may involve some gsave/grestore operations
|
|
self.flushtext()
|
|
|
|
command, args = s[4:].split()[0], s[4:].split()[1:]
|
|
if command == "color_begin":
|
|
if args[0] == "cmyk":
|
|
c = color.cmyk(float(args[1]), float(args[2]), float(args[3]), float(args[4]))
|
|
elif args[0] == "gray":
|
|
c = color.gray(float(args[1]))
|
|
elif args[0] == "hsb":
|
|
c = color.hsb(float(args[1]), float(args[2]), float(args[3]))
|
|
elif args[0] == "rgb":
|
|
c = color.rgb(float(args[1]), float(args[2]), float(args[3]))
|
|
elif args[0] == "RGB":
|
|
c = color.rgb(int(args[1])/255.0, int(args[2])/255.0, int(args[3])/255.0)
|
|
elif args[0] == "texnamed":
|
|
try:
|
|
c = getattr(color.cmyk, args[1])
|
|
except AttributeError:
|
|
raise RuntimeError("unknown TeX color '%s', aborting" % args[1])
|
|
elif args[0] == "pyxcolor":
|
|
# pyx.color.cmyk.PineGreen or
|
|
# pyx.color.cmyk(0,0,0,0.0)
|
|
pat = re.compile(r"(pyx\.)?(color\.)?(?P<model>(cmyk)|(rgb)|(grey)|(gray)|(hsb))[\.]?(?P<arg>.*)")
|
|
sd = pat.match(" ".join(args[1:]))
|
|
if sd:
|
|
sd = sd.groupdict()
|
|
if sd["arg"][0] == "(":
|
|
numpat = re.compile(r"[+-]?((\d+\.\d*)|(\d*\.\d+)|(\d+))([eE][+-]\d+)?")
|
|
arg = tuple([float(x[0]) for x in numpat.findall(sd["arg"])])
|
|
try:
|
|
c = getattr(color, sd["model"])(*arg)
|
|
except TypeError or AttributeError:
|
|
raise RuntimeError("cannot access PyX color '%s' in TeX, aborting" % " ".join(args[1:]))
|
|
else:
|
|
try:
|
|
c = getattr(getattr(color, sd["model"]), sd["arg"])
|
|
except AttributeError:
|
|
raise RuntimeError("cannot access PyX color '%s' in TeX, aborting" % " ".join(args[1:]))
|
|
else:
|
|
raise RuntimeError("cannot access PyX color '%s' in TeX, aborting" % " ".join(args[1:]))
|
|
else:
|
|
raise RuntimeError("color model '%s' cannot be handled by PyX, aborting" % args[0])
|
|
self.actpage.insert(_savecolor())
|
|
self.actpage.insert(c)
|
|
elif command == "color_end":
|
|
self.actpage.insert(_restorecolor())
|
|
elif command == "rotate_begin":
|
|
self.actpage.insert(_savetrafo())
|
|
self.actpage.insert(trafo.rotate_pt(float(args[0]), x, y))
|
|
elif command == "rotate_end":
|
|
self.actpage.insert(_restoretrafo())
|
|
elif command == "scale_begin":
|
|
self.actpage.insert(_savetrafo())
|
|
self.actpage.insert(trafo.scale_pt(float(args[0]), float(args[1]), x, y))
|
|
elif command == "scale_end":
|
|
self.actpage.insert(_restoretrafo())
|
|
elif command == "epsinclude":
|
|
# parse arguments
|
|
argdict = {}
|
|
for arg in args:
|
|
name, value = arg.split("=")
|
|
argdict[name] = value
|
|
|
|
# construct kwargs for epsfile constructor
|
|
epskwargs = {}
|
|
epskwargs["filename"] = argdict["file"]
|
|
epskwargs["bbox"] = bbox.bbox_pt(float(argdict["llx"]), float(argdict["lly"]),
|
|
float(argdict["urx"]), float(argdict["ury"]))
|
|
if argdict.has_key("width"):
|
|
epskwargs["width"] = float(argdict["width"]) * unit.t_pt
|
|
if argdict.has_key("height"):
|
|
epskwargs["height"] = float(argdict["height"]) * unit.t_pt
|
|
if argdict.has_key("clip"):
|
|
epskwargs["clip"] = int(argdict["clip"])
|
|
self.actpage.insert(epsfile.epsfile(x * unit.t_pt, y * unit.t_pt, **epskwargs))
|
|
elif command == "marker":
|
|
if len(args) != 1:
|
|
raise RuntimeError("marker contains spaces")
|
|
for c in args[0]:
|
|
if c not in string.digits + string.letters + "@":
|
|
raise RuntimeError("marker contains invalid characters")
|
|
if self.actpage.markers.has_key(args[0]):
|
|
raise RuntimeError("marker name occurred several times")
|
|
self.actpage.markers[args[0]] = x * unit.t_pt, y * unit.t_pt
|
|
else:
|
|
raise RuntimeError("unknown PyX special '%s', aborting" % command)
|
|
|
|
# routines for pushing and popping different dvi chunks on the reader
|
|
|
|
def _push_dvistring(self, dvi, fonts, afterpos, fontsize):
|
|
""" push dvi string with defined fonts on top of reader
|
|
stack. Every positions gets scaled relatively by the factor
|
|
scale. After the interpreting of the dvi chunk has been finished,
|
|
continue with self.pos=afterpos. The designsize of the virtual
|
|
font is passed as a fix_word
|
|
|
|
"""
|
|
|
|
#if self.debug:
|
|
# self.debugfile.write("executing new dvi chunk\n")
|
|
self.debugstack.append(self.debug)
|
|
self.debug = 0
|
|
|
|
self.statestack.append((self.file, self.fonts, self.activefont, afterpos, self.stack, self.pyxconv, self.tfmconv))
|
|
|
|
# units in vf files are relative to the size of the font and given as fix_words
|
|
# which can be converted to floats by diving by 2**20
|
|
oldpyxconv = self.pyxconv
|
|
self.pyxconv = fontsize/2**20
|
|
rescale = self.pyxconv/oldpyxconv
|
|
|
|
self.file = stringbinfile(dvi)
|
|
self.fonts = fonts
|
|
self.stack = []
|
|
self.filepos = 0
|
|
|
|
# rescale self.pos in order to be consistent with the new scaling
|
|
self.pos = map(lambda x, rescale=rescale:1.0*x/rescale, self.pos)
|
|
|
|
# since tfmconv converts from tfm units to dvi units, rescale it as well
|
|
self.tfmconv /= rescale
|
|
|
|
self.usefont(0)
|
|
|
|
def _pop_dvistring(self):
|
|
self.flushtext()
|
|
#if self.debug:
|
|
# self.debugfile.write("finished executing dvi chunk\n")
|
|
self.debug = self.debugstack.pop()
|
|
|
|
self.file.close()
|
|
self.file, self.fonts, self.activefont, self.pos, self.stack, self.pyxconv, self.tfmconv = self.statestack.pop()
|
|
|
|
# routines corresponding to the different reader states of the dvi maschine
|
|
|
|
def _read_pre(self):
|
|
afile = self.file
|
|
while 1:
|
|
self.filepos = afile.tell()
|
|
cmd = afile.readuchar()
|
|
if cmd == _DVI_NOP:
|
|
pass
|
|
elif cmd == _DVI_PRE:
|
|
if afile.readuchar() != _DVI_VERSION: raise DVIError
|
|
num = afile.readuint32()
|
|
den = afile.readuint32()
|
|
self.mag = afile.readuint32()
|
|
|
|
# For the interpretation of the lengths in dvi and tfm files,
|
|
# three conversion factors are relevant:
|
|
# - self.tfmconv: tfm units -> dvi units
|
|
# - self.pyxconv: dvi units -> (PostScript) points
|
|
# - self.conv: dvi units -> pixels
|
|
self.tfmconv = (25400000.0/num)*(den/473628672.0)/16.0
|
|
|
|
# calculate conv as described in the DVIType docu using
|
|
# a given resolution in dpi
|
|
self.resolution = 300.0
|
|
self.conv = (num/254000.0)*(self.resolution/den)
|
|
|
|
# self.pyxconv is the conversion factor from the dvi units
|
|
# to (PostScript) points. It consists of
|
|
# - self.mag/1000.0: magstep scaling
|
|
# - self.conv: conversion from dvi units to pixels
|
|
# - 1/self.resolution: conversion from pixels to inch
|
|
# - 72 : conversion from inch to points
|
|
self.pyxconv = self.mag/1000.0*self.conv/self.resolution*72
|
|
|
|
comment = afile.read(afile.readuchar())
|
|
return
|
|
else:
|
|
raise DVIError
|
|
|
|
def readpage(self, pageid=None):
|
|
""" reads a page from the dvi file
|
|
|
|
This routine reads a page from the dvi file which is
|
|
returned as a canvas. When there is no page left in the
|
|
dvifile, None is returned and the file is closed properly."""
|
|
|
|
while 1:
|
|
self.filepos = self.file.tell()
|
|
cmd = self.file.readuchar()
|
|
if cmd == _DVI_NOP:
|
|
pass
|
|
elif cmd == _DVI_BOP:
|
|
ispageid = [self.file.readuint32() for i in range(10)]
|
|
if pageid is not None and ispageid != pageid:
|
|
raise DVIError("invalid pageid")
|
|
if self.debug:
|
|
self.debugfile.write("%d: beginning of page %i\n" % (self.filepos, ispageid[0]))
|
|
self.file.readuint32()
|
|
break
|
|
elif cmd == _DVI_POST:
|
|
self.file.close()
|
|
return None # nothing left
|
|
else:
|
|
raise DVIError
|
|
|
|
self.actpage = canvas.canvas()
|
|
self.actpage.markers = {}
|
|
self.pos = [0, 0, 0, 0, 0, 0]
|
|
|
|
# currently active output: text instance currently used
|
|
self.activetext = None
|
|
|
|
while 1:
|
|
afile = self.file
|
|
self.filepos = afile.tell()
|
|
try:
|
|
cmd = afile.readuchar()
|
|
except struct.error:
|
|
# we most probably (if the dvi file is not corrupt) hit the end of a dvi chunk,
|
|
# so we have to continue with the rest of the dvi file
|
|
self._pop_dvistring()
|
|
continue
|
|
if cmd == _DVI_NOP:
|
|
pass
|
|
if cmd >= _DVI_CHARMIN and cmd <= _DVI_CHARMAX:
|
|
self.putchar(cmd)
|
|
elif cmd >= _DVI_SET1234 and cmd < _DVI_SET1234 + 4:
|
|
self.putchar(afile.readint(cmd - _DVI_SET1234 + 1), id1234=cmd-_DVI_SET1234+1)
|
|
elif cmd == _DVI_SETRULE:
|
|
self.putrule(afile.readint32(), afile.readint32())
|
|
elif cmd >= _DVI_PUT1234 and cmd < _DVI_PUT1234 + 4:
|
|
self.putchar(afile.readint(cmd - _DVI_PUT1234 + 1), advancepos=0, id1234=cmd-_DVI_SET1234+1)
|
|
elif cmd == _DVI_PUTRULE:
|
|
self.putrule(afile.readint32(), afile.readint32(), 0)
|
|
elif cmd == _DVI_EOP:
|
|
self.flushtext()
|
|
if self.debug:
|
|
self.debugfile.write("%d: eop\n \n" % self.filepos)
|
|
return self.actpage
|
|
elif cmd == _DVI_PUSH:
|
|
self.stack.append(list(self.pos))
|
|
if self.debug:
|
|
self.debugfile.write("%s: push\n"
|
|
"level %d:(h=%d,v=%d,w=%d,x=%d,y=%d,z=%d,hh=???,vv=???)\n" %
|
|
((self.filepos, len(self.stack)-1) + tuple(self.pos)))
|
|
elif cmd == _DVI_POP:
|
|
self.flushtext()
|
|
self.pos = self.stack.pop()
|
|
if self.debug:
|
|
self.debugfile.write("%s: pop\n"
|
|
"level %d:(h=%d,v=%d,w=%d,x=%d,y=%d,z=%d,hh=???,vv=???)\n" %
|
|
((self.filepos, len(self.stack)) + tuple(self.pos)))
|
|
elif cmd >= _DVI_RIGHT1234 and cmd < _DVI_RIGHT1234 + 4:
|
|
self.flushtext()
|
|
dh = afile.readint(cmd - _DVI_RIGHT1234 + 1, 1)
|
|
if self.debug:
|
|
self.debugfile.write("%d: right%d %d h:=%d%+d=%d, hh:=???\n" %
|
|
(self.filepos,
|
|
cmd - _DVI_RIGHT1234 + 1,
|
|
dh,
|
|
self.pos[_POS_H],
|
|
dh,
|
|
self.pos[_POS_H]+dh))
|
|
self.pos[_POS_H] += dh
|
|
elif cmd == _DVI_W0:
|
|
self.flushtext()
|
|
if self.debug:
|
|
self.debugfile.write("%d: w0 %d h:=%d%+d=%d, hh:=???\n" %
|
|
(self.filepos,
|
|
self.pos[_POS_W],
|
|
self.pos[_POS_H],
|
|
self.pos[_POS_W],
|
|
self.pos[_POS_H]+self.pos[_POS_W]))
|
|
self.pos[_POS_H] += self.pos[_POS_W]
|
|
elif cmd >= _DVI_W1234 and cmd < _DVI_W1234 + 4:
|
|
self.flushtext()
|
|
self.pos[_POS_W] = afile.readint(cmd - _DVI_W1234 + 1, 1)
|
|
if self.debug:
|
|
self.debugfile.write("%d: w%d %d h:=%d%+d=%d, hh:=???\n" %
|
|
(self.filepos,
|
|
cmd - _DVI_W1234 + 1,
|
|
self.pos[_POS_W],
|
|
self.pos[_POS_H],
|
|
self.pos[_POS_W],
|
|
self.pos[_POS_H]+self.pos[_POS_W]))
|
|
self.pos[_POS_H] += self.pos[_POS_W]
|
|
elif cmd == _DVI_X0:
|
|
self.flushtext()
|
|
if self.debug:
|
|
self.debugfile.write("%d: x0 %d h:=%d%+d=%d, hh:=???\n" %
|
|
(self.filepos,
|
|
self.pos[_POS_X],
|
|
self.pos[_POS_H],
|
|
self.pos[_POS_X],
|
|
self.pos[_POS_H]+self.pos[_POS_X]))
|
|
self.pos[_POS_H] += self.pos[_POS_X]
|
|
elif cmd >= _DVI_X1234 and cmd < _DVI_X1234 + 4:
|
|
self.flushtext()
|
|
self.pos[_POS_X] = afile.readint(cmd - _DVI_X1234 + 1, 1)
|
|
if self.debug:
|
|
self.debugfile.write("%d: x%d %d h:=%d%+d=%d, hh:=???\n" %
|
|
(self.filepos,
|
|
cmd - _DVI_X1234 + 1,
|
|
self.pos[_POS_X],
|
|
self.pos[_POS_H],
|
|
self.pos[_POS_X],
|
|
self.pos[_POS_H]+self.pos[_POS_X]))
|
|
self.pos[_POS_H] += self.pos[_POS_X]
|
|
elif cmd >= _DVI_DOWN1234 and cmd < _DVI_DOWN1234 + 4:
|
|
self.flushtext()
|
|
dv = afile.readint(cmd - _DVI_DOWN1234 + 1, 1)
|
|
if self.debug:
|
|
self.debugfile.write("%d: down%d %d v:=%d%+d=%d, vv:=???\n" %
|
|
(self.filepos,
|
|
cmd - _DVI_DOWN1234 + 1,
|
|
dv,
|
|
self.pos[_POS_V],
|
|
dv,
|
|
self.pos[_POS_V]+dv))
|
|
self.pos[_POS_V] += dv
|
|
elif cmd == _DVI_Y0:
|
|
self.flushtext()
|
|
if self.debug:
|
|
self.debugfile.write("%d: y0 %d v:=%d%+d=%d, vv:=???\n" %
|
|
(self.filepos,
|
|
self.pos[_POS_Y],
|
|
self.pos[_POS_V],
|
|
self.pos[_POS_Y],
|
|
self.pos[_POS_V]+self.pos[_POS_Y]))
|
|
self.pos[_POS_V] += self.pos[_POS_Y]
|
|
elif cmd >= _DVI_Y1234 and cmd < _DVI_Y1234 + 4:
|
|
self.flushtext()
|
|
self.pos[_POS_Y] = afile.readint(cmd - _DVI_Y1234 + 1, 1)
|
|
if self.debug:
|
|
self.debugfile.write("%d: y%d %d v:=%d%+d=%d, vv:=???\n" %
|
|
(self.filepos,
|
|
cmd - _DVI_Y1234 + 1,
|
|
self.pos[_POS_Y],
|
|
self.pos[_POS_V],
|
|
self.pos[_POS_Y],
|
|
self.pos[_POS_V]+self.pos[_POS_Y]))
|
|
self.pos[_POS_V] += self.pos[_POS_Y]
|
|
elif cmd == _DVI_Z0:
|
|
self.flushtext()
|
|
if self.debug:
|
|
self.debugfile.write("%d: z0 %d v:=%d%+d=%d, vv:=???\n" %
|
|
(self.filepos,
|
|
self.pos[_POS_Z],
|
|
self.pos[_POS_V],
|
|
self.pos[_POS_Z],
|
|
self.pos[_POS_V]+self.pos[_POS_Z]))
|
|
self.pos[_POS_V] += self.pos[_POS_Z]
|
|
elif cmd >= _DVI_Z1234 and cmd < _DVI_Z1234 + 4:
|
|
self.flushtext()
|
|
self.pos[_POS_Z] = afile.readint(cmd - _DVI_Z1234 + 1, 1)
|
|
if self.debug:
|
|
self.debugfile.write("%d: z%d %d v:=%d%+d=%d, vv:=???\n" %
|
|
(self.filepos,
|
|
cmd - _DVI_Z1234 + 1,
|
|
self.pos[_POS_Z],
|
|
self.pos[_POS_V],
|
|
self.pos[_POS_Z],
|
|
self.pos[_POS_V]+self.pos[_POS_Z]))
|
|
self.pos[_POS_V] += self.pos[_POS_Z]
|
|
elif cmd >= _DVI_FNTNUMMIN and cmd <= _DVI_FNTNUMMAX:
|
|
self.usefont(cmd - _DVI_FNTNUMMIN, 0)
|
|
elif cmd >= _DVI_FNT1234 and cmd < _DVI_FNT1234 + 4:
|
|
# note that according to the DVI docs, for four byte font numbers,
|
|
# the font number is signed. Don't ask why!
|
|
fntnum = afile.readint(cmd - _DVI_FNT1234 + 1, cmd == _DVI_FNT1234 + 3)
|
|
self.usefont(fntnum, id1234=cmd-_DVI_FNT1234+1)
|
|
elif cmd >= _DVI_SPECIAL1234 and cmd < _DVI_SPECIAL1234 + 4:
|
|
self.special(afile.read(afile.readint(cmd - _DVI_SPECIAL1234 + 1)))
|
|
elif cmd >= _DVI_FNTDEF1234 and cmd < _DVI_FNTDEF1234 + 4:
|
|
if cmd == _DVI_FNTDEF1234:
|
|
num = afile.readuchar()
|
|
elif cmd == _DVI_FNTDEF1234+1:
|
|
num = afile.readuint16()
|
|
elif cmd == _DVI_FNTDEF1234+2:
|
|
num = afile.readuint24()
|
|
elif cmd == _DVI_FNTDEF1234+3:
|
|
# Cool, here we have according to docu a signed int. Why?
|
|
num = afile.readint32()
|
|
self.definefont(cmd-_DVI_FNTDEF1234+1,
|
|
num,
|
|
afile.readint32(),
|
|
afile.readint32(),
|
|
afile.readint32(),
|
|
afile.read(afile.readuchar()+afile.readuchar()))
|
|
else:
|
|
raise DVIError
|
|
|
|
|
|
##############################################################################
|
|
# VF file handling
|
|
##############################################################################
|
|
|
|
_VF_LONG_CHAR = 242 # character packet (long version)
|
|
_VF_FNTDEF1234 = _DVI_FNTDEF1234 # font definition
|
|
_VF_PRE = _DVI_PRE # preamble
|
|
_VF_POST = _DVI_POST # postamble
|
|
|
|
_VF_ID = 202 # VF id byte
|
|
|
|
class VFError(exceptions.Exception): pass
|
|
|
|
class vffile:
|
|
def __init__(self, filename, scale, tfmconv, pyxconv, fontmap, debug=0):
|
|
self.filename = filename
|
|
self.scale = scale
|
|
self.tfmconv = tfmconv
|
|
self.pyxconv = pyxconv
|
|
self.fontmap = fontmap
|
|
self.debug = debug
|
|
self.fonts = {} # used fonts
|
|
self.widths = {} # widths of defined chars
|
|
self.chardefs = {} # dvi chunks for defined chars
|
|
|
|
afile = binfile(self.filename, "rb")
|
|
|
|
cmd = afile.readuchar()
|
|
if cmd == _VF_PRE:
|
|
if afile.readuchar() != _VF_ID: raise VFError
|
|
comment = afile.read(afile.readuchar())
|
|
self.cs = afile.readuint32()
|
|
self.ds = afile.readuint32()
|
|
else:
|
|
raise VFError
|
|
|
|
while 1:
|
|
cmd = afile.readuchar()
|
|
if cmd >= _VF_FNTDEF1234 and cmd < _VF_FNTDEF1234 + 4:
|
|
# font definition
|
|
if cmd == _VF_FNTDEF1234:
|
|
num = afile.readuchar()
|
|
elif cmd == _VF_FNTDEF1234+1:
|
|
num = afile.readuint16()
|
|
elif cmd == _VF_FNTDEF1234+2:
|
|
num = afile.readuint24()
|
|
elif cmd == _VF_FNTDEF1234+3:
|
|
num = afile.readint32()
|
|
c = afile.readint32()
|
|
s = afile.readint32() # relative scaling used for font (fix_word)
|
|
d = afile.readint32() # design size of font
|
|
fontname = afile.read(afile.readuchar()+afile.readuchar())
|
|
|
|
# rescaled size of font: s is relative to the scaling
|
|
# of the virtual font itself. Note that realscale has
|
|
# to be a fix_word (like s)
|
|
# XXX: check rounding
|
|
reals = int(round(self.scale * (16*self.ds/16777216L) * s))
|
|
|
|
# print ("defining font %s -- VF scale: %g, VF design size: %d, relative font size: %d => real size: %d" %
|
|
# (fontname, self.scale, self.ds, s, reals)
|
|
# )
|
|
|
|
# XXX allow for virtual fonts here too
|
|
self.fonts[num] = font(fontname, c, reals, d, self.tfmconv, self.pyxconv, self.fontmap, self.debug > 1)
|
|
elif cmd == _VF_LONG_CHAR:
|
|
# character packet (long form)
|
|
pl = afile.readuint32() # packet length
|
|
cc = afile.readuint32() # char code (assumed unsigned, but anyhow only 0 <= cc < 255 is actually used)
|
|
tfm = afile.readuint24() # character width
|
|
dvi = afile.read(pl) # dvi code of character
|
|
self.widths[cc] = tfm
|
|
self.chardefs[cc] = dvi
|
|
elif cmd < _VF_LONG_CHAR:
|
|
# character packet (short form)
|
|
cc = afile.readuchar() # char code
|
|
tfm = afile.readuint24() # character width
|
|
dvi = afile.read(cmd)
|
|
self.widths[cc] = tfm
|
|
self.chardefs[cc] = dvi
|
|
elif cmd == _VF_POST:
|
|
break
|
|
else:
|
|
raise VFError
|
|
|
|
afile.close()
|
|
|
|
def getfonts(self):
|
|
return self.fonts
|
|
|
|
def getchar(self, cc):
|
|
return self.chardefs[cc]
|
|
|
|
|