blob: a72d90306c1669196b9d766cccca614c110c95e1 [file] [log] [blame]
#!/usr/bin/env fuchsia-vendored-python
# 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()