blob: 5cf16bf1d9bb25201e5ec4024363f1f2d648727a [file] [log] [blame]
#!/usr/bin/env python3
# Copyright 2019 The Fuchsia Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""
Get code point coverage for a font file.
The default output is in a compact format that expresses the ranges of code
points as a series of offsets from the end of the previous range. For
example, if the file contains the code points
[1, 2, 3, 8, 9, 13, 17, 18, 19, 20]
the set of ranges is
[[1,3], [8,9], [13, 13], [17, 20]]
and the output will be
"1+2,5+1,4,4+3"
In each entry, the first number is the offset from the end of the previous
range. If the current range has length > 1, then there's a '+x' that shows how
much to add to get the upper bound of the range. Note that the range [13, 13]
has length 1, so it doesn't get a plus suffix.
"""
import os
import sys
# Add fuchsia/third_party/fonttools/Lib to the path.
font_tools_path = os.path.normpath(
os.path.join(os.path.realpath(__file__), '..', '..', '..', '..',
'third_party', 'fonttools', 'Lib'))
sys.path.insert(0, font_tools_path)
import argparse
from typing import NamedTuple, Dict
from fontTools import ttLib
from range_set import RangeSet
class AssetReference(NamedTuple):
"""Reference to font asset, with file path and optional index into the file.
"""
path: str
index: int = 0
class FontInfoReader:
"""Wrapper around ttlib.TTFont for reading font files.
"""
def open_font(self, asset: AssetReference) -> ttLib.TTFont:
"""Opens a font file, optionally with a specific font index."""
if asset.index is not None:
return ttLib.TTFont(asset.path, fontNumber=asset.index)
else:
return ttLib.TTFont(asset.path)
def get_best_cmap(self, asset: AssetReference) -> Dict:
"""Gets the best character map table (cmap) from the font file.
Cf. https://docs.microsoft.com/en-us/typography/opentype/spec/cmap
"""
ttfont = self.open_font(asset)
return ttfont.getBestCmap()
def get_ranges(self, asset: AssetReference) -> RangeSet:
"""Get the set of code points covered by the given asset."""
cmap = self.get_best_cmap(asset)
range_set = RangeSet()
for k, _ in cmap.items():
range_set.append(k)
return range_set
def main():
parser = argparse.ArgumentParser(
formatter_class=argparse.RawDescriptionHelpFormatter)
parser.description = __doc__
parser.add_argument('path', type=str, help='Path to the font file')
parser.add_argument('-i', '--index', type=int, default=None,
help=(
'For font files that contain collections of fonts '
'(.ttc), you must supply an index'))
parser.add_argument('-m', '--metadata', action='store_true',
help=(
'Instead of the code point offset string, '
'print font metadata.'))
args = parser.parse_args()
reader = FontInfoReader()
asset = AssetReference(args.path, args.index)
range_set = reader.get_ranges(asset)
as_offset_string = range_set.to_offset_string()
if args.metadata:
print('')
print('Code points:\n\t%d' % len(range_set))
print('Offset string length:\n\t%d' % len(as_offset_string))
print('')
else:
print(as_offset_string)
pass
if __name__ == '__main__':
main()