1
0
mirror of https://github.com/google/fonts.git synced 2024-11-28 11:00:21 +03:00
fonts/tools/compare_font.py

214 lines
6.5 KiB
Python

"""Utility to compare two font files.
Prints change in glyph count by supported subset and change in size by table.
This is helpful for understanding what is happening when we get a new version of
a font (why is Roboto2 3x larger than Roboto?).
Sample output (abriged run for roboto vs roboto2, at the time slightly smaller):
Subset Coverage Change (codepoints)
cyrillic -1 (316/320 => 315/320)
cyrillic-ext -1 (491/653 => 490/653)
greek -1 (290/363 => 289/363)
greek-ext -1 (291/619 => 290/619)
latin -1 (215/219 => 214/219)
latin-ext -11 (389/1029 => 378/1029)
vietnamese -1 (300/304 => 299/304)
all -13 (896 => 883)
Roboto-Regular.ttf (162876) vs Roboto2-Regular.ttf (145256) (-17620)
Table Changes (delta bytes, from=>to, % change)
BASE, +0, 0=>0, 0.0%
CFF , +0, 0=>0, 0.0%
DSIG, +0, 0=>0, 0.0%
FFTM, +0, 0=>0, 0.0%
GDEF, -516, 580=>64, -0.3%
GPOS, -146, 21028=>20882, -0.1%
GSUB, -5052, 6120=>1068, -3.1%
LTSH, +0, 0=>0, 0.0%
OS/2, +0, 96=>96, 0.0%
VORG, +0, 0=>0, 0.0%
cmap, -2634, 4808=>2174, -1.6%
cvt , +68, 76=>144, 0.0%
fpgm, +2483, 444=>2927, 1.5%
gasp, -4, 12=>8, -0.0%
glyf, -19546, 119606=>100060, -12.0%
hdmx, -1260, 1260=>0, -0.8%
head, +0, 54=>54, 0.0%
hhea, +0, 36=>36, 0.0%
hmtx, -712, 5000=>4288, -0.4%
kern, +0, 0=>0, 0.0%
loca, -356, 2502=>2146, -0.2%
maxp, +0, 32=>32, 0.0%
name, +708, 664=>1372, 0.4%
post, +9437, 32=>9469, 5.8%
prep, -81, 219=>138, -0.0%
vhea, +0, 0=>0, 0.0%
vmtx, +0, 0=>0, 0.0%
TOTAL, -17611, 162569=>144958, -10.8%
"""
import errno
import os
import sys
from fontTools.ttLib import sfnt
from google.apputils import app
import gflags as flags
from util import google_fonts as fonts
FLAGS = flags.FLAGS
flags.DEFINE_boolean('diff_tables', True, 'Whether to print table size diffs')
flags.DEFINE_boolean('diff_coverage', True, 'Whether to print coverage diffs')
_KNOWN_TABLES = ('BASE', 'CFF ', 'DSIG', 'GDEF', 'GPOS', 'GSUB', 'LTSH',
'OS/2', 'VORG', 'cmap', 'cvt ', 'fpgm', 'gasp', 'glyf', 'hdmx',
'head', 'hhea', 'hmtx', 'loca', 'maxp', 'name', 'post', 'prep',
'FFTM', 'kern', 'vhea', 'vmtx')
def CompareSize(font_filename1, font_filename2):
"""Prints a size comparison for two fonts.
If so flagged (--diff_tables), prints per-table size change.
Args:
font_filename1: The first font to compare.
font_filename2: The second font to compare.
Returns:
String describing size differences.
Raises:
OSError: If either argument doesn't point to a file. errno.ENOENT.
"""
if not (os.path.isfile(font_filename1) and os.path.isfile(font_filename2)):
raise OSError(errno.ENOENT, 'Missing at least one of %s and %s' % (
os.path.basename(font_filename1), os.path.basename(font_filename2)))
font_sz1 = os.stat(font_filename1).st_size
font_sz2 = os.stat(font_filename2).st_size
result = '%s (%d) vs %s (%d) (%+d)\n' % (
os.path.basename(font_filename1), font_sz1,
os.path.basename(font_filename2), font_sz2, font_sz2 - font_sz1)
if FLAGS.diff_tables:
result += DiffTables(font_filename1, font_filename2)
return result
def DiffTables(font_filename1, font_filename2):
"""Prints a table-by-table size comparison of two fonts.
Args:
font_filename1: The first font to compare.
font_filename2: The second font to compare.
Returns:
String describing size difference. One line per unique table in either font.
"""
result = [' Table Changes (delta bytes, from=>to, % change)']
sfnt1 = sfnt.SFNTReader(open(font_filename1))
sfnt2 = sfnt.SFNTReader(open(font_filename2))
font_sz1 = os.stat(font_filename1).st_size
sum_tables1 = 0
sum_tables2 = 0
table_l1_l2s = []
for t in fonts.UniqueSort(sfnt1.tables, sfnt2.tables, _KNOWN_TABLES):
table1_sz = sfnt1.tables[t].length if sfnt1.has_key(t) else 0
table2_sz = sfnt2.tables[t].length if sfnt2.has_key(t) else 0
sum_tables1 += table1_sz
sum_tables2 += table2_sz
table_l1_l2s.append((t, table1_sz, table2_sz))
for (table, table1_sz, table2_sz) in table_l1_l2s:
delta_pct = float(table2_sz - table1_sz) / font_sz1 * 100
result.append(' %s, %+d, %d=>%d, %.1f%%' % (
table, table2_sz - table1_sz, table1_sz, table2_sz, delta_pct))
delta_pct = float(sum_tables2 - sum_tables1) / font_sz1 * 100
result.append(' TOTAL, %+d, %d=>%d, %.1f%%' % (
sum_tables2 - sum_tables1, sum_tables1, sum_tables2, delta_pct))
return '\n'.join(result)
def DiffCoverage(font_filename1, font_filename2, subset):
"""Prints a comparison of the coverage of a given subset by two fonts.
Args:
font_filename1: The first font to compare.
font_filename2: The second font to compare.
subset: The lowercase name of the subset to compare coverage of.
"""
f1cps = fonts.CodepointsInFont(font_filename1)
f2cps = fonts.CodepointsInFont(font_filename2)
if subset != 'all':
subset_cps = fonts.CodepointsInSubset(subset)
f1cps &= subset_cps
f2cps &= subset_cps
else:
subset_cps = None
subset_cp_str = ('/%d' % len(subset_cps)) if subset_cps is not None else ''
print ' %s %+d (%d%s => %d%s)' % (
subset, len(f2cps) - len(f1cps), len(f1cps), subset_cp_str, len(f2cps),
subset_cp_str)
def CompareDirs(font1, font2):
"""Compares fonts by assuming font1/2 are dirs containing METADATA.pb."""
m1 = fonts.Metadata(font1)
m2 = fonts.Metadata(font2)
subsets_to_compare = fonts.UniqueSort(m1.subsets, m2.subsets)
subsets_to_compare.remove('menu')
subsets_to_compare.append('all')
font_filename1 = os.path.join(font1, fonts.RegularWeight(m1))
font_filename2 = os.path.join(font2, fonts.RegularWeight(m2))
if FLAGS.diff_coverage:
print 'Subset Coverage Change (codepoints)'
for subset in subsets_to_compare:
DiffCoverage(font_filename1, font_filename2, subset)
print CompareSize(font_filename1, font_filename2)
def CompareFiles(font1, font2):
"""Compares fonts assuming font1/2 are font files."""
print CompareSize(font1, font2)
def main(_):
if len(sys.argv) < 3:
raise app.UsageError('Must pass at least two arguments, font file or font'
' dir to diff')
font1 = sys.argv[1]
font2 = sys.argv[2]
dirs = os.path.isdir(font1) and os.path.isdir(font2)
files = os.path.isfile(font1) and os.path.isfile(font2)
if not dirs and not files:
print '%s and %s must both point to directories or font files' % (
font1, font2)
sys.exit(1)
if dirs:
CompareDirs(font1, font2)
if files:
CompareFiles(font1, font2)
if __name__ == '__main__':
app.run()