comparison enso/graphics/font.py @ 29:47bcdb1de2e8

Added implementation for enso.graphics.font.
author Atul Varma <varmaa@toolness.com>
date Sat, 23 Feb 2008 10:33:54 -0600
parents 203d6a15652c
children 605d8cb2728c
comparison
equal deleted inserted replaced
28:cff69f571315 29:47bcdb1de2e8
1 # Copyright (c) 2008, Humanized, Inc.
2 # All rights reserved.
3 #
4 # Redistribution and use in source and binary forms, with or without
5 # modification, are permitted provided that the following conditions are met:
6 #
7 # 1. Redistributions of source code must retain the above copyright
8 # notice, this list of conditions and the following disclaimer.
9 #
10 # 2. Redistributions in binary form must reproduce the above copyright
11 # notice, this list of conditions and the following disclaimer in the
12 # documentation and/or other materials provided with the distribution.
13 #
14 # 3. Neither the name of Enso nor the names of its contributors may
15 # be used to endorse or promote products derived from this
16 # software without specific prior written permission.
17 #
18 # THIS SOFTWARE IS PROVIDED BY Humanized, Inc. ``AS IS'' AND ANY
19 # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 # DISCLAIMED. IN NO EVENT SHALL Humanized, Inc. BE LIABLE FOR ANY
22 # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25 # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
29 # ----------------------------------------------------------------------------
30 #
31 # enso.graphics.font
32 #
33 # ----------------------------------------------------------------------------
34
35 """
36 This module provides a high-level interface for registering and
37 accessing fonts, including their font metrics information, their
38 glyphs, and their rendering.
39
40 This module requires no initialization or shutdown.
41 """
42
43 # ----------------------------------------------------------------------------
44 # Imports
45 # ----------------------------------------------------------------------------
46
47 import cairo
48
49
50 # ----------------------------------------------------------------------------
51 # Fonts
52 # ----------------------------------------------------------------------------
53
54 class Font:
55 """
56 Encapsulates a font face, which describes both a given typeface
57 and style.
58 """
59
60 def __init__( self, fileName, size, cairoContext ):
61 """
62 Creates a Font from the given filename pointing to a TrueType
63 font file, at the given size (in points).
64 """
65
66 import os
67
68 if not os.path.exists( fileName ):
69 raise IOError( "file not found: %s" % fileName )
70
71 self.fileName = fileName
72 self.size = size
73 self.cairoContext = cairoContext
74
75 cairoContext.save()
76
77 self.loadInto( cairoContext )
78
79 # Make our font metrics information visible to the client.
80
81 ( self.ascent,
82 self.descent,
83 self.height,
84 self.maxXAdvance,
85 self.maxYAdvance ) = cairoContext.font_extents()
86
87 cairoContext.restore()
88
89 def getGlyph( self, char ):
90 """
91 Returns a glyph of the font corresponding to the given Unicode
92 character.
93 """
94
95 # TODO: Memoize this function.
96
97 return FontGlyph( char, self, self.cairoContext )
98
99 def getKerningDistance( self, charLeft, charRight ):
100 """
101 Returns the kerning distance (in points) between the two
102 Unicode characters for this font face.
103 """
104
105 # LONGTERM TODO: Get this to work. This may involve modifying
106 # the source code of Cairo.
107 return 0.0
108
109 def loadInto( self, cairoContext ):
110 """
111 Sets the cairo context's current font to this font.
112 """
113
114 # Note that we are using our own modified 'interpretation' of
115 # the select_font_face() function here, as outlined in our
116 # modified version of the Cairo FreeType 2 Font Module; see
117 # the file 'cairo-ft-font.c' in our modified version of the
118 # Cairo library for more information.
119
120 # TODO: Note also that our modified interpretation of the
121 # function takes an 8-bit string, so we need to do any
122 # necessary encoding/transformation from unicode file-paths
123 # here.
124
125 cairoContext.select_font_face(
126 self.fileName,
127 cairo.FONT_SLANT_NORMAL,
128 cairo.FONT_WEIGHT_NORMAL
129 )
130 cairoContext.set_font_size( self.size )
131
132
133 # ----------------------------------------------------------------------------
134 # Font Glyphs
135 # ----------------------------------------------------------------------------
136
137 class FontGlyph:
138 """
139 Encapsulates a glyph of a font face.
140 """
141
142 def __init__( self, char, font, cairoContext ):
143 """
144 Creates the font glyph corresponding to the given Unicode
145 character, using the font specified by the given Font object
146 and the given cairo context.
147 """
148
149 # Encode the character to UTF-8 because that's what the cairo
150 # API uses.
151 self.charAsUtf8 = char.encode("UTF-8")
152 self.char = char
153 self.font = font
154
155 cairoContext.save()
156
157 self.font.loadInto( cairoContext )
158
159 # Make our font glyph metrics information visible to the client.
160
161 ( xBearing,
162 yBearing,
163 width,
164 height,
165 xAdvance,
166 yAdvance ) = cairoContext.text_extents( self.charAsUtf8 )
167
168 # The xMin, xMax, yMin, yMax, and advance attributes are used
169 # here to correspond to their values in this image:
170 # http://freetype.sourceforge.net/freetype2/docs/glyphs/Image3.png
171
172 self.xMin = xBearing
173 self.xMax = xBearing + width
174 self.yMin = -yBearing + height
175 self.yMax = -yBearing
176 self.advance = xAdvance
177
178 cairoContext.restore()
179
180
181 # ----------------------------------------------------------------------------
182 # The Font Registry
183 # ----------------------------------------------------------------------------
184
185 class FontRegistry:
186 """
187 This singleton represents a registry of font faces, allowing for a
188 client to simply retrieve a Font object in a particular size and
189 style rather without having to know the location of a specific
190 TrueType file.
191 """
192
193 def __init__( self ):
194 """
195 Initializes the font registry.
196 """
197
198 self._registry = {}
199
200 dummySurface = cairo.ImageSurface( cairo.FORMAT_ARGB32, 1, 1 )
201 self.cairoContext = cairo.Context( dummySurface )
202
203
204 def register( self, fileName, name, italic=False ):
205 """
206 Registers the given TrueType font filename as representing the
207 given font name with the given style.
208 """
209
210 registryKey = (name, italic)
211
212 if self._registry.has_key( registryKey ):
213 raise FontAlreadyRegisteredError( registryKey )
214 else:
215 self._registry[registryKey] = fileName
216
217 def get( self, name, size, italic=False ):
218 """
219 Retrieves a Font object corresponding to the given font name
220 at the given size and style.
221 """
222
223 # TODO: Memoize this function.
224
225 registryKey = (name, italic)
226
227 fileName = self._registry[registryKey]
228 return Font( fileName, size, self.cairoContext )
229
230
231 class FontAlreadyRegisteredError( Exception ):
232 """
233 Exception raised when the client attempts to register a font when
234 that font has already been registered.
235 """
236
237 pass
238
239
240 # ----------------------------------------------------------------------------
241 # Singleton Instance
242 # ----------------------------------------------------------------------------
243
244 # The font registry singleton instance.
245 theFontRegistry = FontRegistry()