mirror of
https://github.com/python-pillow/Pillow.git
synced 2024-12-25 17:36:18 +03:00
af5228896a
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.
560 lines
15 KiB
C
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
|
|
|