diff options
Diffstat (limited to 'Imaging/_imagingft.c')
-rw-r--r-- | Imaging/_imagingft.c | 463 |
1 files changed, 463 insertions, 0 deletions
diff --git a/Imaging/_imagingft.c b/Imaging/_imagingft.c new file mode 100644 index 0000000..9ab8470 --- /dev/null +++ b/Imaging/_imagingft.c @@ -0,0 +1,463 @@ +/* + * PIL FreeType Driver + * $Id: _imagingft.c 2756 2006-06-19 06:07:18Z fredrik $ + * + * a FreeType 2.X driver for PIL + * + * history: + * 2001-02-17 fl Created (based on old experimental freetype 1.0 code) + * 2001-04-18 fl Fixed some egcs compiler nits + * 2002-11-08 fl Added unicode support; more font metrics, etc + * 2003-05-20 fl Fixed compilation under 1.5.2 and newer non-unicode builds + * 2003-09-27 fl Added charmap encoding support + * 2004-05-15 fl Fixed compilation for FreeType 2.1.8 + * 2004-09-10 fl Added support for monochrome bitmaps + * 2006-06-18 fl Fixed glyph bearing calculation + * + * Copyright (c) 1998-2006 by Secret Labs AB + */ + +#include "Python.h" +#include "Imaging.h" + +#ifndef USE_FREETYPE_2_0 +/* undef/comment out to use freetype 2.0 */ +#define USE_FREETYPE_2_1 +#endif + +#if defined(USE_FREETYPE_2_1) +/* freetype 2.1 and newer */ +#include <ft2build.h> +#include FT_FREETYPE_H +#else +/* freetype 2.0 */ +#include <freetype/freetype.h> +#endif + +#if PY_VERSION_HEX < 0x01060000 +#define PyObject_New PyObject_NEW +#define PyObject_Del PyMem_DEL +#endif + +#if PY_VERSION_HEX >= 0x01060000 +#if PY_VERSION_HEX < 0x02020000 || defined(Py_USING_UNICODE) +/* defining this enables unicode support (default under 1.6a1 and later) */ +#define HAVE_UNICODE +#endif +#endif + +#ifndef FT_LOAD_TARGET_MONO +#define FT_LOAD_TARGET_MONO FT_LOAD_MONOCHROME +#endif + +/* -------------------------------------------------------------------- */ +/* error table */ + +#undef FTERRORS_H +#undef __FTERRORS_H__ + +#define FT_ERRORDEF( e, v, s ) { e, s }, +#define FT_ERROR_START_LIST { +#define FT_ERROR_END_LIST { 0, 0 } }; + +struct { + int code; + const char* message; +} ft_errors[] = + +#include <freetype/fterrors.h> + +/* -------------------------------------------------------------------- */ +/* font objects */ + +static FT_Library library; + +typedef struct { + PyObject_HEAD + FT_Face face; +} FontObject; + +staticforward PyTypeObject Font_Type; + +/* round a 26.6 pixel coordinate to the nearest larger integer */ +#define PIXEL(x) ((((x)+63) & -64)>>6) + +static PyObject* +geterror(int code) +{ + int i; + + for (i = 0; ft_errors[i].message; i++) + if (ft_errors[i].code == code) { + PyErr_SetString(PyExc_IOError, ft_errors[i].message); + return NULL; + } + + PyErr_SetString(PyExc_IOError, "unknown freetype error"); + return NULL; +} + +static PyObject* +getfont(PyObject* self_, PyObject* args, PyObject* kw) +{ + /* create a font object from a file name and a size (in pixels) */ + + FontObject* self; + int error; + + char* filename; + int size; + int index = 0; + unsigned char* encoding = NULL; + static char* kwlist[] = { + "filename", "size", "index", "encoding", NULL + }; + if (!PyArg_ParseTupleAndKeywords(args, kw, "si|is", kwlist, + &filename, &size, &index, &encoding)) + return NULL; + + if (!library && FT_Init_FreeType(&library)) { + PyErr_SetString( + PyExc_IOError, + "cannot initialize FreeType library" + ); + return NULL; + } + + self = PyObject_New(FontObject, &Font_Type); + if (!self) + return NULL; + + error = FT_New_Face(library, filename, index, &self->face); + + if (!error) + error = FT_Set_Pixel_Sizes(self->face, 0, size); + + if (!error && encoding && strlen((char*) encoding) == 4) { + FT_Encoding encoding_tag = FT_MAKE_TAG( + encoding[0], encoding[1], encoding[2], encoding[3] + ); + error = FT_Select_Charmap(self->face, encoding_tag); + } + + if (error) { + PyObject_Del(self); + return geterror(error); + } + + return (PyObject*) self; +} + +static int +font_getchar(PyObject* string, int index, FT_ULong* char_out) +{ +#if defined(HAVE_UNICODE) + if (PyUnicode_Check(string)) { + Py_UNICODE* p = PyUnicode_AS_UNICODE(string); + int size = PyUnicode_GET_SIZE(string); + if (index >= size) + return 0; + *char_out = p[index]; + return 1; + } +#endif + if (PyString_Check(string)) { + unsigned char* p = (unsigned char*) PyString_AS_STRING(string); + int size = PyString_GET_SIZE(string); + if (index >= size) + return 0; + *char_out = (unsigned char) p[index]; + return 1; + } + return 0; +} + +static PyObject* +font_getsize(FontObject* self, PyObject* args) +{ + int i, x; + FT_ULong ch; + FT_Face face; + int xoffset; + FT_Bool kerning = FT_HAS_KERNING(self->face); + FT_UInt last_index = 0; + + /* calculate size and bearing for a given string */ + + PyObject* string; + if (!PyArg_ParseTuple(args, "O:getsize", &string)) + return NULL; + +#if defined(HAVE_UNICODE) + if (!PyUnicode_Check(string) && !PyString_Check(string)) { +#else + if (!PyString_Check(string)) { +#endif + PyErr_SetString(PyExc_TypeError, "expected string"); + return NULL; + } + + face = NULL; + xoffset = 0; + + for (x = i = 0; font_getchar(string, i, &ch); i++) { + int index, error; + face = self->face; + index = FT_Get_Char_Index(face, ch); + if (kerning && last_index && index) { + FT_Vector delta; + FT_Get_Kerning(self->face, last_index, index, ft_kerning_default, + &delta); + x += delta.x; + } + error = FT_Load_Glyph(face, index, FT_LOAD_DEFAULT); + if (error) + return geterror(error); + if (i == 0) + xoffset = face->glyph->metrics.horiBearingX; + x += face->glyph->metrics.horiAdvance; + last_index = index; + } + + if (face) { + int offset; + /* left bearing */ + if (xoffset < 0) + x -= xoffset; + else + xoffset = 0; + /* right bearing */ + offset = face->glyph->metrics.horiAdvance - + face->glyph->metrics.width - + face->glyph->metrics.horiBearingX; + if (offset < 0) + x -= offset; + } + + return Py_BuildValue( + "(ii)(ii)", + PIXEL(x), PIXEL(self->face->size->metrics.height), + PIXEL(xoffset), 0 + ); +} + +static PyObject* +font_getabc(FontObject* self, PyObject* args) +{ + FT_ULong ch; + FT_Face face; + double a, b, c; + + /* calculate ABC values for a given string */ + + PyObject* string; + if (!PyArg_ParseTuple(args, "O:getabc", &string)) + return NULL; + +#if defined(HAVE_UNICODE) + if (!PyUnicode_Check(string) && !PyString_Check(string)) { +#else + if (!PyString_Check(string)) { +#endif + PyErr_SetString(PyExc_TypeError, "expected string"); + return NULL; + } + + if (font_getchar(string, 0, &ch)) { + int index, error; + face = self->face; + index = FT_Get_Char_Index(face, ch); + error = FT_Load_Glyph(face, index, FT_LOAD_DEFAULT); + if (error) + return geterror(error); + a = face->glyph->metrics.horiBearingX / 64.0; + b = face->glyph->metrics.width / 64.0; + c = (face->glyph->metrics.horiAdvance - + face->glyph->metrics.horiBearingX - + face->glyph->metrics.width) / 64.0; + } else + a = b = c = 0.0; + + return Py_BuildValue("ddd", a, b, c); +} + +static PyObject* +font_render(FontObject* self, PyObject* args) +{ + int i, x, y; + Imaging im; + int index, error, ascender; + int load_flags; + unsigned char *source; + FT_ULong ch; + FT_GlyphSlot glyph; + FT_Bool kerning = FT_HAS_KERNING(self->face); + FT_UInt last_index = 0; + + /* render string into given buffer (the buffer *must* have + the right size, or this will crash) */ + PyObject* string; + long id; + int mask = 0; + if (!PyArg_ParseTuple(args, "Ol|i:render", &string, &id, &mask)) + return NULL; + +#if defined(HAVE_UNICODE) + if (!PyUnicode_Check(string) && !PyString_Check(string)) { +#else + if (!PyString_Check(string)) { +#endif + PyErr_SetString(PyExc_TypeError, "expected string"); + return NULL; + } + + im = (Imaging) id; + + load_flags = FT_LOAD_RENDER; + if (mask) + load_flags |= FT_LOAD_TARGET_MONO; + + for (x = i = 0; font_getchar(string, i, &ch); i++) { + if (i == 0 && self->face->glyph->metrics.horiBearingX < 0) + x = PIXEL(self->face->glyph->metrics.horiBearingX); + index = FT_Get_Char_Index(self->face, ch); + if (kerning && last_index && index) { + FT_Vector delta; + FT_Get_Kerning(self->face, last_index, index, ft_kerning_default, + &delta); + x += delta.x >> 6; + } + error = FT_Load_Glyph(self->face, index, load_flags); + if (error) + return geterror(error); + glyph = self->face->glyph; + if (mask) { + /* use monochrome mask (on palette images, etc) */ + int xx, x0, x1; + source = (unsigned char*) glyph->bitmap.buffer; + ascender = PIXEL(self->face->size->metrics.ascender); + xx = x + glyph->bitmap_left; + x0 = 0; + x1 = glyph->bitmap.width; + if (xx < 0) + x0 = -xx; + if (xx + x1 > im->xsize) + x1 = im->xsize - xx; + for (y = 0; y < glyph->bitmap.rows; y++) { + int yy = y + ascender - glyph->bitmap_top; + if (yy >= 0 && yy < im->ysize) { + /* blend this glyph into the buffer */ + unsigned char *target = im->image8[yy] + xx; + int i, j, m = 128; + for (i = j = 0; j < x1; j++) { + if (j >= x0 && (source[i] & m)) + target[j] = 255; + if (!(m >>= 1)) { + m = 128; + i++; + } + } + } + source += glyph->bitmap.pitch; + } + } else { + /* use antialiased rendering */ + int xx, x0, x1; + source = (unsigned char*) glyph->bitmap.buffer; + ascender = PIXEL(self->face->size->metrics.ascender); + xx = x + glyph->bitmap_left; + x0 = 0; + x1 = glyph->bitmap.width; + if (xx < 0) + x0 = -xx; + if (xx + x1 > im->xsize) + x1 = im->xsize - xx; + for (y = 0; y < glyph->bitmap.rows; y++) { + int yy = y + ascender - glyph->bitmap_top; + if (yy >= 0 && yy < im->ysize) { + /* blend this glyph into the buffer */ + int i; + unsigned char *target = im->image8[yy] + xx; + for (i = x0; i < x1; i++) { + if (target[i] < source[i]) + target[i] = source[i]; + } + } + source += glyph->bitmap.pitch; + } + } + x += PIXEL(glyph->metrics.horiAdvance); + last_index = index; + } + + Py_INCREF(Py_None); + return Py_None; +} + +static void +font_dealloc(FontObject* self) +{ + FT_Done_Face(self->face); + PyObject_Del(self); +} + +static PyMethodDef font_methods[] = { + {"render", (PyCFunction) font_render, METH_VARARGS}, + {"getsize", (PyCFunction) font_getsize, METH_VARARGS}, + {"getabc", (PyCFunction) font_getabc, METH_VARARGS}, + {NULL, NULL} +}; + +static PyObject* +font_getattr(FontObject* self, char* name) +{ + PyObject* res; + + res = Py_FindMethod(font_methods, (PyObject*) self, name); + + if (res) + return res; + + PyErr_Clear(); + + /* attributes */ + if (!strcmp(name, "family")) + return PyString_FromString(self->face->family_name); + if (!strcmp(name, "style")) + return PyString_FromString(self->face->style_name); + + if (!strcmp(name, "ascent")) + return PyInt_FromLong(PIXEL(self->face->size->metrics.ascender)); + if (!strcmp(name, "descent")) + return PyInt_FromLong(-PIXEL(self->face->size->metrics.descender)); + + if (!strcmp(name, "glyphs")) + /* number of glyphs provided by this font */ + return PyInt_FromLong(self->face->num_glyphs); + + PyErr_SetString(PyExc_AttributeError, name); + return NULL; +} + +statichere PyTypeObject Font_Type = { + PyObject_HEAD_INIT(NULL) + 0, "Font", sizeof(FontObject), 0, + /* methods */ + (destructor)font_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + (getattrfunc)font_getattr, /* tp_getattr */ +}; + +static PyMethodDef _functions[] = { + {"getfont", (PyCFunction) getfont, METH_VARARGS|METH_KEYWORDS}, + {NULL, NULL} +}; + +DL_EXPORT(void) +init_imagingft(void) +{ + /* Patch object type */ + Font_Type.ob_type = &PyType_Type; + + Py_InitModule("_imagingft", _functions); +} |