Pillow/_imagingft.c
Brian Crowell af5228896a py3k: Add module initialization and unicode/bytes int/long thunks
This commit:

* Adds Python 3 module initialization functions. I split out the main init
  of each module into a static setup_module function.
* Adds a py3.h which unifies int/long in Python 3 and unicode/bytes in
  Python 2. _imagingft.c unfortunately looks a little kludgy after this
  because it was already using PyUnicode functions, and I had to mix and
  match there manually.

With this commit, the modules all build successfully under Python 3.

What this commit does NOT do is patch all of the uses of PyArg_ParseTuple
and Py_BuildValue, which all need to be checked for proper use of bytes
and unicode codes. It also does not let selftest.py run yet, because there
are probably hundreds of issues to fix in the Python code itself.
2013-01-10 08:46:37 -06:00

560 lines
15 KiB
C

/*
* 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 !defined(Py_RETURN_NONE)
#define Py_RETURN_NONE return Py_INCREF(Py_None), Py_None
#endif
#define KEEP_PY_UNICODE
#include "py3.h"
#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;
static 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, "eti|is", kwlist,
Py_FileSystemDefaultEncoding, &filename,
&size, &index, &encoding))
return NULL;
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 (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;
}
#if PY_VERSION_HEX < 0x03000000
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;
}
#endif
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 PY_VERSION_HEX >= 0x03000000
if (!PyUnicode_Check(string)) {
#else
if (!PyUnicode_Check(string) && !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 PY_VERSION_HEX >= 0x03000000
if (!PyUnicode_Check(string)) {
#else
if (!PyUnicode_Check(string) && !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 PY_VERSION_HEX >= 0x03000000
if (!PyUnicode_Check(string)) {
#else
if (!PyUnicode_Check(string) && !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_family(FontObject* self, void* closure)
{
#if PY_VERSION_HEX >= 0x03000000
if (self->face->family_name)
return PyUnicode_FromString(self->face->family_name);
#else
if (self->face->family_name)
return PyString_FromString(self->face->family_name);
#endif
Py_RETURN_NONE;
}
static PyObject*
font_getattr_style(FontObject* self, void* closure)
{
#if PY_VERSION_HEX >= 0x03000000
if (self->face->style_name)
return PyUnicode_FromString(self->face->style_name);
#else
if (self->face->style_name)
return PyString_FromString(self->face->style_name);
#endif
Py_RETURN_NONE;
}
static PyObject*
font_getattr_ascent(FontObject* self, void* closure)
{
return PyInt_FromLong(PIXEL(self->face->size->metrics.ascender));
}
static PyObject*
font_getattr_descent(FontObject* self, void* closure)
{
return PyInt_FromLong(-PIXEL(self->face->size->metrics.descender));
}
static PyObject*
font_getattr_glyphs(FontObject* self, void* closure)
{
return PyInt_FromLong(self->face->num_glyphs);
}
static struct PyGetSetDef font_getsetters[] = {
{ "family", (getter) font_getattr_family },
{ "style", (getter) font_getattr_style },
{ "ascent", (getter) font_getattr_ascent },
{ "descent", (getter) font_getattr_descent },
{ "glyphs", (getter) font_getattr_glyphs },
{ NULL }
};
static PyTypeObject Font_Type = {
PyVarObject_HEAD_INIT(NULL, 0)
"Font", sizeof(FontObject), 0,
/* methods */
(destructor)font_dealloc, /* tp_dealloc */
0, /* tp_print */
0, /*tp_getattr*/
0, /*tp_setattr*/
0, /*tp_compare*/
0, /*tp_repr*/
0, /*tp_as_number */
0, /*tp_as_sequence */
0, /*tp_as_mapping */
0, /*tp_hash*/
0, /*tp_call*/
0, /*tp_str*/
0, /*tp_getattro*/
0, /*tp_setattro*/
0, /*tp_as_buffer*/
Py_TPFLAGS_DEFAULT, /*tp_flags*/
0, /*tp_doc*/
0, /*tp_traverse*/
0, /*tp_clear*/
0, /*tp_richcompare*/
0, /*tp_weaklistoffset*/
0, /*tp_iter*/
0, /*tp_iternext*/
font_methods, /*tp_methods*/
0, /*tp_members*/
font_getsetters, /*tp_getset*/
};
static PyMethodDef _functions[] = {
{"getfont", (PyCFunction) getfont, METH_VARARGS|METH_KEYWORDS},
{NULL, NULL}
};
static int
setup_module(PyObject* m) {
PyObject* d;
PyObject* v;
int major, minor, patch;
d = PyModule_GetDict(m);
/* Ready object type */
PyType_Ready(&Font_Type);
if (FT_Init_FreeType(&library))
return 0; /* leave it uninitalized */
FT_Library_Version(library, &major, &minor, &patch);
#if PY_VERSION_HEX >= 0x03000000
v = PyUnicode_FromFormat("%d.%d.%d", major, minor, patch);
#else
v = PyString_FromFormat("%d.%d.%d", major, minor, patch);
#endif
PyDict_SetItemString(d, "freetype2_version", v);
return 0;
}
#if PY_VERSION_HEX >= 0x03000000
PyMODINIT_FUNC
PyInit__imagingft(void) {
PyObject* m;
static PyModuleDef module_def = {
PyModuleDef_HEAD_INIT,
"_imagingft", /* m_name */
NULL, /* m_doc */
-1, /* m_size */
_functions, /* m_methods */
};
m = PyModule_Create(&module_def);
if (setup_module(m) < 0)
return NULL;
return m;
}
#else
PyMODINIT_FUNC
init_imagingft(void)
{
PyObject* m = Py_InitModule("_imagingft", _functions);
setup_module(m);
}
#endif