From f1835deacff3ae8d5817150f176980a6e7aa8a9b Mon Sep 17 00:00:00 2001 From: Terje Skjaeveland Date: Sun, 6 Aug 2017 15:38:16 +0200 Subject: [PATCH] Pango text provider: Initial commit --- .gitignore | 1 + kivy/core/text/__init__.py | 3 + kivy/core/text/_text_pango.pyx | 341 +++++++++++++++++++++++++++++++++ kivy/core/text/text_pango.py | 63 ++++++ kivy/lib/pangoft2.h | 10 + kivy/lib/pangoft2.pxi | 186 ++++++++++++++++++ setup.py | 8 + 7 files changed, 612 insertions(+) create mode 100644 kivy/core/text/_text_pango.pyx create mode 100644 kivy/core/text/text_pango.py create mode 100644 kivy/lib/pangoft2.h create mode 100644 kivy/lib/pangoft2.pxi diff --git a/.gitignore b/.gitignore index f2344dd68..e90c76c92 100644 --- a/.gitignore +++ b/.gitignore @@ -22,6 +22,7 @@ examples/*/bin examples/*/.buildozer kivy/*.c kivy/*.pyd +kivy/core/text/_text_pango.c kivy/core/text/text_layout.c kivy/core/text/text_layout.pyd kivy/core/window/window_info.c diff --git a/kivy/core/text/__init__.py b/kivy/core/text/__init__.py index 04f76a50e..d83d5a0ec 100644 --- a/kivy/core/text/__init__.py +++ b/kivy/core/text/__init__.py @@ -778,6 +778,9 @@ class LabelBase(object): # Load the appropriate provider label_libs = [] +if platform in ('linux', ): + label_libs += [('pango', 'text_pango', 'LabelPango')] + if USE_SDL2: label_libs += [('sdl2', 'text_sdl2', 'LabelSDL2')] else: diff --git a/kivy/core/text/_text_pango.pyx b/kivy/core/text/_text_pango.pyx new file mode 100644 index 000000000..a6a8fec81 --- /dev/null +++ b/kivy/core/text/_text_pango.pyx @@ -0,0 +1,341 @@ +# NOTE: We could probably use a single global FcConfig, but it's not obvious +# how since adding a font doesn't give you back something that can be directly +# used to render with. Apparently you must look it up via a font description. +# Where do you get that from? I don't know... maybe FcConfigGetFonts then +# pango_fc_font_description_from_pattern(), somehow? +# +# The current implementation uses pango_fc_font_map_set_config() to force the +# loaded TTF to be used, seems to work. As far as I can tell alternative is: +# +# cdef FcConfig *oldconfig = FcConfigGetCurrent() +# FcConfigSetCurrent(context.fc_config) +# <-- render --> +# FcConfigSetCurrent(oldconfig) +# + +cimport cython +from libc.stdint cimport uint32_t +from libc.string cimport memset +from cpython.mem cimport PyMem_Malloc, PyMem_Free + +include "../../lib/pangoft2.pxi" +from kivy.logger import Logger +from kivy.core.image import ImageData + +# Cached contexts; dict key + list value is font filename ("font_name_r") +# Dict values are ContextContainer instances, one for each font. +cdef dict kivy_pango_cache = {} +cdef list kivy_pango_cache_order = [] + +# Map text direction to pango constant - neutral is considered auto, anything +# else will call pango_layout_set_auto_dir(the_layout, FALSE) +cdef dict kivy_pango_text_direction = { + 'ltr': PANGO_DIRECTION_LTR, + 'rtl': PANGO_DIRECTION_RTL, + 'weak_ltr': PANGO_DIRECTION_WEAK_LTR, + 'weak_rtl': PANGO_DIRECTION_WEAK_RTL, + 'neutral': PANGO_DIRECTION_NEUTRAL, + 'auto': PANGO_DIRECTION_NEUTRAL} + + +# Fontconfig and pango structures (one per loaded font). Can't use +# ctypedef struct here, because that won't fit in the cache dict +cdef class ContextContainer: + cdef PangoContext *context + cdef PangoFontMap *fontmap + cdef PangoFontDescription *fontdesc + cdef PangoLayout *layout + cdef FcConfig *fc_config + + # Called explicitly on malloc fail to release as fast as possible + # Note: calling a method from __dealloc__ can lead to revived object + def __dealloc__(self): + if self.fontdesc: + pango_font_description_free(self.fontdesc) + if self.layout: + g_object_unref(self.layout) + if self.context: + g_object_unref(self.context) + if self.fontmap: + g_object_unref(self.fontmap) + if self.fc_config: + FcConfigDestroy(self.fc_config) + + +# Add a contextcontainer to cache + keep max 64 open fonts +cdef inline _add_pango_cache(unicode fontid, ContextContainer cc): + global kivy_pango_cache, kivy_pango_cache_order + + cdef unicode popid + while len(kivy_pango_cache_order) >= 64: + popid = kivy_pango_cache_order.pop(0) + del kivy_pango_cache[popid] + + kivy_pango_cache[fontid] = cc + kivy_pango_cache_order.append(fontid) + + +# NOTE: for future, this applies to font selection, irrelevant with one font +#cdef _configure_pattern_destroy_data(gpointer data): +# print("_configure_pattern_destroy_data()!") + +#cdef _configure_pattern_callback(FcPattern *pattern, gpointer data): +# cdef unsigned int flags = GPOINTER_TO_UINT(data) +# print("_configure_pattern_callback()!") +# FcPatternDel(pattern, FC_HINTING) +# FcPatternAddBool(pattern, FC_HINTING, ...) +# FcPatternDel(pattern, FC_AUTOHINT) +# FcPatternAddBool(pattern, FC_AUTOHINT, ...) +# FcPatternDel(pattern, FC_HINT_STYLE) +# FcPatternAddInteger(pattern, FC_HINT_STYLE, ...) +# FcPatternDel(pattern, FC_ANTIALIAS) +# FcPatternAddBool(pattern, FC_ANTIALIAS, ...) + + +# Creates a ContextContainer for the font_name_r of the label +cdef _get_context_container(kivylabel): + global kivy_pango_text_direction + cdef dict options = kivylabel.options + cdef unicode fontid = options['font_name_r'] + if fontid in kivy_pango_cache: + return kivy_pango_cache.get(fontid) + + # Creat a new context + cdef ContextContainer cc = ContextContainer() + + # Create blank FcConfig (fontconfig), and load the TTF file + cc.fc_config = FcConfigCreate() + cdef bytes filename = options['font_name_r'].encode('UTF-8') + if FcConfigAppFontAddFile(cc.fc_config, filename) == FcFalse: + Logger.warn("_text_pango: Error loadinging font '{}'".format(filename)) + cc.__dealloc__() + return + + # Create a blank font map and assign the config from above (one TTF file) + cc.fontmap = pango_ft2_font_map_new() + if not cc.fontmap: + Logger.warn("_text_pango: Could not create new font map") + cc.__dealloc__() + return + pango_fc_font_map_set_config(PANGO_FC_FONT_MAP(cc.fontmap), cc.fc_config) + + # FIXME: should we configure this? + #pango_ft2_font_map_set_resolution(cc.fontmap, n, n) + + # FIXME: This may become relevant, leaving for now + #cc.callback_data_ptr = GUINT_TO_POINTER(flags) + #pango_ft2_font_map_set_default_substitute( + # PANGO_FT2_FONT_MAP(cc.fontmap), + # &_configure_pattern_callback, + # cc.callback_data_ptr, + # &_configure_pattern_destroy_data) + + # Finally create our pango context from the fontmap + cc.context = pango_font_map_create_context(cc.fontmap) + if not cc.context: + Logger.warn("_text_pango: Could not create pango context") + cc.__dealloc__() + return + + # Configure the context's base direction. If user specified something + # explicit, force it. Otherwise (auto/neutral), let pango decide. + # FIXME: add text_direction externally so it's available. + cdef PangoDirection text_dir = kivy_pango_text_direction.get( + options.get('text_direction', 'neutral'), PANGO_DIRECTION_NEUTRAL) + pango_context_set_base_dir(cc.context, text_dir) + + # Create layout from context + cc.layout = pango_layout_new(cc.context) + if not cc.layout: + Logger.warn("_text_pango: Could not create pango layout") + cc.__dealloc__() + return + + # If autodir is false, the context's base direction is used (set above) + if text_dir == PANGO_DIRECTION_NEUTRAL: + pango_layout_set_auto_dir(cc.layout, TRUE) + else: + pango_layout_set_auto_dir(cc.layout, FALSE) + + # The actual font size is specified in pango markup, and the + # actual font is whatever TTF is loaded in this context. This + # may not be needed at all. + cc.fontdesc = pango_font_description_from_string("Arial") + pango_layout_set_font_description(cc.layout, cc.fontdesc) + + # FIXME: does this need to change w/label settings? + pango_layout_set_alignment(cc.layout, PANGO_ALIGN_LEFT) + #pango_layout_set_spacing(cc.layout, n) + + _add_pango_cache(fontid, cc) + return cc + + +# Renders the pango layout to a grayscale bitmap, and blits RGBA at x, y +cdef _render_context(ContextContainer cc, unsigned char *dstbuf, + int x, int y, int final_w, int final_h, + unsigned char textcolor[]): + if not dstbuf or final_w == 0 or final_h == 0 or x > final_w or y > final_h: + Logger.warn('_text_pango: Invalid blit: final={}x{} x={} y={}' + .format(final_w, final_h, x, y)) + return + + # Note, w/h refers to the current subimage size, final_w/h is end result + cdef int w, h + pango_layout_get_pixel_size(cc.layout, &w, &h) + if w == 0 or h == 0 or x + w > final_w or y + h > final_h: + Logger.warn('_text_pango: Invalid blit: final={}x{} x={} y={} w={} h={}' + .format(final_w, final_h, x, y, w, h)) + return + + cdef FT_Bitmap bitmap + cdef int xi, yi + cdef unsigned char graysrc + cdef unsigned long grayidx + cdef unsigned long yi_w + cdef unsigned long offset + cdef unsigned long offset_fixed = x + (y * final_w) + cdef unsigned long offset_yi = final_w - w + cdef unsigned long maxpos = final_w * final_h + with nogil: + # Prepare ft2 bitmap for pango's grayscale data + FT_Bitmap_Init(&bitmap) + bitmap.width = w + bitmap.rows = h + bitmap.pitch = w # 1-byte grayscale + bitmap.pixel_mode = FT_PIXEL_MODE_GRAY # no BGRA in pango (ft2 has it) + bitmap.num_grays = 256 + bitmap.buffer = g_malloc0(w * h) + if not bitmap.buffer: + with gil: + Logger.warn('_text_pango: Could not malloc FT_Bitmap.buffer') + return + + # Render the layout as 1 byte per pixel grayscale bitmap + # FIXME: does render_layout_subpixel() do us any good? + pango_ft2_render_layout(&bitmap, cc.layout, 0, 0) + + # Blit the bitmap as RGBA at x, y in dstbuf (w/h is the ft2 bitmap) + for yi in range(0, h): + offset = offset_fixed + (yi * offset_yi) + yi_w = yi * w + if offset + yi_w + w - 1 > maxpos: + with gil: + Logger.warn('_text_pango: OOB blit: yi={} final={}x{} ' + 'x={} y={} w={} h={} maxpos={}'.format( + yi, final_w, final_h, x, y, w, h, maxpos)) + break + + # FIXME: Handle big endian - either use variable shifts here, or + # return as abgr + handle elsewhere + for xi in range(0, w): + grayidx = yi_w + xi + graysrc = (bitmap.buffer)[grayidx] + (dstbuf)[offset + grayidx] = ( + (((textcolor[0] * graysrc) / 255)) | + (((textcolor[1] * graysrc) / 255) << 8) | + (((textcolor[2] * graysrc) / 255) << 16) | + (((textcolor[3] * graysrc) / 255) << 24) ) + g_free(bitmap.buffer) + # /nogil blit + + +cdef class KivyPangoRenderer: + # w, h is the final bitmap size, drawn by 1+ render() calls in *pixels + cdef int w, h, canary + cdef unsigned char *pixels + + def __cinit__(self, int w, int h): + self.canary = 1 + self.w = w + self.h = h + self.pixels = PyMem_Malloc(w * h * 4) + if self.pixels: + memset(self.pixels, 0, w * h * 4) + + # Kivy's markup system breaks labels down to smaller chunks and render + # separately. In that case, we get several calls to render() with misc + # options and x/y positions. End result is stored in self.pixels. + def render(self, kivylabel, text, x, y): + if not self.pixels: + Logger.warn('_text_pango: render() called, but self.pixels is NULL') + return + cdef ContextContainer cc = _get_context_container(kivylabel) + if not cc: + Logger.warn('_text_pango: Could not get context container, aborting') + return + + # Set markup, this could use kivylabel.options + attrs + markup = text.encode('UTF-8') + pango_layout_set_markup(cc.layout, markup, len(markup)) + + # Kivy normalized text color -> 0-255 rgba + cdef unsigned char textcolor[4] + textcolor[:] = [ min(255, int(c * 255)) for c in kivylabel.options['color'] ] + + # Finally render the layout and blit it to self.pixels + _render_context(cc, self.pixels, x, y, self.w, self.h, textcolor) + self.canary = 0 + + # Return ImageData instance with the rendered pixels + def get_ImageData(self): + if not self.pixels: + Logger.warn('_text_pango: get_ImageData() self.pixels == NULL') + return + + if self.canary: + Logger.warn("_text_pango: Dead canary in get_ImageData()") + return + self.canary = 1 + + try: + b_pixels = self.pixels[:self.w * self.h * 4] + return ImageData(self.w, self.h, 'rgba', b_pixels) + finally: + PyMem_Free(self.pixels) + + +def kpango_get_extents(kivylabel, text): + cdef ContextContainer cc = _get_context_container(kivylabel) + cdef int w, h + if not cc: + Logger.warn('_text_pango: Could not get container for extents: {}' + .format(kivylabel.options['font_name_r'])) + return 0, 0 + + markup = text.encode('UTF-8') + pango_layout_set_markup(cc.layout, markup, len(markup)) + pango_layout_get_pixel_size(cc.layout, &w, &h) + return w, h + + +def kpango_get_ascent(kivylabel): + cdef ContextContainer cc = _get_context_container(kivylabel) + if not cc: + Logger.warn('_text_pango: Could not get container for ascent: {}' + .format(kivylabel.options['font_name_r'])) + return 0 + + # FIXME: pango_language_from_string(kivylabel.text_language) + cdef PangoFontMetrics *fm = pango_context_get_metrics( + cc.context, cc.fontdesc, pango_language_get_default()) + try: + return (pango_font_metrics_get_ascent(fm) / PANGO_SCALE) + finally: + pango_font_metrics_unref(fm) + + +def kpango_get_descent(kivylabel): + cdef ContextContainer cc = _get_context_container(kivylabel) + if not cc: + Logger.warn('_text_pango: Could not get container for decent: {}' + .format(kivylabel.options['font_name_r'])) + return 0 + + # FIXME: pango_language_from_string(kivylabel.text_language) + cdef PangoFontMetrics *fm = pango_context_get_metrics( + cc.context, cc.fontdesc, pango_language_get_default()) + try: + return (pango_font_metrics_get_descent(fm) / PANGO_SCALE) + finally: + pango_font_metrics_unref(fm) diff --git a/kivy/core/text/text_pango.py b/kivy/core/text/text_pango.py new file mode 100644 index 000000000..c8316921d --- /dev/null +++ b/kivy/core/text/text_pango.py @@ -0,0 +1,63 @@ +''' +Pango text provider +=================== + +''' + +__all__ = ('LabelPango', ) + +from kivy.compat import PY2 +from kivy.core.text import LabelBase +from kivy.core.text._text_pango import (KivyPangoRenderer, kpango_get_extents, + kpango_get_ascent, kpango_get_descent) + + +class LabelPango(LabelBase): + + # This is a hack to avoid dealing with glib attrs to configure layout, + # we just create markup out of the options and let pango set attrs + def _pango_markup(self, text): + markup = text.replace('<', '<').replace('>', '>') + options = self.options + tags = [] + if options['bold']: + markup = '{}'.format(markup) + if options['underline']: + markup = '{}'.format(markup) + if options['strikethrough']: + markup = '{}'.format(markup) + if options['font_hinting'] == 'mono': + markup = '{}'.format(markup) + + # FIXME: does this do the right thing? .. don't see much w/roboto + weight_attr = '' + if options['font_hinting'] in ('light', 'normal'): + weight_attr = ' weight="{}"'.format(options['font_hinting']) + + return b'{}'.format( + int(self.options['font_size']), + weight_attr, + markup) + + def get_extents(self, text): + if not text: + return (0, 0) + w, h = kpango_get_extents(self, self._pango_markup(text)) + return (w, h) + + def get_ascent(self): + return kpango_get_ascent(self) + + def get_descent(self): + return kpango_get_descent(self) + + def _render_begin(self): + self._rdr = KivyPangoRenderer(self._size[0], self._size[1]) + + def _render_text(self, text, x, y): + self._rdr.render(self, self._pango_markup(text), x, y) + + def _render_end(self): + imgdata = self._rdr.get_ImageData() + del self._rdr + return imgdata diff --git a/kivy/lib/pangoft2.h b/kivy/lib/pangoft2.h new file mode 100644 index 000000000..2482218dd --- /dev/null +++ b/kivy/lib/pangoft2.h @@ -0,0 +1,10 @@ +#ifndef KIVY_PANGOFT2_HEADER +#define KIVY_PANGOFT2_HEADER + +// FreeType2 headers must be included via macros in ft2build.h +#include +#include FT_FREETYPE_H +#include FT_BITMAP_H + +#endif + diff --git a/kivy/lib/pangoft2.pxi b/kivy/lib/pangoft2.pxi new file mode 100644 index 000000000..50535548d --- /dev/null +++ b/kivy/lib/pangoft2.pxi @@ -0,0 +1,186 @@ +cdef extern from "glib.h" nogil: + ctypedef void *gpointer + ctypedef int gint + ctypedef unsigned int guint + ctypedef unsigned long gsize + ctypedef gint gboolean + gboolean TRUE + gboolean FALSE + + void *g_malloc(gsize n_bytes) + void *g_malloc0(gsize n_bytes) + void g_free(gpointer mem) + void g_object_unref(gpointer obj) +# gpointer GUINT_TO_POINTER(guint u) +# guint GPOINTER_TO_UINT(gpointer p) + + +# https://www.freetype.org/freetype2/docs/reference/ft2-index.html +cdef extern from "../../lib/pangoft2.h" nogil: + ctypedef struct FT_Library: + pass + + ctypedef enum FT_Pixel_Mode: + FT_PIXEL_MODE_NONE = 0 + FT_PIXEL_MODE_MONO + FT_PIXEL_MODE_GRAY + FT_PIXEL_MODE_GRAY2 + FT_PIXEL_MODE_GRAY4 + FT_PIXEL_MODE_LCD + FT_PIXEL_MODE_LCD_V + FT_PIXEL_MODE_BGRA + FT_PIXEL_MODE_MAX + + ctypedef struct FT_Bitmap: + unsigned int rows + unsigned int width + int pitch + unsigned char* buffer + unsigned short num_grays + unsigned char pixel_mode + unsigned char palette_mode + void* palette + + void FT_Bitmap_Init(FT_Bitmap *bitmap) + void FT_Bitmap_Done(FT_Library library, FT_Bitmap *bitmap) + + +# https://www.freedesktop.org/software/fontconfig/fontconfig-devel/t1.html +cdef extern from "fontconfig/fontconfig.h" nogil: + ctypedef struct FcConfig: + pass + ctypedef struct FcPattern: + pass +# ctypedef struct FcFontSet: +# int nfont +# int sfont +# FcPattern **fonts + + ctypedef bint FcBool + ctypedef unsigned char FcChar8 + bint FcTrue + bint FcFalse + + FcConfig *FcConfigCreate() + void FcConfigDestroy(FcConfig *config) + FcConfig *FcConfigGetCurrent() + FcBool FcConfigSetCurrent(FcConfig *config) + FcBool FcConfigAppFontAddFile(FcConfig *config, const FcChar8 *file) + +# FcPattern *FcPatternCreate() +# void FcPatternDestroy(FcPattern *p) +# FcBool FcPatternDel(FcPattern *p, const char *object) +# FcBool FcPatternAddInteger (FcPattern *p, const char *object, int i) +# FcBool FcPatternAddDouble (FcPattern *p, const char *object, double d) +# FcBool FcPatternAddString (FcPattern *p, const char *object, const FcChar8 *s) +# FcBool FcPatternAddMatrix (FcPattern *p, const char *object, const FcMatrix *m) +# FcBool FcPatternAddCharSet (FcPattern *p, const char *object, const FcCharSet *c) +# FcBool FcPatternAddBool (FcPattern *p, const char *object, FcBool b) +# FcBool FcPatternAddFTFace (FcPattern *p, const char *object, const FT_Facef) +# FcBool FcPatternAddLangSet (FcPattern *p, const char *object, const FcLangSet *l) +# FcBool FcPatternAddRange (FcPattern *p, const char *object, const FcRange *r) + + +# https://developer.gnome.org/pango/stable/pango-Glyph-Storage.html +cdef extern from "pango/pango-types.h" nogil: +# ctypedef struct PangoRectangle: +# int x +# int y +# int width +# int height + unsigned int PANGO_SCALE + + +# https://developer.gnome.org/pango/stable/pango-Scripts-and-Languages.html +cdef extern from "pango/pango-language.h" nogil: + ctypedef struct PangoLanguage: + pass + + PangoLanguage *pango_language_get_default() + PangoLanguage *pango_language_from_string(const char *language) + + +# https://developer.gnome.org/pango/stable/pango-FreeType-Fonts-and-Rendering.html +cdef extern from "pango/pangoft2.h" nogil: + ctypedef struct PangoFT2FontMap: + pass + ctypedef void *PangoFT2SubstituteFunc + ctypedef void *GDestroyNotify + + PangoFT2FontMap *PANGO_FT2_FONT_MAP(PangoFontMap *fontmap) + void pango_ft2_render_layout(FT_Bitmap *bitmap, PangoLayout *layout, int x, int y) + void pango_ft2_render_layout_subpixel(FT_Bitmap *bitmap, PangoLayout *layout, int x, int y) + void pango_ft2_font_map_set_default_substitute(PangoFT2FontMap *fontmap, PangoFT2SubstituteFunc func, gpointer data, GDestroyNotify notify) + + +# https://developer.gnome.org/pango/stable/pango-Text-Processing.html +cdef extern from "pango/pango-context.h" nogil: + ctypedef struct PangoContext: + pass + + void pango_context_set_base_dir(PangoContext *context, PangoDirection direction) + PangoFontMetrics *pango_context_get_metrics(PangoContext *context, const PangoFontDescription *desc, PangoLanguage *language) + + +# https://developer.gnome.org/pango/stable/pango-Bidirectional-Text.html +cdef extern from "pango/pango-bidi-type.h" nogil: + ctypedef enum PangoDirection: + PANGO_DIRECTION_LTR + PANGO_DIRECTION_RTL + PANGO_DIRECTION_TTB_LTR # deprecated + PANGO_DIRECTION_TTB_RTL # deprecated + PANGO_DIRECTION_WEAK_LTR + PANGO_DIRECTION_WEAK_RTL + PANGO_DIRECTION_NEUTRAL + + +# https://developer.gnome.org/pango/stable/pango-Fonts.html +cdef extern from "pango/pango-font.h" nogil: + ctypedef struct PangoFontMap: + pass + ctypedef struct PangoFontDescription: + pass + ctypedef struct PangoFontMetrics: + pass + + PangoFontDescription* pango_font_description_from_string(const char *string) + void pango_font_description_free(PangoFontDescription *desc) +# void pango_font_description_set_size(PangoFontDescription *desc, gint size) + PangoContext *pango_font_map_create_context(PangoFontMap *fontmap) + PangoFontMap *pango_ft2_font_map_new() + int pango_font_metrics_get_ascent(PangoFontMetrics *metrics) + int pango_font_metrics_get_descent(PangoFontMetrics *metrics) + void pango_font_metrics_unref(PangoFontMetrics *metrics) + + +# https://developer.gnome.org/pango/stable/PangoFcFontMap.html +cdef extern from "pango/pangofc-fontmap.h" nogil: + ctypedef struct PangoFcFontMap: + pass + + PangoFcFontMap *PANGO_FC_FONT_MAP(PangoFontMap *fontmap) + void pango_fc_font_map_set_config(PangoFcFontMap *fontmap, FcConfig *config) + + +# https://developer.gnome.org/pango/stable/pango-Layout-Objects.html +cdef extern from "pango/pango-layout.h" nogil: + ctypedef struct PangoLayout: + pass + + ctypedef enum PangoAlignment: + PANGO_ALIGN_LEFT + PANGO_ALIGN_CENTER + PANGO_ALIGN_RIGHT + + PangoLayout *pango_layout_new(PangoContext *context) + void pango_layout_get_pixel_size(PangoLayout *layout, int *width, int *height) + void pango_layout_get_size(PangoLayout *layout, int *width, int *height) + void pango_layout_set_alignment(PangoLayout *layout, PangoAlignment alignment) + void pango_layout_set_auto_dir(PangoLayout *layout, gboolean auto_dir) + void pango_layout_set_markup(PangoLayout *layout, const char *markup, int length) + void pango_layout_set_font_description(PangoLayout *layout, const PangoFontDescription *desc) + void pango_layout_set_text(PangoLayout *layout, const char *text, int length) + void pango_layout_set_width(PangoLayout *layout, int width) + void pango_layout_set_height(PangoLayout *layout, int height) + void pango_layout_set_spacing(PangoLayout *layout, int spacing) + diff --git a/setup.py b/setup.py index ff4386959..f2d22f885 100644 --- a/setup.py +++ b/setup.py @@ -811,11 +811,19 @@ if c_options['use_sdl2'] and sdl2_flags: for source_file in ('core/window/_window_sdl2.pyx', 'core/image/_img_sdl2.pyx', 'core/text/_text_sdl2.pyx', + 'core/text/_text_pango.pyx', 'core/audio/audio_sdl2.pyx', 'core/clipboard/_clipboard_sdl2.pyx'): sources[source_file] = merge( base_flags, sdl2_flags, sdl2_depends) +pango_flags = pkgconfig('pangoft2') +if pango_flags: + pango_depends = {'depends': ['lib/pangoft2.pxi', + 'lib/pangoft2.h']} + sources['core/text/_text_pango.pyx'] = merge( + base_flags, pango_flags, pango_depends) + if platform in ('darwin', 'ios'): # activate ImageIO provider for our core image if platform == 'ios':