Skip to content

Commit 81720ad

Browse files
committed
refactor all metrics values and calculations to new class
1 parent 5681125 commit 81720ad

File tree

3 files changed

+168
-80
lines changed

3 files changed

+168
-80
lines changed

lib/fontline/commands.py

Lines changed: 35 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -4,132 +4,87 @@
44
import os.path
55

66
from fontTools import ttLib
7-
from fontline.utilities import get_sha1
8-
97
from standardstreams import stderr
108

119

10+
from fontline.metrics import MetricsObject
11+
12+
1213
def get_font_report(fontpath):
1314
tt = ttLib.TTFont(fontpath)
15+
metrics = MetricsObject(tt, fontpath)
1416

15-
# Vertical metrics values as integers
16-
os2_typo_ascender = tt["OS/2"].sTypoAscender
17-
os2_typo_descender = tt["OS/2"].sTypoDescender
18-
os2_win_ascent = tt["OS/2"].usWinAscent
19-
os2_win_descent = tt["OS/2"].usWinDescent
20-
os2_typo_linegap = tt["OS/2"].sTypoLineGap
21-
try:
22-
os2_x_height = tt["OS/2"].sxHeight
23-
except AttributeError:
24-
os2_x_height = "---- (OS/2 table does not contain sxHeight record)"
25-
try:
26-
os2_cap_height = tt["OS/2"].sCapHeight
27-
except AttributeError:
28-
os2_cap_height = "---- (OS/2 table does not contain sCapHeight record)"
29-
hhea_ascent = tt["hhea"].ascent
30-
hhea_descent = tt["hhea"].descent
31-
hhea_linegap = tt["hhea"].lineGap
32-
ymax = tt["head"].yMax
33-
ymin = tt["head"].yMin
34-
units_per_em = tt["head"].unitsPerEm
35-
36-
# Bit flag checks
37-
fsselection_bit7_mask = 1 << 7
38-
fsselection_bit7_set = (tt["OS/2"].fsSelection & fsselection_bit7_mask) != 0
39-
40-
# Calculated values
41-
os2_typo_total_height = os2_typo_ascender + -(os2_typo_descender)
42-
os2_win_total_height = os2_win_ascent + os2_win_descent
43-
hhea_total_height = hhea_ascent + -(hhea_descent)
44-
45-
hhea_btb_distance = hhea_total_height + hhea_linegap
46-
typo_btb_distance = os2_typo_total_height + os2_typo_linegap
47-
win_external_leading = hhea_linegap - (
48-
(os2_win_ascent + os2_win_descent) - (hhea_ascent - hhea_descent)
49-
)
50-
if win_external_leading < 0:
51-
win_external_leading = 0
52-
win_btb_distance = os2_win_ascent + os2_win_descent + win_external_leading
17+
# The file path header
18+
report = ["=== " + fontpath + " ==="]
5319

54-
typo_to_upm = 1.0 * typo_btb_distance / units_per_em
55-
winascdesc_to_upm = 1.0 * win_btb_distance / units_per_em
56-
hheaascdesc_to_upm = 1.0 * hhea_btb_distance / units_per_em
20+
report.append(metrics.version)
5721

58-
# The file path header
59-
report = [" "]
60-
report.append("=== " + fontpath + " ===")
61-
namerecord_list = tt["name"].names
62-
# The version string
63-
for needle in namerecord_list:
64-
if needle.nameID == 5:
65-
report.append(needle.toStr())
66-
break
6722
# The SHA1 string
68-
report.append("SHA1: " + get_sha1(fontpath))
23+
report.append("SHA1: " + metrics.sha1)
6924
report.append("")
7025
# The vertical metrics strings
7126
report.append("--- Metrics ---")
72-
report.append("[head] Units per Em: {}".format(units_per_em))
73-
report.append("[head] yMax: {}".format(ymax))
74-
report.append("[head] yMin: {}".format(ymin))
75-
report.append("[OS/2] CapHeight: {}".format(os2_cap_height))
76-
report.append("[OS/2] xHeight: {}".format(os2_x_height))
77-
report.append("[OS/2] TypoAscender: {}".format(os2_typo_ascender))
78-
report.append("[OS/2] TypoDescender: {}".format(os2_typo_descender))
79-
report.append("[OS/2] WinAscent: {}".format(os2_win_ascent))
80-
report.append("[OS/2] WinDescent: {}".format(os2_win_descent))
81-
report.append("[hhea] Ascent: {}".format(hhea_ascent))
82-
report.append("[hhea] Descent: {}".format(hhea_descent))
27+
report.append("[head] Units per Em: {}".format(metrics.units_per_em))
28+
report.append("[head] yMax: {}".format(metrics.ymax))
29+
report.append("[head] yMin: {}".format(metrics.ymin))
30+
report.append("[OS/2] CapHeight: {}".format(metrics.os2_cap_height))
31+
report.append("[OS/2] xHeight: {}".format(metrics.os2_x_height))
32+
report.append("[OS/2] TypoAscender: {}".format(metrics.os2_typo_ascender))
33+
report.append("[OS/2] TypoDescender: {}".format(metrics.os2_typo_descender))
34+
report.append("[OS/2] WinAscent: {}".format(metrics.os2_win_ascent))
35+
report.append("[OS/2] WinDescent: {}".format(metrics.os2_win_descent))
36+
report.append("[hhea] Ascent: {}".format(metrics.hhea_ascent))
37+
report.append("[hhea] Descent: {}".format(metrics.hhea_descent))
8338
report.append("")
84-
report.append("[hhea] LineGap: {}".format(hhea_linegap))
85-
report.append("[OS/2] TypoLineGap: {}".format(os2_typo_linegap))
39+
report.append("[hhea] LineGap: {}".format(metrics.hhea_linegap))
40+
report.append("[OS/2] TypoLineGap: {}".format(metrics.os2_typo_linegap))
8641
report.append("")
8742

8843
report.append("--- Ascent to Descent Calculations ---")
89-
report.append("[hhea] Ascent to Descent: {}".format(hhea_total_height))
44+
report.append("[hhea] Ascent to Descent: {}".format(metrics.hhea_total_height))
9045
report.append(
91-
"[OS/2] TypoAscender to TypoDescender: {}".format(os2_typo_total_height)
46+
"[OS/2] TypoAscender to TypoDescender: {}".format(metrics.os2_typo_total_height)
9247
)
9348
report.append(
94-
"[OS/2] WinAscent to WinDescent: {}".format(os2_win_total_height)
49+
"[OS/2] WinAscent to WinDescent: {}".format(metrics.os2_win_total_height)
9550
)
9651
report.append("")
9752

9853
report.append("--- Delta Values ---")
9954
report.append(
10055
"[hhea] Ascent to [OS/2] TypoAscender: {}".format(
101-
hhea_ascent - os2_typo_ascender
56+
metrics.hhea_ascent - metrics.os2_typo_ascender
10257
)
10358
)
10459
report.append(
10560
"[hhea] Descent to [OS/2] TypoDescender: {}".format(
106-
os2_typo_descender - hhea_descent
61+
metrics.os2_typo_descender - metrics.hhea_descent
10762
)
10863
)
10964
report.append(
11065
"[OS/2] WinAscent to [OS/2] TypoAscender: {}".format(
111-
os2_win_ascent - os2_typo_ascender
66+
metrics.os2_win_ascent - metrics.os2_typo_ascender
11267
)
11368
)
11469
report.append(
11570
"[OS/2] WinDescent to [OS/2] TypoDescender: {}".format(
116-
os2_win_descent - -(os2_typo_descender)
71+
metrics.os2_win_descent + metrics.os2_typo_descender
11772
)
11873
)
11974
report.append("")
12075
report.append("--- Baseline to Baseline Distances ---")
121-
report.append("hhea metrics: {}".format(hhea_btb_distance))
122-
report.append("typo metrics: {}".format(typo_btb_distance))
123-
report.append("win metrics: {}".format(win_btb_distance))
76+
report.append("hhea metrics: {}".format(metrics.hhea_btb_distance))
77+
report.append("typo metrics: {}".format(metrics.typo_btb_distance))
78+
report.append("win metrics: {}".format(metrics.win_btb_distance))
12479
report.append("")
12580
report.append(
126-
"[OS/2] fsSelection USE_TYPO_METRICS bit set: {}".format(fsselection_bit7_set)
81+
"[OS/2] fsSelection USE_TYPO_METRICS bit set: {}".format(metrics.fsselection_bit7_set)
12782
)
12883
report.append("")
12984
report.append("--- Ratios ---")
130-
report.append("hhea metrics / UPM: {0:.3g}".format(hheaascdesc_to_upm))
131-
report.append("typo metrics / UPM: {0:.3g}".format(typo_to_upm))
132-
report.append("win metrics / UPM: {0:.3g}".format(winascdesc_to_upm))
85+
report.append("hhea metrics / UPM: {0:.3g}".format(metrics.hheaascdesc_to_upm))
86+
report.append("typo metrics / UPM: {0:.3g}".format(metrics.typo_to_upm))
87+
report.append("win metrics / UPM: {0:.3g}".format(metrics.winascdesc_to_upm))
13388

13489
return "\n".join(report)
13590

lib/fontline/metrics.py

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
#!/usr/bin/env python3
2+
3+
from fontline.utilities import get_sha1
4+
5+
6+
class MetricsObject:
7+
def __init__(self, tt, filepath):
8+
self.tt = tt
9+
self.filepath = filepath
10+
self.sha1 = get_sha1(self.filepath)
11+
namerecord_list = tt["name"].names
12+
# Version string
13+
self.version = ""
14+
for needle in tt["name"].names:
15+
if needle.nameID == 5:
16+
self.version = needle.toStr()
17+
break
18+
# Vertical metrics values as integers
19+
self.os2_typo_ascender = tt["OS/2"].sTypoAscender
20+
self.os2_typo_descender = tt["OS/2"].sTypoDescender
21+
self.os2_win_ascent = tt["OS/2"].usWinAscent
22+
self.os2_win_descent = tt["OS/2"].usWinDescent
23+
self.os2_typo_linegap = tt["OS/2"].sTypoLineGap
24+
try:
25+
self.os2_x_height = tt["OS/2"].sxHeight
26+
except AttributeError:
27+
self.os2_x_height = "---- (OS/2 table does not contain sxHeight record)"
28+
try:
29+
self.os2_cap_height = tt["OS/2"].sCapHeight
30+
except AttributeError:
31+
self.os2_cap_height = "---- (OS/2 table does not contain sCapHeight record)"
32+
self.hhea_ascent = tt["hhea"].ascent
33+
self.hhea_descent = tt["hhea"].descent
34+
self.hhea_linegap = tt["hhea"].lineGap
35+
self.ymax = tt["head"].yMax
36+
self.ymin = tt["head"].yMin
37+
self.units_per_em = tt["head"].unitsPerEm
38+
39+
# Bit flag checks
40+
self.fsselection_bit7_mask = 1 << 7
41+
self.fsselection_bit7_set = (tt["OS/2"].fsSelection & self.fsselection_bit7_mask) != 0
42+
43+
# Calculated values
44+
self.os2_typo_total_height = self.os2_typo_ascender - self.os2_typo_descender
45+
self.os2_win_total_height = self.os2_win_ascent + self.os2_win_descent
46+
self.hhea_total_height = self.hhea_ascent - self.hhea_descent
47+
48+
self.hhea_btb_distance = self.hhea_total_height + self.hhea_linegap
49+
self.typo_btb_distance = self.os2_typo_total_height + self.os2_typo_linegap
50+
self.win_external_leading = self.hhea_linegap - (
51+
(self.os2_win_ascent + self.os2_win_descent) - (self.hhea_ascent - self.hhea_descent)
52+
)
53+
if self.win_external_leading < 0:
54+
self.win_external_leading = 0
55+
self.win_btb_distance = self.os2_win_ascent + self.os2_win_descent + self.win_external_leading
56+
57+
self.typo_to_upm = 1.0 * self.typo_btb_distance / self.units_per_em
58+
self.winascdesc_to_upm = 1.0 * self.win_btb_distance / self.units_per_em
59+
self.hheaascdesc_to_upm = 1.0 * self.hhea_btb_distance / self.units_per_em
60+
61+

tests/test_metrics.py

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
#!/usr/bin/env python3
2+
3+
import os
4+
5+
import pytest
6+
from fontTools.ttLib import TTFont
7+
8+
from fontline.metrics import MetricsObject
9+
10+
11+
@pytest.fixture()
12+
def metricsobject():
13+
filepath = os.path.join("tests", "testingfiles", "FiraMono-Regular.ttf")
14+
tt = TTFont(filepath)
15+
yield MetricsObject(tt, filepath)
16+
17+
18+
@pytest.fixture()
19+
def metricsobject_bit7_clear():
20+
filepath = os.path.join("tests", "testingfiles", "Hack-Regular.ttf")
21+
tt = TTFont(filepath)
22+
yield MetricsObject(tt, filepath)
23+
24+
25+
def test_mo_metadata(metricsobject):
26+
filepath = os.path.join("tests","testingfiles", "FiraMono-Regular.ttf")
27+
assert metricsobject.filepath == filepath
28+
assert metricsobject.version == "Version 3.206"
29+
assert metricsobject.sha1 == "e2526f6d8ab566afc7cf75ec192c1df30fd5913b"
30+
31+
32+
def test_mo_metricsdata(metricsobject):
33+
assert metricsobject.units_per_em == 1000
34+
assert metricsobject.ymax == 1050
35+
assert metricsobject.ymin == -500
36+
assert metricsobject.os2_cap_height == 689
37+
assert metricsobject.os2_x_height == 527
38+
assert metricsobject.os2_typo_ascender == 935
39+
assert metricsobject.os2_typo_descender == -265
40+
assert metricsobject.os2_win_ascent == 935
41+
assert metricsobject.os2_win_descent == 265
42+
assert metricsobject.hhea_ascent == 935
43+
assert metricsobject.hhea_descent == -265
44+
assert metricsobject.hhea_linegap == 0
45+
assert metricsobject.os2_typo_linegap == 0
46+
47+
48+
def test_mo_ascent_to_descent_values(metricsobject):
49+
assert metricsobject.hhea_total_height == 1200
50+
assert metricsobject.os2_typo_total_height == 1200
51+
assert metricsobject.os2_win_total_height == 1200
52+
53+
54+
def test_mo_b2bd_values(metricsobject):
55+
assert metricsobject.hhea_btb_distance == 1200
56+
assert metricsobject.typo_btb_distance == 1200
57+
assert metricsobject.win_btb_distance == 1200
58+
59+
60+
def test_mo_fsselection_bit7_set(metricsobject):
61+
assert metricsobject.fsselection_bit7_set is True
62+
63+
64+
def test_mo_fsselection_bit7_clear(metricsobject_bit7_clear):
65+
assert metricsobject_bit7_clear.fsselection_bit7_set is False
66+
67+
68+
def test_mo_ratios(metricsobject):
69+
assert metricsobject.hheaascdesc_to_upm == 1.2
70+
assert metricsobject.typo_to_upm == 1.2
71+
assert metricsobject.winascdesc_to_upm == 1.2
72+

0 commit comments

Comments
 (0)