Skip to content

Commit

Permalink
further splitting out checks into individual .py files
Browse files Browse the repository at this point in the history
  • Loading branch information
felipesanches committed Nov 12, 2024
1 parent 36d7423 commit 591d885
Show file tree
Hide file tree
Showing 67 changed files with 2,056 additions and 2,017 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ A more detailed list of changes is available in the corresponding milestones for
### Changes to existing checks
### On the Universal profile
- **[family/control_chars]:** renamed to **control_chars** as it is not realy a family-wide check. (issue #4896)
- **[glyf_nested_components]** renamed to **nested_components**.


## 0.13.0a5 (2024-Nov-10)
Expand Down
61 changes: 61 additions & 0 deletions Lib/fontbakery/checks/STAT_in_statics.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
from fontbakery.prelude import FAIL, Message, check


@check(
id="STAT_in_statics",
conditions=["not is_variable_font", "has_STAT_table"],
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/fonttools/fontbakery/issues/4149",
)
def 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

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:
yield FAIL, Message(
"multiple-STAT-entries",
"The STAT table has more than a single entry for the"
f" '{tag_name}' axis ({entries[tag_name]}) on this"
" static font which will causes problems on Windows.",
)
43 changes: 43 additions & 0 deletions Lib/fontbakery/checks/STAT_strings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
from fontbakery.prelude import FAIL, Message, check


@check(
id="STAT_strings",
conditions=["has_STAT_table"],
rationale="""
On the STAT table, the "Italic" keyword must not be used on AxisValues
for variation axes other than 'ital'.
""",
proposal="https://github.com/fonttools/fontbakery/issues/2863",
)
def check_STAT_strings(ttFont):
"""Check correctness of STAT table strings"""
ital_axis_index = None
for index, axis in enumerate(ttFont["STAT"].table.DesignAxisRecord.Axis):
if axis.AxisTag == "ital":
ital_axis_index = index
break

nameIDs = set()
if ttFont["STAT"].table.AxisValueArray:
for value in ttFont["STAT"].table.AxisValueArray.AxisValue:
if hasattr(value, "AxisIndex"):
if value.AxisIndex != ital_axis_index:
nameIDs.add(value.ValueNameID)

if hasattr(value, "AxisValueRecord"):
for record in value.AxisValueRecord:
if record.AxisIndex != ital_axis_index:
nameIDs.add(value.ValueNameID)

bad_values = set()
for name in ttFont["name"].names:
if name.nameID in nameIDs and "italic" in name.toUnicode().lower():
bad_values.add(f"nameID {name.nameID}: {name.toUnicode()}")

if bad_values:
yield FAIL, Message(
"bad-italic",
"The following AxisValue entries on the STAT table"
f' should not contain "Italic":\n{sorted(bad_values)}',
)
Original file line number Diff line number Diff line change
Expand Up @@ -2,57 +2,6 @@
from fontbakery.utils import get_glyph_name


@check(
id="arabic_spacing_symbols",
proposal=[
"https://github.com/googlefonts/fontbakery/issues/4295",
],
rationale="""
Unicode has a few spacing symbols representing Arabic dots and other marks,
but they are purposefully not classified as marks.
Many fonts mistakenly classify them as marks, making them unsuitable
for their original purpose as stand-alone symbols to used in pedagogical
contexts discussing Arabic consonantal marks.
""",
severity=4,
)
def check_arabic_spacing_symbols(ttFont):
"""Check that Arabic spacing symbols U+FBB2–FBC1 aren't classified as marks."""

# code-points
ARABIC_SPACING_SYMBOLS = {
0xFBB2, # Dot Above
0xFBB3, # Dot Below
0xFBB4, # Two Dots Above
0xFBB5, # Two Dots Below
0xFBB6, # Three Dots Above
0xFBB7, # Three Dots Below
0xFBB8, # Three Dots Pointing Downwards Above
0xFBB9, # Three Dots Pointing Downwards Below
0xFBBA, # Four Dots Above
0xFBBB, # Four Dots Below
0xFBBC, # Double Vertical Bar Below
0xFBBD, # Two Dots Vertically Above
0xFBBE, # Two Dots Vertically Below
0xFBBF, # Ring
0xFBC0, # Small Tah Above
0xFBC1, # Small Tah Below
0xFBC2, # Wasla Above
}

if "GDEF" in ttFont and ttFont["GDEF"].table.GlyphClassDef:
class_def = ttFont["GDEF"].table.GlyphClassDef.classDefs
reverseCmap = ttFont["cmap"].buildReversed()
for name in reverseCmap:
if reverseCmap[name].intersection(ARABIC_SPACING_SYMBOLS):
if name in class_def and class_def[name] == 3:
yield FAIL, Message(
"mark-in-gdef",
f'"{name}" is defined in GDEF as a mark (class 3).',
)


@check(
id="arabic_high_hamza",
proposal=[
Expand Down
52 changes: 52 additions & 0 deletions Lib/fontbakery/checks/arabic_spacing_symbols.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
from fontbakery.prelude import FAIL, Message, check


@check(
id="arabic_spacing_symbols",
proposal=[
"https://github.com/googlefonts/fontbakery/issues/4295",
],
rationale="""
Unicode has a few spacing symbols representing Arabic dots and other marks,
but they are purposefully not classified as marks.
Many fonts mistakenly classify them as marks, making them unsuitable
for their original purpose as stand-alone symbols to used in pedagogical
contexts discussing Arabic consonantal marks.
""",
severity=4,
)
def check_arabic_spacing_symbols(ttFont):
"""Check that Arabic spacing symbols U+FBB2–FBC1 aren't classified as marks."""

# code-points
ARABIC_SPACING_SYMBOLS = {
0xFBB2, # Dot Above
0xFBB3, # Dot Below
0xFBB4, # Two Dots Above
0xFBB5, # Two Dots Below
0xFBB6, # Three Dots Above
0xFBB7, # Three Dots Below
0xFBB8, # Three Dots Pointing Downwards Above
0xFBB9, # Three Dots Pointing Downwards Below
0xFBBA, # Four Dots Above
0xFBBB, # Four Dots Below
0xFBBC, # Double Vertical Bar Below
0xFBBD, # Two Dots Vertically Above
0xFBBE, # Two Dots Vertically Below
0xFBBF, # Ring
0xFBC0, # Small Tah Above
0xFBC1, # Small Tah Below
0xFBC2, # Wasla Above
}

if "GDEF" in ttFont and ttFont["GDEF"].table.GlyphClassDef:
class_def = ttFont["GDEF"].table.GlyphClassDef.classDefs
reverseCmap = ttFont["cmap"].buildReversed()
for name in reverseCmap:
if reverseCmap[name].intersection(ARABIC_SPACING_SYMBOLS):
if name in class_def and class_def[name] == 3:
yield FAIL, Message(
"mark-in-gdef",
f'"{name}" is defined in GDEF as a mark (class 3).',
)
65 changes: 65 additions & 0 deletions Lib/fontbakery/checks/caps_vertically_centered.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
from fontTools.pens.boundsPen import BoundsPen
from fontbakery.prelude import check, Message, PASS, WARN, SKIP


@check(
id="caps_vertically_centered",
rationale="""
This check suggests one possible approach to designing vertical metrics,
but can be ingnored if you follow a different approach.
In order to center text in buttons, lists, and grid systems
with minimal additional CSS work, the uppercase glyphs should be
vertically centered in the em box.
This check mainly applies to Latin, Greek, Cyrillic, and other similar scripts.
For non-latin scripts like Arabic, this check might not be applicable.
There is a detailed description of this subject at:
https://x.com/romanshamin_en/status/1562801657691672576
""",
proposal="https://github.com/fonttools/fontbakery/issues/4139",
)
def check_caps_vertically_centered(ttFont):
"""Check if uppercase glyphs are vertically centered."""

from copy import deepcopy

ttFont_copy = deepcopy(ttFont)

SOME_UPPERCASE_GLYPHS = ["A", "B", "C", "D", "E", "H", "I", "M", "O", "S", "T", "X"]
glyphSet = ttFont_copy.getGlyphSet()

for glyphname in SOME_UPPERCASE_GLYPHS:
if glyphname not in glyphSet.keys():
yield SKIP, Message(
"lacks-ascii",
"The implementation of this check relies on a few samples"
" of uppercase latin characters that are not available in this font.",
)
return

highest_point_list = []
lowest_point_list = []
for glyphName in SOME_UPPERCASE_GLYPHS:
pen = BoundsPen(glyphSet)
glyphSet[glyphName].draw(pen)
_, lowest_point, _, highest_point = pen.bounds
highest_point_list.append(highest_point)
lowest_point_list.append(lowest_point)

upm = ttFont_copy["head"].unitsPerEm
line_spacing_factor = 1.20
error_margin = (line_spacing_factor * upm) * 0.18
average_cap_height = sum(highest_point_list) / len(highest_point_list)
average_descender = sum(lowest_point_list) / len(lowest_point_list)

top_margin = ttFont["hhea"].ascent - average_cap_height
bottom_margin = abs(ttFont["hhea"].descent) + average_descender

difference = abs(top_margin - bottom_margin)

if difference > error_margin:
yield WARN, Message(
"vertical-metrics-not-centered",
"Uppercase glyphs are not vertically centered in the em box.",
)
else:
yield PASS, "Uppercase glyphs are vertically centered in the em box."
File renamed without changes.
File renamed without changes.
Loading

0 comments on commit 591d885

Please sign in to comment.