4
* a FreeType 2.X driver for PIL
7
* 2001-02-17 fl Created (based on old experimental freetype 1.0 code)
8
* 2001-04-18 fl Fixed some egcs compiler nits
9
* 2002-11-08 fl Added unicode support; more font metrics, etc
10
* 2003-05-20 fl Fixed compilation under 1.5.2 and newer non-unicode builds
11
* 2003-09-27 fl Added charmap encoding support
12
* 2004-05-15 fl Fixed compilation for FreeType 2.1.8
13
* 2004-09-10 fl Added support for monochrome bitmaps
14
* 2006-06-18 fl Fixed glyph bearing calculation
15
* 2007-12-23 fl Fixed crash in family/style attribute fetch
16
* 2008-01-02 fl Handle Unicode filenames properly
18
* Copyright (c) 1998-2007 by Secret Labs AB
24
#if !defined(USE_FREETYPE_2_0)
25
/* undef/comment out to use freetype 2.0 */
26
#define USE_FREETYPE_2_1
29
#if defined(USE_FREETYPE_2_1)
30
/* freetype 2.1 and newer */
32
#include FT_FREETYPE_H
35
#include <freetype/freetype.h>
38
#if PY_VERSION_HEX < 0x01060000
39
#define PyObject_New PyObject_NEW
40
#define PyObject_Del PyMem_DEL
43
#if PY_VERSION_HEX >= 0x01060000
44
#if PY_VERSION_HEX < 0x02020000 || defined(Py_USING_UNICODE)
45
/* defining this enables unicode support (default under 1.6a1 and later) */
50
#if !defined(Py_RETURN_NONE)
51
#define Py_RETURN_NONE return Py_INCREF(Py_None), Py_None
54
#if !defined(FT_LOAD_TARGET_MONO)
55
#define FT_LOAD_TARGET_MONO FT_LOAD_MONOCHROME
58
/* -------------------------------------------------------------------- */
64
#define FT_ERRORDEF( e, v, s ) { e, s },
65
#define FT_ERROR_START_LIST {
66
#define FT_ERROR_END_LIST { 0, 0 } };
73
#include <freetype/fterrors.h>
75
/* -------------------------------------------------------------------- */
78
static FT_Library library;
85
staticforward PyTypeObject Font_Type;
87
/* round a 26.6 pixel coordinate to the nearest larger integer */
88
#define PIXEL(x) ((((x)+63) & -64)>>6)
95
for (i = 0; ft_errors[i].message; i++)
96
if (ft_errors[i].code == code) {
97
PyErr_SetString(PyExc_IOError, ft_errors[i].message);
101
PyErr_SetString(PyExc_IOError, "unknown freetype error");
106
getfont(PyObject* self_, PyObject* args, PyObject* kw)
108
/* create a font object from a file name and a size (in pixels) */
116
unsigned char* encoding = NULL;
117
static char* kwlist[] = {
118
"filename", "size", "index", "encoding", NULL
121
#if defined(HAVE_UNICODE) && PY_VERSION_HEX >= 0x02020000
122
if (!PyArg_ParseTupleAndKeywords(args, kw, "eti|is", kwlist,
123
Py_FileSystemDefaultEncoding, &filename,
124
&size, &index, &encoding))
127
if (!PyArg_ParseTupleAndKeywords(args, kw, "si|is", kwlist,
128
&filename, &size, &index, &encoding))
135
"failed to initialize FreeType library"
140
self = PyObject_New(FontObject, &Font_Type);
144
error = FT_New_Face(library, filename, index, &self->face);
147
error = FT_Set_Pixel_Sizes(self->face, 0, size);
149
if (!error && encoding && strlen((char*) encoding) == 4) {
150
FT_Encoding encoding_tag = FT_MAKE_TAG(
151
encoding[0], encoding[1], encoding[2], encoding[3]
153
error = FT_Select_Charmap(self->face, encoding_tag);
158
return geterror(error);
161
return (PyObject*) self;
165
font_getchar(PyObject* string, int index, FT_ULong* char_out)
167
#if defined(HAVE_UNICODE)
168
if (PyUnicode_Check(string)) {
169
Py_UNICODE* p = PyUnicode_AS_UNICODE(string);
170
int size = PyUnicode_GET_SIZE(string);
173
*char_out = p[index];
177
if (PyString_Check(string)) {
178
unsigned char* p = (unsigned char*) PyString_AS_STRING(string);
179
int size = PyString_GET_SIZE(string);
182
*char_out = (unsigned char) p[index];
189
font_getsize(FontObject* self, PyObject* args)
195
FT_Bool kerning = FT_HAS_KERNING(self->face);
196
FT_UInt last_index = 0;
198
/* calculate size and bearing for a given string */
201
if (!PyArg_ParseTuple(args, "O:getsize", &string))
204
#if defined(HAVE_UNICODE)
205
if (!PyUnicode_Check(string) && !PyString_Check(string)) {
207
if (!PyString_Check(string)) {
209
PyErr_SetString(PyExc_TypeError, "expected string");
216
for (x = i = 0; font_getchar(string, i, &ch); i++) {
219
index = FT_Get_Char_Index(face, ch);
220
if (kerning && last_index && index) {
222
FT_Get_Kerning(self->face, last_index, index, ft_kerning_default,
226
error = FT_Load_Glyph(face, index, FT_LOAD_DEFAULT);
228
return geterror(error);
230
xoffset = face->glyph->metrics.horiBearingX;
231
x += face->glyph->metrics.horiAdvance;
243
offset = face->glyph->metrics.horiAdvance -
244
face->glyph->metrics.width -
245
face->glyph->metrics.horiBearingX;
250
return Py_BuildValue(
252
PIXEL(x), PIXEL(self->face->size->metrics.height),
258
font_getabc(FontObject* self, PyObject* args)
264
/* calculate ABC values for a given string */
267
if (!PyArg_ParseTuple(args, "O:getabc", &string))
270
#if defined(HAVE_UNICODE)
271
if (!PyUnicode_Check(string) && !PyString_Check(string)) {
273
if (!PyString_Check(string)) {
275
PyErr_SetString(PyExc_TypeError, "expected string");
279
if (font_getchar(string, 0, &ch)) {
282
index = FT_Get_Char_Index(face, ch);
283
error = FT_Load_Glyph(face, index, FT_LOAD_DEFAULT);
285
return geterror(error);
286
a = face->glyph->metrics.horiBearingX / 64.0;
287
b = face->glyph->metrics.width / 64.0;
288
c = (face->glyph->metrics.horiAdvance -
289
face->glyph->metrics.horiBearingX -
290
face->glyph->metrics.width) / 64.0;
294
return Py_BuildValue("ddd", a, b, c);
298
font_render(FontObject* self, PyObject* args)
302
int index, error, ascender;
304
unsigned char *source;
307
FT_Bool kerning = FT_HAS_KERNING(self->face);
308
FT_UInt last_index = 0;
310
/* render string into given buffer (the buffer *must* have
311
the right size, or this will crash) */
315
if (!PyArg_ParseTuple(args, "Ol|i:render", &string, &id, &mask))
318
#if defined(HAVE_UNICODE)
319
if (!PyUnicode_Check(string) && !PyString_Check(string)) {
321
if (!PyString_Check(string)) {
323
PyErr_SetString(PyExc_TypeError, "expected string");
329
load_flags = FT_LOAD_RENDER;
331
load_flags |= FT_LOAD_TARGET_MONO;
333
for (x = i = 0; font_getchar(string, i, &ch); i++) {
334
if (i == 0 && self->face->glyph->metrics.horiBearingX < 0)
335
x = -PIXEL(self->face->glyph->metrics.horiBearingX);
336
index = FT_Get_Char_Index(self->face, ch);
337
if (kerning && last_index && index) {
339
FT_Get_Kerning(self->face, last_index, index, ft_kerning_default,
343
error = FT_Load_Glyph(self->face, index, load_flags);
345
return geterror(error);
346
glyph = self->face->glyph;
348
/* use monochrome mask (on palette images, etc) */
350
source = (unsigned char*) glyph->bitmap.buffer;
351
ascender = PIXEL(self->face->size->metrics.ascender);
352
xx = x + glyph->bitmap_left;
354
x1 = glyph->bitmap.width;
357
if (xx + x1 > im->xsize)
359
for (y = 0; y < glyph->bitmap.rows; y++) {
360
int yy = y + ascender - glyph->bitmap_top;
361
if (yy >= 0 && yy < im->ysize) {
362
/* blend this glyph into the buffer */
363
unsigned char *target = im->image8[yy] + xx;
365
for (i = j = 0; j < x1; j++) {
366
if (j >= x0 && (source[i] & m))
374
source += glyph->bitmap.pitch;
377
/* use antialiased rendering */
379
source = (unsigned char*) glyph->bitmap.buffer;
380
ascender = PIXEL(self->face->size->metrics.ascender);
381
xx = x + glyph->bitmap_left;
383
x1 = glyph->bitmap.width;
386
if (xx + x1 > im->xsize)
388
for (y = 0; y < glyph->bitmap.rows; y++) {
389
int yy = y + ascender - glyph->bitmap_top;
390
if (yy >= 0 && yy < im->ysize) {
391
/* blend this glyph into the buffer */
393
unsigned char *target = im->image8[yy] + xx;
394
for (i = x0; i < x1; i++) {
395
if (target[i] < source[i])
396
target[i] = source[i];
399
source += glyph->bitmap.pitch;
402
x += PIXEL(glyph->metrics.horiAdvance);
410
font_dealloc(FontObject* self)
412
FT_Done_Face(self->face);
416
static PyMethodDef font_methods[] = {
417
{"render", (PyCFunction) font_render, METH_VARARGS},
418
{"getsize", (PyCFunction) font_getsize, METH_VARARGS},
419
{"getabc", (PyCFunction) font_getabc, METH_VARARGS},
424
font_getattr(FontObject* self, char* name)
428
res = Py_FindMethod(font_methods, (PyObject*) self, name);
436
if (!strcmp(name, "family")) {
437
if (self->face->family_name)
438
return PyString_FromString(self->face->family_name);
441
if (!strcmp(name, "style")) {
442
if (self->face->style_name)
443
return PyString_FromString(self->face->style_name);
446
if (!strcmp(name, "ascent"))
447
return PyInt_FromLong(PIXEL(self->face->size->metrics.ascender));
448
if (!strcmp(name, "descent"))
449
return PyInt_FromLong(-PIXEL(self->face->size->metrics.descender));
451
if (!strcmp(name, "glyphs"))
452
/* number of glyphs provided by this font */
453
return PyInt_FromLong(self->face->num_glyphs);
455
PyErr_SetString(PyExc_AttributeError, name);
459
statichere PyTypeObject Font_Type = {
460
PyObject_HEAD_INIT(NULL)
461
0, "Font", sizeof(FontObject), 0,
463
(destructor)font_dealloc, /* tp_dealloc */
465
(getattrfunc)font_getattr, /* tp_getattr */
468
static PyMethodDef _functions[] = {
469
{"getfont", (PyCFunction) getfont, METH_VARARGS|METH_KEYWORDS},
479
int major, minor, patch;
481
/* Patch object type */
482
Font_Type.ob_type = &PyType_Type;
484
m = Py_InitModule("_imagingft", _functions);
485
d = PyModule_GetDict(m);
487
if (FT_Init_FreeType(&library))
488
return; /* leave it uninitalized */
490
FT_Library_Version(library, &major, &minor, &patch);
492
#if PY_VERSION_HEX >= 0x02020000
493
v = PyString_FromFormat("%d.%d.%d", major, minor, patch);
497
sprintf(buffer, "%d.%d.%d", major, minor, patch);
498
v = PyString_FromString(buffer);
501
PyDict_SetItemString(d, "freetype2_version", v);