Skip to content

Commit

Permalink
a few other checks that also draw with pens
Browse files Browse the repository at this point in the history
and for that reason could affect the results of other checks:

- arabic_spacing_symbols
- empty_glyph_on_gid1_for_colrv0
- iso15008/interword_spacing
- linegaps
- caps_vertically_centered
- production_glyphs_similarity
  • Loading branch information
felipesanches committed Sep 6, 2024
1 parent 7efe458 commit b70b881
Show file tree
Hide file tree
Showing 6 changed files with 66 additions and 27 deletions.
22 changes: 14 additions & 8 deletions Lib/fontbakery/checks/arabic.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,22 +73,28 @@ def check_arabic_spacing_symbols(ttFont):
def check_arabic_high_hamza(ttFont):
"""Check that glyph for U+0675 ARABIC LETTER HIGH HAMZA is not a mark."""
from fontTools.pens.areaPen import AreaPen
from copy import deepcopy

# This check modifies the font file with `.draw(pen)`
# so here we'll work with a copy of the object so that we
# do not affect other checks:
ttFont_copy = deepcopy(ttFont)

ARABIC_LETTER_HAMZA = 0x0621
ARABIC_LETTER_HIGH_HAMZA = 0x0675

cmap = ttFont.getBestCmap()
cmap = ttFont_copy.getBestCmap()
if ARABIC_LETTER_HAMZA not in cmap or ARABIC_LETTER_HIGH_HAMZA not in cmap:
yield SKIP, Message(
"glyphs-missing",
"This check will only run on fonts that have both glyphs U+0621 and U+0675",
)
return

if "GDEF" in ttFont and ttFont["GDEF"].table.GlyphClassDef:
class_def = ttFont["GDEF"].table.GlyphClassDef.classDefs
reverseCmap = ttFont["cmap"].buildReversed()
glyphOrder = ttFont.getGlyphOrder()
if "GDEF" in ttFont_copy and ttFont_copy["GDEF"].table.GlyphClassDef:
class_def = ttFont_copy["GDEF"].table.GlyphClassDef.classDefs
reverseCmap = ttFont_copy["cmap"].buildReversed()
glyphOrder = ttFont_copy.getGlyphOrder()
for name in glyphOrder:
if ARABIC_LETTER_HIGH_HAMZA in reverseCmap.get(name, set()):
if name in class_def and class_def[name] == 3:
Expand All @@ -100,14 +106,14 @@ def check_arabic_high_hamza(ttFont):
# Also validate the bounding box of the glyph and compare
# it to U+0621 expecting them to have roughly the same size
# (within a certain tolerance margin)
glyph_set = ttFont.getGlyphSet()
glyph_set = ttFont_copy.getGlyphSet()
area_pen = AreaPen(glyph_set)

glyph_set[get_glyph_name(ttFont, ARABIC_LETTER_HAMZA)].draw(area_pen)
glyph_set[get_glyph_name(ttFont_copy, ARABIC_LETTER_HAMZA)].draw(area_pen)
hamza_area = area_pen.value

area_pen.value = 0
glyph_set[get_glyph_name(ttFont, ARABIC_LETTER_HIGH_HAMZA)].draw(area_pen)
glyph_set[get_glyph_name(ttFont_copy, ARABIC_LETTER_HIGH_HAMZA)].draw(area_pen)
high_hamza_area = area_pen.value

if abs((high_hamza_area - hamza_area) / hamza_area) > 0.1:
Expand Down
3 changes: 2 additions & 1 deletion Lib/fontbakery/checks/color.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,11 +145,12 @@ def check_empty_glyph_on_gid1_for_colrv0(ttFont):
" This could be the space glyph."
)
from fontTools.pens.areaPen import AreaPen
from copy import deepcopy

# This check modifies the font file with `.draw(pen)`
# so here we'll work with a copy of the object so that we
# do not affect other checks:
from copy import deepcopy

ttFont_copy = deepcopy(ttFont)

glyphOrder = ttFont_copy.getGlyphOrder()
Expand Down
20 changes: 14 additions & 6 deletions Lib/fontbakery/checks/iso15008.py
Original file line number Diff line number Diff line change
Expand Up @@ -249,29 +249,37 @@ def check_iso15008_intercharacter_spacing(font, ttFont):
)
def check_iso15008_interword_spacing(font, ttFont):
"""Check if spacing between words is adequate for display use"""
l_intersections = xheight_intersections(ttFont, "l")

# This check modifies the font file with `.draw(pen)`
# so here we'll work with a copy of the object so that we
# do not affect other checks:
from copy import deepcopy

ttFont_copy = deepcopy(ttFont)

l_intersections = xheight_intersections(ttFont_copy, "l")
if len(l_intersections) < 2:
yield FAIL, Message(
"glyph-not-present",
"There was no 'l' glyph in the font, so the spacing could not be tested",
)
return

l_advance = ttFont["hmtx"]["l"][0]
l_advance = ttFont_copy["hmtx"]["l"][0]
l_rsb = l_advance - l_intersections[-1].point.x

glyphset = ttFont.getGlyphSet()
glyphset = ttFont_copy.getGlyphSet()
pen = BoundsPen(glyphset)
glyphset["m"].draw(pen)
(xMin, yMin, xMax, yMax) = pen.bounds
m_advance = ttFont["hmtx"]["m"][0]
m_advance = ttFont_copy["hmtx"]["m"][0]
m_lsb = xMin
m_rsb = m_advance - (m_lsb + xMax - xMin)

n_lsb = ttFont["hmtx"]["n"][1]
n_lsb = ttFont_copy["hmtx"]["n"][1]

l_m = l_rsb + pair_kerning(font, "l", "m") + m_lsb
space_width = ttFont["hmtx"]["space"][0]
space_width = ttFont_copy["hmtx"]["space"][0]
# Add spacing caused by normal sidebearings
space_width += m_rsb + n_lsb

Expand Down
13 changes: 10 additions & 3 deletions Lib/fontbakery/checks/metrics.py
Original file line number Diff line number Diff line change
Expand Up @@ -282,11 +282,18 @@ def check_linegaps(ttFont):
def check_typoascender_exceeds_Agrave(ttFont):
"""Checking that the typoAscender exceeds the yMax of the /Agrave."""

if "OS/2" not in ttFont:
# This check modifies the font file with `.draw(pen)`
# so here we'll work with a copy of the object so that we
# do not affect other checks:
from copy import deepcopy

ttFont_copy = deepcopy(ttFont)

if "OS/2" not in ttFont_copy:
yield FAIL, Message("lacks-OS/2", "Font file lacks OS/2 table")
return

glyphset = ttFont.getGlyphSet()
glyphset = ttFont_copy.getGlyphSet()

if "Agrave" not in glyphset and "uni00C0" not in glyphset:
yield SKIP, Message(
Expand All @@ -304,7 +311,7 @@ def check_typoascender_exceeds_Agrave(ttFont):

yMax = pen.bounds[-1]

typoAscender = ttFont["OS/2"].sTypoAscender
typoAscender = ttFont_copy["OS/2"].sTypoAscender

if typoAscender < yMax:
yield FAIL, Message(
Expand Down
14 changes: 11 additions & 3 deletions Lib/fontbakery/checks/some_other_checks.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,10 +110,18 @@ def check_family_single_directory(fonts):
)
def check_caps_vertically_centered(ttFont):
"""Check if uppercase glyphs are vertically centered."""

# This check modifies the font file with `.draw(pen)`
# so here we'll work with a copy of the object so that we
# do not affect other checks:
from copy import deepcopy

ttFont_copy = deepcopy(ttFont)

from fontTools.pens.boundsPen import BoundsPen

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

for glyphname in SOME_UPPERCASE_GLYPHS:
if glyphname not in glyphSet.keys():
Expand All @@ -131,10 +139,10 @@ def check_caps_vertically_centered(ttFont):
highest_point = pen.bounds[3]
highest_point_list.append(highest_point)

upm = ttFont["head"].unitsPerEm
upm = ttFont_copy["head"].unitsPerEm
error_margin = upm * 0.05
average_cap_height = sum(highest_point_list) / len(highest_point_list)
descender = ttFont["hhea"].descent
descender = ttFont_copy["hhea"].descent
top_margin = upm - average_cap_height
difference = abs(top_margin - abs(descender))
vertically_centered = difference <= error_margin
Expand Down
21 changes: 15 additions & 6 deletions Lib/fontbakery/checks/vendorspecific/googlefonts/hosted.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,14 +55,23 @@ def check_version_bump(ttFont, api_gfonts_ttFont, github_gfonts_ttFont):
)
def check_production_glyphs_similarity(ttFont, api_gfonts_ttFont, config):
"""Glyphs are similiar to Google Fonts version?"""

# This check modifies the font file with `.draw(pen)`
# so here we'll work with a copy of the object so that we
# do not affect other checks:
from copy import deepcopy

ttFont_copy = deepcopy(ttFont)
api_gfonts_ttFont_copy = deepcopy(api_gfonts_ttFont)

from fontbakery.utils import pretty_print_list

def glyphs_surface_area(ttFont):
def glyphs_surface_area(a_ttFont):
"""Calculate the surface area of a glyph's ink"""
from fontTools.pens.areaPen import AreaPen

glyphs = {}
glyph_set = ttFont.getGlyphSet()
glyph_set = a_ttFont.getGlyphSet()
area_pen = AreaPen(glyph_set)

for glyph in glyph_set.keys():
Expand All @@ -74,13 +83,13 @@ def glyphs_surface_area(ttFont):
return glyphs

bad_glyphs = []
these_glyphs = glyphs_surface_area(ttFont)
gfonts_glyphs = glyphs_surface_area(api_gfonts_ttFont)
these_glyphs = glyphs_surface_area(ttFont_copy)
gfonts_glyphs = glyphs_surface_area(api_gfonts_ttFont_copy)

shared_glyphs = set(these_glyphs) & set(gfonts_glyphs)

this_upm = ttFont["head"].unitsPerEm
gfonts_upm = api_gfonts_ttFont["head"].unitsPerEm
this_upm = ttFont_copy["head"].unitsPerEm
gfonts_upm = api_gfonts_ttFont_copy["head"].unitsPerEm

for glyph in shared_glyphs:
# Normalize area difference against comparison's upm
Expand Down

0 comments on commit b70b881

Please sign in to comment.