test_web.py 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. from fontbakery.callable import check
  2. from fontbakery.callable import condition
  3. from fontbakery.checkrunner import Section, PASS, FAIL, WARN
  4. from fontbakery.fonts_profile import profile_factory
  5. from tests.test_general import (
  6. font_family,
  7. font_style,
  8. font_features,
  9. com_roboto_fonts_check_italic_angle,
  10. com_roboto_fonts_check_fs_type,
  11. com_roboto_fonts_check_vendorid,
  12. com_roboto_fonts_check_digit_widths,
  13. com_roboto_fonts_check_features,
  14. com_roboto_fonts_check_charset_coverage,
  15. exclude_glyphs,
  16. )
  17. from fontbakery.profiles.shared_conditions import is_italic
  18. @condition
  19. def include_glyphs():
  20. # Ensure superior and inferior figures are included
  21. # https://github.com/googlefonts/roboto-classic/issues/97
  22. return frozenset([
  23. 0x2070,
  24. 0x2074,
  25. 0x2075,
  26. 0x2076,
  27. 0x2077,
  28. 0x2078,
  29. 0x2079,
  30. 0x2080,
  31. 0x2081,
  32. 0x2082,
  33. 0x2083,
  34. 0x2084,
  35. 0x2085,
  36. 0x2086,
  37. 0x2087,
  38. 0x2088,
  39. 0x2089,
  40. ])
  41. @condition
  42. def include_features():
  43. return set([
  44. 'c2sc', 'ccmp', 'cpsp', 'dlig', 'dnom', 'frac', 'kern', 'liga', 'lnum',
  45. 'locl', 'numr', 'onum', 'pnum', 'smcp', 'ss01', 'ss02', 'ss03', 'ss04',
  46. 'ss05', 'ss06', 'ss07', 'tnum', 'sups', 'subs'])
  47. profile = profile_factory(default_section=Section("Roboto web v3"))
  48. ROBOTO_PROFILE_CHECKS = [
  49. "com.roboto.fonts/check/vertical_metrics",
  50. "com.roboto.fonts/check/oblique_bits_not_set",
  51. "com.roboto.fonts/check/unique_id",
  52. "com.roboto.fonts/check/hinting",
  53. "com.roboto.fonts/check/italic_angle",
  54. "com.roboto.fonts/check/fs_type",
  55. "com.roboto.fonts/check/vendorid",
  56. "com.roboto.fonts/check/digit_widths",
  57. "com.roboto.fonts/check/features",
  58. "com.roboto.fonts/check/charset_coverage",
  59. ]
  60. @check(
  61. id="com.roboto.fonts/check/vertical_metrics",
  62. )
  63. def com_roboto_fonts_check_vertical_metrics(ttFont):
  64. """Check vertical metrics are correct"""
  65. failed = []
  66. expected = {
  67. # android requires this, and web fonts expect this
  68. ("head", "yMin"): -555,
  69. ("head", "yMax"): 2163,
  70. # test hhea ascent, descent, and lineGap to be equal to Roboto v1 values
  71. ("hhea", "descent"): -500,
  72. ("hhea", "ascent"): 1900,
  73. ("hhea", "lineGap"): 0,
  74. # test OS/2 vertical metrics to be equal to old OS/2 win values
  75. # since fsSelection bit 7 is now enabled
  76. ("OS/2", "sTypoDescender"): -512,
  77. ("OS/2", "sTypoAscender"): 1536,
  78. ("OS/2", "sTypoLineGap"): 102,
  79. ("OS/2", "usWinDescent"): 512,
  80. ("OS/2", "usWinAscent"): 1946,
  81. }
  82. for (table, k), v in expected.items():
  83. font_val = getattr(ttFont[table], k)
  84. if font_val != v:
  85. failed.append((table, k, v, font_val))
  86. if not failed:
  87. yield PASS, "Fonts have correct vertical metrics"
  88. else:
  89. msg = "\n".join(
  90. [
  91. f"- {tbl}.{k} is {font_val} it should be {v}"
  92. for tbl, k, v, font_val in failed
  93. ]
  94. )
  95. yield FAIL, f"Fonts have incorrect vertical metrics:\n{msg}"
  96. @check(
  97. id="com.roboto.fonts/check/oblique_bits_not_set",
  98. )
  99. def com_roboto_fonts_check_oblique_bits_not_set(ttFont):
  100. """Check oblique bits are not set in fonts"""
  101. if ttFont['OS/2'].fsSelection & (1 << 9) != 0:
  102. yield FAIL, "fsSelection bit 9 (Oblique) must not be enabled"
  103. else:
  104. yield PASS, "fsSelection bit 9 is disabled"
  105. @check(
  106. id="com.roboto.fonts/check/unique_id",
  107. )
  108. def com_roboto_fonts_check_unique_id(ttFont):
  109. """Check font unique id is correct"""
  110. family_name = font_family(ttFont)
  111. style = font_style(ttFont)
  112. if style != "Regular":
  113. expected = f"{family_name} {style}"
  114. else:
  115. expected = f"{family_name}"
  116. font_unique_id = ttFont['name'].getName(3, 3, 1, 1033).toUnicode()
  117. if font_unique_id == expected:
  118. yield PASS, "Unique ID is correct"
  119. else:
  120. yield FAIL, f"Unique ID, '{font_unique_id}' is incorrect. It should be '{expected}'"
  121. @check(
  122. id="com.roboto.fonts/check/hinting",
  123. )
  124. def com_roboto_fonts_check_hinting(ttFont):
  125. """Check glyphs have hinting"""
  126. missing_hints = []
  127. # we can ignore these according to Mike D
  128. # https://github.com/TypeNetwork/Roboto/issues/70#issuecomment-641221200
  129. ignore = ['.notdef', 'uni0488', 'uni0489', 'uniFFFC', 'uniFFFD']
  130. for glyph_name in ttFont.getGlyphOrder():
  131. glyph = ttFont['glyf'][glyph_name]
  132. if glyph.numberOfContours <= 0:
  133. continue
  134. if len(glyph.program.bytecode) <= 0 and glyph_name not in ignore:
  135. missing_hints.append(glyph_name)
  136. if missing_hints:
  137. yield FAIL, f"Following glyphs are missing hinting {missing_hints}"
  138. else:
  139. yield PASS, "All glyphs are hinted"
  140. profile.auto_register(globals())
  141. profile.test_expected_checks(ROBOTO_PROFILE_CHECKS, exclusive=True)