Skip to content

Commit

Permalink
Correct variable font axis value (#16)
Browse files Browse the repository at this point in the history
* If the AxisValueArray is None, return an empty array.
* [Font] If there is an unknow problem when parsing the font raise an exception with a good message
* Bump version to 2.0.4
  • Loading branch information
moi15moi authored Feb 4, 2023
1 parent 1493505 commit 21276d8
Show file tree
Hide file tree
Showing 5 changed files with 86 additions and 58 deletions.
2 changes: 1 addition & 1 deletion font_collector/_version.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "2.0.3"
__version__ = "2.0.4"
119 changes: 63 additions & 56 deletions font_collector/font.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import logging
import os
from .parse_font import ParseFont
from fontTools.ttLib.ttFont import TTFont
from fontTools.ttLib.ttCollection import TTCollection
Expand Down Expand Up @@ -59,76 +60,82 @@ def from_font_path(cls, font_path: str) -> List["Font"]:
f'The file "{font_path}" is not a valid font file'
)

# Read font attributes
for font_index, ttFont in enumerate(ttFonts):
try:

families = set()
exact_names = set()
# Read font attributes
for font_index, ttFont in enumerate(ttFonts):

# If is Variable Font, else "normal" font
if is_var := ("fvar" in ttFont and "STAT" in ttFont):
families = set()
exact_names = set()

for instance in ttFont["fvar"].instances:
axis_value_tables = ParseFont.get_axis_value_from_coordinates(
ttFont, instance.coordinates
)
(
family_name,
full_font_name,
) = ParseFont.get_var_font_family_fullname(
ttFont, axis_value_tables
)
# If is Variable Font, else "normal" font
if is_var := ("fvar" in ttFont and "STAT" in ttFont):

families.add(family_name)
families.add(full_font_name)
for instance in ttFont["fvar"].instances:
axis_value_tables = ParseFont.get_axis_value_from_coordinates(
ttFont, instance.coordinates
)
(
family_name,
full_font_name,
) = ParseFont.get_var_font_family_fullname(
ttFont, axis_value_tables
)

else:
# From https://github.com/fonttools/fonttools/discussions/2619
is_truetype = "glyf" in ttFont
families.add(family_name)
families.add(full_font_name)

families, fullnames = ParseFont.get_font_family_fullname_property(
ttFont["name"].names
)
else:
# From https://github.com/fonttools/fonttools/discussions/2619
is_truetype = "glyf" in ttFont

# This is something like: https://github.com/libass/libass/blob/a2b39cde4ecb74d5e6fccab4a5f7d8ad52b2b1a4/libass/ass_fontselect.c#L303-L311
if len(families) == 0:
familyName = ParseFont.get_name_by_id(1, ttFont["name"].names)
families, fullnames = ParseFont.get_font_family_fullname_property(
ttFont["name"].names
)

if familyName:
families.add(familyName)
# This is something like: https://github.com/libass/libass/blob/a2b39cde4ecb74d5e6fccab4a5f7d8ad52b2b1a4/libass/ass_fontselect.c#L303-L311
if len(families) == 0:
familyName = ParseFont.get_name_by_id(1, ttFont["name"].names)

if familyName:
families.add(familyName)
else:
# Skip the font since it is invalid
_logger.info(
f'Warning: The index {font_index} of the font "{font_path}" does not contain an valid family name. The font index will be ignored.'
)
continue

if is_truetype:
exact_names = fullnames
else:
# Skip the font since it is invalid
_logger.info(
f'Warning: The index {font_index} of the font "{font_path}" does not contain an valid family name. The font index will be ignored.'
)
continue
# If not TrueType, it is OpenType

if is_truetype:
exact_names = fullnames
else:
# If not TrueType, it is OpenType
postscript_name = ParseFont.get_font_postscript_property(
font_path, font_index
)
if postscript_name is not None:
exact_names.add(postscript_name)

postscript_name = ParseFont.get_font_postscript_property(
font_path, font_index
is_italic, weight = ParseFont.get_font_italic_bold_property(
ttFont, font_path, font_index
)
fonts.append(
Font(
font_path,
font_index,
families,
weight,
is_italic,
exact_names,
is_var,
)
if postscript_name is not None:
exact_names.add(postscript_name)

is_italic, weight = ParseFont.get_font_italic_bold_property(
ttFont, font_path, font_index
)
fonts.append(
Font(
font_path,
font_index,
families,
weight,
is_italic,
exact_names,
is_var,
)
except Exception:
_logger.error(
f'An unknown error occurred while reading the font "{font_path}"{os.linesep}Please open an issue on github, share the font and the following error message:'
)

raise
return fonts

@property
Expand Down
4 changes: 4 additions & 0 deletions font_collector/parse_font.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,10 @@ def get_axis_value_from_coordinates(font: TTFont, coordinates: Dict) -> List:
raise ValueError("Cannot get axis_value since there is no STAT table.")
stat = font["STAT"].table

# If there isn't any axis, return an empty list
if stat.AxisValueArray is None:
return []

axisLimits = AxisLimits(coordinates).populateDefaults(font)
axis_value_tables = axisValuesFromAxisLimits(stat, axisLimits)

Expand Down
Binary file added tests/fonts/font without axis_value.ttf
Binary file not shown.
19 changes: 18 additions & 1 deletion tests/test_font.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
dir_path = os.path.dirname(os.path.realpath(__file__))
font_without_os2_table = os.path.join(dir_path, "fonts", "BRUSHSTP.TTF")
font_without_stat_table = os.path.join(dir_path, "fonts", "Cabin VF Beta Regular.ttf")
font_without_axis_value = os.path.join(dir_path, "fonts", "font without axis_value.ttf")


def test_font_without_os2_table():

Expand All @@ -19,6 +21,7 @@ def test_font_without_os2_table():
assert font.exact_names == set()
assert font.is_var == False


def test_font_with_fvar_table_but_without_stat_table():

font = Font.from_font_path(font_without_stat_table)
Expand All @@ -29,4 +32,18 @@ def test_font_with_fvar_table_but_without_stat_table():
assert font.weight == 400
assert font.italic == False
assert font.exact_names == set(["cabin vf beta regular"])
assert font.is_var == False
assert font.is_var == False


def test_font_without_axis_value():

font = Font.from_font_path(font_without_axis_value)
assert len(font) == 1
font = font[0]

assert font.family_names == set(["inter", "inter regular"])
# I don't know which weight gdi take. It maybe takes the DefaultValue from the AxisTag=wght in the fvar table
assert font.weight == 400
assert font.italic == False
assert font.exact_names == set([])
assert font.is_var == True

0 comments on commit 21276d8

Please sign in to comment.