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
2 changes: 1 addition & 1 deletion MANIFEST.in
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@ include docs/kiva/agg/notes
recursive-include docs *.py *.rst *.txt *.css *.png *.ico *.doc
recursive-include examples *.py *.txt *.gif *.jpg *.enaml
recursive-include kiva/quartz *.pyx *.pxi *.pxd mac_context*.*
recursive-include kiva/fonttools/tests/data *.txt *.ttc
recursive-include kiva/fonttools/tests/data *.txt *.ttc *.ttf
74 changes: 43 additions & 31 deletions kiva/fonttools/font_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -220,17 +220,42 @@ def is_string_like(obj):
return False
try:
obj + ''
except:
except Exception:
return False
return True


def decode_prop(prop):
""" Decode a prop string.

Parameters
----------
prop : bytestring

Returns
-------
string
"""
# Adapted from: https://gist.github.com/pklaus/dce37521579513c574d0
encoding = "utf-16-be" if b"\x00" in prop else "utf-8"

return prop.decode(encoding)


def getPropDict(font):
n = font['name']
propdict = {}
for prop in n.names:
prop_key = (prop.platformID, prop.platEncID, prop.langID, prop.nameID)
propdict[prop_key] = prop.string
try:
if 'name' in propdict and 'sfnt4' in propdict:
break
elif prop.nameID == 1 and 'name' not in propdict:
propdict['name'] = decode_prop(prop.string)
elif prop.nameID == 4 and 'sfnt4' not in propdict:
propdict['sfnt4'] = decode_prop(prop.string)
except UnicodeDecodeError:
continue

return propdict


Expand Down Expand Up @@ -522,32 +547,17 @@ def ttfFontProperty(fpath, font):
*font* is a :class:`FT2Font` instance.
"""
props = getPropDict(font)
name = props[(1, 0, 0, 1)].decode()
name = props.get('name')
if name is None:
raise KeyError("No name could be found for: {}".format(fpath))

# Styles are: italic, oblique, and normal (default)
sfnt4 = props.get('sfnt4', '')

try:
sfnt2 = props[(1, 0, 0, 2)]
except:
sfnt2 = None
try:
sfnt4 = props[(1, 0, 0, 4)]
except:
sfnt4 = None
if sfnt2:
sfnt2 = sfnt2.lower().decode()
else:
sfnt2 = ''
if sfnt4:
sfnt4 = sfnt4.lower().decode()
else:
sfnt4 = ''
if sfnt4.find('oblique') >= 0:
style = 'oblique'
elif sfnt4.find('italic') >= 0:
style = 'italic'
elif sfnt2.find('regular') >= 0:
style = 'normal'
else:
style = 'normal'

Expand Down Expand Up @@ -665,6 +675,7 @@ def afmFontProperty(fontpath, font):

return FontEntry(fontpath, name, style, variant, weight, stretch, size)


def createFontList(fontfiles, fontext='ttf'):
"""
A function to create a font lookup list. The default is to create
Expand All @@ -685,7 +696,7 @@ def createFontList(fontfiles, fontext='ttf'):
if fontext == 'afm':
try:
fh = open(fpath, 'r')
except:
except Exception:
logger.error(
"Could not open font file %s", fpath, exc_info=True)
continue
Expand All @@ -710,7 +721,7 @@ def createFontList(fontfiles, fontext='ttf'):
_, ext = os.path.splitext(fpath)
try:
if ext.lower() == ".ttc":
collection = TTCollection(str(fpath))
collection = TTCollection(six.text_type(fpath))
try:
props = []
for font in collection.fonts:
Expand All @@ -724,7 +735,7 @@ def createFontList(fontfiles, fontext='ttf'):
)
continue
else:
font = TTFont(str(fpath))
font = TTFont(six.text_type(fpath))
except (RuntimeError, TTLibError):
logger.error(
"Could not open font file %s", fpath, exc_info=True)
Expand Down Expand Up @@ -841,8 +852,8 @@ def __init__(self, family=None, style=None, variant=None, weight=None,
self.set_size(size)

def __hash__(self):
l = [(k, getattr(self, "get" + k)()) for k in sorted(self.__dict__)]
return hash(repr(l))
lst = [(k, getattr(self, "get" + k)()) for k in sorted(self.__dict__)]
return hash(repr(lst))

def __str__(self):
return str((self._family, self._slant, self._variant,
Expand All @@ -859,12 +870,13 @@ def get_name(self):
Return the name of the font that best matches the font
properties.
"""
filename = str(fontManager.findfont(self))
filename = six.text_type(fontManager.findfont(self))
if filename.endswith('.afm'):
return afm.AFM(open(filename)).get_familyname()

font = fontManager.findfont(self)
return getPropDict(TTFont(str(font)))[(1, 0, 0, 1)]
prop_dict = getPropDict(TTFont(six.text_type(font)))
return prop_dict['name']

def get_style(self):
"""
Expand Down Expand Up @@ -1088,7 +1100,7 @@ def __init__(self, size=None, weight='normal'):
else:
paths.append(ttfpath)

logger.debug("font search path %s", str(paths))
logger.debug("font search path %s", six.text_type(paths))
# Load TrueType fonts and create font dictionary.

self.ttffiles = findSystemFonts(paths) + findSystemFonts()
Expand Down Expand Up @@ -1444,7 +1456,7 @@ def findfont(prop, fontext='ttf'):
else:
fontManager.default_size = None
logger.debug("Using fontManager instance from %s", _fmcache)
except:
except Exception:
_rebuild()

def findfont(prop, **kw):
Expand Down
Binary file added kiva/fonttools/tests/data/TestTTF.ttf
Binary file not shown.
1 change: 1 addition & 0 deletions kiva/fonttools/tests/data/source.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ Files and orginal authors:
----------------------------------------------------------------------------
kiva/fonttools/tests/data:
TestTTC.ttc | fonttools
TestTTF.ttf | fonttools
28 changes: 27 additions & 1 deletion kiva/fonttools/tests/test_font_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@
import mock

from pkg_resources import resource_filename
from fontTools.ttLib import TTFont

from ..font_manager import FontEntry, createFontList
from ..font_manager import FontEntry, createFontList, ttfFontProperty

data_dir = resource_filename('kiva.fonttools.tests', 'data')


class TestCreateFontList(unittest.TestCase):

def setUp(self):
Expand Down Expand Up @@ -44,3 +46,27 @@ def test_ttc_exception_on_TTCollection(self, m_TTCollection):
# Then
self.assertEqual(len(fontlist), 0)
self.assertEqual(m_TTCollection.call_count, 1)


class TestTTFFontProperty(unittest.TestCase):

def test_font(self):
# Given
test_font = os.path.join(data_dir, "TestTTF.ttf")
exp_name = "Test TTF"
exp_style = "normal"
exp_variant = "normal"
exp_weight = 400
exp_stretch = "normal"
exp_size = "scalable"

# When
entry = ttfFontProperty(test_font, TTFont(test_font))

# Then
self.assertEqual(entry.name, exp_name)
self.assertEqual(entry.style, exp_style)
self.assertEqual(entry.variant, exp_variant)
self.assertEqual(entry.weight, exp_weight)
self.assertEqual(entry.stretch, exp_stretch)
self.assertEqual(entry.size, exp_size)
1 change: 1 addition & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,7 @@ def run(self):
'enable.savage.trait_defs.ui.wx': ['data/*.svg'],
'kiva': ['tests/agg/doubleprom_soho_full.jpg',
'fonttools/tests/data/*.ttc',
'fonttools/tests/data/*.ttf',
'fonttools/tests/data/*.txt'],
},
platforms=["Windows", "Linux", "Mac OS-X", "Unix", "Solaris"],
Expand Down