source package: freetype-0.1.0.tar.gz
windows binaries (Python 2.2.1): freetype-0.1.0.win32-py2.2.exe
Robert Kern has written a more complete Python interface to Freetype 1 called PyFT. Robert and I discussed updating PyFT to Freetype 2 and fixing some issues with memory management, etc. Together, we decided that a less ambitious interface that served the needs of Chaco was a better solution. The result is a small package (about 250 lines for the interface and 800 lines of py/C in the weave wrapper) for querying the available fonts on a system and rendering arbitrarily transformed text (both ascii and unicode) into a Numeric array. The rendering mode can be set to aliased or anti-aliased.
The freetype
wrapper is considered alpha. I have integrated it
into the wxPython backend of Chaco, and it does work. But the test are fairly
limited. Still, since the interface is small, the major issues (if any) should
be found quickly.
freetype
package is part of Chaco. Chaco is currently only
accessible through CVS. However, I've also bundled freetype up so that people
can try it out separately. When Chaco is released, the freetype package
will install as its own package (not nested within Chaco) so code using this
version will continue to work with the final distribution (barring interface
changes).
Downloads:
source package: freetype-0.1.0.tar.gz
windows binaries (Python 2.2.1): freetype-0.1.0.win32-py2.2.exe
The source package contains a setup.py
file, and only depends on
Numeric for standard usage. The dependencies on weave have been removed in the
distribution to simplify builds. If a recent CVS version of scipy.weave
is present, the _ft.cpp
will be rebuilt.
Also, if you want to use the view()
method for Glyphs
objects (see the samples below), you'll also need wxPython and SciPy.
If you want to build from the CVS, you'll need the CVS version of
scipy.weave
. It has support for unicode objects that older
versions don't have.
To build from source, do the following (on Unix):
On windows, you can build from source using either the mingw32 compiler or MSVC++. Pretty much the same instructions apply, except you may want to use winzip for the unpacking process if you don't have tar installed on your machine. The easiest approach on windows is to use the binaries supplied above.> tar -xzvf freetype-0.1.0.tar.gz > cd freetype-0.1.0 > python setup.py build > python setup.py install
FreeType
class.
# only needed if you'd like to view the rendered text in a plot window >>> import gui_thread>>> from scipy import plt >>> import freetype >>> ft = freetype.FreeType() >>> rendered_text = ft.render("please") >>> rendered_text.view()
The rendered_text
object is a Glyphs
instance that
holds the image array for the rendered text as well as some other information
about the size of the image, etc. The image is stored as an unsigned character
array (type UnsignedInt8) of gray scale values.
The results here don't look so good because some of the characters run together, and the resolution is somewhat low. Increasing the rendering resolution can improve the results (although I thought this should be better than it is.)
dpi
keyword argument that set the
"dots per inch" of the display device you want to render for. This affects the
size and resolution of fonts for a given point size (10, 12, etc.). 72 dpi is
the standard and default. I've noticed though, that this gives pretty lousy
font rendering for font sizes below 14 points. I use 133.3 on my laptop because
it has a high resolution screen, and this looks fairly good with the text
rendered at the correct size. However, on low resolution screens the letters
will appear too large.
>>> ft = freetype.FreeType(dpi=133.3) >>> rendered_text = ft.render("please") >>> rendered_text.view()
Note: I'd really like a to know how to query the system for its dpi value. If you know how to do this on a particular system, please email me.
FreeType
renders antialiased text. The
antialias()
method will disable this if desired.
>>> ft = freetype.FreeType(dpi=133.3) >>> rendered_text = ft.antialias(0) >>> rendered_text = ft.render("please") >>> rendered_text.view()
FreeType can handle a lot of different font formats, but we currently only
support scalable fonts (this limitation will probably continue) and have only
tested with TrueType on Windows. freetype
reads the available
fonts from a user specified directory (I'd like this to be
automatically found) and keeps track of the available fonts. This is
managed by a FontLookup
class, but most of the information
is also exposed through the FreeType
class.
>>> ft = freetype.FreeType(dpi=133.3) >>> ft.available_fonts() ['arial', 'arial black', 'arial narrow', 'arial unicode ms', 'batang', 'book ant iqua', 'bookman old style', 'century', 'century gothic', 'comic sans ms', 'courier new', 'estrangelo edessa', 'franklin gothic medium', 'garamond', 'gautami', 'georgia', 'haettenschweiler', 'impact', 'latha', 'lucida console', 'lucida sans unicode', 'mangal', 'marlett', 'microsoft sans serif', 'monotype corsiva', 'ms mincho', 'ms outlook', 'mv boli', 'opensymbol', 'palatino linotype', 'pmingliu', 'raavi', 'shruti', 'simsun', 'sylfaen', 'symbol', 'tahoma', 'times new roman', ' trebuchet ms', 'tunga', 'verdana', 'webdings', 'wingdings', 'wingdings 2', 'wingdings 3'] >>> ft.select_font("times new roman",size=24) >>> rendered_text = ft.render("please") >>> rendered_text.view()
Note: Font management is one of the areas I'd like to see some improvement. We need some simple name matching to handle "times" instead of "times new roman", etc. Also, I have no idea where the font files are stored on Unix systems or even if there is a common location for these guys. It would be very good to auto detect the location of font files instead of having a hard coded path. On windows systems, it'll probably be pretty easy because there are only about 3 places to look (I think). On Unix, it could be a lot more difficult.Another thought is to also include 4 or 5 standard fonts with the
freetype
package so that there are always several available fonts. I'm not entirely happy without how the Microsoft fonts are rendering, so there might be some aesthetic benefits also if there are some "freetype optimized" fonts out there that are open sourced.
select_font()
method has the following full definition:
def select_font(self,name, size=12, style='regular', encoding=None)
The size
keyword specifies the size of the font in points. Note
that the actual size of the font also relies on the font itself and the
dpi
setting used to initialize the FreeType
object.
style
is one of 4 strings: 'regular'
,
'bold'
, 'italic'
, or 'bold italic'
.
Currently, bold
and italic
settings are only available
for fonts that have a specialized font to handle the styles. Adding a style
transformation matrix to turn fonts that only have a regular
style
font into styled fonts shouldn't be hard, but hasn't been attempted yet.
Note: Does anyone have a set of transformation matrices that does a good job of
creating each of these styles?
The encoding
string specifies the character map that maps characters
to the glyph that displays them. By default, unicode ('unic'
)
is used for all the fonts that support that style. If unicode is supported, then
symbol ('symb'
) is attempted. Another common style is Apple
Roman('armn'
). You can ask a font object what encodings it supports:
Note: I'm not sure why 'unic' is listed twice.>>> ft = freetype.FreeType(dpi=133.3) >>> ft.select_font("times new roman",size=24) >>> the_font.supported_encodings() ['unic', 'armn', 'unic']
If you try to set the encoding to an unsupported value, it'll raise a ValueError. Here is the list of potentially supported encodings:
symbol | "symb" |
unicode | "unic" |
latin1 | "lat1" |
latin2 | "lat2" |
SJIS | "sjis" |
gb2312 | "gb" |
big5 | "big5" |
wansung | "wans" |
johab | "joha" |
adobe standard | "ADOB" |
adobe expert | "ADBE" |
adobe custom | "ADBC" |
apple roman | "armn" |
I haven't used encodings much yet, so I don't have any practical examples to share. So far, accepting the default encoding has worked well.
Note: I haven't experimented with how the various encodings interact with the fact that all strings passed into the freetype renderer are unicode. I don't think it is an issue, but it might have some affect.
font()
method.
It allows you to set and retrieve the font of a FreeType object. It will only
allow font objects created by the same FreeType
object to be
assigned to the object. If font
is called without an argument, it
returns a reference to the current font object.
The Font
class has the following methods:
For more info, you'll have to look at the source.class Font: def name(self): def style(self): def supported_encodings(self): def glyph_count(self): def size(self,sz=None): def encoding(self,encoding=None): def char_index_from_int(self,value):
The transform()
method sets a 4 element transformation
matrix used during rendering. The [a,b,c,d] elements are positioned as
follows:
|a b| |c d|
transform()
also returns the current transform for the
FreeType
object. You can call it without any arguments if
you just want to query for the transform value.
Here is an example where the text is rotated 45 degrees using the affine matrix tools in Chaco.
>>> from scipy import * >>> import affine >>> m = affine.affine_identity() >>> m = affine.rotate(m,pi/4) >>> a,b,c,d,tx,ty = affine.affine_params(m) >>> ft = freetype.FreeType(dpi=133.3) >>> ft.transform([a,b,c,d]) array([ 0.70710678, 0.70710678, -0.70710678, 0.70710678]) >>> ft.select_font("times new roman",size=24) >>> rendered_text = ft.render("please") >>> rendered_text.view()
>>> ft = freetype.FreeType(dpi=133.3) >>> ft.select_font("arial unicode ms",size=24) >>> rendered_text = ft.render(u'Angstrom \xc5') >>> rendered_text.view()
And here is a kanji string:
>>> ft = freetype.FreeType(dpi=133.3) >>> ft.select_font("arial unicode ms",size=24) >>> rendered_text = ft.render(u'\u4ee0\u4ee1\u4ee2\u4ee3\u4ee4') >>> rendered_text.view()
Note: Someone with a more intelligent kanji string, please send it to me. I just picked 4 sequential characters at random from the unicode table.
create_glyph_list
method doesn't do any caching at all.
In simple tests, it looked like creating this list took the same amount
of time as rendering the glyphs. FreeType has a caching API that might
cut this time down to imperceptible. I doubt adding support for it is
that hard, but it isn't a high priority of mine. I'd be happy if someone
that knew the caching API took up the cause though.
Rendering glyphs also takes a noticeable amount of time. Building freetype with optimization flags sped it up by 33%, but it is still quite a bit more expensive than having the system draw the fonts. I don't know if this will be noticeable in the end.
The conversion from an array into a wxPython bitmap turned out to be a real mess and is probably really slow. A weave method -- and probably a bug fix to wxPython's bitmap masks rendering-- will improve this markedly.
The bug I'm talking about in the wxPython masks appears because Chaco flips the direction of positive y in the device context. As a result, bitmaps are rendered upside down. However, the mask is not! It is rendered right side up. I don't know if this is a wxPython issue or is a problem in the underlying windows API. Anyway, the current fix is to set the mask separately to the correct orientation which is time consuming. This will also come up in image rendering.
AlphaBlend
method which we can weavify
for use in wxPython. If GTK has a similar API method, then wxPython can
be made to work with alpha blending. OpenGL I'm sure can do it pretty
easily. TkInter... I don't know.
Anyway, alpha blending isn't the highest priority, so for now I'm sticking
with aliased rendering.