update upload data
[PyX.git] / pyx / dvi / texfont.py
blobee0cfb7d26b81ef0d034c5bf1d57252316f89618
1 # -*- encoding: utf-8 -*-
4 # Copyright (C) 2007-2011 Jörg Lehmann <joergl@users.sourceforge.net>
5 # Copyright (C) 2007-2011 André Wobst <wobsta@users.sourceforge.net>
7 # This file is part of PyX (http://pyx.sourceforge.net/).
9 # PyX is free software; you can redistribute it and/or modify
10 # it under the terms of the GNU General Public License as published by
11 # the Free Software Foundation; either version 2 of the License, or
12 # (at your option) any later version.
14 # PyX is distributed in the hope that it will be useful,
15 # but WITHOUT ANY WARRANTY; without even the implied warranty of
16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 # GNU General Public License for more details.
19 # You should have received a copy of the GNU General Public License
20 # along with PyX; if not, write to the Free Software
21 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
24 from pyx import bbox, canvasitem, font, filelocator
25 import tfmfile, vffile
28 class TeXfont:
30 def __init__(self, name, c, q, d, tfmconv, pyxconv, debug=0):
31 self.name = name
32 self.q = q # desired size of font (fix_word) in TeX points
33 self.d = d # design size of font (fix_word) in TeX points
34 self.tfmconv = tfmconv # conversion factor from tfm units to dvi units
35 self.pyxconv = pyxconv # conversion factor from dvi units to PostScript points
36 file = filelocator.open(self.name, [filelocator.format.tfm], "rb")
37 self.TFMfile = tfmfile.TFMfile(file, debug)
38 file.close()
40 # We only check for equality of font checksums if none of them
41 # is zero. The case c == 0 happend in some VF files and
42 # according to the VFtoVP documentation, paragraph 40, a check
43 # is only performed if TFMfile.checksum > 0. Anyhow, being
44 # more generous here seems to be reasonable
45 if self.TFMfile.checksum != c and self.TFMfile.checksum != 0 and c != 0:
46 raise DVIError("check sums do not agree: %d vs. %d" %
47 (self.TFMfile.checksum, c))
49 # Check whether the given design size matches the one defined in the tfm file
50 if abs(self.TFMfile.designsize - d) > 2:
51 raise DVIError("design sizes do not agree: %d vs. %d" % (self.TFMfile.designsize, d))
52 #if q < 0 or q > 134217728:
53 # raise DVIError("font '%s' not loaded: bad scale" % self.name)
54 if d < 0 or d > 134217728:
55 raise DVIError("font '%s' not loaded: bad design size" % self.name)
57 def __str__(self):
58 return "font %s designed at %g TeX pts used at %g TeX pts" % (self.name,
59 16.0*self.d/16777216L,
60 16.0*self.q/16777216L)
62 def getsize_pt(self):
63 """ return size of font in (PS) points """
64 # The factor 16L/16777216L=2**(-20) converts a fix_word (here self.q)
65 # to the corresponding float. Furthermore, we have to convert from TeX
66 # points to points, hence the factor 72/72.27.
67 return 72/72.27 * 16*self.q/16777216
69 def _convert_tfm_to_dvi(self, length):
70 # doing the integer math with long integers will lead to different roundings
71 # return 16*length*int(round(self.q*self.tfmconv))/16777216
73 # Knuth instead suggests the following algorithm based on 4 byte integer logic only
74 # z = int(round(self.q*self.tfmconv))
75 # b0, b1, b2, b3 = [ord(c) for c in struct.pack(">L", length)]
76 # assert b0 == 0 or b0 == 255
77 # shift = 4
78 # while z >= 8388608:
79 # z >>= 1
80 # shift -= 1
81 # assert shift >= 0
82 # result = ( ( ( ( ( b3 * z ) >> 8 ) + ( b2 * z ) ) >> 8 ) + ( b1 * z ) ) >> shift
83 # if b0 == 255:
84 # result = result - (z << (8-shift))
86 # however, we can simplify this using a single long integer multiplication,
87 # but take into account the transformation of z
88 z = int(round(self.q*self.tfmconv))
89 assert -16777216 <= length < 16777216 # -(1 << 24) <= length < (1 << 24)
90 assert z < 134217728 # 1 << 27
91 shift = 20 # 1 << 20
92 while z >= 8388608: # 1 << 23
93 z >>= 1
94 shift -= 1
95 # length*z is a long integer, but the result will be a regular integer
96 return int(length*long(z) >> shift)
98 def _convert_tfm_to_pt(self, length):
99 return (16*long(round(length*float(self.q)*self.tfmconv))/16777216) * self.pyxconv
101 # routines returning lengths as integers in dvi units
103 def getwidth_dvi(self, charcode):
104 return self._convert_tfm_to_dvi(self.TFMfile.width[self.TFMfile.char_info[charcode].width_index])
106 def getheight_dvi(self, charcode):
107 return self._convert_tfm_to_dvi(self.TFMfile.height[self.TFMfile.char_info[charcode].height_index])
109 def getdepth_dvi(self, charcode):
110 return self._convert_tfm_to_dvi(self.TFMfile.depth[self.TFMfile.char_info[charcode].depth_index])
112 def getitalic_dvi(self, charcode):
113 return self._convert_tfm_to_dvi(self.TFMfile.italic[self.TFMfile.char_info[charcode].italic_index])
115 # routines returning lengths as floats in PostScript points
117 def getwidth_pt(self, charcode):
118 return self._convert_tfm_to_pt(self.TFMfile.width[self.TFMfile.char_info[charcode].width_index])
120 def getheight_pt(self, charcode):
121 return self._convert_tfm_to_pt(self.TFMfile.height[self.TFMfile.char_info[charcode].height_index])
123 def getdepth_pt(self, charcode):
124 return self._convert_tfm_to_pt(self.TFMfile.depth[self.TFMfile.char_info[charcode].depth_index])
126 def getitalic_pt(self, charcode):
127 return self._convert_tfm_to_pt(self.TFMfile.italic[self.TFMfile.char_info[charcode].italic_index])
129 def text_pt(self, x_pt, y_pt, charcodes, fontmap=None):
130 return TeXtext_pt(self, x_pt, y_pt, charcodes, self.getsize_pt(), fontmap=fontmap)
132 def getMAPline(self, fontmap):
133 if self.name not in fontmap:
134 raise RuntimeError("missing font information for '%s'; check fontmapping file(s)" % self.name)
135 return fontmap[self.name]
138 class virtualfont(TeXfont):
140 def __init__(self, name, file, c, q, d, tfmconv, pyxconv, debug=0):
141 TeXfont.__init__(self, name, c, q, d, tfmconv, pyxconv, debug)
142 self.vffile = vffile.vffile(file, 1.0*q/d, tfmconv, pyxconv, debug > 1)
144 def getfonts(self):
145 """ return fonts used in virtual font itself """
146 return self.vffile.getfonts()
148 def getchar(self, cc):
149 """ return dvi chunk corresponding to char code cc """
150 return self.vffile.getchar(cc)
152 def text_pt(self, *args, **kwargs):
153 raise RuntimeError("you don't know what you're doing")
156 class TeXtext_pt(font.text_pt):
158 def __init__(self, font, x_pt, y_pt, charcodes, size_pt, fontmap=None):
159 self.font = font
160 self.x_pt = x_pt
161 self.y_pt = y_pt
162 self.charcodes = charcodes
163 self.size_pt = size_pt
164 self.fontmap = fontmap
166 self.width_pt = sum([self.font.getwidth_pt(charcode) for charcode in charcodes])
167 self.height_pt = max([self.font.getheight_pt(charcode) for charcode in charcodes])
168 self.depth_pt = max([self.font.getdepth_pt(charcode) for charcode in charcodes])
170 self._bbox = bbox.bbox_pt(self.x_pt, self.y_pt-self.depth_pt, self.x_pt+self.width_pt, self.y_pt+self.height_pt)
172 def bbox(self):
173 return self._bbox
175 def processPS(self, file, writer, context, registry, bbox):
176 bbox += self.bbox()
177 if self.fontmap is not None:
178 mapline = self.font.getMAPline(self.fontmap)
179 else:
180 mapline = self.font.getMAPline(writer.getfontmap())
181 font = mapline.getfont()
182 text = font.text_pt(self.x_pt, self.y_pt, self.charcodes, self.size_pt, decoding=mapline.getencoding(), slant=mapline.slant, ignorebbox=True)
183 text.processPS(file, writer, context, registry, bbox)
185 def processPDF(self, file, writer, context, registry, bbox):
186 bbox += self.bbox()
188 mapline = self.font.getMAPline(writer.getfontmap())
189 font = mapline.getfont()
190 text = font.text_pt(self.x_pt, self.y_pt, self.charcodes, self.size_pt, decoding=mapline.getencoding(), slant=mapline.slant, ignorebbox=True)
191 text.processPDF(file, writer, context, registry, bbox)