Skip to content

Commit

Permalink
New check: CFF top dict in ASCII range.
Browse files Browse the repository at this point in the history
Checks if all strings in a font's CFF table top dict fit in the range of ASCII values.

EXPERIMENTAL - com.adobe.fonts/check/cff_ascii_strings
Added to the Universal profile.

(issue #4619)
  • Loading branch information
felipesanches committed Jun 20, 2024
1 parent 9fe43b4 commit a326e75
Show file tree
Hide file tree
Showing 6 changed files with 92 additions and 2 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ A more detailed list of changes is available in the corresponding milestones for

## Upcoming release: 0.12.8 (2024-Jun-??)
### New checks
#### Added to the OpenType profile
- **EXPERIMENTAL - [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 (issue #4619)

#### Added to the Universal profile
- **EXPERIMENTAL - [com.google.fonts/check/gsub/smallcaps_before_ligatures]:** Ensure 'smcp' (small caps) lookups are defined before ligature lookups in the 'GSUB' table (issue #3020)

Expand Down
47 changes: 46 additions & 1 deletion Lib/fontbakery/checks/opentype/cff.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,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 @@ -61,6 +62,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 @@ -109,7 +118,11 @@ def cff_analysis(font):
ttFont = TTFont(font.file) # Use our own copy here since we are decompiling

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 @@ -218,3 +231,35 @@ def com_adobe_fonts_check_cff_deprecated_operators(cff_analysis):
f'Glyph "{gn}" has deprecated use of "endchar"'
f" operator to build accented characters (seac).",
)


@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/fonttools/fontbakery/issues/4619",
experimental="Since 2024/Jun/20",
)
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}",
)
1 change: 1 addition & 0 deletions Lib/fontbakery/profiles/adobefonts.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,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",
],
"fontwerk": [
"com.fontwerk/check/inconsistencies_between_fvar_stat", # IS_OVERRIDDEN
Expand Down
3 changes: 2 additions & 1 deletion Lib/fontbakery/profiles/opentype.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,10 @@
"com.google.fonts/check/varfont/wdth_valid_range",
"com.google.fonts/check/varfont/stat_axis_record_for_each_axis",
"com.google.fonts/check/loca/maxp_num_glyphs",
"com.adobe.fonts/check/cff2_call_depth",
"com.adobe.fonts/check/cff_ascii_strings",
"com.adobe.fonts/check/cff_call_depth",
"com.adobe.fonts/check/cff_deprecated_operators",
"com.adobe.fonts/check/cff2_call_depth",
"com.google.fonts/check/font_version",
"com.google.fonts/check/post_table_version",
"com.google.fonts/check/monospace",
Expand Down
Binary file not shown.
40 changes: 40 additions & 0 deletions tests/checks/opentype/cff_test.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from fontTools.ttLib import TTFont

from fontbakery.codetesting import (
assert_PASS,
assert_results_contain,
Expand Down Expand Up @@ -98,3 +100,41 @@ def test_check_cff_deprecated_operators():
" to build accented characters (seac)."
),
)


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

ttFont = TTFont(TEST_FILE("source-sans-pro/OTF/SourceSansPro-Regular.otf"))

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

# FIXME: The condition cff_analysis creates a new ttFont object
# and, consequently, discards the modifications we made
# here prior to invoking the check.
#
# # put an out of range char into FullName field:
# rawDict = ttFont["CFF "].cff.topDictIndex[0].rawDict
# rawDict["FullName"] = "S\u00F2urceSansPro-Regular"
# assert_results_contain(
# check(ttFont),
# FAIL,
# "cff-string-not-in-ascii-range",
# (
# "The following CFF TopDict strings are not in the ASCII range:"
# f"- FullName: {rawDict['FullName']}"
# ),
# )

# Out-of-ascii-range char in the FontName field will cause decode issues:
ttFont = TTFont(TEST_FILE("unicode-decode-err/unicode-decode-err-cff.otf"))
assert_results_contain(
check(ttFont),
FAIL,
"cff-unable-to-decode",
(
"Unable to decode CFF table, possibly due to out "
"of ASCII range strings. Please check table strings."
),
)

0 comments on commit a326e75

Please sign in to comment.