Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/Imaging/_imagingft.c
diff options
context:
space:
mode:
Diffstat (limited to 'Imaging/_imagingft.c')
-rw-r--r--Imaging/_imagingft.c463
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);
+}