146 lines
5.6 KiB
Python
146 lines
5.6 KiB
Python
'''
|
|
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://<PATH>` - `FcInitLoadConfig()` + `FcAppFontAddDir()`
|
|
* `fontconfig://<PATH>` - `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('<span font="20">Hello <b>World!</b></span>')
|
|
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))
|