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
, font
, config
25 from . 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 with config
.open(self
.name
, [config
.format
.tfm
]) as file:
38 self
.TFMfile
= tfmfile
.TFMfile(file, debug
)
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 TeXFontError("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
) > 4: # XXX: why the deviation?
51 raise TeXFontError("design sizes do not agree: %d vs. %d" % (self
.TFMfile
.designsize
, d
))
52 #if q < 0 or q > 134217728:
53 # raise TeXFontError("font '%s' not loaded: bad scale" % self.name)
54 if d
< 0 or d
> 134217728:
55 raise TeXFontError("font '%s' not loaded: bad design size" % self
.name
)
58 return "font %s designed at %g TeX pts used at %g TeX pts" % (self
.name
,
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
82 # result = ( ( ( ( ( b3 * z ) >> 8 ) + ( b2 * z ) ) >> 8 ) + ( b1 * z ) ) >> shift
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
92 while z
>= 8388608: # 1 << 23
95 # length*z is a long integer, but the result will be a regular integer
96 return int(length
*int(z
) >> shift
)
98 def _convert_tfm_to_pt(self
, length
):
99 return (16*int(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)
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):
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
)
175 def _text(self
, writer
):
176 if self
.fontmap
is not None:
177 mapline
= self
.font
.getMAPline(self
.fontmap
)
179 mapline
= self
.font
.getMAPline(writer
.getfontmap())
180 font
= mapline
.getfont()
181 return font
.text_pt(self
.x_pt
, self
.y_pt
, self
.charcodes
, self
.size_pt
, decoding
=mapline
.getencoding(), slant
=mapline
.slant
, ignorebbox
=True)
184 from pyx
import pswriter
185 return self
._text
(pswriter
._PSwriter
()).textpath()
187 def processPS(self
, file, writer
, context
, registry
, bbox
):
189 self
._text
(writer
).processPS(file, writer
, context
, registry
, bbox
)
191 def processPDF(self
, file, writer
, context
, registry
, bbox
):
193 self
._text
(writer
).processPDF(file, writer
, context
, registry
, bbox
)
195 def processSVG(self
, xml
, writer
, context
, registry
, bbox
):
197 self
._text
(writer
).processSVG(xml
, writer
, context
, registry
, bbox
)