Dynamically load libraqm, now an optional runtime dependency

This commit is contained in:
Eric Soroos 2017-09-22 13:25:03 +00:00
parent 6d2a02c6b1
commit a4b7a6d215
4 changed files with 241 additions and 43 deletions

View File

@ -30,6 +30,8 @@ from ._util import isDirectory, isPath
import os
import sys
LAYOUT_BASIC = 0
LAYOUT_RAQM = 1
class _imagingft_not_installed(object):
# module placeholder
@ -42,8 +44,6 @@ try:
except ImportError:
core = _imagingft_not_installed()
LAYOUT_BASIC = 0
LAYOUT_RAQM = 1
# FIXME: add support for pilfont2 format (see FontFile.py)

View File

@ -28,6 +28,8 @@
#define KEEP_PY_UNICODE
#include "py3.h"
#include <dlfcn.h>
#if !defined(FT_LOAD_TARGET_MONO)
#define FT_LOAD_TARGET_MONO FT_LOAD_MONOCHROME
#endif
@ -41,9 +43,8 @@
#define FT_ERRORDEF( e, v, s ) { e, s },
#define FT_ERROR_START_LIST {
#define FT_ERROR_END_LIST { 0, 0 } };
#ifdef HAVE_RAQM
#include <raqm.h>
#endif
#define LAYOUT_FALLBACK 0
#define LAYOUT_RAQM 1
@ -75,6 +76,32 @@ typedef struct {
static PyTypeObject Font_Type;
typedef struct {
void* raqm;
raqm_t* (*create)(void);
int (*set_text)(raqm_t *rq,
const uint32_t *text,
size_t len);
bool (*set_text_utf8) (raqm_t *rq,
const char *text,
size_t len);
bool (*set_par_direction) (raqm_t *rq,
raqm_direction_t dir);
bool (*add_font_feature) (raqm_t *rq,
const char *feature,
int len);
bool (*set_freetype_face) (raqm_t *rq,
FT_Face face);
bool (*layout) (raqm_t *rq);
raqm_glyph_t* (*get_glyphs) (raqm_t *rq,
size_t *length);
void (*destroy) (raqm_t *rq);
} p_raqm_func;
static p_raqm_func p_raqm;
/* round a 26.6 pixel coordinate to the nearest larger integer */
#define PIXEL(x) ((((x)+63) & -64)>>6)
@ -93,6 +120,35 @@ geterror(int code)
return NULL;
}
static int
setraqm(void)
{
/* set the static function pointers for dynamic raqm linking */
p_raqm.raqm = NULL;
p_raqm.raqm = dlopen("libraqm.so.0", RTLD_LAZY);
if (!p_raqm.raqm) {
return 1;
}
p_raqm.create = dlsym(p_raqm.raqm, "raqm_create");
p_raqm.set_text = dlsym(p_raqm.raqm, "raqm_set_text");
p_raqm.set_text_utf8 = dlsym(p_raqm.raqm, "raqm_set_text_utf8");
p_raqm.set_par_direction = dlsym(p_raqm.raqm, "raqm_set_par_direction");
p_raqm.add_font_feature = dlsym(p_raqm.raqm, "raqm_add_font_feature");
p_raqm.set_freetype_face = dlsym(p_raqm.raqm, "raqm_set_freetype_face");
p_raqm.layout = dlsym(p_raqm.raqm, "raqm_layout");
p_raqm.get_glyphs = dlsym(p_raqm.raqm, "raqm_get_glyphs");
p_raqm.destroy = dlsym(p_raqm.raqm, "raqm_destroy");
if (dlerror()) {
dlclose(p_raqm.raqm);
p_raqm.raqm = NULL;
return 2;
}
return 0;
}
static PyObject*
getfont(PyObject* self_, PyObject* args, PyObject* kw)
{
@ -205,7 +261,6 @@ font_getchar(PyObject* string, int index, FT_ULong* char_out)
return 0;
}
#ifdef HAVE_RAQM
static size_t
text_layout_raqm(PyObject* string, FontObject* self, const char* dir,
PyObject *features ,GlyphInfo **glyph_info, int mask)
@ -216,7 +271,7 @@ text_layout_raqm(PyObject* string, FontObject* self, const char* dir,
raqm_glyph_t *glyphs;
raqm_direction_t direction;
rq = raqm_create();
rq = (*p_raqm.create)();
if (rq == NULL) {
PyErr_SetString(PyExc_ValueError, "raqm_create() failed.");
goto failed;
@ -230,7 +285,7 @@ text_layout_raqm(PyObject* string, FontObject* self, const char* dir,
and raqm fails with empty strings */
goto failed;
}
if (!raqm_set_text(rq, (const uint32_t *)(text), size)) {
if (!(*p_raqm.set_text)(rq, (const uint32_t *)(text), size)) {
PyErr_SetString(PyExc_ValueError, "raqm_set_text() failed");
goto failed;
}
@ -242,7 +297,7 @@ text_layout_raqm(PyObject* string, FontObject* self, const char* dir,
if (! size) {
goto failed;
}
if (!raqm_set_text_utf8(rq, text, size)) {
if (!(*p_raqm.set_text_utf8)(rq, text, size)) {
PyErr_SetString(PyExc_ValueError, "raqm_set_text_utf8() failed");
goto failed;
}
@ -267,7 +322,7 @@ text_layout_raqm(PyObject* string, FontObject* self, const char* dir,
}
}
if (!raqm_set_par_direction(rq, direction)) {
if (!(*p_raqm.set_par_direction)(rq, direction)) {
PyErr_SetString(PyExc_ValueError, "raqm_set_par_direction() failed");
goto failed;
}
@ -308,24 +363,24 @@ text_layout_raqm(PyObject* string, FontObject* self, const char* dir,
size = PyString_GET_SIZE(item);
}
#endif
if (!raqm_add_font_feature(rq, feature, size)) {
if (!(*p_raqm.add_font_feature)(rq, feature, size)) {
PyErr_SetString(PyExc_ValueError, "raqm_add_font_feature() failed");
goto failed;
}
}
}
if (!raqm_set_freetype_face(rq, self->face)) {
PyErr_SetString(PyExc_RuntimeError, "raqm_set_freetype_face() failed.");
goto failed;
if (!(*p_raqm.set_freetype_face)(rq, self->face)) {
PyErr_SetString(PyExc_RuntimeError, "raqm_set_freetype_face() failed.");
goto failed;
}
if (!raqm_layout (rq)) {
PyErr_SetString(PyExc_RuntimeError, "raqm_layout() failed.");
goto failed;
if (!(*p_raqm.layout)(rq)) {
PyErr_SetString(PyExc_RuntimeError, "raqm_layout() failed.");
goto failed;
}
glyphs = raqm_get_glyphs(rq, &count);
glyphs = (*p_raqm.get_glyphs)(rq, &count);
if (glyphs == NULL) {
PyErr_SetString(PyExc_ValueError, "raqm_get_glyphs() failed.");
count = 0;
@ -348,10 +403,9 @@ text_layout_raqm(PyObject* string, FontObject* self, const char* dir,
}
failed:
raqm_destroy (rq);
(*p_raqm.destroy)(rq);
return count;
}
#endif
static size_t
text_layout_fallback(PyObject* string, FontObject* self, const char* dir,
@ -424,15 +478,12 @@ text_layout(PyObject* string, FontObject* self, const char* dir,
PyObject *features, GlyphInfo **glyph_info, int mask)
{
size_t count;
#ifdef HAVE_RAQM
if (self->layout_engine == LAYOUT_RAQM) {
if (p_raqm.raqm && self->layout_engine == LAYOUT_RAQM) {
count = text_layout_raqm(string, self, dir, features, glyph_info, mask);
} else {
count = text_layout_fallback(string, self, dir, features, glyph_info, mask);
}
#else
count = text_layout_fallback(string, self, dir, features, glyph_info, mask);
#endif
return count;
}
@ -853,11 +904,8 @@ setup_module(PyObject* m) {
PyDict_SetItemString(d, "freetype2_version", v);
#ifdef HAVE_RAQM
v = PyBool_FromLong(1);
#else
v = PyBool_FromLong(0);
#endif
setraqm();
v = PyBool_FromLong(!!p_raqm.raqm);
PyDict_SetItemString(d, "HAVE_RAQM", v);
return 0;

163
libImaging/raqm.h Normal file
View File

@ -0,0 +1,163 @@
/*
* Copyright © 2015 Information Technology Authority (ITA) <foss@ita.gov.om>
* Copyright © 2016 Khaled Hosny <khaledhosny@eglug.org>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
*/
#ifndef _RAQM_H_
#define _RAQM_H_
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdbool.h>
#include <stdint.h>
#include <ft2build.h>
#include FT_FREETYPE_H
#ifdef __cplusplus
extern "C" {
#endif
/**
* raqm_t:
*
* This is the main object holding all state of the currently processed text as
* well as its output.
*
* Since: 0.1
*/
typedef struct _raqm raqm_t;
/**
* raqm_direction_t:
* @RAQM_DIRECTION_DEFAULT: Detect paragraph direction automatically.
* @RAQM_DIRECTION_RTL: Paragraph is mainly right-to-left text.
* @RAQM_DIRECTION_LTR: Paragraph is mainly left-to-right text.
* @RAQM_DIRECTION_TTB: Paragraph is mainly vertical top-to-bottom text.
*
* Base paragraph direction, see raqm_set_par_direction().
*
* Since: 0.1
*/
typedef enum
{
RAQM_DIRECTION_DEFAULT,
RAQM_DIRECTION_RTL,
RAQM_DIRECTION_LTR,
RAQM_DIRECTION_TTB
} raqm_direction_t;
/**
* raqm_glyph_t:
* @index: the index of the glyph in the font file.
* @x_advance: the glyph advance width in horizontal text.
* @y_advance: the glyph advance width in vertical text.
* @x_offset: the horizontal movement of the glyph from the current point.
* @y_offset: the vertical movement of the glyph from the current point.
* @cluster: the index of original character in input text.
* @ftface: the @FT_Face of the glyph.
*
* The structure that holds information about output glyphs, returned from
* raqm_get_glyphs().
*/
typedef struct raqm_glyph_t {
unsigned int index;
int x_advance;
int y_advance;
int x_offset;
int y_offset;
uint32_t cluster;
FT_Face ftface;
} raqm_glyph_t;
raqm_t *
raqm_create (void);
raqm_t *
raqm_reference (raqm_t *rq);
void
raqm_destroy (raqm_t *rq);
bool
raqm_set_text (raqm_t *rq,
const uint32_t *text,
size_t len);
bool
raqm_set_text_utf8 (raqm_t *rq,
const char *text,
size_t len);
bool
raqm_set_par_direction (raqm_t *rq,
raqm_direction_t dir);
bool
raqm_set_language (raqm_t *rq,
const char *lang,
size_t start,
size_t len);
bool
raqm_add_font_feature (raqm_t *rq,
const char *feature,
int len);
bool
raqm_set_freetype_face (raqm_t *rq,
FT_Face face);
bool
raqm_set_freetype_face_range (raqm_t *rq,
FT_Face face,
size_t start,
size_t len);
bool
raqm_set_freetype_load_flags (raqm_t *rq,
int flags);
bool
raqm_layout (raqm_t *rq);
raqm_glyph_t *
raqm_get_glyphs (raqm_t *rq,
size_t *length);
bool
raqm_index_to_position (raqm_t *rq,
size_t *index,
int *x,
int *y);
bool
raqm_position_to_index (raqm_t *rq,
int x,
int y,
size_t *index);
#ifdef __cplusplus
}
#endif
#endif /* _RAQM_H_ */

View File

@ -144,7 +144,6 @@ IMAGEQUANT_ROOT = None
TIFF_ROOT = None
FREETYPE_ROOT = None
LCMS_ROOT = None
RAQM_ROOT = None
def _pkg_config(name):
@ -164,7 +163,7 @@ def _pkg_config(name):
class pil_build_ext(build_ext):
class feature:
features = ['zlib', 'jpeg', 'tiff', 'freetype', 'raqm', 'lcms', 'webp',
features = ['zlib', 'jpeg', 'tiff', 'freetype', 'lcms', 'webp',
'webpmux', 'jpeg2000', 'imagequant']
required = {'jpeg', 'zlib'}
@ -546,14 +545,6 @@ class pil_build_ext(build_ext):
if subdir:
_add_directory(self.compiler.include_dirs, subdir, 0)
if feature.want('raqm'):
_dbg('Looking for raqm')
if _find_include_file(self, "raqm.h"):
if _find_library_file(self, "raqm") and \
_find_library_file(self, "harfbuzz") and \
_find_library_file(self, "fribidi"):
feature.raqm = ["raqm", "harfbuzz", "fribidi"]
if feature.want('lcms'):
_dbg('Looking for lcms')
if _find_include_file(self, "lcms2.h"):
@ -639,9 +630,6 @@ class pil_build_ext(build_ext):
if feature.freetype:
libs = ["freetype"]
defs = []
if feature.raqm:
libs.extend(feature.raqm)
defs.append(('HAVE_RAQM', None))
exts.append(Extension(
"PIL._imagingft", ["_imagingft.c"], libraries=libs,
define_macros=defs))
@ -706,7 +694,6 @@ class pil_build_ext(build_ext):
(feature.imagequant, "LIBIMAGEQUANT"),
(feature.tiff, "LIBTIFF"),
(feature.freetype, "FREETYPE2"),
(feature.raqm, "RAQM"),
(feature.lcms, "LITTLECMS2"),
(feature.webp, "WEBP"),
(feature.webpmux, "WEBPMUX"),