Pillow/src/_imagingft.c

1291 lines
39 KiB
C
Raw Normal View History

2010-07-31 06:52:47 +04:00
/*
* 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
*/
2019-03-26 05:50:57 +03:00
#define PY_SSIZE_T_CLEAN
2010-07-31 06:52:47 +04:00
#include "Python.h"
#include "Imaging.h"
#include <ft2build.h>
#include FT_FREETYPE_H
#include FT_GLYPH_H
2019-07-28 23:40:03 +03:00
#include FT_STROKER_H
2019-06-12 13:27:11 +03:00
#include FT_MULTIPLE_MASTERS_H
#include FT_SFNT_NAMES_H
#define KEEP_PY_UNICODE
2020-05-23 22:24:11 +03:00
#ifndef _WIN32
#include <dlfcn.h>
2017-09-22 17:48:55 +03:00
#endif
2010-07-31 06:52:47 +04:00
#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 } };
2016-01-27 08:09:26 +03:00
#include <raqm.h>
2017-06-13 19:03:23 +03:00
#define LAYOUT_FALLBACK 0
#define LAYOUT_RAQM 1
2016-01-27 08:09:26 +03:00
typedef struct
{
2019-05-31 13:31:41 +03:00
int index, x_offset, x_advance, y_offset, y_advance;
2016-01-27 08:09:26 +03:00
unsigned int cluster;
} GlyphInfo;
2010-07-31 06:52:47 +04:00
struct {
int code;
const char* message;
} ft_errors[] =
2013-11-28 16:58:43 +04:00
#include FT_ERRORS_H
2010-07-31 06:52:47 +04:00
/* -------------------------------------------------------------------- */
/* font objects */
static FT_Library library;
typedef struct {
PyObject_HEAD
FT_Face face;
unsigned char *font_bytes;
2017-06-13 19:03:23 +03:00
int layout_engine;
2010-07-31 06:52:47 +04:00
} FontObject;
static PyTypeObject Font_Type;
2010-07-31 06:52:47 +04:00
typedef const char* (*t_raqm_version_string) (void);
2019-05-31 13:25:28 +03:00
typedef bool (*t_raqm_version_atleast)(unsigned int major,
unsigned int minor,
unsigned int micro);
2017-09-24 23:52:01 +03:00
typedef raqm_t* (*t_raqm_create)(void);
typedef int (*t_raqm_set_text)(raqm_t *rq,
const uint32_t *text,
size_t len);
2017-09-24 23:52:01 +03:00
typedef bool (*t_raqm_set_text_utf8) (raqm_t *rq,
const char *text,
size_t len);
2017-09-24 23:52:01 +03:00
typedef bool (*t_raqm_set_par_direction) (raqm_t *rq,
raqm_direction_t dir);
typedef bool (*t_raqm_set_language) (raqm_t *rq,
const char *lang,
size_t start,
size_t len);
2017-09-24 23:52:01 +03:00
typedef bool (*t_raqm_add_font_feature) (raqm_t *rq,
const char *feature,
int len);
2017-09-24 23:52:01 +03:00
typedef bool (*t_raqm_set_freetype_face) (raqm_t *rq,
FT_Face face);
2017-09-24 23:52:01 +03:00
typedef bool (*t_raqm_layout) (raqm_t *rq);
typedef raqm_glyph_t* (*t_raqm_get_glyphs) (raqm_t *rq,
size_t *length);
typedef raqm_glyph_t_01* (*t_raqm_get_glyphs_01) (raqm_t *rq,
size_t *length);
2017-09-24 23:52:01 +03:00
typedef void (*t_raqm_destroy) (raqm_t *rq);
typedef struct {
2017-09-22 21:29:52 +03:00
void* raqm;
int version;
t_raqm_version_string version_string;
2019-05-31 13:25:28 +03:00
t_raqm_version_atleast version_atleast;
2017-09-24 23:52:01 +03:00
t_raqm_create create;
t_raqm_set_text set_text;
t_raqm_set_text_utf8 set_text_utf8;
t_raqm_set_par_direction set_par_direction;
t_raqm_set_language set_language;
2017-09-24 23:52:01 +03:00
t_raqm_add_font_feature add_font_feature;
t_raqm_set_freetype_face set_freetype_face;
2018-01-27 09:02:56 +03:00
t_raqm_layout layout;
2017-09-24 23:52:01 +03:00
t_raqm_get_glyphs get_glyphs;
t_raqm_get_glyphs_01 get_glyphs_01;
2017-09-24 23:52:01 +03:00
t_raqm_destroy destroy;
} p_raqm_func;
static p_raqm_func p_raqm;
2017-09-22 21:29:52 +03:00
2010-07-31 06:52:47 +04:00
/* 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;
2020-05-10 12:56:36 +03:00
for (i = 0; ft_errors[i].message; i++) {
2010-07-31 06:52:47 +04:00
if (ft_errors[i].code == code) {
PyErr_SetString(PyExc_OSError, ft_errors[i].message);
2010-07-31 06:52:47 +04:00
return NULL;
}
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
PyErr_SetString(PyExc_OSError, "unknown freetype error");
2010-07-31 06:52:47 +04:00
return NULL;
}
static int
setraqm(void)
{
/* set the static function pointers for dynamic raqm linking */
p_raqm.raqm = NULL;
2017-09-22 17:48:55 +03:00
/* Microsoft needs a totally different system */
2020-05-23 22:24:11 +03:00
#ifndef _WIN32
p_raqm.raqm = dlopen("libraqm.so.0", RTLD_LAZY);
2017-09-22 18:21:54 +03:00
if (!p_raqm.raqm) {
p_raqm.raqm = dlopen("libraqm.dylib", RTLD_LAZY);
}
2017-09-22 17:48:55 +03:00
#else
p_raqm.raqm = LoadLibrary("libraqm");
2020-06-13 07:44:05 +03:00
/* MSYS */
2020-05-23 22:24:11 +03:00
if (!p_raqm.raqm) {
p_raqm.raqm = LoadLibrary("libraqm-0");
}
2017-09-22 17:48:55 +03:00
#endif
if (!p_raqm.raqm) {
return 1;
}
2020-05-23 22:24:11 +03:00
#ifndef _WIN32
p_raqm.version_string = (t_raqm_version_atleast)dlsym(p_raqm.raqm, "raqm_version_string");
2019-05-31 13:25:28 +03:00
p_raqm.version_atleast = (t_raqm_version_atleast)dlsym(p_raqm.raqm, "raqm_version_atleast");
2017-09-24 23:52:01 +03:00
p_raqm.create = (t_raqm_create)dlsym(p_raqm.raqm, "raqm_create");
p_raqm.set_text = (t_raqm_set_text)dlsym(p_raqm.raqm, "raqm_set_text");
p_raqm.set_text_utf8 = (t_raqm_set_text_utf8)dlsym(p_raqm.raqm, "raqm_set_text_utf8");
p_raqm.set_par_direction = (t_raqm_set_par_direction)dlsym(p_raqm.raqm, "raqm_set_par_direction");
p_raqm.set_language = (t_raqm_set_language)dlsym(p_raqm.raqm, "raqm_set_language");
2017-09-24 23:52:01 +03:00
p_raqm.add_font_feature = (t_raqm_add_font_feature)dlsym(p_raqm.raqm, "raqm_add_font_feature");
p_raqm.set_freetype_face = (t_raqm_set_freetype_face)dlsym(p_raqm.raqm, "raqm_set_freetype_face");
p_raqm.layout = (t_raqm_layout)dlsym(p_raqm.raqm, "raqm_layout");
p_raqm.destroy = (t_raqm_destroy)dlsym(p_raqm.raqm, "raqm_destroy");
if(dlsym(p_raqm.raqm, "raqm_index_to_position")) {
p_raqm.get_glyphs = (t_raqm_get_glyphs)dlsym(p_raqm.raqm, "raqm_get_glyphs");
p_raqm.version = 2;
} else {
p_raqm.version = 1;
p_raqm.get_glyphs_01 = (t_raqm_get_glyphs_01)dlsym(p_raqm.raqm, "raqm_get_glyphs");
}
2017-09-22 17:48:55 +03:00
if (dlerror() ||
!(p_raqm.create &&
p_raqm.set_text &&
p_raqm.set_text_utf8 &&
p_raqm.set_par_direction &&
p_raqm.set_language &&
2017-09-22 17:48:55 +03:00
p_raqm.add_font_feature &&
p_raqm.set_freetype_face &&
p_raqm.layout &&
(p_raqm.get_glyphs || p_raqm.get_glyphs_01) &&
2017-09-22 17:48:55 +03:00
p_raqm.destroy)) {
dlclose(p_raqm.raqm);
p_raqm.raqm = NULL;
return 2;
}
2017-09-22 17:48:55 +03:00
#else
p_raqm.version_string = (t_raqm_version_atleast)GetProcAddress(p_raqm.raqm, "raqm_version_string");
2019-05-31 13:25:28 +03:00
p_raqm.version_atleast = (t_raqm_version_atleast)GetProcAddress(p_raqm.raqm, "raqm_version_atleast");
2017-09-24 23:52:01 +03:00
p_raqm.create = (t_raqm_create)GetProcAddress(p_raqm.raqm, "raqm_create");
p_raqm.set_text = (t_raqm_set_text)GetProcAddress(p_raqm.raqm, "raqm_set_text");
p_raqm.set_text_utf8 = (t_raqm_set_text_utf8)GetProcAddress(p_raqm.raqm, "raqm_set_text_utf8");
p_raqm.set_par_direction = (t_raqm_set_par_direction)GetProcAddress(p_raqm.raqm, "raqm_set_par_direction");
p_raqm.set_language = (t_raqm_set_language)GetProcAddress(p_raqm.raqm, "raqm_set_language");
2017-09-24 23:52:01 +03:00
p_raqm.add_font_feature = (t_raqm_add_font_feature)GetProcAddress(p_raqm.raqm, "raqm_add_font_feature");
p_raqm.set_freetype_face = (t_raqm_set_freetype_face)GetProcAddress(p_raqm.raqm, "raqm_set_freetype_face");
p_raqm.layout = (t_raqm_layout)GetProcAddress(p_raqm.raqm, "raqm_layout");
p_raqm.destroy = (t_raqm_destroy)GetProcAddress(p_raqm.raqm, "raqm_destroy");
2017-09-25 14:38:23 +03:00
if(GetProcAddress(p_raqm.raqm, "raqm_index_to_position")) {
p_raqm.get_glyphs = (t_raqm_get_glyphs)GetProcAddress(p_raqm.raqm, "raqm_get_glyphs");
p_raqm.version = 2;
} else {
p_raqm.version = 1;
p_raqm.get_glyphs_01 = (t_raqm_get_glyphs_01)GetProcAddress(p_raqm.raqm, "raqm_get_glyphs");
}
2017-09-22 17:48:55 +03:00
if (!(p_raqm.create &&
p_raqm.set_text &&
p_raqm.set_text_utf8 &&
p_raqm.set_par_direction &&
p_raqm.set_language &&
2017-09-22 17:48:55 +03:00
p_raqm.add_font_feature &&
p_raqm.set_freetype_face &&
p_raqm.layout &&
(p_raqm.get_glyphs || p_raqm.get_glyphs_01) &&
2017-09-25 00:01:04 +03:00
p_raqm.destroy)) {
2017-09-22 17:48:55 +03:00
FreeLibrary(p_raqm.raqm);
p_raqm.raqm = NULL;
return 2;
}
#endif
return 0;
}
2010-07-31 06:52:47 +04:00
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 = 0;
2010-07-31 06:52:47 +04:00
char* filename = NULL;
2019-03-26 05:50:57 +03:00
Py_ssize_t size;
Py_ssize_t index = 0;
Py_ssize_t layout_engine = 0;
2013-04-25 20:48:43 +04:00
unsigned char* encoding;
2013-04-26 08:15:32 +04:00
unsigned char* font_bytes;
2019-03-26 05:50:57 +03:00
Py_ssize_t font_bytes_size = 0;
2010-07-31 06:52:47 +04:00
static char* kwlist[] = {
"filename", "size", "index", "encoding", "font_bytes",
2017-06-13 19:03:23 +03:00
"layout_engine", NULL
2010-07-31 06:52:47 +04:00
};
if (!library) {
PyErr_SetString(
PyExc_OSError,
2010-07-31 06:52:47 +04:00
"failed to initialize FreeType library"
);
return NULL;
}
2019-09-26 15:12:28 +03:00
if (!PyArg_ParseTupleAndKeywords(args, kw, "etn|nsy#n", kwlist,
Py_FileSystemDefaultEncoding, &filename,
&size, &index, &encoding, &font_bytes,
2017-06-13 19:03:23 +03:00
&font_bytes_size, &layout_engine)) {
return NULL;
}
2010-07-31 06:52:47 +04:00
self = PyObject_New(FontObject, &Font_Type);
if (!self) {
2020-05-10 12:56:36 +03:00
if (filename) {
PyMem_Free(filename);
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
return NULL;
}
2016-09-03 05:23:42 +03:00
2016-05-30 13:56:28 +03:00
self->face = NULL;
2017-06-13 19:03:23 +03:00
self->layout_engine = layout_engine;
2016-09-03 05:23:42 +03:00
2013-04-26 08:15:32 +04:00
if (filename && font_bytes_size <= 0) {
self->font_bytes = NULL;
error = FT_New_Face(library, filename, index, &self->face);
} else {
/* need to have allocated storage for font_bytes for the life of the object.*/
/* Don't free this before FT_Done_Face */
self->font_bytes = PyMem_Malloc(font_bytes_size);
if (!self->font_bytes) {
2015-10-11 13:24:35 +03:00
error = 65; // Out of Memory in Freetype.
}
if (!error) {
memcpy(self->font_bytes, font_bytes, (size_t)font_bytes_size);
2015-10-11 13:24:35 +03:00
error = FT_New_Memory_Face(library, (FT_Byte*)self->font_bytes,
font_bytes_size, index, &self->face);
}
}
2020-05-10 12:56:36 +03:00
if (!error) {
2010-07-31 06:52:47 +04:00
error = FT_Set_Pixel_Sizes(self->face, 0, size);
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
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);
}
2020-05-10 12:56:36 +03:00
if (filename) {
PyMem_Free(filename);
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
if (error) {
if (self->font_bytes) {
PyMem_Free(self->font_bytes);
self->font_bytes = NULL;
2015-10-11 13:24:35 +03:00
}
Py_DECREF(self);
2010-07-31 06:52:47 +04:00
return geterror(error);
}
return (PyObject*) self;
}
2010-07-31 06:52:47 +04:00
static int
font_getchar(PyObject* string, int index, FT_ULong* char_out)
{
2019-04-07 22:56:57 +03:00
if (PyUnicode_Check(string)) {
2020-05-10 12:56:36 +03:00
if (index >= PyUnicode_GET_LENGTH(string)) {
2019-04-07 22:56:57 +03:00
return 0;
2020-05-10 12:56:36 +03:00
}
*char_out = PyUnicode_READ_CHAR(string, index);
2019-04-07 22:56:57 +03:00
return 1;
}
2010-07-31 06:52:47 +04:00
return 0;
}
2016-02-16 10:52:59 +03:00
static size_t
text_layout_raqm(PyObject* string, FontObject* self, const char* dir, PyObject *features,
const char* lang, GlyphInfo **glyph_info, int mask)
2010-07-31 06:52:47 +04:00
{
2019-04-15 14:01:44 +03:00
size_t i = 0, count = 0, start = 0;
raqm_t *rq;
raqm_glyph_t *glyphs = NULL;
raqm_glyph_t_01 *glyphs_01 = NULL;
2016-01-27 08:09:26 +03:00
raqm_direction_t direction;
2010-07-31 06:52:47 +04:00
rq = (*p_raqm.create)();
if (rq == NULL) {
PyErr_SetString(PyExc_ValueError, "raqm_create() failed.");
goto failed;
}
#if (defined(PYPY_VERSION_NUM) && (PYPY_VERSION_NUM < 0x07020000))
if (PyUnicode_Check(string)) {
Py_UNICODE *text = PyUnicode_AS_UNICODE(string);
Py_ssize_t size = PyUnicode_GET_SIZE(string);
2017-10-04 13:21:13 +03:00
if (! size) {
/* return 0 and clean up, no glyphs==no size,
and raqm fails with empty strings */
goto failed;
}
if (!(*p_raqm.set_text)(rq, (const uint32_t *)(text), size)) {
PyErr_SetString(PyExc_ValueError, "raqm_set_text() failed");
goto failed;
}
if (lang) {
if (!(*p_raqm.set_language)(rq, lang, start, size)) {
PyErr_SetString(PyExc_ValueError, "raqm_set_language() failed");
goto failed;
}
}
}
2019-04-07 22:56:57 +03:00
#else
if (PyUnicode_Check(string)) {
Py_UCS4 *text = PyUnicode_AsUCS4Copy(string);
Py_ssize_t size = PyUnicode_GET_LENGTH(string);
2019-04-07 22:56:57 +03:00
if (!text || !size) {
/* return 0 and clean up, no glyphs==no size,
and raqm fails with empty strings */
goto failed;
}
2019-10-15 23:58:50 +03:00
int set_text = (*p_raqm.set_text)(rq, text, size);
2019-04-07 22:56:57 +03:00
PyMem_Free(text);
if (!set_text) {
PyErr_SetString(PyExc_ValueError, "raqm_set_text() failed");
goto failed;
}
if (lang) {
if (!(*p_raqm.set_language)(rq, lang, start, size)) {
2019-03-06 09:26:05 +03:00
PyErr_SetString(PyExc_ValueError, "raqm_set_language() failed");
goto failed;
}
}
}
2010-07-31 06:52:47 +04:00
#endif
else {
2010-07-31 06:52:47 +04:00
PyErr_SetString(PyExc_TypeError, "expected string");
goto failed;
}
direction = RAQM_DIRECTION_DEFAULT;
if (dir) {
2020-05-11 00:46:12 +03:00
if (strcmp(dir, "rtl") == 0) {
direction = RAQM_DIRECTION_RTL;
2020-05-11 00:46:12 +03:00
} else if (strcmp(dir, "ltr") == 0) {
direction = RAQM_DIRECTION_LTR;
2020-05-11 00:46:12 +03:00
} else if (strcmp(dir, "ttb") == 0) {
direction = RAQM_DIRECTION_TTB;
2019-05-31 13:25:28 +03:00
if (p_raqm.version_atleast == NULL || !(*p_raqm.version_atleast)(0, 7, 0)) {
2019-05-28 13:23:15 +03:00
PyErr_SetString(PyExc_ValueError, "libraqm 0.7 or greater required for 'ttb' direction");
2019-05-31 13:31:41 +03:00
goto failed;
}
} else {
PyErr_SetString(PyExc_ValueError, "direction must be either 'rtl', 'ltr' or 'ttb'");
goto failed;
}
}
if (!(*p_raqm.set_par_direction)(rq, direction)) {
PyErr_SetString(PyExc_ValueError, "raqm_set_par_direction() failed");
goto failed;
}
2016-01-27 08:09:26 +03:00
if (features != Py_None) {
2019-04-15 14:01:44 +03:00
int j, len;
PyObject *seq = PySequence_Fast(features, "expected a sequence");
if (!seq) {
goto failed;
}
len = PySequence_Size(seq);
2019-04-15 14:01:44 +03:00
for (j = 0; j < len; j++) {
PyObject *item = PySequence_Fast_GET_ITEM(seq, j);
char *feature = NULL;
Py_ssize_t size = 0;
2016-01-25 14:16:35 +03:00
PyObject *bytes;
2016-01-25 14:16:35 +03:00
if (!PyUnicode_Check(item)) {
PyErr_SetString(PyExc_TypeError, "expected a string");
goto failed;
}
2016-01-25 14:16:35 +03:00
2016-01-27 08:09:26 +03:00
if (PyUnicode_Check(item)) {
2016-01-25 14:16:35 +03:00
bytes = PyUnicode_AsUTF8String(item);
2020-05-10 12:56:36 +03:00
if (bytes == NULL) {
2016-01-27 08:09:26 +03:00
goto failed;
2020-05-10 12:56:36 +03:00
}
2016-01-25 14:16:35 +03:00
feature = PyBytes_AS_STRING(bytes);
size = PyBytes_GET_SIZE(bytes);
}
if (!(*p_raqm.add_font_feature)(rq, feature, size)) {
PyErr_SetString(PyExc_ValueError, "raqm_add_font_feature() failed");
goto failed;
}
}
}
if (!(*p_raqm.set_freetype_face)(rq, self->face)) {
PyErr_SetString(PyExc_RuntimeError, "raqm_set_freetype_face() failed.");
goto failed;
}
if (!(*p_raqm.layout)(rq)) {
PyErr_SetString(PyExc_RuntimeError, "raqm_layout() failed.");
goto failed;
}
2019-05-31 13:31:41 +03:00
if (p_raqm.version == 1) {
glyphs_01 = (*p_raqm.get_glyphs_01)(rq, &count);
if (glyphs_01 == NULL) {
PyErr_SetString(PyExc_ValueError, "raqm_get_glyphs() failed.");
count = 0;
goto failed;
}
2018-01-27 09:02:56 +03:00
} else { /* version == 2 */
glyphs = (*p_raqm.get_glyphs)(rq, &count);
if (glyphs == NULL) {
PyErr_SetString(PyExc_ValueError, "raqm_get_glyphs() failed.");
count = 0;
goto failed;
}
2010-07-31 06:52:47 +04:00
}
2016-01-27 08:09:26 +03:00
(*glyph_info) = PyMem_New(GlyphInfo, count);
if ((*glyph_info) == NULL) {
PyErr_SetString(PyExc_MemoryError, "PyMem_New() failed");
count = 0;
goto failed;
}
2019-05-31 13:31:41 +03:00
if (p_raqm.version == 1) {
for (i = 0; i < count; i++) {
(*glyph_info)[i].index = glyphs_01[i].index;
(*glyph_info)[i].x_offset = glyphs_01[i].x_offset;
(*glyph_info)[i].x_advance = glyphs_01[i].x_advance;
(*glyph_info)[i].y_offset = glyphs_01[i].y_offset;
2019-05-31 13:31:41 +03:00
(*glyph_info)[i].y_advance = glyphs_01[i].y_advance;
(*glyph_info)[i].cluster = glyphs_01[i].cluster;
}
} else {
for (i = 0; i < count; i++) {
(*glyph_info)[i].index = glyphs[i].index;
(*glyph_info)[i].x_offset = glyphs[i].x_offset;
(*glyph_info)[i].x_advance = glyphs[i].x_advance;
(*glyph_info)[i].y_offset = glyphs[i].y_offset;
2019-05-31 13:31:41 +03:00
(*glyph_info)[i].y_advance = glyphs[i].y_advance;
(*glyph_info)[i].cluster = glyphs[i].cluster;
}
2016-01-27 08:09:26 +03:00
}
failed:
(*p_raqm.destroy)(rq);
2016-01-27 08:09:26 +03:00
return count;
}
2016-01-27 08:09:26 +03:00
static size_t
text_layout_fallback(PyObject* string, FontObject* self, const char* dir, PyObject *features,
const char* lang, GlyphInfo **glyph_info, int mask)
{
2016-01-27 08:09:26 +03:00
int error, load_flags;
FT_ULong ch;
Py_ssize_t count;
FT_GlyphSlot glyph;
FT_Bool kerning = FT_HAS_KERNING(self->face);
FT_UInt last_index = 0;
2016-12-13 11:37:27 +03:00
int i;
2016-12-13 08:07:29 +03:00
if (features != Py_None || dir != NULL || lang != NULL) {
PyErr_SetString(PyExc_KeyError, "setting text direction, language or font features is not supported without libraqm");
}
2016-01-27 08:09:26 +03:00
if (!PyUnicode_Check(string)) {
PyErr_SetString(PyExc_TypeError, "expected string");
return 0;
}
count = 0;
while (font_getchar(string, count, &ch)) {
2016-01-27 08:09:26 +03:00
count++;
}
if (count == 0) {
2016-01-27 08:09:26 +03:00
return 0;
}
2016-01-27 08:09:26 +03:00
(*glyph_info) = PyMem_New(GlyphInfo, count);
if ((*glyph_info) == NULL) {
PyErr_SetString(PyExc_MemoryError, "PyMem_New() failed");
return 0;
}
load_flags = FT_LOAD_NO_BITMAP;
if (mask) {
2016-01-27 08:09:26 +03:00
load_flags |= FT_LOAD_TARGET_MONO;
}
2016-01-27 08:09:26 +03:00
for (i = 0; font_getchar(string, i, &ch); i++) {
(*glyph_info)[i].index = FT_Get_Char_Index(self->face, ch);
error = FT_Load_Glyph(self->face, (*glyph_info)[i].index, load_flags);
if (error) {
geterror(error);
return 0;
}
glyph = self->face->glyph;
(*glyph_info)[i].x_offset=0;
(*glyph_info)[i].y_offset=0;
if (kerning && last_index && (*glyph_info)[i].index) {
FT_Vector delta;
if (FT_Get_Kerning(self->face, last_index, (*glyph_info)[i].index,
2020-03-29 14:24:18 +03:00
ft_kerning_default,&delta) == 0) {
(*glyph_info)[i-1].x_advance += PIXEL(delta.x);
(*glyph_info)[i-1].y_advance += PIXEL(delta.y);
}
2016-01-27 08:09:26 +03:00
}
(*glyph_info)[i].x_advance = glyph->metrics.horiAdvance;
2019-05-31 13:31:41 +03:00
(*glyph_info)[i].y_advance = glyph->metrics.vertAdvance;
2016-01-27 08:09:26 +03:00
last_index = (*glyph_info)[i].index;
(*glyph_info)[i].cluster = ch;
}
return count;
}
static size_t
text_layout(PyObject* string, FontObject* self, const char* dir, PyObject *features,
const char* lang, GlyphInfo **glyph_info, int mask)
{
size_t count;
if (p_raqm.raqm && self->layout_engine == LAYOUT_RAQM) {
count = text_layout_raqm(string, self, dir, features, lang, glyph_info, mask);
2017-06-13 19:03:23 +03:00
} else {
count = text_layout_fallback(string, self, dir, features, lang, glyph_info, mask);
2017-06-13 19:03:23 +03:00
}
return count;
}
static PyObject*
font_getsize(FontObject* self, PyObject* args)
{
int x_position, x_max, x_min, y_max, y_min;
FT_Face face;
int xoffset, yoffset;
2019-05-31 13:31:41 +03:00
int horizontal_dir;
2020-05-28 00:21:32 +03:00
int mask = 0;
int load_flags;
const char *dir = NULL;
const char *lang = NULL;
2019-04-15 14:01:44 +03:00
size_t i, count;
GlyphInfo *glyph_info = NULL;
PyObject *features = Py_None;
/* calculate size and bearing for a given string */
PyObject* string;
2020-05-28 00:21:32 +03:00
if (!PyArg_ParseTuple(args, "O|izOz:getsize", &string, &mask, &dir, &features, &lang)) {
return NULL;
2020-05-10 12:56:36 +03:00
}
2020-05-28 00:21:32 +03:00
count = text_layout(string, self, dir, features, lang, &glyph_info, mask);
2017-08-31 22:31:05 +03:00
if (PyErr_Occurred()) {
return NULL;
}
face = NULL;
xoffset = yoffset = 0;
x_position = x_max = x_min = y_max = y_min = 0;
2019-05-31 13:31:41 +03:00
horizontal_dir = dir && strcmp(dir, "ttb") == 0 ? 0 : 1;
for (i = 0; i < count; i++) {
int index, error, offset, x_advanced;
FT_BBox bbox;
FT_Glyph glyph;
face = self->face;
index = glyph_info[i].index;
/* Note: bitmap fonts within ttf fonts do not work, see #891/pr#960
* Yifu Yu<root@jackyyf.com>, 2014-10-15
*/
2020-05-28 00:21:32 +03:00
load_flags = FT_LOAD_NO_BITMAP;
if (mask) {
load_flags |= FT_LOAD_TARGET_MONO;
}
error = FT_Load_Glyph(face, index, load_flags);
2020-05-10 12:56:36 +03:00
if (error) {
return geterror(error);
2020-05-10 12:56:36 +03:00
}
2019-05-31 13:31:41 +03:00
if (i == 0) {
if (horizontal_dir) {
if (face->glyph->metrics.horiBearingX < 0) {
xoffset = face->glyph->metrics.horiBearingX;
x_position -= xoffset;
2019-05-31 13:31:41 +03:00
}
} else {
if (face->glyph->metrics.vertBearingY < 0) {
yoffset = face->glyph->metrics.vertBearingY;
y_max -= yoffset;
}
}
}
FT_Get_Glyph(face->glyph, &glyph);
FT_Glyph_Get_CBox(glyph, FT_GLYPH_BBOX_SUBPIXELS, &bbox);
2019-05-31 13:31:41 +03:00
if (horizontal_dir) {
x_position += glyph_info[i].x_advance;
x_advanced = x_position;
offset = glyph_info[i].x_advance -
face->glyph->metrics.width -
face->glyph->metrics.horiBearingX;
2020-05-10 12:56:36 +03:00
if (offset < 0) {
x_advanced -= offset;
2020-05-10 12:56:36 +03:00
}
if (x_advanced > x_max) {
x_max = x_advanced;
2020-05-10 12:56:36 +03:00
}
2019-05-31 13:31:41 +03:00
bbox.yMax += glyph_info[i].y_offset;
bbox.yMin += glyph_info[i].y_offset;
2020-05-10 12:56:36 +03:00
if (bbox.yMax > y_max) {
2019-05-31 13:31:41 +03:00
y_max = bbox.yMax;
2020-05-10 12:56:36 +03:00
}
if (bbox.yMin < y_min) {
2019-05-31 13:31:41 +03:00
y_min = bbox.yMin;
2020-05-10 12:56:36 +03:00
}
2019-05-31 13:31:41 +03:00
// find max distance of baseline from top
2020-05-10 12:56:36 +03:00
if (face->glyph->metrics.horiBearingY > yoffset) {
2019-05-31 13:31:41 +03:00
yoffset = face->glyph->metrics.horiBearingY;
2020-05-10 12:56:36 +03:00
}
2019-05-31 13:31:41 +03:00
} else {
y_max -= glyph_info[i].y_advance;
if (i == count - 1) {
// trim end gap from final glyph
int offset;
offset = -glyph_info[i].y_advance -
face->glyph->metrics.height -
face->glyph->metrics.vertBearingY;
2020-05-11 00:46:12 +03:00
if (offset < 0) {
2019-05-31 13:31:41 +03:00
y_max -= offset;
2020-05-11 00:46:12 +03:00
}
2019-05-31 13:31:41 +03:00
}
2020-05-10 12:56:36 +03:00
if (bbox.xMax > x_max) {
2019-05-31 13:31:41 +03:00
x_max = bbox.xMax;
2020-05-10 12:56:36 +03:00
}
if (i == 0 || bbox.xMin < x_min) {
2019-05-31 13:31:41 +03:00
x_min = bbox.xMin;
2020-05-10 12:56:36 +03:00
}
2019-05-31 13:31:41 +03:00
}
FT_Done_Glyph(glyph);
}
2017-07-18 16:40:47 +03:00
if (glyph_info) {
PyMem_Free(glyph_info);
glyph_info = NULL;
}
2018-01-27 09:02:56 +03:00
if (face) {
2019-05-31 13:31:41 +03:00
if (horizontal_dir) {
// left bearing
2020-05-10 12:56:36 +03:00
if (xoffset < 0) {
2019-05-31 13:31:41 +03:00
x_max -= xoffset;
2020-05-10 12:56:36 +03:00
} else {
2019-05-31 13:31:41 +03:00
xoffset = 0;
2020-05-10 12:56:36 +03:00
}
2019-05-31 13:31:41 +03:00
/* difference between the font ascender and the distance of
* the baseline from the top */
yoffset = PIXEL(self->face->size->metrics.ascender - yoffset);
} else {
// top bearing
2020-05-10 12:56:36 +03:00
if (yoffset < 0) {
2019-05-31 13:31:41 +03:00
y_max -= yoffset;
2020-05-10 12:56:36 +03:00
} else {
2019-05-31 13:31:41 +03:00
yoffset = 0;
2020-05-10 12:56:36 +03:00
}
2019-05-31 13:31:41 +03:00
}
}
return Py_BuildValue(
"(ii)(ii)",
2019-05-31 13:31:41 +03:00
PIXEL(x_max - x_min), PIXEL(y_max - y_min),
PIXEL(xoffset), yoffset
);
}
2016-01-27 08:09:26 +03:00
static PyObject*
font_render(FontObject* self, PyObject* args)
{
2019-04-15 14:01:44 +03:00
int x;
unsigned int y;
2016-01-27 08:09:26 +03:00
Imaging im;
2019-05-31 13:31:41 +03:00
int index, error, ascender, horizontal_dir;
2016-01-27 08:09:26 +03:00
int load_flags;
unsigned char *source;
2019-07-28 23:40:03 +03:00
FT_Glyph glyph;
FT_GlyphSlot glyph_slot;
FT_Bitmap bitmap;
FT_BitmapGlyph bitmap_glyph;
int stroke_width = 0;
FT_Stroker stroker = NULL;
FT_Int left;
2016-01-27 08:09:26 +03:00
/* render string into given buffer (the buffer *must* have
the right size, or this will crash) */
PyObject* string;
Py_ssize_t id;
int mask = 0;
int temp;
int xx, x0, x1;
2019-05-31 13:31:41 +03:00
int yy;
unsigned int bitmap_y;
2016-01-27 08:09:26 +03:00
const char *dir = NULL;
const char *lang = NULL;
2019-04-15 14:01:44 +03:00
size_t i, count;
2016-01-27 08:09:26 +03:00
GlyphInfo *glyph_info;
PyObject *features = NULL;
2019-07-28 23:40:03 +03:00
if (!PyArg_ParseTuple(args, "On|izOzi:render", &string, &id, &mask, &dir, &features, &lang,
&stroke_width)) {
2016-01-27 08:09:26 +03:00
return NULL;
}
2016-01-27 08:09:26 +03:00
glyph_info = NULL;
count = text_layout(string, self, dir, features, lang, &glyph_info, mask);
2017-08-31 22:31:05 +03:00
if (PyErr_Occurred()) {
2016-01-27 08:09:26 +03:00
return NULL;
}
if (count == 0) {
2017-08-31 18:52:29 +03:00
Py_RETURN_NONE;
}
2016-01-27 08:09:26 +03:00
2019-07-28 23:40:03 +03:00
if (stroke_width) {
error = FT_Stroker_New(library, &stroker);
if (error) {
return geterror(error);
}
FT_Stroker_Set(stroker, (FT_Fixed)stroke_width*64, FT_STROKER_LINECAP_ROUND, FT_STROKER_LINEJOIN_ROUND, 0);
}
2010-07-31 06:52:47 +04:00
im = (Imaging) id;
2015-01-08 08:34:20 +03:00
/* Note: bitmap fonts within ttf fonts do not work, see #891/pr#960 */
2019-07-28 23:40:03 +03:00
load_flags = FT_LOAD_NO_BITMAP;
if (mask) {
2010-07-31 06:52:47 +04:00
load_flags |= FT_LOAD_TARGET_MONO;
2019-07-28 23:40:03 +03:00
}
2010-07-31 06:52:47 +04:00
ascender = 0;
for (i = 0; i < count; i++) {
index = glyph_info[i].index;
2020-02-16 12:40:23 +03:00
error = FT_Load_Glyph(self->face, index, load_flags | FT_LOAD_RENDER);
2019-07-28 23:40:03 +03:00
if (error) {
2016-01-27 08:09:26 +03:00
return geterror(error);
2019-07-28 23:40:03 +03:00
}
2016-01-27 08:09:26 +03:00
2019-07-28 23:40:03 +03:00
glyph_slot = self->face->glyph;
bitmap = glyph_slot->bitmap;
temp = bitmap.rows - glyph_slot->bitmap_top;
temp -= PIXEL(glyph_info[i].y_offset);
2020-05-10 12:56:36 +03:00
if (temp > ascender) {
ascender = temp;
2020-05-10 12:56:36 +03:00
}
}
2020-02-16 12:40:23 +03:00
if (stroker == NULL) {
load_flags |= FT_LOAD_RENDER;
}
2019-05-31 13:31:41 +03:00
x = y = 0;
horizontal_dir = dir && strcmp(dir, "ttb") == 0 ? 0 : 1;
for (i = 0; i < count; i++) {
2016-02-16 10:52:59 +03:00
index = glyph_info[i].index;
2010-07-31 06:52:47 +04:00
error = FT_Load_Glyph(self->face, index, load_flags);
2019-07-28 23:40:03 +03:00
if (error) {
2016-01-27 08:09:26 +03:00
return geterror(error);
2019-07-28 23:40:03 +03:00
}
glyph_slot = self->face->glyph;
if (stroker != NULL) {
error = FT_Get_Glyph(glyph_slot, &glyph);
if (!error) {
error = FT_Glyph_Stroke(&glyph, stroker, 1);
}
if (!error) {
FT_Vector origin = {0, 0};
error = FT_Glyph_To_Bitmap(&glyph, FT_RENDER_MODE_NORMAL, &origin, 1);
}
if (error) {
return geterror(error);
}
bitmap_glyph = (FT_BitmapGlyph)glyph;
bitmap = bitmap_glyph->bitmap;
left = bitmap_glyph->left;
} else {
bitmap = glyph_slot->bitmap;
left = glyph_slot->bitmap_left;
}
2013-04-12 09:25:16 +04:00
2019-05-31 13:31:41 +03:00
if (horizontal_dir) {
2019-07-28 23:40:03 +03:00
if (i == 0 && glyph_slot->metrics.horiBearingX < 0) {
x = -glyph_slot->metrics.horiBearingX;
2019-05-31 13:31:41 +03:00
}
2019-07-28 23:40:03 +03:00
xx = PIXEL(x) + left;
xx += PIXEL(glyph_info[i].x_offset) + stroke_width;
2019-05-31 13:31:41 +03:00
} else {
2019-07-28 23:40:03 +03:00
if (glyph_slot->metrics.vertBearingX < 0) {
x = -glyph_slot->metrics.vertBearingX;
2019-05-31 13:31:41 +03:00
}
2019-07-28 23:40:03 +03:00
xx = im->xsize / 2 - bitmap.width / 2;
2019-05-31 13:31:41 +03:00
}
2013-04-12 09:25:16 +04:00
x0 = 0;
2019-07-28 23:40:03 +03:00
x1 = bitmap.width;
2020-05-10 12:56:36 +03:00
if (xx < 0) {
2013-04-12 09:25:16 +04:00
x0 = -xx;
2020-05-10 12:56:36 +03:00
}
if (xx + x1 > im->xsize) {
2013-04-12 09:25:16 +04:00
x1 = im->xsize - xx;
2020-05-10 12:56:36 +03:00
}
2013-04-12 09:25:16 +04:00
2019-07-28 23:40:03 +03:00
source = (unsigned char*) bitmap.buffer;
for (bitmap_y = 0; bitmap_y < bitmap.rows; bitmap_y++) {
2019-05-31 13:31:41 +03:00
if (horizontal_dir) {
2019-07-28 23:40:03 +03:00
yy = bitmap_y + im->ysize - (PIXEL(glyph_slot->metrics.horiBearingY) + ascender);
yy -= PIXEL(glyph_info[i].y_offset) + stroke_width * 2;
2019-05-31 13:31:41 +03:00
} else {
2019-07-28 23:40:03 +03:00
yy = bitmap_y + PIXEL(y + glyph_slot->metrics.vertBearingY) + ascender;
2019-05-31 13:31:41 +03:00
yy += PIXEL(glyph_info[i].y_offset);
}
if (yy >= 0 && yy < im->ysize) {
// blend this glyph into the buffer
unsigned char *target = im->image8[yy] + xx;
if (mask) {
// use monochrome mask (on palette images, etc)
int j, k, m = 128;
for (j = k = 0; j < x1; j++) {
2020-05-10 12:56:36 +03:00
if (j >= x0 && (source[k] & m)) {
2010-07-31 06:52:47 +04:00
target[j] = 255;
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
if (!(m >>= 1)) {
m = 128;
2019-05-31 13:31:41 +03:00
k++;
2010-07-31 06:52:47 +04:00
}
}
2019-05-31 13:31:41 +03:00
} else {
// use antialiased rendering
int k;
for (k = x0; k < x1; k++) {
2020-05-10 12:56:36 +03:00
if (target[k] < source[k]) {
2019-05-31 13:31:41 +03:00
target[k] = source[k];
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
}
}
}
2019-07-28 23:40:03 +03:00
source += bitmap.pitch;
2010-07-31 06:52:47 +04:00
}
2016-02-03 14:24:10 +03:00
x += glyph_info[i].x_advance;
2019-05-31 13:31:41 +03:00
y -= glyph_info[i].y_advance;
if (stroker != NULL) {
FT_Done_Glyph(glyph);
}
2010-07-31 06:52:47 +04:00
}
2016-02-16 10:52:59 +03:00
2019-07-28 23:40:03 +03:00
FT_Stroker_Done(stroker);
2016-01-27 08:09:26 +03:00
PyMem_Del(glyph_info);
2010-07-31 06:52:47 +04:00
Py_RETURN_NONE;
}
2019-06-12 13:27:11 +03:00
#if FREETYPE_MAJOR > 2 ||\
(FREETYPE_MAJOR == 2 && FREETYPE_MINOR > 9) ||\
(FREETYPE_MAJOR == 2 && FREETYPE_MINOR == 9 && FREETYPE_PATCH == 1)
static PyObject*
font_getvarnames(FontObject* self)
2019-06-12 13:27:11 +03:00
{
int error;
FT_UInt i, j, num_namedstyles, name_count;
FT_MM_Var *master;
FT_SfntName name;
PyObject *list_names, *list_name;
error = FT_Get_MM_Var(self->face, &master);
2020-05-10 12:56:36 +03:00
if (error) {
2019-06-12 13:27:11 +03:00
return geterror(error);
2020-05-10 12:56:36 +03:00
}
2019-06-12 13:27:11 +03:00
num_namedstyles = master->num_namedstyles;
list_names = PyList_New(num_namedstyles);
name_count = FT_Get_Sfnt_Name_Count(self->face);
for (i = 0; i < name_count; i++) {
error = FT_Get_Sfnt_Name(self->face, i, &name);
2020-05-10 12:56:36 +03:00
if (error) {
2019-06-12 13:27:11 +03:00
return geterror(error);
2020-05-10 12:56:36 +03:00
}
2019-06-12 13:27:11 +03:00
for (j = 0; j < num_namedstyles; j++) {
2020-05-10 12:56:36 +03:00
if (PyList_GetItem(list_names, j) != NULL) {
2019-06-12 13:27:11 +03:00
continue;
2020-05-10 12:56:36 +03:00
}
2019-06-12 13:27:11 +03:00
if (master->namedstyle[j].strid == name.name_id) {
2019-09-26 15:12:28 +03:00
list_name = Py_BuildValue("y#", name.string, name.string_len);
2019-06-12 13:27:11 +03:00
PyList_SetItem(list_names, j, list_name);
break;
}
}
}
FT_Done_MM_Var(library, master);
return list_names;
}
static PyObject*
font_getvaraxes(FontObject* self)
2019-06-12 13:27:11 +03:00
{
int error;
FT_UInt i, j, num_axis, name_count;
FT_MM_Var* master;
FT_Var_Axis axis;
FT_SfntName name;
PyObject *list_axes, *list_axis, *axis_name;
error = FT_Get_MM_Var(self->face, &master);
2020-05-10 12:56:36 +03:00
if (error) {
2019-06-12 13:27:11 +03:00
return geterror(error);
2020-05-10 12:56:36 +03:00
}
2019-06-12 13:27:11 +03:00
num_axis = master->num_axis;
name_count = FT_Get_Sfnt_Name_Count(self->face);
list_axes = PyList_New(num_axis);
for (i = 0; i < num_axis; i++) {
axis = master->axis[i];
list_axis = PyDict_New();
PyDict_SetItemString(list_axis, "minimum",
2019-09-26 15:12:28 +03:00
PyLong_FromLong(axis.minimum / 65536));
2019-06-12 13:27:11 +03:00
PyDict_SetItemString(list_axis, "default",
2019-09-26 15:12:28 +03:00
PyLong_FromLong(axis.def / 65536));
2019-06-12 13:27:11 +03:00
PyDict_SetItemString(list_axis, "maximum",
2019-09-26 15:12:28 +03:00
PyLong_FromLong(axis.maximum / 65536));
2019-06-12 13:27:11 +03:00
for (j = 0; j < name_count; j++) {
error = FT_Get_Sfnt_Name(self->face, j, &name);
2020-05-10 12:56:36 +03:00
if (error) {
2019-06-12 13:27:11 +03:00
return geterror(error);
2020-05-10 12:56:36 +03:00
}
2019-06-12 13:27:11 +03:00
if (name.name_id == axis.strid) {
2019-09-26 15:12:28 +03:00
axis_name = Py_BuildValue("y#", name.string, name.string_len);
2019-06-12 13:27:11 +03:00
PyDict_SetItemString(list_axis, "name", axis_name);
break;
}
}
PyList_SetItem(list_axes, i, list_axis);
}
FT_Done_MM_Var(library, master);
return list_axes;
}
static PyObject*
font_setvarname(FontObject* self, PyObject* args)
{
int error;
int instance_index;
2020-05-10 12:56:36 +03:00
if (!PyArg_ParseTuple(args, "i", &instance_index)) {
2019-06-12 13:27:11 +03:00
return NULL;
2020-05-10 12:56:36 +03:00
}
2019-06-12 13:27:11 +03:00
error = FT_Set_Named_Instance(self->face, instance_index);
2020-05-10 12:56:36 +03:00
if (error) {
2019-06-12 13:27:11 +03:00
return geterror(error);
2020-05-10 12:56:36 +03:00
}
2019-06-12 13:27:11 +03:00
Py_INCREF(Py_None);
return Py_None;
}
static PyObject*
font_setvaraxes(FontObject* self, PyObject* args)
{
int error;
PyObject *axes, *item;
Py_ssize_t i, num_coords;
FT_Fixed *coords;
FT_Fixed coord;
2020-05-10 12:56:36 +03:00
if (!PyArg_ParseTuple(args, "O", &axes)) {
2019-06-12 13:27:11 +03:00
return NULL;
2020-05-10 12:56:36 +03:00
}
2019-06-12 13:27:11 +03:00
if (!PyList_Check(axes)) {
PyErr_SetString(PyExc_TypeError, "argument must be a list");
return NULL;
}
num_coords = PyObject_Length(axes);
coords = malloc(2 * sizeof(coords));
2019-06-25 23:33:49 +03:00
if (coords == NULL) {
return PyErr_NoMemory();
}
2019-06-12 13:27:11 +03:00
for (i = 0; i < num_coords; i++) {
item = PyList_GET_ITEM(axes, i);
2020-05-10 12:56:36 +03:00
if (PyFloat_Check(item)) {
2019-06-12 13:27:11 +03:00
coord = PyFloat_AS_DOUBLE(item);
2020-05-10 12:56:36 +03:00
} else if (PyLong_Check(item)) {
2019-09-26 15:12:28 +03:00
coord = (float) PyLong_AS_LONG(item);
2020-05-10 12:56:36 +03:00
} else if (PyNumber_Check(item)) {
2019-06-12 13:27:11 +03:00
coord = PyFloat_AsDouble(item);
2020-05-10 12:56:36 +03:00
} else {
2019-06-25 23:33:49 +03:00
free(coords);
2019-06-12 13:27:11 +03:00
PyErr_SetString(PyExc_TypeError, "list must contain numbers");
return NULL;
}
coords[i] = coord * 65536;
}
error = FT_Set_Var_Design_Coordinates(self->face, num_coords, coords);
2019-06-25 23:33:49 +03:00
free(coords);
2020-05-10 12:56:36 +03:00
if (error) {
2019-06-12 13:27:11 +03:00
return geterror(error);
2020-05-10 12:56:36 +03:00
}
2019-06-12 13:27:11 +03:00
Py_INCREF(Py_None);
return Py_None;
}
#endif
2010-07-31 06:52:47 +04:00
static void
font_dealloc(FontObject* self)
{
2016-05-30 13:56:28 +03:00
if (self->face) {
FT_Done_Face(self->face);
}
if (self->font_bytes) {
PyMem_Free(self->font_bytes);
2015-10-11 13:24:35 +03:00
}
2010-07-31 06:52:47 +04:00
PyObject_Del(self);
}
static PyMethodDef font_methods[] = {
{"render", (PyCFunction) font_render, METH_VARARGS},
{"getsize", (PyCFunction) font_getsize, METH_VARARGS},
2019-06-12 13:27:11 +03:00
#if FREETYPE_MAJOR > 2 ||\
(FREETYPE_MAJOR == 2 && FREETYPE_MINOR > 9) ||\
(FREETYPE_MAJOR == 2 && FREETYPE_MINOR == 9 && FREETYPE_PATCH == 1)
{"getvarnames", (PyCFunction) font_getvarnames, METH_NOARGS },
{"getvaraxes", (PyCFunction) font_getvaraxes, METH_NOARGS },
2019-06-12 13:27:11 +03:00
{"setvarname", (PyCFunction) font_setvarname, METH_VARARGS},
{"setvaraxes", (PyCFunction) font_setvaraxes, METH_VARARGS},
#endif
2010-07-31 06:52:47 +04:00
{NULL, NULL}
};
static PyObject*
font_getattr_family(FontObject* self, void* closure)
2010-07-31 06:52:47 +04:00
{
2020-05-10 12:56:36 +03:00
if (self->face->family_name) {
return PyUnicode_FromString(self->face->family_name);
2020-05-10 12:56:36 +03:00
}
Py_RETURN_NONE;
}
2010-07-31 06:52:47 +04:00
static PyObject*
font_getattr_style(FontObject* self, void* closure)
{
2020-05-10 12:56:36 +03:00
if (self->face->style_name) {
return PyUnicode_FromString(self->face->style_name);
2020-05-10 12:56:36 +03:00
}
Py_RETURN_NONE;
}
2010-07-31 06:52:47 +04:00
static PyObject*
font_getattr_ascent(FontObject* self, void* closure)
{
2019-09-26 15:12:28 +03:00
return PyLong_FromLong(PIXEL(self->face->size->metrics.ascender));
}
2010-07-31 06:52:47 +04:00
static PyObject*
font_getattr_descent(FontObject* self, void* closure)
{
2019-09-26 15:12:28 +03:00
return PyLong_FromLong(-PIXEL(self->face->size->metrics.descender));
}
2010-07-31 06:52:47 +04:00
2015-11-18 18:02:16 +03:00
static PyObject*
font_getattr_height(FontObject* self, void* closure)
{
2019-09-26 15:12:28 +03:00
return PyLong_FromLong(PIXEL(self->face->size->metrics.height));
2015-11-18 18:02:16 +03:00
}
static PyObject*
font_getattr_x_ppem(FontObject* self, void* closure)
{
2019-09-26 15:12:28 +03:00
return PyLong_FromLong(self->face->size->metrics.x_ppem);
2015-11-18 18:02:16 +03:00
}
static PyObject*
font_getattr_y_ppem(FontObject* self, void* closure)
{
2019-09-26 15:12:28 +03:00
return PyLong_FromLong(self->face->size->metrics.y_ppem);
2015-11-18 18:02:16 +03:00
}
static PyObject*
font_getattr_glyphs(FontObject* self, void* closure)
{
2019-09-26 15:12:28 +03:00
return PyLong_FromLong(self->face->num_glyphs);
2010-07-31 06:52:47 +04:00
}
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 },
2015-11-18 18:02:16 +03:00
{ "height", (getter) font_getattr_height },
{ "x_ppem", (getter) font_getattr_x_ppem },
{ "y_ppem", (getter) font_getattr_y_ppem },
{ "glyphs", (getter) font_getattr_glyphs },
{ NULL }
};
static PyTypeObject Font_Type = {
PyVarObject_HEAD_INIT(NULL, 0)
"Font", sizeof(FontObject), 0,
2010-07-31 06:52:47 +04:00
/* 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*/
2010-07-31 06:52:47 +04:00
};
static PyMethodDef _functions[] = {
{"getfont", (PyCFunction) getfont, METH_VARARGS|METH_KEYWORDS},
{NULL, NULL}
};
static int
setup_module(PyObject* m) {
2010-07-31 06:52:47 +04:00
PyObject* d;
PyObject* v;
int major, minor, patch;
d = PyModule_GetDict(m);
/* Ready object type */
PyType_Ready(&Font_Type);
2010-07-31 06:52:47 +04:00
2020-05-10 12:56:36 +03:00
if (FT_Init_FreeType(&library)) {
2015-06-02 15:50:22 +03:00
return 0; /* leave it uninitialized */
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
FT_Library_Version(library, &major, &minor, &patch);
v = PyUnicode_FromFormat("%d.%d.%d", major, minor, patch);
2010-07-31 06:52:47 +04:00
PyDict_SetItemString(d, "freetype2_version", v);
2016-12-15 13:43:02 +03:00
setraqm();
v = PyBool_FromLong(!!p_raqm.raqm);
2017-06-13 17:57:40 +03:00
PyDict_SetItemString(d, "HAVE_RAQM", v);
if (p_raqm.version_string) {
PyDict_SetItemString(d, "raqm_version", PyUnicode_FromString(p_raqm.version_string()));
}
2016-12-15 13:43:02 +03:00
return 0;
2010-07-31 06:52:47 +04:00
}
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);
2020-05-10 12:56:36 +03:00
if (setup_module(m) < 0) {
return NULL;
2020-05-10 12:56:36 +03:00
}
return m;
}