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
27
class TeXFontError(Exception): pass
31
def __init__(self, name, c, q, d, tfmconv, pyxconv, debug=0):
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)
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))
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)
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)
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
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
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
83
# result = ( ( ( ( ( b3 * z ) >> 8 ) + ( b2 * z ) ) >> 8 ) + ( b1 * z ) ) >> shift
85
# result = result - (z << (8-shift))
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
93
while z >= 8388608: # 1 << 23
96
# length*z is a long integer, but the result will be a regular integer
97
return int(length*long(z) >> shift)
99
def _convert_tfm_to_pt(self, length):
100
return (16*long(round(length*float(self.q)*self.tfmconv))/16777216) * self.pyxconv
102
# routines returning lengths as integers in dvi units
104
def getwidth_dvi(self, charcode):
105
return self._convert_tfm_to_dvi(self.TFMfile.width[self.TFMfile.char_info[charcode].width_index])
107
def getheight_dvi(self, charcode):
108
return self._convert_tfm_to_dvi(self.TFMfile.height[self.TFMfile.char_info[charcode].height_index])
110
def getdepth_dvi(self, charcode):
111
return self._convert_tfm_to_dvi(self.TFMfile.depth[self.TFMfile.char_info[charcode].depth_index])
113
def getitalic_dvi(self, charcode):
114
return self._convert_tfm_to_dvi(self.TFMfile.italic[self.TFMfile.char_info[charcode].italic_index])
116
# routines returning lengths as floats in PostScript points
118
def getwidth_pt(self, charcode):
119
return self._convert_tfm_to_pt(self.TFMfile.width[self.TFMfile.char_info[charcode].width_index])
121
def getheight_pt(self, charcode):
122
return self._convert_tfm_to_pt(self.TFMfile.height[self.TFMfile.char_info[charcode].height_index])
124
def getdepth_pt(self, charcode):
125
return self._convert_tfm_to_pt(self.TFMfile.depth[self.TFMfile.char_info[charcode].depth_index])
127
def getitalic_pt(self, charcode):
128
return self._convert_tfm_to_pt(self.TFMfile.italic[self.TFMfile.char_info[charcode].italic_index])
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)
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]
139
class virtualfont(TeXfont):
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)
146
""" return fonts used in virtual font itself """
147
return self.vffile.getfonts()
149
def getchar(self, cc):
150
""" return dvi chunk corresponding to char code cc """
151
return self.vffile.getchar(cc)
153
def text_pt(self, *args, **kwargs):
154
raise RuntimeError("you don't know what you're doing")
157
class TeXtext_pt(font.text_pt):
159
def __init__(self, font, x_pt, y_pt, charcodes, size_pt, fontmap=None):
163
self.charcodes = charcodes
164
self.size_pt = size_pt
165
self.fontmap = fontmap
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])
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)
176
def processPS(self, file, writer, context, registry, bbox):
178
if self.fontmap is not None:
179
mapline = self.font.getMAPline(self.fontmap)
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)
186
def processPDF(self, file, writer, context, registry, bbox):
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)