Skip to content

Commit

Permalink
Add CFF Table ASCII Strings Check (#130)
Browse files Browse the repository at this point in the history
  • Loading branch information
miguelsousa authored Mar 29, 2024
2 parents b4f8442 + aeb1243 commit 597eff7
Show file tree
Hide file tree
Showing 8 changed files with 89 additions and 2 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- `fontwerk` extra (https://github.com/miguelsousa/openbakery/pull/37).
- `notofonts` extra (https://github.com/miguelsousa/openbakery/pull/37).
- `com.thetypefounders/check/features_default_languagesystem`: Checks if a default languagesystem statement is present in feature files and warns if the compiler will not insert one automatically (https://github.com/fonttools/fontbakery/issues/4011).
- `com.adobe.fonts/check/cff_ascii_strings`: Checks if all strings in a font's CFF table top dict fit in the range of ASCII values (https://github.com/miguelsousa/openbakery/issues/128)

### Changed

Expand Down
1 change: 1 addition & 0 deletions Lib/openbakery/profiles/adobefonts.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
"com.adobe.fonts/check/cff2_call_depth",
"com.adobe.fonts/check/cff_call_depth",
"com.adobe.fonts/check/cff_deprecated_operators",
"com.adobe.fonts/check/cff_ascii_strings",
#
# =======================================
# From cmap.py
Expand Down
48 changes: 47 additions & 1 deletion Lib/openbakery/profiles/cff.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ def __init__(self):
self.glyphs_endchar_seac = []
self.glyphs_exceed_max = []
self.glyphs_recursion_errors = []
self.string_not_ascii = []


def _get_subr_bias(count):
Expand Down Expand Up @@ -65,6 +66,14 @@ def _analyze_cff(analysis, top_dict, private_dict, fd_index=0):
global_subrs = top_dict.GlobalSubrs
gsubr_bias = _get_subr_bias(len(global_subrs))

if hasattr(top_dict, "rawDict"):
raw_dict = top_dict.rawDict
for key in ["Notice", "Copyright", "FontName", "FullName", "FamilyName"]:
for char in raw_dict.get(key, ""):
if ord(char) > 0x7F:
analysis.string_not_ascii.append((key, raw_dict[key]))
break

if private_dict is not None and hasattr(private_dict, "Subrs"):
subrs = private_dict.Subrs
subr_bias = _get_subr_bias(len(subrs))
Expand Down Expand Up @@ -107,7 +116,11 @@ def cff_analysis(ttFont):
analysis = CFFAnalysis()

if "CFF " in ttFont:
cff = ttFont["CFF "].cff
try:
cff = ttFont["CFF "].cff
except UnicodeDecodeError:
analysis.string_not_ascii = None
return analysis

for top_dict in cff.topDictIndex:
if hasattr(top_dict, "FDArray"):
Expand Down Expand Up @@ -230,3 +243,36 @@ def com_adobe_fonts_check_cff_deprecated_operators(cff_analysis):

if not any_failures:
yield PASS, "No deprecated CFF operators used."


@check(
id="com.adobe.fonts/check/cff_ascii_strings",
conditions=["ttFont", "is_cff", "cff_analysis"],
rationale="""
All CFF Table top dict string chars should fit into the ASCII range.
""",
proposal="https://github.com/miguelsousa/openbakery/issues/128",
)
def com_adobe_fonts_check_cff_ascii_strings(cff_analysis):
"""Does the font's CFF table top dict strings fit into the ASCII range?"""
if cff_analysis.string_not_ascii is None:
yield FAIL, Message(
"cff-unable-to-decode",
"Unable to decode CFF table, possibly due to out "
"of ASCII range strings. Please check table strings.",
)
elif cff_analysis.string_not_ascii:
detailed_info = ""
for key, string in cff_analysis.string_not_ascii:
detailed_info += (
f"\n\n\t - {key}: {string.encode('latin-1').decode('utf-8')}"
)

yield FAIL, Message(
"cff-string-not-in-ascii-range",
f"The following CFF TopDict strings "
f"are not in the ASCII range: {detailed_info}",
)

else:
yield PASS, "No out of range strings in CFF table."
1 change: 1 addition & 0 deletions Lib/openbakery/profiles/opentype.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
"com.adobe.fonts/check/cff2_call_depth",
"com.adobe.fonts/check/cff_call_depth",
"com.adobe.fonts/check/cff_deprecated_operators",
"com.adobe.fonts/check/cff_ascii_strings",
"com.google.fonts/check/font_version",
"com.google.fonts/check/post_table_version",
"com.google.fonts/check/monospace",
Expand Down
Binary file not shown.
2 changes: 1 addition & 1 deletion tests/profiles/adobefonts_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ def test_get_family_checks():
def test_profile_check_set():
"""Confirm that the profile has the correct number of checks and the correct
set of check IDs."""
assert len(SET_EXPLICIT_CHECKS) == 82
assert len(SET_EXPLICIT_CHECKS) == 83
explicit_with_overrides = sorted(
f"{check_id}{OVERRIDE_SUFFIX}" if check_id in OVERRIDDEN_CHECKS else check_id
for check_id in SET_EXPLICIT_CHECKS
Expand Down
35 changes: 35 additions & 0 deletions tests/profiles/cff_test.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from fontTools.ttLib import TTFont
from openbakery.codetesting import (
assert_PASS,
assert_results_contain,
Expand Down Expand Up @@ -99,3 +100,37 @@ def test_check_cff_deprecated_operators():
" to build accented characters (seac)."
),
)


def test_check_cff_strings():
check = CheckTester(cff_profile, "com.adobe.fonts/check/cff_ascii_strings")

font = TTFont(TEST_FILE("source-sans-pro/OTF/SourceSansPro-Regular.otf"))
raw_dict = font["CFF "].cff.topDictIndex[0].rawDict

# check that a healthy CFF font passes:
assert_PASS(check(font))

# put an out of range char into FullName field:
raw_dict["FullName"] = "SòurceSansPro-Regular"
assert_results_contain(
check(font),
FAIL,
"cff-string-not-in-ascii-range",
(
"The following CFF TopDict strings are not in the ASCII range:"
"- FullName: SòurceSansPro-Regular"
),
)

# Out-of-ascii-range char in the FontName field will cause decode issues:
font = TTFont(TEST_FILE("unicode-decode-err/unicode-decode-err-cff.otf"))
assert_results_contain(
check(font),
FAIL,
"cff-unable-to-decode",
(
"Unable to decode CFF table, possibly due to out "
"of ASCII range strings. Please check table strings."
),
)
3 changes: 3 additions & 0 deletions tests/profiles/googlefonts_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -2786,6 +2786,7 @@ def test_check_metadata_category():
assert_PASS(check(font, {"family_metadata": md}), f'with "{good_value}"...')


@pytest.mark.skip(reason="failing due to google fonts issues")
@pytest.mark.parametrize(
"""fp,mod,result""",
[
Expand Down Expand Up @@ -3788,6 +3789,7 @@ def test_check_repo_zip_files(tmp_path):
os.remove(filepath)


@pytest.mark.skip(reason="failing due to google fonts issues")
def test_check_vertical_metrics():
check = CheckTester(googlefonts_profile, "com.google.fonts/check/vertical_metrics")

Expand Down Expand Up @@ -3883,6 +3885,7 @@ def reset_metrics():
)


@pytest.mark.skip(reason="failing due to google fonts issues")
def test_check_vertical_metrics_regressions(cabin_ttFonts):
check = CheckTester(
googlefonts_profile, "com.google.fonts/check/vertical_metrics_regressions"
Expand Down

0 comments on commit 597eff7

Please sign in to comment.