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
33 changes: 19 additions & 14 deletions kiva/fonttools/_constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,18 +88,23 @@
}

weight_dict = {
"ultralight": 100,
"light": 200,
"normal": 400,
"regular": 400,
"book": 400,
"medium": 500,
"roman": 500,
"semibold": 600,
"demibold": 600,
"demi": 600,
"bold": 700,
"heavy": 800,
"extra bold": 800,
"black": 900,
'thin': 100,
'hairline': 100,
'extralight': 200,
'ultralight': 200,
'light': 300,
'normal': 400,
'regular': 400,
'book': 400,
'medium': 500,
'roman': 500,
'semibold': 600,
'demibold': 600,
'demi': 600,
'bold': 700,
'extrabold': 800,
'ultrabold': 800,
'black': 900,
'heavy': 900,
'extraheavy': 1000,
}
78 changes: 41 additions & 37 deletions kiva/fonttools/_scan_parse.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
from fontTools.afmLib import AFM
from fontTools.ttLib import TTCollection, TTFont, TTLibError

from kiva.fonttools._constants import weight_dict
from kiva.fonttools._constants import stretch_dict, weight_dict
from kiva.fonttools._database import FontDatabase, FontEntry
from kiva.fonttools._util import get_ttf_prop_dict, weight_as_number

Expand Down Expand Up @@ -167,15 +167,9 @@ def _afm_font_property(fpath, font):
else:
stretch = "normal"

# Sizes can be absolute and relative.
# Absolute sizes are: xx-small, x-small, small, medium, large, x-large,
# and xx-large.
# Relative sizes are: larger, smaller
# Length value is an absolute font size, e.g. 12pt
# Percentage values are in 'em's. Most robust specification.

# All AFM fonts are apparently scalable.
# All AFM fonts are scalable.
size = "scalable"

return FontEntry(
fname=fpath,
family=family,
Expand Down Expand Up @@ -205,16 +199,22 @@ def _ttf_font_property(fpath, font, face_index=0):
# For backwards compatibility with previous parsing behavior
style_prop = full_name

# Styles are: italic, oblique, and normal (default)
if style_prop.find("oblique") >= 0:
# Styles are: italic, oblique, and normal (default)
# We prefer to get the information from the "slope" property if it is
# available, as that is more accurate.
if "slope" in props:
style = props["slope"]
elif "oblique" in style_prop:
style = "oblique"
elif style_prop.find("italic") >= 0:
elif "italic" in style_prop:
style = "italic"
else:
style = "normal"

# Variants are: small-caps and normal (default)
# NOTE: Not sure how many fonts actually have these strings in their family
# Variants are: small-caps and normal (default)
# NOTE: Small caps is usually handled through alternative mappings of the
# characters to glyphs eg. via "smcp" feature. However some older fonts
# may indicate it via the name, in which case they may be preferred.
variant = "normal"
for value in ("capitals", "small-caps", "smallcaps"):
if value in family.lower():
Expand All @@ -224,13 +224,16 @@ def _ttf_font_property(fpath, font, face_index=0):
# Weights are: 100, 200, 300, 400 (normal: default), 500 (medium),
# 600 (semibold, demibold), 700 (bold), 800 (heavy), 900 (black)
# lighter and bolder are also allowed.
weight = None
for w in weight_dict.keys():
if style_prop.find(w) >= 0:
weight = w
break
# We prefer weight values from the OS/2 table, if available, otherwise
# infer from the "name" table's style
weight = props.get("weight")
if not weight:
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I'm pretty sure this was meant to be if weight is None:, just with the bad habits of years past.

weight = 400
for w in weight_dict.keys():
if w in style_prop:
weight = w
break
if not weight:
weight = 400
weight = weight_as_number(weight)

# Stretch can be absolute and relative
Expand All @@ -239,26 +242,27 @@ def _ttf_font_property(fpath, font, face_index=0):
# and ultra-expanded.
# Relative stretches are: wider, narrower
# Child value is: inherit
if full_name.find("demi cond") >= 0:
stretch = "semi-condensed"
elif (full_name.find("narrow") >= 0
or full_name.find("condensed") >= 0
or full_name.find("cond") >= 0):
stretch = "condensed"
elif full_name.find("wide") >= 0 or full_name.find("expanded") >= 0:
stretch = "expanded"
if "stretch" in props:
stretch = props["stretch"]
else:
stretch = "normal"

# Sizes can be absolute and relative.
# Absolute sizes are: xx-small, x-small, small, medium, large, x-large,
# and xx-large.
# Relative sizes are: larger, smaller
# Length value is an absolute font size, e.g. 12pt
# Percentage values are in 'em's. Most robust specification.
for stretch in stretch_dict:
if stretch in full_name:
break
else:
if "demi cond" in full_name:
stretch = "semi-condensed"
elif ("narrow" in full_name
or "condensed" in full_name
or "cond" in full_name):
stretch = "condensed"
elif "wide" in full_name or "expanded" in full_name:
stretch = "expanded"
else:
stretch = "normal"

# !!!! Incomplete
# TrueType and OpenType fonts are always scalable
size = "scalable"

return FontEntry(
fname=fpath,
family=family,
Expand Down
32 changes: 29 additions & 3 deletions kiva/fonttools/_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,17 @@
97: "Glagolitic",
98: "Tifinagh",
}
_ot_width = {
1: 'ultra-condensed',
2: 'extra-condensed',
3: 'condensed',
4: 'semi-condensed',
5: 'normal',
6: 'semi-expanded',
7: 'expanded',
8: 'extra-expanded',
9: 'ultra-expanded',
}


def get_ttf_prop_dict(font):
Expand Down Expand Up @@ -143,7 +154,8 @@ def get_ttf_prop_dict(font):
propdict[key] = rec.toStr()

# NOTE: Not all fonts have the "OS/2" table, but it's the easiest to
# extract language support information from.
# extract language support information from. There is also information
# about styles and weights that can be used.
propdict["languages"] = set()
try:
table = font["OS/2"]
Expand All @@ -165,7 +177,22 @@ def get_ttf_prop_dict(font):
pass
# Lock the set so that it's hashable
propdict["languages"] = frozenset(languages)

# This is the numeric weight value from 100 to 1000
if 100 <= table.usWeightClass <= 1000:
propdict["weight"] = table.usWeightClass

# This is the numeric stretch value from 1-9
if 1 <= table.usWidthClass <= 9:
propdict["stretch"] = _ot_width[table.usWidthClass]

# Do we match the italic or oblique bits
if table.fsSelection % 0x0001:
propdict["slope"] = "italic"
elif table.fsSelection % 0x0010:
propdict["slope"] = "oblique"
except KeyError:
# don't have an OS/2 table
pass

# Make sure "languages" is never empty
Expand All @@ -180,13 +207,12 @@ def weight_as_number(weight):

String values are converted to their corresponding numeric value.
"""
allowed_weights = set(weight_dict.values())
if isinstance(weight, str):
try:
weight = weight_dict[weight.lower()]
except KeyError:
weight = weight_dict["regular"]
elif weight in allowed_weights:
elif 100 <= weight <= 1000:
pass
else:
raise ValueError("weight not a valid integer")
Expand Down
2 changes: 1 addition & 1 deletion kiva/fonttools/font_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ class FontManager:
# Increment this version number whenever the font cache data
# format or behavior has changed and requires a existing font
# cache files to be rebuilt.
__version__ = 12
__version__ = 13

def __init__(self):
self._version = self.__version__
Expand Down