Skip to content

Commit

Permalink
new check for STAT table in static fonts
Browse files Browse the repository at this point in the history
Added to the Universal Profile
com.google.fonts/check/STAT_in_statics
Static fonts with more than a single entry per design axis cause trouble on Windows
(issue #4149)
  • Loading branch information
felipesanches committed May 26, 2023
1 parent edc351c commit 5e9f5b1
Show file tree
Hide file tree
Showing 3 changed files with 99 additions and 1 deletion.
5 changes: 4 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
Below are the most important changes from each release.
A more detailed list of changes is available in the corresponding milestones for each release in the Github issue tracker (https://github.com/googlefonts/fontbakery/milestones?state=closed).

## Upcoming release: 0.8.12 (2023-Apr-??)
## Upcoming release: 0.8.12 (2023-May-??)
### New Checks
#### Added to the Universal Profile
- **[com.google.fonts/check/STAT_in_statics]:** Static fonts with more than a single entry per design axis cause trouble on Windows (issue #4149)

#### Added to the Google Fonts Profile
- **[com.google.fonts/check/metadata/unreachable_subsetting]:** Implemented checks to ensure that all encoded glyphs in the font are covered by a subset declared in the METADATA.pb (issue #4097)

Expand Down
65 changes: 65 additions & 0 deletions Lib/fontbakery/profiles/universal.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@
'com.google.fonts/check/interpolation_issues',
'com.google.fonts/check/math_signs_width',
'com.google.fonts/check/linegaps',
'com.google.fonts/check/STAT_in_statics',
]


Expand Down Expand Up @@ -2207,5 +2208,69 @@ def com_google_fonts_check_linegaps(ttFont):
yield PASS, "OS/2 sTypoLineGap and hhea lineGap are both 0."


@check(
id = 'com.google.fonts/check/STAT_in_statics',
conditions = ['not is_variable_font'],
rationale = """
Adobe feature syntax allows for the definition of a STAT table. Fonts built
with a hand-coded STAT table in feature syntax may be built either as static
or variable, but will end up with the same STAT table.
This is a problem, because a STAT table which works on variable fonts
will not be appropriate for static instances. The examples in the OpenType spec
of non-variable fonts with a STAT table show that the table entries must be
restricted to those entries which refer to the static font's position in
the designspace. i.e. a Regular weight static should only have the following
entry for the weight axis:
<AxisIndex value="0"/>
<Flags value="2"/> <!-- ElidableAxisValueName -->
<ValueNameID value="265"/> <!-- Regular -->
<Value value="400.0"/>
However, if the STAT table intended for a variable font is compiled into a
static, it will have many entries for this axis. In this case, Windows will
read the first entry only, causing all instances to report themselves
as "Thin Condensed".
""",
proposal = 'https://github.com/googlefonts/fontbakery/issues/4149'
)
def com_google_fonts_check_STAT_in_statics(ttFont):
"""Checking STAT table entries in static fonts."""

entries = {}
def count_entries(tag_name):
if tag_name in entries:
entries[tag_name] += 1
else:
entries[tag_name] = 1

passed = True
if "STAT" in ttFont:
stat = ttFont["STAT"].table
designAxes = stat.DesignAxisRecord.Axis
for axisValueTable in stat.AxisValueArray.AxisValue:
axisValueFormat = axisValueTable.Format
if axisValueFormat in (1, 2, 3):
axisTag = designAxes[axisValueTable.AxisIndex].AxisTag
count_entries(axisTag)
elif axisValueFormat == 4:
for rec in axisValueTable.AxisValueRecord:
axisTag = designAxes[rec.AxisIndex].AxisTag
count_entries(axisTag)

for tag_name in entries:
if entries[tag_name] > 1:
passed = False
yield FAIL,\
Message("multiple-STAT-entries",
f"The STAT table has more than a single entry for the"
f" '{tag_name}' axis ({entries[tag_name]}) on this"
f" static font which will causes problems on Windows.")

if passed:
yield PASS, "Looks good!"


profile.auto_register(globals())
profile.test_expected_checks(UNIVERSAL_PROFILE_CHECKS, exclusive=True)
30 changes: 30 additions & 0 deletions tests/profiles/universal_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -1275,3 +1275,33 @@ def test_check_linegaps():
# Confirm the check yields FAIL if the font doesn't have a required table
del ttFont['OS/2']
assert_results_contain(check(ttFont), FAIL, "lacks-table")


def test_check_STAT_in_statics():
""" Checking STAT table on static fonts. """
check = CheckTester(universal_profile,
"com.google.fonts/check/STAT_in_statics")

ttFont = TTFont(TEST_FILE("varfont/RobotoSerif[GRAD,opsz,wdth,wght].ttf"))
assert_SKIP(check(ttFont),
'with a variable font...')

# fake it: Remove fvar table to make Font Bakery think it is dealing with a static font
del ttFont["fvar"]

# We know that our reference RobotoSerif varfont (which the check is induced
# here to think it is a static font) has multiple records per design axis in its
# STAT table:
assert_results_contain(check(ttFont),
FAIL, 'multiple-STAT-entries',
'with a static font with a bad STAT table...')

# Remove all entries except the very first one:
stat = ttFont["STAT"].table
stat.AxisValueArray.AxisCount = 1
stat.AxisValueArray.AxisValue = [stat.AxisValueArray.AxisValue[0]]

# And finally, completely remove STAT and it should PASS:
del ttFont["STAT"]
assert_PASS(check(ttFont))

0 comments on commit 5e9f5b1

Please sign in to comment.