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
|
2013-04-11 14:16:26 +04:00
|
|
|
#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
|
2013-04-11 14:16:26 +04:00
|
|
|
|
2012-10-14 08:47:30 +04:00
|
|
|
#define KEEP_PY_UNICODE
|
|
|
|
|
2017-09-22 17:48:55 +03:00
|
|
|
#if !defined(_MSC_VER)
|
2017-09-22 16:25:03 +03:00
|
|
|
#include <dlfcn.h>
|
2017-09-22 17:48:55 +03:00
|
|
|
#endif
|
2017-09-22 16:25:03 +03:00
|
|
|
|
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 } };
|
2017-09-22 16:25:03 +03:00
|
|
|
|
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;
|
2014-01-28 11:10:49 +04:00
|
|
|
unsigned char *font_bytes;
|
2017-06-13 19:03:23 +03:00
|
|
|
int layout_engine;
|
2010-07-31 06:52:47 +04:00
|
|
|
} FontObject;
|
|
|
|
|
2012-10-13 20:53:07 +04:00
|
|
|
static PyTypeObject Font_Type;
|
2010-07-31 06:52:47 +04:00
|
|
|
|
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,
|
2017-09-25 14:23:59 +03:00
|
|
|
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,
|
2017-09-25 14:23:59 +03:00
|
|
|
const char *text,
|
|
|
|
size_t len);
|
2017-09-24 23:52:01 +03:00
|
|
|
typedef bool (*t_raqm_set_par_direction) (raqm_t *rq,
|
2017-09-25 14:23:59 +03:00
|
|
|
raqm_direction_t dir);
|
2019-03-06 07:11:42 +03:00
|
|
|
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,
|
2017-09-25 14:23:59 +03:00
|
|
|
const char *feature,
|
|
|
|
int len);
|
2017-09-24 23:52:01 +03:00
|
|
|
typedef bool (*t_raqm_set_freetype_face) (raqm_t *rq,
|
2017-09-25 14:23:59 +03:00
|
|
|
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,
|
2017-09-25 14:23:59 +03:00
|
|
|
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);
|
|
|
|
|
2017-09-22 16:25:03 +03:00
|
|
|
typedef struct {
|
2017-09-22 21:29:52 +03:00
|
|
|
void* raqm;
|
2017-09-25 14:23:59 +03:00
|
|
|
int version;
|
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;
|
2019-03-06 07:11:42 +03:00
|
|
|
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;
|
2017-09-25 14:23:59 +03:00
|
|
|
t_raqm_get_glyphs_01 get_glyphs_01;
|
2017-09-24 23:52:01 +03:00
|
|
|
t_raqm_destroy destroy;
|
2017-09-22 16:25:03 +03:00
|
|
|
} p_raqm_func;
|
|
|
|
|
|
|
|
static p_raqm_func p_raqm;
|
2017-09-22 21:29:52 +03:00
|
|
|
|
2017-09-22 16:25:03 +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) {
|
2020-04-07 09:58:21 +03:00
|
|
|
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
|
|
|
|
2020-04-07 09:58:21 +03:00
|
|
|
PyErr_SetString(PyExc_OSError, "unknown freetype error");
|
2010-07-31 06:52:47 +04:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2017-09-22 16:25:03 +03:00
|
|
|
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 */
|
|
|
|
#if !defined(_MSC_VER)
|
2017-09-22 16:25:03 +03:00
|
|
|
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");
|
|
|
|
#endif
|
|
|
|
|
2017-09-22 16:25:03 +03:00
|
|
|
if (!p_raqm.raqm) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2017-09-22 17:48:55 +03:00
|
|
|
#if !defined(_MSC_VER)
|
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");
|
2019-03-06 07:11:42 +03:00
|
|
|
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");
|
2017-09-25 14:23:59 +03:00
|
|
|
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 &&
|
2019-03-06 07:11:42 +03:00
|
|
|
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 &&
|
2017-09-25 14:23:59 +03:00
|
|
|
(p_raqm.get_glyphs || p_raqm.get_glyphs_01) &&
|
2017-09-22 17:48:55 +03:00
|
|
|
p_raqm.destroy)) {
|
2017-09-22 16:25:03 +03:00
|
|
|
dlclose(p_raqm.raqm);
|
|
|
|
p_raqm.raqm = NULL;
|
|
|
|
return 2;
|
|
|
|
}
|
2017-09-22 17:48:55 +03:00
|
|
|
#else
|
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");
|
2019-03-06 07:11:42 +03:00
|
|
|
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")) {
|
2017-09-25 14:23:59 +03:00
|
|
|
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 &&
|
2019-03-06 07:11:42 +03:00
|
|
|
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 &&
|
2017-09-25 14:23:59 +03:00
|
|
|
(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
|
2017-09-22 16:25:03 +03:00
|
|
|
|
|
|
|
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;
|
2014-01-28 11:10:49 +04:00
|
|
|
int error = 0;
|
2010-07-31 06:52:47 +04:00
|
|
|
|
2013-04-22 13:48:06 +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[] = {
|
2017-07-15 10:39:39 +03:00
|
|
|
"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(
|
2020-04-07 09:58:21 +03:00
|
|
|
PyExc_OSError,
|
2010-07-31 06:52:47 +04:00
|
|
|
"failed to initialize FreeType library"
|
|
|
|
);
|
|
|
|
return NULL;
|
|
|
|
}
|
2014-01-28 11:10:49 +04:00
|
|
|
|
2019-09-26 15:12:28 +03:00
|
|
|
if (!PyArg_ParseTupleAndKeywords(args, kw, "etn|nsy#n", kwlist,
|
2013-12-30 00:33:55 +04:00
|
|
|
Py_FileSystemDefaultEncoding, &filename,
|
|
|
|
&size, &index, &encoding, &font_bytes,
|
2017-06-13 19:03:23 +03:00
|
|
|
&font_bytes_size, &layout_engine)) {
|
2013-12-30 00:33:55 +04:00
|
|
|
return NULL;
|
2014-01-28 11:10:49 +04:00
|
|
|
}
|
2010-07-31 06:52:47 +04:00
|
|
|
|
|
|
|
self = PyObject_New(FontObject, &Font_Type);
|
2013-12-30 00:33:55 +04:00
|
|
|
if (!self) {
|
2020-05-10 12:56:36 +03:00
|
|
|
if (filename) {
|
2013-12-30 00:33:55 +04:00
|
|
|
PyMem_Free(filename);
|
2020-05-10 12:56:36 +03:00
|
|
|
}
|
2010-07-31 06:52:47 +04:00
|
|
|
return NULL;
|
2013-12-30 00:33:55 +04:00
|
|
|
}
|
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) {
|
2014-01-28 11:24:42 +04:00
|
|
|
self->font_bytes = NULL;
|
2013-04-22 13:48:06 +04:00
|
|
|
error = FT_New_Face(library, filename, index, &self->face);
|
|
|
|
} else {
|
2014-01-28 11:10:49 +04:00
|
|
|
/* 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.
|
2014-01-28 11:10:49 +04:00
|
|
|
}
|
|
|
|
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,
|
2014-01-28 11:10:49 +04:00
|
|
|
font_bytes_size, index, &self->face);
|
|
|
|
}
|
2013-04-22 13:48:06 +04:00
|
|
|
}
|
2013-07-01 02:42:19 +04:00
|
|
|
|
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) {
|
2013-12-30 00:33:55 +04:00
|
|
|
PyMem_Free(filename);
|
2020-05-10 12:56:36 +03:00
|
|
|
}
|
2010-07-31 06:52:47 +04:00
|
|
|
|
|
|
|
if (error) {
|
2014-01-28 11:10:49 +04:00
|
|
|
if (self->font_bytes) {
|
|
|
|
PyMem_Free(self->font_bytes);
|
2019-06-23 04:53:01 +03:00
|
|
|
self->font_bytes = NULL;
|
2015-10-11 13:24:35 +03:00
|
|
|
}
|
2016-05-30 13:16:16 +03:00
|
|
|
Py_DECREF(self);
|
2010-07-31 06:52:47 +04:00
|
|
|
return geterror(error);
|
|
|
|
}
|
|
|
|
|
|
|
|
return (PyObject*) self;
|
|
|
|
}
|
2013-04-11 14:16:26 +04:00
|
|
|
|
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
|
|
|
}
|
2019-04-07 23:12:25 +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
|
2019-03-06 14:01:25 +03:00
|
|
|
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;
|
2016-01-13 09:31:49 +03:00
|
|
|
raqm_t *rq;
|
2017-09-25 14:23:59 +03:00
|
|
|
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
|
|
|
|
2017-09-22 16:25:03 +03:00
|
|
|
rq = (*p_raqm.create)();
|
2016-01-13 09:31:49 +03:00
|
|
|
if (rq == NULL) {
|
|
|
|
PyErr_SetString(PyExc_ValueError, "raqm_create() failed.");
|
|
|
|
goto failed;
|
|
|
|
}
|
|
|
|
|
2019-10-13 02:11:48 +03:00
|
|
|
#if (defined(PYPY_VERSION_NUM) && (PYPY_VERSION_NUM < 0x07020000))
|
2016-01-13 09:31:49 +03:00
|
|
|
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;
|
|
|
|
}
|
2017-09-22 16:25:03 +03:00
|
|
|
if (!(*p_raqm.set_text)(rq, (const uint32_t *)(text), size)) {
|
2016-01-13 09:31:49 +03:00
|
|
|
PyErr_SetString(PyExc_ValueError, "raqm_set_text() failed");
|
|
|
|
goto failed;
|
|
|
|
}
|
2019-03-06 07:11:42 +03:00
|
|
|
if (lang) {
|
|
|
|
if (!(*p_raqm.set_language)(rq, lang, start, size)) {
|
|
|
|
PyErr_SetString(PyExc_ValueError, "raqm_set_language() failed");
|
|
|
|
goto failed;
|
|
|
|
}
|
|
|
|
}
|
2019-07-02 09:52:15 +03:00
|
|
|
}
|
2019-04-07 22:56:57 +03:00
|
|
|
#else
|
|
|
|
if (PyUnicode_Check(string)) {
|
|
|
|
Py_UCS4 *text = PyUnicode_AsUCS4Copy(string);
|
2019-04-07 23:12:25 +03:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
2016-01-13 09:31:49 +03:00
|
|
|
}
|
2010-07-31 06:52:47 +04:00
|
|
|
#endif
|
2016-01-13 09:31:49 +03:00
|
|
|
else {
|
2010-07-31 06:52:47 +04:00
|
|
|
PyErr_SetString(PyExc_TypeError, "expected string");
|
2016-01-13 09:31:49 +03:00
|
|
|
goto failed;
|
|
|
|
}
|
|
|
|
|
|
|
|
direction = RAQM_DIRECTION_DEFAULT;
|
|
|
|
if (dir) {
|
|
|
|
if (strcmp(dir, "rtl") == 0)
|
|
|
|
direction = RAQM_DIRECTION_RTL;
|
|
|
|
else if (strcmp(dir, "ltr") == 0)
|
|
|
|
direction = RAQM_DIRECTION_LTR;
|
2019-05-31 13:31:41 +03:00
|
|
|
else if (strcmp(dir, "ttb") == 0) {
|
2016-01-13 09:31:49 +03:00
|
|
|
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 {
|
2016-01-13 09:31:49 +03:00
|
|
|
PyErr_SetString(PyExc_ValueError, "direction must be either 'rtl', 'ltr' or 'ttb'");
|
|
|
|
goto failed;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-09-22 16:25:03 +03:00
|
|
|
if (!(*p_raqm.set_par_direction)(rq, direction)) {
|
2016-01-13 09:31:49 +03:00
|
|
|
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;
|
2016-01-13 09:31:49 +03:00
|
|
|
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);
|
2016-01-13 09:31:49 +03:00
|
|
|
char *feature = NULL;
|
|
|
|
Py_ssize_t size = 0;
|
2016-01-25 14:16:35 +03:00
|
|
|
PyObject *bytes;
|
2016-01-13 09:31:49 +03:00
|
|
|
|
2016-01-25 14:16:35 +03:00
|
|
|
if (!PyUnicode_Check(item)) {
|
2016-01-13 09:31:49 +03:00
|
|
|
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);
|
|
|
|
}
|
2017-09-22 16:25:03 +03:00
|
|
|
if (!(*p_raqm.add_font_feature)(rq, feature, size)) {
|
2016-01-13 09:31:49 +03:00
|
|
|
PyErr_SetString(PyExc_ValueError, "raqm_add_font_feature() failed");
|
|
|
|
goto failed;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-09-22 16:25:03 +03:00
|
|
|
if (!(*p_raqm.set_freetype_face)(rq, self->face)) {
|
|
|
|
PyErr_SetString(PyExc_RuntimeError, "raqm_set_freetype_face() failed.");
|
|
|
|
goto failed;
|
2016-01-13 09:31:49 +03:00
|
|
|
}
|
|
|
|
|
2017-09-22 16:25:03 +03:00
|
|
|
if (!(*p_raqm.layout)(rq)) {
|
|
|
|
PyErr_SetString(PyExc_RuntimeError, "raqm_layout() failed.");
|
|
|
|
goto failed;
|
2016-01-13 09:31:49 +03:00
|
|
|
}
|
|
|
|
|
2019-05-31 13:31:41 +03:00
|
|
|
if (p_raqm.version == 1) {
|
2017-09-25 14:23:59 +03:00
|
|
|
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 */
|
2017-09-25 14:23:59 +03:00
|
|
|
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) {
|
2017-09-25 14:23:59 +03:00
|
|
|
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;
|
2017-09-25 14:23:59 +03:00
|
|
|
(*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;
|
2017-09-25 14:23:59 +03:00
|
|
|
(*glyph_info)[i].cluster = glyphs[i].cluster;
|
|
|
|
}
|
2016-01-27 08:09:26 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
failed:
|
2017-09-22 16:25:03 +03:00
|
|
|
(*p_raqm.destroy)(rq);
|
2016-01-27 08:09:26 +03:00
|
|
|
return count;
|
2016-12-14 10:07:09 +03:00
|
|
|
}
|
2016-01-27 08:09:26 +03:00
|
|
|
|
2016-12-14 10:07:09 +03:00
|
|
|
static size_t
|
2019-03-06 14:01:25 +03:00
|
|
|
text_layout_fallback(PyObject* string, FontObject* self, const char* dir, PyObject *features,
|
|
|
|
const char* lang, GlyphInfo **glyph_info, int mask)
|
2016-12-14 10:07:09 +03:00
|
|
|
{
|
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
|
|
|
|
2019-03-06 07:11:42 +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-12-14 10:07:09 +03:00
|
|
|
}
|
2016-01-27 08:09:26 +03:00
|
|
|
if (!PyUnicode_Check(string)) {
|
|
|
|
PyErr_SetString(PyExc_TypeError, "expected string");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
count = 0;
|
2016-12-14 10:07:09 +03:00
|
|
|
while (font_getchar(string, count, &ch)) {
|
2016-01-27 08:09:26 +03:00
|
|
|
count++;
|
2016-12-14 10:07:09 +03:00
|
|
|
}
|
|
|
|
if (count == 0) {
|
2016-01-27 08:09:26 +03:00
|
|
|
return 0;
|
2016-12-14 10:07:09 +03: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");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
load_flags = FT_LOAD_RENDER|FT_LOAD_NO_BITMAP;
|
2016-12-14 10:07:09 +03:00
|
|
|
if (mask) {
|
2016-01-27 08:09:26 +03:00
|
|
|
load_flags |= FT_LOAD_TARGET_MONO;
|
2016-12-14 10:07:09 +03:00
|
|
|
}
|
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;
|
2016-12-14 10:07:09 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static size_t
|
2019-03-06 14:01:25 +03:00
|
|
|
text_layout(PyObject* string, FontObject* self, const char* dir, PyObject *features,
|
|
|
|
const char* lang, GlyphInfo **glyph_info, int mask)
|
2016-12-14 10:07:09 +03:00
|
|
|
{
|
|
|
|
size_t count;
|
2017-09-22 16:25:03 +03:00
|
|
|
|
|
|
|
if (p_raqm.raqm && self->layout_engine == LAYOUT_RAQM) {
|
2019-03-06 14:01:25 +03:00
|
|
|
count = text_layout_raqm(string, self, dir, features, lang, glyph_info, mask);
|
2017-06-13 19:03:23 +03:00
|
|
|
} else {
|
2019-03-06 14:01:25 +03:00
|
|
|
count = text_layout_fallback(string, self, dir, features, lang, glyph_info, mask);
|
2017-06-13 19:03:23 +03:00
|
|
|
}
|
2016-12-14 10:07:09 +03:00
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
|
|
|
static PyObject*
|
|
|
|
font_getsize(FontObject* self, PyObject* args)
|
|
|
|
{
|
2019-06-19 14:00:20 +03:00
|
|
|
int x_position, x_max, x_min, y_max, y_min;
|
2016-12-14 10:07:09 +03:00
|
|
|
FT_Face face;
|
|
|
|
int xoffset, yoffset;
|
2019-05-31 13:31:41 +03:00
|
|
|
int horizontal_dir;
|
2016-12-14 10:07:09 +03:00
|
|
|
const char *dir = NULL;
|
2019-03-06 07:11:42 +03:00
|
|
|
const char *lang = NULL;
|
2019-04-15 14:01:44 +03:00
|
|
|
size_t i, count;
|
2016-12-14 10:07:09 +03:00
|
|
|
GlyphInfo *glyph_info = NULL;
|
|
|
|
PyObject *features = Py_None;
|
|
|
|
|
|
|
|
/* calculate size and bearing for a given string */
|
|
|
|
|
|
|
|
PyObject* string;
|
2020-05-10 12:56:36 +03:00
|
|
|
if (!PyArg_ParseTuple(args, "O|zOz:getsize", &string, &dir, &features, &lang)) {
|
2016-12-14 10:07:09 +03:00
|
|
|
return NULL;
|
2020-05-10 12:56:36 +03:00
|
|
|
}
|
2016-12-14 10:07:09 +03:00
|
|
|
|
2019-03-06 14:01:25 +03:00
|
|
|
count = text_layout(string, self, dir, features, lang, &glyph_info, 0);
|
2017-08-31 22:31:05 +03:00
|
|
|
if (PyErr_Occurred()) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2019-06-19 14:00:20 +03:00
|
|
|
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++) {
|
2019-06-19 14:00:20 +03:00
|
|
|
int index, error, offset, x_advanced;
|
2016-12-14 10:07:09 +03:00
|
|
|
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
|
|
|
|
*/
|
|
|
|
error = FT_Load_Glyph(face, index, FT_LOAD_DEFAULT|FT_LOAD_NO_BITMAP);
|
2020-05-10 12:56:36 +03:00
|
|
|
if (error) {
|
2016-12-14 10:07:09 +03:00
|
|
|
return geterror(error);
|
2020-05-10 12:56:36 +03:00
|
|
|
}
|
2016-12-14 10:07:09 +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;
|
2019-06-19 14:00:20 +03:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
2016-12-14 10:07:09 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
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) {
|
2019-06-19 14:00:20 +03:00
|
|
|
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) {
|
2019-06-19 14:00:20 +03:00
|
|
|
x_advanced -= offset;
|
2020-05-10 12:56:36 +03:00
|
|
|
}
|
|
|
|
if (x_advanced > x_max) {
|
2019-06-19 14:00:20 +03:00
|
|
|
x_max = x_advanced;
|
2020-05-10 12:56:36 +03:00
|
|
|
}
|
2019-05-31 13:31:41 +03:00
|
|
|
|
2019-05-23 13:54:48 +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
|
|
|
}
|
2016-12-14 10:07:09 +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;
|
|
|
|
if (offset < 0)
|
|
|
|
y_max -= offset;
|
|
|
|
}
|
|
|
|
|
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
|
|
|
}
|
2016-12-14 10:07:09 +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
|
|
|
|
2016-12-14 10:07:09 +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
|
|
|
}
|
2016-12-14 10:07:09 +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),
|
2016-12-14 10:07:09 +03:00
|
|
|
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;
|
2019-03-06 07:11:42 +03:00
|
|
|
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-12-14 10:07:09 +03:00
|
|
|
}
|
2016-01-27 08:09:26 +03:00
|
|
|
|
|
|
|
glyph_info = NULL;
|
2019-03-06 14:01:25 +03:00
|
|
|
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;
|
2016-12-14 10:07:09 +03:00
|
|
|
}
|
|
|
|
if (count == 0) {
|
2017-08-31 18:52:29 +03:00
|
|
|
Py_RETURN_NONE;
|
2016-12-14 10:07:09 +03:00
|
|
|
}
|
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
|
|
|
|
2013-04-11 14:16:26 +04:00
|
|
|
ascender = 0;
|
2016-01-13 09:31:49 +03:00
|
|
|
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;
|
2016-01-13 09:31:49 +03:00
|
|
|
temp -= PIXEL(glyph_info[i].y_offset);
|
2020-05-10 12:56:36 +03:00
|
|
|
if (temp > ascender) {
|
2013-04-11 14:16:26 +04:00
|
|
|
ascender = temp;
|
2020-05-10 12:56:36 +03:00
|
|
|
}
|
2013-04-11 14:16:26 +04: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;
|
2019-11-01 13:34:38 +03:00
|
|
|
if (stroker != NULL) {
|
2019-10-22 00:47:51 +03:00
|
|
|
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*
|
2020-02-21 14:24:20 +03:00
|
|
|
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*
|
2020-02-21 14:24:20 +03:00
|
|
|
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);
|
|
|
|
}
|
2014-01-28 11:10:49 +04:00
|
|
|
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)
|
2020-02-21 14:24:20 +03:00
|
|
|
{"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}
|
|
|
|
};
|
|
|
|
|
2012-10-13 20:53:07 +04:00
|
|
|
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) {
|
2012-10-14 08:47:30 +04:00
|
|
|
return PyUnicode_FromString(self->face->family_name);
|
2020-05-10 12:56:36 +03:00
|
|
|
}
|
2012-10-13 20:53:07 +04:00
|
|
|
Py_RETURN_NONE;
|
|
|
|
}
|
2010-07-31 06:52:47 +04:00
|
|
|
|
2012-10-13 20:53:07 +04:00
|
|
|
static PyObject*
|
|
|
|
font_getattr_style(FontObject* self, void* closure)
|
|
|
|
{
|
2020-05-10 12:56:36 +03:00
|
|
|
if (self->face->style_name) {
|
2012-10-14 08:47:30 +04:00
|
|
|
return PyUnicode_FromString(self->face->style_name);
|
2020-05-10 12:56:36 +03:00
|
|
|
}
|
2012-10-13 20:53:07 +04:00
|
|
|
Py_RETURN_NONE;
|
|
|
|
}
|
2010-07-31 06:52:47 +04:00
|
|
|
|
2012-10-13 20:53:07 +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));
|
2012-10-13 20:53:07 +04:00
|
|
|
}
|
2010-07-31 06:52:47 +04:00
|
|
|
|
2012-10-13 20:53:07 +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));
|
2012-10-13 20:53:07 +04:00
|
|
|
}
|
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
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-10-13 20:53:07 +04: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
|
|
|
}
|
|
|
|
|
2012-10-13 20:53:07 +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 },
|
2012-10-13 20:53:07 +04:00
|
|
|
{ "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 */
|
2012-10-13 20:53:07 +04:00
|
|
|
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}
|
|
|
|
};
|
|
|
|
|
2012-10-14 08:47:30 +04:00
|
|
|
static int
|
|
|
|
setup_module(PyObject* m) {
|
2010-07-31 06:52:47 +04:00
|
|
|
PyObject* d;
|
|
|
|
PyObject* v;
|
|
|
|
int major, minor, patch;
|
|
|
|
|
2012-10-14 08:47:30 +04:00
|
|
|
d = PyModule_GetDict(m);
|
|
|
|
|
2012-10-13 20:53:07 +04:00
|
|
|
/* 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);
|
|
|
|
|
2012-10-14 08:47:30 +04:00
|
|
|
v = PyUnicode_FromFormat("%d.%d.%d", major, minor, patch);
|
2010-07-31 06:52:47 +04:00
|
|
|
PyDict_SetItemString(d, "freetype2_version", v);
|
2012-10-14 08:47:30 +04:00
|
|
|
|
2016-12-15 13:43:02 +03:00
|
|
|
|
2017-09-25 14:23:59 +03:00
|
|
|
setraqm();
|
2017-09-22 16:25:03 +03:00
|
|
|
v = PyBool_FromLong(!!p_raqm.raqm);
|
2017-06-13 17:57:40 +03:00
|
|
|
PyDict_SetItemString(d, "HAVE_RAQM", v);
|
2016-12-15 13:43:02 +03:00
|
|
|
|
2012-10-14 08:47:30 +04:00
|
|
|
return 0;
|
2010-07-31 06:52:47 +04:00
|
|
|
}
|
2012-10-14 08:47:30 +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) {
|
2012-10-14 08:47:30 +04:00
|
|
|
return NULL;
|
2020-05-10 12:56:36 +03:00
|
|
|
}
|
2012-10-14 08:47:30 +04:00
|
|
|
|
|
|
|
return m;
|
|
|
|
}
|