''' Pango text provider =================== .. versionadded:: 1.11.0 .. warning:: The low-level Pango API is experimental, and subject to change without notice for as long as this warning is present. Installation ------------ 1. Install pangoft2 (`apt install libfreetype6-dev libpango1.0-dev libpangoft2-1.0-0`) or ensure it is available in pkg-config 2. Recompile kivy. Check that pangoft2 is found `use_pangoft2 = 1` 3. Test it! Enforce the text core renderer to pango using environment variable: `export KIVY_TEXT=pango` This has been tested on OSX and Linux, Python 3.6. Font context types for FontConfig+FreeType2 backend --------------------------------------------------- * `system://` - `FcInitLoadConfigAndFonts()` * `systemconfig://` - `FcInitLoadConfig()` * `directory://` - `FcInitLoadConfig()` + `FcAppFontAddDir()` * `fontconfig://` - `FcConfigCreate()` + `FcConfigParseAndLoad()` * Any other context name - `FcConfigCreate()` Low-level Pango access ---------------------- Since Kivy currently does its own text layout, the Label and TextInput widgets do not take full advantage of Pango. For example, line breaks do not take language/script into account, and switching alignment per paragraph (for bi- directional text) is not supported. For advanced i18n requirements, we provide a simple wrapper around PangoLayout that you can use to render text. * https://developer.gnome.org/pango/1.40/pango-Layout-Objects.html * https://developer.gnome.org/pango/1.40/PangoMarkupFormat.html * See the `kivy/core/text/_text_pango.pyx` file @ `cdef class KivyPangoLayout` for more information. Not all features of PangoLayout are implemented. .. python:: from kivy.core.window import Window # OpenGL must be initialized from kivy.core.text._text_pango import KivyPangoLayout layout = KivyPangoLayout('system://') layout.set_markup('Hello World!') tex = layout.render_as_Texture() Known limitations ----------------- * Pango versions older than v1.38 has not been tested. It may work on some systems with older pango and newer FontConfig/FreeType2 versions. * Kivy's text layout is used, not Pango. This means we do not use Pango's line-breaking feature (which is superior to Kivy's), and we can't use Pango's bidirectional cursor helpers in TextInput. * Font family collissions can happen. For example, if you use a `system://` context and add a custom `Arial.ttf`, using `arial` as the `font_family` may or may not draw with your custom font (depending on whether or not there is already a system-wide "arial" font installed) * Rendering is inefficient; the normal way to integrate Pango would be using a dedicated PangoLayout per widget. This is not currently practical due to missing abstractions in Kivy core (in the current implementation, we have a dedicated PangoLayout *per font context,* which is rendered once for each LayoutWord) ''' __all__ = ('LabelPango', ) from types import MethodType from os.path import isfile from kivy.resources import resource_find from kivy.core.text import LabelBase, FontContextManagerBase from kivy.core.text._text_pango import ( KivyPangoRenderer, kpango_get_extents, kpango_get_ascent, kpango_get_descent, kpango_find_base_dir, kpango_font_context_exists, kpango_font_context_create, kpango_font_context_destroy, kpango_font_context_add_font, kpango_font_context_list, kpango_font_context_list_custom, kpango_font_context_list_families) class LabelPango(LabelBase): _font_family_support = True def __init__(self, *largs, **kwargs): self.get_extents = MethodType(kpango_get_extents, self) self.get_ascent = MethodType(kpango_get_ascent, self) self.get_descent = MethodType(kpango_get_descent, self) super(LabelPango, self).__init__(*largs, **kwargs) find_base_direction = staticmethod(kpango_find_base_dir) def _render_begin(self): self._rdr = KivyPangoRenderer(*self._size) def _render_text(self, text, x, y): self._rdr.render(self, text, x, y) def _render_end(self): imgdata = self._rdr.get_ImageData() del self._rdr return imgdata class PangoFontContextManager(FontContextManagerBase): create = staticmethod(kpango_font_context_create) exists = staticmethod(kpango_font_context_exists) destroy = staticmethod(kpango_font_context_destroy) list = staticmethod(kpango_font_context_list) list_families = staticmethod(kpango_font_context_list_families) list_custom = staticmethod(kpango_font_context_list_custom) @staticmethod def add_font(font_context, filename, autocreate=True, family=None): if not autocreate and not PangoFontContextManager.exists(font_context): raise Exception("FontContextManager: Attempt to add font file " "'{}' to non-existing context '{}' without " "autocreate.".format(filename, font_context)) if not filename: raise Exception("FontContextManager: Cannot add empty font file") if not isfile(filename): filename = resource_find(filename) if not isfile(filename): if not filename.endswith('.ttf'): filename = resource_find('{}.ttf'.format(filename)) if filename and isfile(filename): return kpango_font_context_add_font(font_context, filename) raise Exception("FontContextManager: Attempt to add non-existent " "font file: '{}' to context '{}'" .format(filename, font_context))