~ubuntu-branches/debian/sid/pyx/sid

« back to all changes in this revision

Viewing changes to pyx/dvi/texfont.py

  • Committer: Bazaar Package Importer
  • Author(s): Stuart Prescott
  • Date: 2011-05-20 00:13:52 UTC
  • mto: (9.1.1 experimental)
  • mto: This revision was merged to the branch mainline in revision 8.
  • Revision ID: james.westby@ubuntu.com-20110520001352-odcuqpdezuusbbw1
Tags: upstream-0.11.1
Import upstream version 0.11.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# -*- encoding: utf-8 -*-
 
2
#
 
3
#
 
4
# Copyright (C) 2007-2011 Jörg Lehmann <joergl@users.sourceforge.net>
 
5
# Copyright (C) 2007-2011 André Wobst <wobsta@users.sourceforge.net>
 
6
#
 
7
# This file is part of PyX (http://pyx.sourceforge.net/).
 
8
#
 
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.
 
13
#
 
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.
 
18
#
 
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
 
22
 
 
23
 
 
24
from pyx import bbox, canvasitem, font, filelocator
 
25
import tfmfile, vffile
 
26
 
 
27
class TeXFontError(Exception): pass
 
28
 
 
29
class TeXfont:
 
30
 
 
31
    def __init__(self, name, c, q, d, tfmconv, pyxconv, debug=0):
 
32
        self.name = name
 
33
        self.q = q                  # desired size of font (fix_word) in TeX points
 
34
        self.d = d                  # design size of font (fix_word) in TeX points
 
35
        self.tfmconv = tfmconv      # conversion factor from tfm units to dvi units
 
36
        self.pyxconv = pyxconv      # conversion factor from dvi units to PostScript points
 
37
        file = filelocator.open(self.name, [filelocator.format.tfm], "rb")
 
38
        self.TFMfile = tfmfile.TFMfile(file, debug)
 
39
        file.close()
 
40
 
 
41
        # We only check for equality of font checksums if none of them
 
42
        # is zero. The case c == 0 happend in some VF files and
 
43
        # according to the VFtoVP documentation, paragraph 40, a check
 
44
        # is only performed if TFMfile.checksum > 0. Anyhow, being
 
45
        # more generous here seems to be reasonable
 
46
        if self.TFMfile.checksum != c and self.TFMfile.checksum != 0 and c != 0:
 
47
            raise TeXFontError("check sums do not agree: %d vs. %d" %
 
48
                               (self.TFMfile.checksum, c))
 
49
 
 
50
        # Check whether the given design size matches the one defined in the tfm file
 
51
        if abs(self.TFMfile.designsize - d) > 4: # XXX: why the deviation?
 
52
            raise TeXFontError("design sizes do not agree: %d vs. %d" % (self.TFMfile.designsize, d))
 
53
        #if q < 0 or q > 134217728:
 
54
        #    raise TeXFontError("font '%s' not loaded: bad scale" % self.name)
 
55
        if d < 0 or d > 134217728:
 
56
            raise TeXFontError("font '%s' not loaded: bad design size" % self.name)
 
57
 
 
58
    def __str__(self):
 
59
        return "font %s designed at %g TeX pts used at %g TeX pts" % (self.name, 
 
60
                                                                      16.0*self.d/16777216L,
 
61
                                                                      16.0*self.q/16777216L)
 
62
 
 
63
    def getsize_pt(self):
 
64
        """ return size of font in (PS) points """
 
65
        # The factor 16L/16777216L=2**(-20) converts a fix_word (here self.q)
 
66
        # to the corresponding float. Furthermore, we have to convert from TeX
 
67
        # points to points, hence the factor 72/72.27.
 
68
        return 72/72.27 * 16*self.q/16777216
 
69
 
 
70
    def _convert_tfm_to_dvi(self, length):
 
71
        # doing the integer math with long integers will lead to different roundings
 
72
        # return 16*length*int(round(self.q*self.tfmconv))/16777216
 
73
 
 
74
        # Knuth instead suggests the following algorithm based on 4 byte integer logic only
 
75
        # z = int(round(self.q*self.tfmconv))
 
76
        # b0, b1, b2, b3 = [ord(c) for c in struct.pack(">L", length)]
 
77
        # assert b0 == 0 or b0 == 255
 
78
        # shift = 4
 
79
        # while z >= 8388608:
 
80
        #     z >>= 1
 
81
        #     shift -= 1
 
82
        # assert shift >= 0
 
83
        # result = ( ( ( ( ( b3 * z ) >> 8 ) + ( b2 * z ) ) >> 8 ) + ( b1 * z ) ) >> shift
 
84
        # if b0 == 255:
 
85
        #     result = result - (z << (8-shift))
 
86
 
 
87
        # however, we can simplify this using a single long integer multiplication,
 
88
        # but take into account the transformation of z
 
89
        z = int(round(self.q*self.tfmconv))
 
90
        assert -16777216 <= length < 16777216 # -(1 << 24) <= length < (1 << 24)
 
91
        assert z < 134217728 # 1 << 27
 
92
        shift = 20 # 1 << 20
 
93
        while z >= 8388608: # 1 << 23
 
94
            z >>= 1
 
95
            shift -= 1
 
96
        # length*z is a long integer, but the result will be a regular integer
 
97
        return int(length*long(z) >> shift)
 
98
 
 
99
    def _convert_tfm_to_pt(self, length):
 
100
        return (16*long(round(length*float(self.q)*self.tfmconv))/16777216) * self.pyxconv
 
101
 
 
102
    # routines returning lengths as integers in dvi units
 
103
 
 
104
    def getwidth_dvi(self, charcode):
 
105
        return self._convert_tfm_to_dvi(self.TFMfile.width[self.TFMfile.char_info[charcode].width_index])
 
106
 
 
107
    def getheight_dvi(self, charcode):
 
108
        return self._convert_tfm_to_dvi(self.TFMfile.height[self.TFMfile.char_info[charcode].height_index])
 
109
 
 
110
    def getdepth_dvi(self, charcode):
 
111
        return self._convert_tfm_to_dvi(self.TFMfile.depth[self.TFMfile.char_info[charcode].depth_index])
 
112
 
 
113
    def getitalic_dvi(self, charcode):
 
114
        return self._convert_tfm_to_dvi(self.TFMfile.italic[self.TFMfile.char_info[charcode].italic_index])
 
115
 
 
116
    # routines returning lengths as floats in PostScript points
 
117
 
 
118
    def getwidth_pt(self, charcode):
 
119
        return self._convert_tfm_to_pt(self.TFMfile.width[self.TFMfile.char_info[charcode].width_index])
 
120
 
 
121
    def getheight_pt(self, charcode):
 
122
        return self._convert_tfm_to_pt(self.TFMfile.height[self.TFMfile.char_info[charcode].height_index])
 
123
 
 
124
    def getdepth_pt(self, charcode):
 
125
        return self._convert_tfm_to_pt(self.TFMfile.depth[self.TFMfile.char_info[charcode].depth_index])
 
126
 
 
127
    def getitalic_pt(self, charcode):
 
128
        return self._convert_tfm_to_pt(self.TFMfile.italic[self.TFMfile.char_info[charcode].italic_index])
 
129
 
 
130
    def text_pt(self, x_pt, y_pt, charcodes, fontmap=None):
 
131
        return TeXtext_pt(self, x_pt, y_pt, charcodes, self.getsize_pt(), fontmap=fontmap)
 
132
 
 
133
    def getMAPline(self, fontmap):
 
134
        if self.name not in fontmap:
 
135
            raise RuntimeError("missing font information for '%s'; check fontmapping file(s)" % self.name)
 
136
        return fontmap[self.name]
 
137
 
 
138
 
 
139
class virtualfont(TeXfont):
 
140
 
 
141
    def __init__(self, name, file, c, q, d, tfmconv, pyxconv, debug=0):
 
142
        TeXfont.__init__(self, name, c, q, d, tfmconv, pyxconv, debug)
 
143
        self.vffile = vffile.vffile(file, 1.0*q/d, tfmconv, pyxconv, debug > 1)
 
144
 
 
145
    def getfonts(self):
 
146
        """ return fonts used in virtual font itself """
 
147
        return self.vffile.getfonts()
 
148
 
 
149
    def getchar(self, cc):
 
150
        """ return dvi chunk corresponding to char code cc """
 
151
        return self.vffile.getchar(cc)
 
152
 
 
153
    def text_pt(self, *args, **kwargs):
 
154
        raise RuntimeError("you don't know what you're doing")
 
155
 
 
156
 
 
157
class TeXtext_pt(font.text_pt):
 
158
 
 
159
    def __init__(self, font, x_pt, y_pt, charcodes, size_pt, fontmap=None):
 
160
        self.font = font
 
161
        self.x_pt = x_pt
 
162
        self.y_pt = y_pt
 
163
        self.charcodes = charcodes
 
164
        self.size_pt = size_pt
 
165
        self.fontmap = fontmap
 
166
 
 
167
        self.width_pt = sum([self.font.getwidth_pt(charcode) for charcode in charcodes])
 
168
        self.height_pt = max([self.font.getheight_pt(charcode) for charcode in charcodes])
 
169
        self.depth_pt = max([self.font.getdepth_pt(charcode) for charcode in charcodes])
 
170
 
 
171
        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
 
 
173
    def bbox(self):
 
174
        return self._bbox
 
175
 
 
176
    def processPS(self, file, writer, context, registry, bbox):
 
177
        bbox += self.bbox()
 
178
        if self.fontmap is not None:
 
179
            mapline = self.font.getMAPline(self.fontmap)
 
180
        else:
 
181
            mapline = self.font.getMAPline(writer.getfontmap())
 
182
        font = mapline.getfont()
 
183
        text = font.text_pt(self.x_pt, self.y_pt, self.charcodes, self.size_pt, decoding=mapline.getencoding(), slant=mapline.slant, ignorebbox=True)
 
184
        text.processPS(file, writer, context, registry, bbox)
 
185
 
 
186
    def processPDF(self, file, writer, context, registry, bbox):
 
187
        bbox += self.bbox()
 
188
 
 
189
        mapline = self.font.getMAPline(writer.getfontmap())
 
190
        font = mapline.getfont()
 
191
        text = font.text_pt(self.x_pt, self.y_pt, self.charcodes, self.size_pt, decoding=mapline.getencoding(), slant=mapline.slant, ignorebbox=True)
 
192
        text.processPDF(file, writer, context, registry, bbox)
 
193