Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

503 lines (432 sloc) 13.843 kb
/*
* PIL FreeType Driver
*
* 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
* 2007-12-23 fl Fixed crash in family/style attribute fetch
* 2008-01-02 fl Handle Unicode filenames properly
*
* Copyright (c) 1998-2007 by Secret Labs AB
*/
#include "Python.h"
#include "Imaging.h"
#if !defined(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
#if !defined(Py_RETURN_NONE)
#define Py_RETURN_NONE return Py_INCREF(Py_None), Py_None
#endif
#if !defined(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 defined(HAVE_UNICODE) && PY_VERSION_HEX >= 0x02020000
if (!PyArg_ParseTupleAndKeywords(args, kw, "eti|is", kwlist,
Py_FileSystemDefaultEncoding, &filename,
&size, &index, &encoding))
return NULL;
#else
if (!PyArg_ParseTupleAndKeywords(args, kw, "si|is", kwlist,
&filename, &size, &index, &encoding))
return NULL;
#endif
if (!library) {
PyErr_SetString(
PyExc_IOError,
"failed to 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_RETURN_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")) {
if (self->face->family_name)
return PyString_FromString(self->face->family_name);
Py_RETURN_NONE;
}
if (!strcmp(name, "style")) {
if (self->face->style_name)
return PyString_FromString(self->face->style_name);
Py_RETURN_NONE;
}
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)
{
PyObject* m;
PyObject* d;
PyObject* v;
int major, minor, patch;
/* Patch object type */
Font_Type.ob_type = &PyType_Type;
m = Py_InitModule("_imagingft", _functions);
d = PyModule_GetDict(m);
if (FT_Init_FreeType(&library))
return; /* leave it uninitalized */
FT_Library_Version(library, &major, &minor, &patch);
#if PY_VERSION_HEX >= 0x02020000
v = PyString_FromFormat("%d.%d.%d", major, minor, patch);
#else
{
char buffer[100];
sprintf(buffer, "%d.%d.%d", major, minor, patch);
v = PyString_FromString(buffer);
}
#endif
PyDict_SetItemString(d, "freetype2_version", v);
}
Jump to Line
Something went wrong with that request. Please try again.