Skip to content

Commit

Permalink
opentype/name/italic_names moved to Universal profile
Browse files Browse the repository at this point in the history
and renamed to 'name/italic_names'.

This check uses the filename to determine whether the font should have various italic bits set. File naming is a matter for the OpenType specification.

(issue #4858)
  • Loading branch information
felipesanches committed Oct 11, 2024
1 parent a4e8b3f commit 9c41991
Show file tree
Hide file tree
Showing 8 changed files with 136 additions and 135 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ A more detailed list of changes is available in the corresponding milestones for
## Upcoming release: 0.13.0a2 (2024-Oct-??)
### Migration of checks
#### Moved from OpenType to Universal profile
- **[name/no_copyright_on_description]**: Adding copyright information to the description name entry is bad practice but is not a breach of specification (issue #4857)
- **[name/no_copyright_on_description]**: Adding copyright information to the description name entry is bad practice but is not a breach of specification. (issue #4857)
- **[name/italic_names]**: This check uses the filename to determine whether the font should have various italic bits set. File naming is a matter for the OpenType specification. (issue #4858)

### Changes to existing checks
#### On the Google Fonts profile
Expand Down
56 changes: 55 additions & 1 deletion Lib/fontbakery/checks/name.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
RIBBI_STYLE_NAMES,
NameID,
)
from fontbakery.prelude import check, disable, Message, FAIL, WARN
from fontbakery.prelude import check, disable, Message, FAIL, WARN, SKIP
from fontbakery.utils import get_name_entry_strings


Expand Down Expand Up @@ -323,3 +323,57 @@ def check_name_no_copyright_on_description(ttFont):
f" copyright notices to them.)",
)
break


@check(
id="name/italic_names",
conditions=["style"],
rationale="""
This check ensures that several entries in the name table
conform to the font's Upright or Italic style,
namely IDs 1 & 2 as well as 16 & 17 if they're present.
""",
proposal="https://github.com/fonttools/fontbakery/issues/3666",
)
def check_name_italic_names(ttFont, style):
"""Check name table IDs 1, 2, 16, 17 to conform to Italic style."""

def get_name(nameID):
for entry in ttFont["name"].names:
if entry.nameID == nameID:
return entry.toUnicode()

if "Italic" not in style:
yield SKIP, ("Font is not Italic.")
else:
# Name ID 1 (Family Name)
if "Italic" in get_name(NameID.FONT_FAMILY_NAME):
yield FAIL, Message(
"bad-familyname", "Name ID 1 (Family Name) must not contain 'Italic'."
)

# Name ID 2 (Subfamily Name)
subfamily_name = get_name(NameID.FONT_SUBFAMILY_NAME)
if subfamily_name not in ("Italic", "Bold Italic"):
yield FAIL, Message(
"bad-subfamilyname",
"Name ID 2 (Subfamily Name) does not conform to specs."
" Only R/I/B/BI are allowed.\n"
f"Got: '{subfamily_name}'.",
)

# Name ID 16 (Typographic Family Name)
if get_name(NameID.TYPOGRAPHIC_FAMILY_NAME):
if "Italic" in get_name(NameID.TYPOGRAPHIC_FAMILY_NAME):
yield FAIL, Message(
"bad-typographicfamilyname",
"Name ID 16 (Typographic Family Name) must not contain 'Italic'.",
)

# Name ID 17 (Typographic Subfamily Name)
if get_name(NameID.TYPOGRAPHIC_SUBFAMILY_NAME):
if not get_name(NameID.TYPOGRAPHIC_SUBFAMILY_NAME).endswith("Italic"):
yield FAIL, Message(
"bad-typographicsubfamilyname",
"Name ID 17 (Typographic Subfamily Name) must contain 'Italic'.",
)
56 changes: 1 addition & 55 deletions Lib/fontbakery/checks/opentype/name.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from fontbakery.callable import check
from fontbakery.status import FAIL, PASS, WARN, INFO, SKIP
from fontbakery.status import FAIL, PASS, WARN, INFO
from fontbakery.message import Message
from fontbakery.constants import (
NameID,
Expand Down Expand Up @@ -692,57 +692,3 @@ def check_consistent_font_family_name(ttFonts):
f"{''.join(detail_str_arr)}"
)
yield FAIL, Message("inconsistent-family-name", msg)


@check(
id="opentype/name/italic_names",
conditions=["style"],
rationale="""
This check ensures that several entries in the name table
conform to the font's Upright or Italic style,
namely IDs 1 & 2 as well as 16 & 17 if they're present.
""",
proposal="https://github.com/fonttools/fontbakery/issues/3666",
)
def check_name_italic_names(ttFont, style):
"""Check name table IDs 1, 2, 16, 17 to conform to Italic style."""

def get_name(nameID):
for entry in ttFont["name"].names:
if entry.nameID == nameID:
return entry.toUnicode()

if "Italic" not in style:
yield SKIP, ("Font is not Italic.")
else:
# Name ID 1 (Family Name)
if "Italic" in get_name(NameID.FONT_FAMILY_NAME):
yield FAIL, Message(
"bad-familyname", "Name ID 1 (Family Name) must not contain 'Italic'."
)

# Name ID 2 (Subfamily Name)
subfamily_name = get_name(NameID.FONT_SUBFAMILY_NAME)
if subfamily_name not in ("Italic", "Bold Italic"):
yield FAIL, Message(
"bad-subfamilyname",
"Name ID 2 (Subfamily Name) does not conform to specs."
" Only R/I/B/BI are allowed.\n"
f"Got: '{subfamily_name}'.",
)

# Name ID 16 (Typographic Family Name)
if get_name(NameID.TYPOGRAPHIC_FAMILY_NAME):
if "Italic" in get_name(NameID.TYPOGRAPHIC_FAMILY_NAME):
yield FAIL, Message(
"bad-typographicfamilyname",
"Name ID 16 (Typographic Family Name) must not contain 'Italic'.",
)

# Name ID 17 (Typographic Subfamily Name)
if get_name(NameID.TYPOGRAPHIC_SUBFAMILY_NAME):
if not get_name(NameID.TYPOGRAPHIC_SUBFAMILY_NAME).endswith("Italic"):
yield FAIL, Message(
"bad-typographicsubfamilyname",
"Name ID 17 (Typographic Subfamily Name) must contain 'Italic'.",
)
2 changes: 1 addition & 1 deletion Lib/fontbakery/profiles/adobefonts.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@
"opentype/italic_axis_in_stat_is_boolean",
"opentype/italic_axis_last",
"opentype/mac_style",
"opentype/name/italic_names",
"opentype/slant_direction",
"opentype/varfont/family_axis_ranges",
"opentype/varfont/ital_range",
Expand Down Expand Up @@ -68,6 +67,7 @@
"missing_small_caps_glyphs",
"name/ascii_only_entries",
"name/family_and_style_max_length",
"name/italic_names",
"no_debugging_tables",
"no_mac_entries",
"render_own_name",
Expand Down
1 change: 0 additions & 1 deletion Lib/fontbakery/profiles/opentype.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@
"opentype/maxadvancewidth",
"opentype/monospace",
"opentype/name/empty_records",
"opentype/name/italic_names",
"opentype/name/match_familyname_fullfont",
"opentype/name/postscript_name_consistency",
"opentype/name/postscript_vs_cff",
Expand Down
1 change: 1 addition & 0 deletions Lib/fontbakery/profiles/universal.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
"name/family_and_style_max_length",
"name/trailing_spaces",
"name/no_copyright_on_description",
"name/italic_names",
"no_debugging_tables",
"no_mac_entries",
"os2_metrics_match_hhea",
Expand Down
76 changes: 76 additions & 0 deletions tests/test_checks_name.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

from fontbakery.codetesting import (
assert_PASS,
assert_SKIP,
assert_results_contain,
CheckTester,
GLYPHSAPP_TEST_FILE,
Expand Down Expand Up @@ -243,3 +244,78 @@ def test_check_name_no_copyright_on_description():
assert_results_contain(
check(ttFont), FAIL, "copyright-on-description", "with a bad font..."
)


def test_check_italic_names():
check = CheckTester("name/italic_names")

def get_name(font, nameID):
for entry in font["name"].names:
if entry.nameID == nameID:
return entry.toUnicode()

def set_name(font, nameID, string):
for record in font["name"].names:
if record.nameID == nameID:
old_string = record.toUnicode()
if string != old_string:
font["name"].setName(
string,
record.nameID,
record.platformID,
record.platEncID,
record.langID,
)

# Fonts without Name ID 16 & 17

# PASS or SKIP
ttFont = TTFont(TEST_FILE("cabin/Cabin-Regular.ttf"))
assert_SKIP(check(ttFont))

ttFont = TTFont(TEST_FILE("cabin/Cabin-Italic.ttf"))
assert_PASS(check(ttFont))

ttFont = TTFont(TEST_FILE("cabin/Cabin-Medium.ttf"))
assert_SKIP(check(ttFont))

ttFont = TTFont(TEST_FILE("cabin/Cabin-Bold.ttf"))
assert_SKIP(check(ttFont))

ttFont = TTFont(TEST_FILE("cabin/Cabin-BoldItalic.ttf"))
assert_PASS(check(ttFont))

# FAIL
ttFont = TTFont(TEST_FILE("cabin/Cabin-Italic.ttf"))
set_name(ttFont, 1, get_name(ttFont, 1) + " Italic")
assert_results_contain(check(ttFont), FAIL, "bad-familyname")

ttFont = TTFont(TEST_FILE("cabin/Cabin-Italic.ttf"))
set_name(ttFont, 2, "Regular")
assert_results_contain(check(ttFont), FAIL, "bad-subfamilyname")

# This file is faulty as-is
ttFont = TTFont(TEST_FILE("cabin/Cabin-MediumItalic.ttf"))
assert_results_contain(check(ttFont), FAIL, "bad-subfamilyname")
# Fix it
set_name(ttFont, 1, "Cabin Medium")
set_name(ttFont, 2, "Italic")
assert_PASS(check(ttFont))

# Fonts with Name ID 16 & 17

# PASS or SKIP
ttFont = TTFont(TEST_FILE("shantell/ShantellSans[BNCE,INFM,SPAC,wght].ttf"))
assert_SKIP(check(ttFont))

ttFont = TTFont(TEST_FILE("shantell/ShantellSans-Italic[BNCE,INFM,SPAC,wght].ttf"))
assert_PASS(check(ttFont))

# FAIL
ttFont = TTFont(TEST_FILE("shantell/ShantellSans-Italic[BNCE,INFM,SPAC,wght].ttf"))
set_name(ttFont, 16, "Shantell Sans Italic")
assert_results_contain(check(ttFont), FAIL, "bad-typographicfamilyname")

ttFont = TTFont(TEST_FILE("shantell/ShantellSans-Italic[BNCE,INFM,SPAC,wght].ttf"))
set_name(ttFont, 17, "Light")
assert_results_contain(check(ttFont), FAIL, "bad-typographicsubfamilyname")
76 changes: 0 additions & 76 deletions tests/test_checks_opentype_name.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
from fontbakery.result import Subresult
from fontbakery.codetesting import (
assert_PASS,
assert_SKIP,
assert_results_contain,
CheckTester,
portable_path,
Expand Down Expand Up @@ -556,81 +555,6 @@ def test_check_consistent_font_family_name():
assert "'wrong-name-1' was found" in msg


def test_check_italic_names():
check = CheckTester("opentype/name/italic_names")

def get_name(font, nameID):
for entry in font["name"].names:
if entry.nameID == nameID:
return entry.toUnicode()

def set_name(font, nameID, string):
for record in font["name"].names:
if record.nameID == nameID:
old_string = record.toUnicode()
if string != old_string:
font["name"].setName(
string,
record.nameID,
record.platformID,
record.platEncID,
record.langID,
)

# Fonts without Name ID 16 & 17

# PASS or SKIP
ttFont = TTFont(TEST_FILE("cabin/Cabin-Regular.ttf"))
assert_SKIP(check(ttFont))

ttFont = TTFont(TEST_FILE("cabin/Cabin-Italic.ttf"))
assert_PASS(check(ttFont), PASS)

ttFont = TTFont(TEST_FILE("cabin/Cabin-Medium.ttf"))
assert_SKIP(check(ttFont))

ttFont = TTFont(TEST_FILE("cabin/Cabin-Bold.ttf"))
assert_SKIP(check(ttFont))

ttFont = TTFont(TEST_FILE("cabin/Cabin-BoldItalic.ttf"))
assert_PASS(check(ttFont), PASS)

# FAIL
ttFont = TTFont(TEST_FILE("cabin/Cabin-Italic.ttf"))
set_name(ttFont, 1, get_name(ttFont, 1) + " Italic")
assert_results_contain(check(ttFont), FAIL, "bad-familyname")

ttFont = TTFont(TEST_FILE("cabin/Cabin-Italic.ttf"))
set_name(ttFont, 2, "Regular")
assert_results_contain(check(ttFont), FAIL, "bad-subfamilyname")

# This file is faulty as-is
ttFont = TTFont(TEST_FILE("cabin/Cabin-MediumItalic.ttf"))
assert_results_contain(check(ttFont), FAIL, "bad-subfamilyname")
# Fix it
set_name(ttFont, 1, "Cabin Medium")
set_name(ttFont, 2, "Italic")
assert_PASS(check(ttFont), PASS)

# Fonts with Name ID 16 & 17

# PASS or SKIP
ttFont = TTFont(TEST_FILE("shantell/ShantellSans[BNCE,INFM,SPAC,wght].ttf"))
assert_SKIP(check(ttFont))

ttFont = TTFont(TEST_FILE("shantell/ShantellSans-Italic[BNCE,INFM,SPAC,wght].ttf"))
assert_PASS(check(ttFont), PASS)

# FAIL
ttFont = TTFont(TEST_FILE("shantell/ShantellSans-Italic[BNCE,INFM,SPAC,wght].ttf"))
set_name(ttFont, 16, "Shantell Sans Italic")
assert_results_contain(check(ttFont), FAIL, "bad-typographicfamilyname")

ttFont = TTFont(TEST_FILE("shantell/ShantellSans-Italic[BNCE,INFM,SPAC,wght].ttf"))
set_name(ttFont, 17, "Light")
assert_results_contain(check(ttFont), FAIL, "bad-typographicsubfamilyname")


def test_check_name_postscript():
check = CheckTester("opentype/postscript_name")

Expand Down

0 comments on commit 9c41991

Please sign in to comment.