Pillow/src/_imagingft.c

1418 lines
44 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
2020-03-28 07:58:35 +03:00
#ifdef FT_COLOR_H
#include FT_COLOR_H
#endif
#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
2020-09-05 08:21:40 +03:00
/* round a 26.6 pixel coordinate to the nearest integer */
#define PIXEL(x) ((((x)+32) & -64)>>6)
2010-07-31 06:52:47 +04:00
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_string)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_string)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,
2020-03-28 07:58:35 +03:00
const char* lang, GlyphInfo **glyph_info, int mask, int color)
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,
2020-03-28 07:58:35 +03:00
const char* lang, GlyphInfo **glyph_info, int mask, int color)
{
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;
}
2020-03-28 07:58:35 +03:00
#ifdef FT_LOAD_COLOR
if (color) {
load_flags |= FT_LOAD_COLOR;
}
#endif
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;
// y_advance is only used in ttb, which is not supported by basic layout
(*glyph_info)[i].y_advance = 0;
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,
2020-03-28 07:58:35 +03:00
const char* lang, GlyphInfo **glyph_info, int mask, int color)
{
size_t count;
if (p_raqm.raqm && self->layout_engine == LAYOUT_RAQM) {
2020-03-28 07:58:35 +03:00
count = text_layout_raqm(string, self, dir, features, lang, glyph_info, mask, color);
2017-06-13 19:03:23 +03:00
} else {
2020-03-28 07:58:35 +03:00
count = text_layout_fallback(string, self, dir, features, lang, glyph_info, mask, color);
2017-06-13 19:03:23 +03:00
}
return count;
}
static PyObject*
font_getsize(FontObject* self, PyObject* args)
{
int position; /* pen position along primary axis, in 26.6 precision */
int advanced; /* pen position along primary axis, in pixels */
2020-09-05 08:21:40 +03:00
int px, py; /* position of current glyph, in pixels */
int x_min, x_max, y_min, y_max; /* text bounding box, in pixels */
int x_anchor, y_anchor; /* offset of point drawn at (0, 0), in pixels */
int load_flags; /* FreeType load_flags parameter */
int error;
FT_Face face;
FT_Glyph glyph;
FT_BBox bbox; /* glyph bounding box */
GlyphInfo *glyph_info = NULL; /* computed text layout */
size_t i, count; /* glyph_info index and length */
int horizontal_dir; /* is primary axis horizontal? */
int mask = 0; /* is FT_LOAD_TARGET_MONO enabled? */
2020-03-28 07:58:35 +03:00
int color = 0; /* is FT_LOAD_COLOR enabled? */
const char *mode = NULL;
const char *dir = NULL;
const char *lang = NULL;
const char *anchor = NULL;
PyObject *features = Py_None;
PyObject *string;
/* calculate size and bearing for a given string */
2020-03-28 07:58:35 +03:00
if (!PyArg_ParseTuple(args, "O|zzOzz:getsize", &string, &mode, &dir, &features, &lang, &anchor)) {
return NULL;
2020-05-10 12:56:36 +03:00
}
horizontal_dir = dir && strcmp(dir, "ttb") == 0 ? 0 : 1;
2020-03-28 07:58:35 +03:00
mask = mode && strcmp(mode, "1") == 0;
color = mode && strcmp(mode, "RGBA") == 0;
if (anchor == NULL) {
anchor = horizontal_dir ? "la" : "lt";
}
if (strlen(anchor) != 2) {
goto bad_anchor;
}
2020-03-28 07:58:35 +03:00
count = text_layout(string, self, dir, features, lang, &glyph_info, mask, color);
2017-08-31 22:31:05 +03:00
if (PyErr_Occurred()) {
return NULL;
}
/* Note: bitmap fonts within ttf fonts do not work, see #891/pr#960
* Yifu Yu<root@jackyyf.com>, 2014-10-15
*/
load_flags = FT_LOAD_NO_BITMAP;
if (mask) {
load_flags |= FT_LOAD_TARGET_MONO;
}
2020-03-28 07:58:35 +03:00
#ifdef FT_LOAD_COLOR
if (color) {
load_flags |= FT_LOAD_COLOR;
}
#endif
/*
* text bounds are given by:
* - bounding boxes of individual glyphs
* - pen line, i.e. 0 to `advanced` along primary axis
* this means point (0, 0) is part of the text bounding box
*/
face = NULL;
position = x_min = x_max = y_min = y_max = 0;
2019-05-31 13:31:41 +03:00
for (i = 0; i < count; i++) {
face = self->face;
if (horizontal_dir) {
2020-09-05 08:21:40 +03:00
px = PIXEL(position + glyph_info[i].x_offset);
py = PIXEL(glyph_info[i].y_offset);
position += glyph_info[i].x_advance;
advanced = PIXEL(position);
if (advanced > x_max) {
x_max = advanced;
}
} else {
2020-09-05 08:21:40 +03:00
px = PIXEL(glyph_info[i].x_offset);
py = PIXEL(position + glyph_info[i].y_offset);
position += glyph_info[i].y_advance;
advanced = PIXEL(position);
if (advanced < y_min) {
y_min = advanced;
}
}
error = FT_Load_Glyph(face, glyph_info[i].index, load_flags);
2020-05-10 12:56:36 +03:00
if (error) {
return geterror(error);
2020-05-10 12:56:36 +03:00
}
error = FT_Get_Glyph(face->glyph, &glyph);
if (error) {
return geterror(error);
}
2020-04-14 11:07:28 +03:00
FT_Glyph_Get_CBox(glyph, FT_GLYPH_BBOX_PIXELS, &bbox);
2020-09-05 08:21:40 +03:00
bbox.xMax += px;
if (bbox.xMax > x_max) {
x_max = bbox.xMax;
}
2020-09-05 08:21:40 +03:00
bbox.xMin += px;
if (bbox.xMin < x_min) {
x_min = bbox.xMin;
}
2020-09-05 08:21:40 +03:00
bbox.yMax += py;
if (bbox.yMax > y_max) {
y_max = bbox.yMax;
}
2020-09-05 08:21:40 +03:00
bbox.yMin += py;
if (bbox.yMin < y_min) {
y_min = bbox.yMin;
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
x_anchor = y_anchor = 0;
if (face) {
2019-05-31 13:31:41 +03:00
if (horizontal_dir) {
switch (anchor[0]) {
case 'l': // left
x_anchor = 0;
break;
case 'm': // middle (left + right) / 2
x_anchor = PIXEL(position / 2);
break;
case 'r': // right
x_anchor = PIXEL(position);
break;
case 's': // vertical baseline
default:
goto bad_anchor;
}
switch (anchor[1]) {
case 'a': // ascender
y_anchor = PIXEL(self->face->size->metrics.ascender);
break;
case 't': // top
y_anchor = y_max;
break;
case 'm': // middle (ascender + descender) / 2
y_anchor = PIXEL((self->face->size->metrics.ascender + self->face->size->metrics.descender) / 2);
break;
case 's': // horizontal baseline
y_anchor = 0;
break;
case 'b': // bottom
y_anchor = y_min;
break;
case 'd': // descender
y_anchor = PIXEL(self->face->size->metrics.descender);
break;
default:
goto bad_anchor;
}
2019-05-31 13:31:41 +03:00
} else {
switch (anchor[0]) {
case 'l': // left
x_anchor = x_min;
break;
case 'm': // middle (left + right) / 2
x_anchor = (x_min + x_max) / 2;
break;
case 'r': // right
x_anchor = x_max;
break;
case 's': // vertical baseline
x_anchor = 0;
break;
default:
goto bad_anchor;
}
switch (anchor[1]) {
case 't': // top
y_anchor = 0;
break;
case 'm': // middle (top + bottom) / 2
y_anchor = PIXEL(position / 2);
break;
case 'b': // bottom
y_anchor = PIXEL(position);
break;
case 'a': // ascender
case 's': // horizontal baseline
case 'd': // descender
default:
goto bad_anchor;
}
2019-05-31 13:31:41 +03:00
}
}
return Py_BuildValue(
"(ii)(ii)",
(x_max - x_min), (y_max - y_min),
(-x_anchor + x_min), -(-y_anchor + y_max)
);
bad_anchor:
PyErr_Format(PyExc_ValueError, "bad anchor specified: %s", anchor);
return NULL;
}
2016-01-27 08:09:26 +03:00
static PyObject*
font_render(FontObject* self, PyObject* args)
{
int x, y; /* pen position, in 26.6 precision */
2020-09-05 08:21:40 +03:00
int px, py; /* position of current glyph, in pixels */
int x_min, y_max; /* text offset in 26.6 precision */
int load_flags; /* FreeType load_flags parameter */
int error;
2019-07-28 23:40:03 +03:00
FT_Glyph glyph;
FT_GlyphSlot glyph_slot;
FT_Bitmap bitmap;
FT_BitmapGlyph bitmap_glyph;
FT_Stroker stroker = NULL;
GlyphInfo *glyph_info = NULL; /* computed text layout */
size_t i, count; /* glyph_info index and length */
int xx, yy; /* pixel offset of current glyph bitmap */
int x0, x1; /* horizontal bounds of glyph bitmap to copy */
unsigned int bitmap_y; /* glyph bitmap y index */
unsigned char *source; /* glyph bitmap source buffer */
Imaging im;
2016-01-27 08:09:26 +03:00
Py_ssize_t id;
int horizontal_dir; /* is primary axis horizontal? */
int mask = 0; /* is FT_LOAD_TARGET_MONO enabled? */
2020-03-28 07:58:35 +03:00
int color = 0; /* is FT_LOAD_COLOR enabled? */
int stroke_width = 0;
2020-03-28 07:58:35 +03:00
PY_LONG_LONG foreground_ink = 0;
const char *mode = NULL;
2016-01-27 08:09:26 +03:00
const char *dir = NULL;
const char *lang = NULL;
PyObject *features = Py_None;
PyObject* string;
/* render string into given buffer (the buffer *must* have
the right size, or this will crash) */
2016-01-27 08:09:26 +03:00
2020-03-28 07:58:35 +03:00
if (!PyArg_ParseTuple(args, "On|zzOziL:render", &string, &id, &mode, &dir, &features, &lang,
&stroke_width, &foreground_ink)) {
2016-01-27 08:09:26 +03:00
return NULL;
}
2016-01-27 08:09:26 +03:00
horizontal_dir = dir && strcmp(dir, "ttb") == 0 ? 0 : 1;
2020-03-28 07:58:35 +03:00
mask = mode && strcmp(mode, "1") == 0;
color = mode && strcmp(mode, "RGBA") == 0;
#ifdef FT_COLOR_H
if (color) {
FT_Color foreground_color;
foreground_color.red = (FT_Byte) (foreground_ink);
foreground_color.green = (FT_Byte) (foreground_ink >> 8);
foreground_color.blue = (FT_Byte) (foreground_ink >> 16);
foreground_color.alpha = (FT_Byte) 255; /* ink alpha is handled in ImageDraw.text */
FT_Palette_Set_Foreground_Color(self->face, foreground_color);
}
#endif
count = text_layout(string, self, dir, features, lang, &glyph_info, mask, color);
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
}
2020-03-28 07:58:35 +03:00
#ifdef FT_LOAD_COLOR
if (color) {
load_flags |= FT_LOAD_COLOR;
}
#endif
2010-07-31 06:52:47 +04:00
/*
* calculate x_min and y_max
* must match font_getsize or there may be clipping!
*/
x = y = x_min = y_max = 0;
for (i = 0; i < count; i++) {
2020-09-05 08:21:40 +03:00
px = PIXEL(x + glyph_info[i].x_offset);
py = PIXEL(y + glyph_info[i].y_offset);
error = FT_Load_Glyph(self->face, glyph_info[i].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;
2020-09-05 08:21:40 +03:00
if (glyph_slot->bitmap_top + py > y_max) {
y_max = glyph_slot->bitmap_top + py;
}
2020-09-05 08:21:40 +03:00
if (glyph_slot->bitmap_left + px < x_min) {
x_min = glyph_slot->bitmap_left + px;
2020-05-10 12:56:36 +03:00
}
x += glyph_info[i].x_advance;
y += glyph_info[i].y_advance;
2020-04-14 11:07:28 +03:00
}
/* set pen position to text origin */
2020-09-05 08:21:40 +03:00
x = (-x_min + stroke_width) << 6;
y = (-y_max + (-stroke_width)) << 6;
2020-02-16 12:40:23 +03:00
if (stroker == NULL) {
load_flags |= FT_LOAD_RENDER;
}
2019-05-31 13:31:41 +03:00
for (i = 0; i < count; i++) {
2020-09-05 08:21:40 +03:00
px = PIXEL(x + glyph_info[i].x_offset);
py = PIXEL(y + glyph_info[i].y_offset);
error = FT_Load_Glyph(self->face, glyph_info[i].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;
2020-09-05 08:21:40 +03:00
xx = px + bitmap_glyph->left;
yy = -(py + bitmap_glyph->top);
2019-07-28 23:40:03 +03:00
} else {
bitmap = glyph_slot->bitmap;
2020-09-05 08:21:40 +03:00
xx = px + glyph_slot->bitmap_left;
yy = -(py + glyph_slot->bitmap_top);
2019-05-31 13:31:41 +03:00
}
2013-04-12 09:25:16 +04:00
/* clip glyph bitmap width to target image bounds */
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++, yy++) {
/* clip glyph bitmap height to target image bounds */
2019-05-31 13:31:41 +03:00
if (yy >= 0 && yy < im->ysize) {
// blend this glyph into the buffer
2020-03-28 07:58:35 +03:00
if (color) {
/* target[RGB] returns the color, target[A] returns the mask */
/* target bands get split again in ImageDraw.text */
unsigned char *target = im->image[yy] + xx * 4;
#ifdef FT_LOAD_COLOR
if (bitmap.pixel_mode == FT_PIXEL_MODE_BGRA) {
// paste color glyph
int k;
for (k = x0; k < x1; k++) {
if (target[k * 4 + 3] < source[k * 4 + 3]) {
/* unpremultiply BGRa to RGBA */
target[k * 4 + 0] = CLIP8((255 * (int)source[k * 4 + 2]) / source[k * 4 + 3]);
target[k * 4 + 1] = CLIP8((255 * (int)source[k * 4 + 1]) / source[k * 4 + 3]);
target[k * 4 + 2] = CLIP8((255 * (int)source[k * 4 + 0]) / source[k * 4 + 3]);
target[k * 4 + 3] = source[k * 4 + 3];
}
2020-05-10 12:56:36 +03:00
}
2020-03-28 07:58:35 +03:00
} else
#endif
{ // pixel_mode should be FT_PIXEL_MODE_GRAY
// fill with ink
int k;
for (k = x0; k < x1; k++) {
if (target[k * 4 + 3] < source[k]) {
target[k * 4 + 0] = (unsigned char) (foreground_ink);
target[k * 4 + 1] = (unsigned char) (foreground_ink >> 8);
target[k * 4 + 2] = (unsigned char) (foreground_ink >> 16);
target[k * 4 + 3] = source[k];
}
2010-07-31 06:52:47 +04:00
}
}
2019-05-31 13:31:41 +03:00
} else {
2020-03-28 07:58:35 +03:00
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++) {
if (j >= x0 && (source[k] & m)) {
target[j] = 255;
}
if (!(m >>= 1)) {
m = 128;
k++;
}
}
} else {
// use antialiased rendering
int k;
for (k = x0; k < x1; k++) {
if (target[k] < source[k]) {
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;
2020-04-14 04:16:32 +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;
}