Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/workflows/bleeding-edge.yml
Original file line number Diff line number Diff line change
Expand Up @@ -63,13 +63,17 @@ jobs:
- name: Sanity check package version
run: python -m pip list
- name: Run enable test suite
env:
PYTHONFAULTHANDLER: 1
uses: GabrielBB/xvfb-action@v1
with:
# kiva agg requires at least 15-bit color depth.
# The --server-args assumes xvfb-run is called, hence Linux only.
run: --server-args="-screen 0 1024x768x24" python -m unittest discover -v enable
working-directory: ${{ runner.temp }}
- name: Run kiva test suite
env:
PYTHONFAULTHANDLER: 1
uses: GabrielBB/xvfb-action@v1
with:
run: python -m unittest discover -v kiva
Expand Down
9 changes: 5 additions & 4 deletions ci/edmtool.py
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ def install(runtime, toolkit, environment, source):
("edm --config {edm_config} environments create {environment} "
"--force --version={runtime}"),
("edm --config {edm_config} install -y -e {environment} {packages} "
"--add-repository enthought/lgpl"),
"--add-repository enthought/lgpl"),
("edm run -e {environment} -- pip install -r ci/requirements.txt"
" --no-dependencies"),
]
Expand Down Expand Up @@ -352,12 +352,13 @@ def test(runtime, toolkit, environment):
"""
parameters = get_parameters(runtime, toolkit, environment)
environ = environment_vars.get(toolkit, {}).copy()
environ['PYTHONUNBUFFERED'] = "1"
environ["PYTHONUNBUFFERED"] = "1"
environ["PYTHONFAULTHANDLER"] = "1"
commands = [
("edm run -e {environment} -- python -W default -m"
"coverage run -m unittest discover enable -v"),
"coverage run -m unittest discover enable -v"),
("edm run -e {environment} -- python -W default -m"
"coverage run -a -m unittest discover kiva -v"),
"coverage run -a -m unittest discover kiva -v"),
]

# We run in a tempdir to avoid accidentally picking up wrong traitsui
Expand Down
3 changes: 2 additions & 1 deletion kiva/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@
=====

- :class:`~.Font`
- :func:`~.add_application_fonts`

Font Constants
--------------
Expand Down Expand Up @@ -146,4 +147,4 @@
INVERTED_TRIANGLE_MARKER, PLUS_MARKER, DOT_MARKER, PIXEL_MARKER
)
from ._cython_speedups import points_in_polygon
from .fonttools import Font
from .fonttools import add_application_fonts, Font
3 changes: 3 additions & 0 deletions kiva/fonttools/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,7 @@
# is also available online at http://www.enthought.com/licenses/BSD.txt
#
# Thanks for using Enthought open source!
from .app_font import add_application_fonts
from .font import Font, str_to_font

__all__ = ["add_application_fonts", "Font", "str_to_font"]
37 changes: 37 additions & 0 deletions kiva/fonttools/_scan_sys.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,43 @@ def scan_system_fonts(fontpaths=None, fontext="ttf"):
return [fname for fname in fontfiles if os.path.exists(fname)]


def scan_user_fonts(fontpaths=None, fontext="ttf"):
""" Search for fonts in the specified font paths.
Comment thread
jwiggins marked this conversation as resolved.

Returns
-------
filepaths : list of str
A list of unique font file paths.
"""
if fontpaths is None:
return []

if isinstance(fontpaths, str):
fontpaths = [fontpaths]

fontfiles = set()
fontexts = _get_fontext_synonyms(fontext)
for path in fontpaths:
path = os.path.abspath(path)
if os.path.isdir(path):
# For directories, find all the fonts within
files = []
for ext in fontexts:
files.extend(glob.glob(os.path.join(path, "*." + ext)))
files.extend(glob.glob(os.path.join(path, "*." + ext.upper())))

for fname in files:
if os.path.exists(fname) and not os.path.isdir(fname):
fontfiles.add(fname)
elif os.path.exists(path):
# For files, make sure they have the correct extension
ext = os.path.splitext(path)[-1][1:].lower()
if ext in fontexts:
fontfiles.add(path)

return sorted(fontfiles)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't know that sorting a set will return a list - in case anyone was wondering if it'll be a problem that we're returning an empty list above but a sorted set here.



# ----------------------------------------------------------------------------
# utility funcs

Expand Down
54 changes: 54 additions & 0 deletions kiva/fonttools/app_font.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# (C) Copyright 2005-2021 Enthought, Inc., Austin, TX
# All rights reserved.
#
# This software is provided without warranty under the terms of the BSD
# license included in LICENSE.txt and may be redistributed only under
# the conditions described in the aforementioned license. The license
# is also available online at http://www.enthought.com/licenses/BSD.txt
#
# Thanks for using Enthought open source!
import warnings

from traits.etsconfig.api import ETSConfig

from kiva.fonttools.font_manager import default_font_manager


def add_application_fonts(filenames):
""" Add a TrueType font to the system in a way that makes it available to
both the GUI toolkit and Kiva.

Parameters
----------
filenames : list of str
Filesystem paths of TrueType or OpenType font files.
"""
if isinstance(filenames, str):
filenames = [filenames]

# Handle Kiva
fm = default_font_manager()
fm.update_fonts(filenames)

# Handle the GUI toolkit
if ETSConfig.toolkit.startswith("qt"):
_qt_impl(filenames)
elif ETSConfig.toolkit == "wx":
_wx_impl(filenames)


def _qt_impl(filenames):
from pyface.qt import QtGui

for fname in filenames:
QtGui.QFontDatabase.addApplicationFont(fname)


def _wx_impl(filenames):
import wx

if hasattr(wx.Font, "CanUsePrivateFont") and wx.Font.CanUsePrivateFont():
for fname in filenames:
wx.Font.AddPrivateFont(fname)
else:
warnings.warn("Wx does not support private fonts! Failed to add.")
21 changes: 16 additions & 5 deletions kiva/fonttools/font_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
from traits.etsconfig.api import ETSConfig

from kiva.fonttools._scan_parse import create_font_list
from kiva.fonttools._scan_sys import scan_system_fonts
from kiva.fonttools._scan_sys import scan_system_fonts, scan_user_fonts
from kiva.fonttools._score import (
score_family, score_size, score_stretch, score_style, score_variant,
score_weight
Expand Down Expand Up @@ -137,12 +137,23 @@ def set_default_weight(self, weight):
"""
self.__default_weight = weight

def update_fonts(self, filenames):
def update_fonts(self, paths):
""" Update the font lists with new font files.
Currently not implemented.

The specified ``paths`` will be searched for valid font files and those
files will have their fonts added to internal collections searched by
:meth:`findfont`.

Parameters
----------
filenames : list of str
A list of font file paths or directory paths.
"""
# !!!! Needs implementing
raise NotImplementedError
afm_paths = scan_user_fonts(paths, fontext="afm")
ttf_paths = scan_user_fonts(paths, fontext="ttf")

self.afmlist.extend(create_font_list(afm_paths))
self.ttflist.extend(create_font_list(ttf_paths))

def findfont(self, prop, fontext="ttf", directory=None,
fallback_to_default=True, rebuild_if_missing=True):
Expand Down
100 changes: 100 additions & 0 deletions kiva/fonttools/tests/test_app_font.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
# (C) Copyright 2005-2021 Enthought, Inc., Austin, TX
# All rights reserved.
#
# This software is provided without warranty under the terms of the BSD
# license included in LICENSE.txt and may be redistributed only under
# the conditions described in the aforementioned license. The license
# is also available online at http://www.enthought.com/licenses/BSD.txt
#
# Thanks for using Enthought open source!
import os
import unittest

import pkg_resources

from traits.etsconfig.api import ETSConfig

from kiva.api import add_application_fonts, Font

is_null = (ETSConfig.toolkit in ("", "null"))
is_qt = ETSConfig.toolkit.startswith("qt")
is_wx = (ETSConfig.toolkit == "wx")
data_dir = pkg_resources.resource_filename("kiva.fonttools.tests", "data")


@unittest.skipIf(not is_null, "Test only for null toolkit")
class TestNullApplicationFonts(unittest.TestCase):
def test_add_application_font(self):
path = os.path.join(data_dir, "TestTTF.ttf")
family = "Test TTF"
kivafont = Font(family)

# Before adding the font
with self.assertWarns(UserWarning):
self.assertNotEqual(kivafont.findfont().filename, path)

add_application_fonts([path])

# After adding the font
self.assertEqual(kivafont.findfont().filename, path)


@unittest.skipIf(not is_qt, "Test only for qt")
class TestQtApplicationFonts(unittest.TestCase):
def setUp(self):
from pyface.qt import QtGui

application = QtGui.QApplication.instance()
if application is None:
self.application = QtGui.QApplication([])
else:
self.application = application
unittest.TestCase.setUp(self)

def test_add_application_font(self):
from pyface.qt import QtGui

path = os.path.join(data_dir, "TestTTF.ttf")
family = "Test TTF"
font_db = QtGui.QFontDatabase()

# Before adding the font
self.assertNotIn(family, font_db.families())

add_application_fonts([path])

# After adding the font
self.assertIn(family, font_db.families())


@unittest.skipIf(not is_wx, "Test only for wx")
class TestWxApplicationFonts(unittest.TestCase):
def setUp(self):
import wx

application = wx.App.Get()
if application is None:
self.application = wx.App()
else:
self.application = application
unittest.TestCase.setUp(self)

# XXX: How do we check to see if Wx loaded our font?
@unittest.expectedFailure
def test_add_application_font(self):
import wx

path = os.path.join(data_dir, "TestTTF.ttf")
family = "Test TTF"

fontinfo = wx.FontInfo()
fontinfo.FaceName(family)
wxfont = wx.Font(fontinfo)

# Before adding the font
self.assertFalse(wxfont.IsOk())

add_application_fonts([path])

# After adding the font
self.assertTrue(wxfont.IsOk())
9 changes: 8 additions & 1 deletion kiva/fonttools/tests/test_scan_sys.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

from pkg_resources import resource_filename

from .._scan_sys import scan_system_fonts
from .._scan_sys import scan_system_fonts, scan_user_fonts

data_dir = resource_filename("kiva.fonttools.tests", "data")
is_macos = (sys.platform == "darwin")
Expand Down Expand Up @@ -49,6 +49,13 @@ def test_directories_scanning(self):
fonts = scan_system_fonts([data_dir], fontext="ttf")
self.assertListEqual(sorted(expected), sorted(fonts))

def test_user_font_scanning(self):
ttf_fonts = scan_user_fonts(data_dir, fontext="ttf")
self.assertEqual(len(ttf_fonts), 3)

afm_fonts = scan_user_fonts(data_dir, fontext="afm")
self.assertEqual(len(afm_fonts), 1)

@unittest.skipIf(not is_generic, "This test is only for generic platforms")
def test_generic_scanning(self):
fonts = scan_system_fonts(fontext="ttf")
Expand Down
4 changes: 2 additions & 2 deletions kiva/tests/test_gl_drawing.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@

from kiva.tests.drawing_tester import DrawingImageTester

is_windows = sys.platform in ("win32", "cygwin")
is_linux = (sys.platform == "linux")


@unittest.skipIf(is_windows, "Pyglet/GL backend issues on Windows")
@unittest.skipIf(not is_linux, "Pyglet/GL backend issues on most platforms")
@unittest.skipIf(PYGLET_NOT_AVAILABLE, "Cannot import pyglet")
class TestGLDrawing(DrawingImageTester, unittest.TestCase):
def tearDown(self):
Expand Down