97 lines
2.6 KiB
Python
Executable File
97 lines
2.6 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""
|
|
Report which Unicode code points have vector outlines in ALL of the given font files.
|
|
|
|
Usage: python3 font-glyphs.py font1.ttf font2.ttf ...
|
|
"""
|
|
|
|
import sys
|
|
import unicodedata
|
|
from fontTools.ttLib import TTFont
|
|
from fontTools.pens.statisticsPen import StatisticsPen
|
|
|
|
|
|
def get_vector_codepoints(path):
|
|
"""Return the set of code points that have actual vector outlines in the font."""
|
|
font = TTFont(path)
|
|
cmap = font.getBestCmap()
|
|
if cmap is None:
|
|
print(f"WARNING: {path} has no cmap table", file=sys.stderr)
|
|
return set()
|
|
|
|
glyf = font.get("glyf") # TrueType outlines
|
|
cff = font.get("CFF ") # CFF outlines
|
|
|
|
result = set()
|
|
for codepoint, glyph_name in cmap.items():
|
|
has_outline = False
|
|
if glyf is not None:
|
|
g = glyf.get(glyph_name)
|
|
if g is not None and g.numberOfContours != 0:
|
|
has_outline = True
|
|
if cff is not None:
|
|
# CFF fonts store outlines in charstrings.
|
|
try:
|
|
cs = cff.cff.topDictIndex[0].CharStrings[glyph_name]
|
|
pen = StatisticsPen(glyphset=font.getGlyphSet())
|
|
cs.draw(pen)
|
|
if pen.area != 0:
|
|
has_outline = True
|
|
except (KeyError, AttributeError):
|
|
pass
|
|
if has_outline:
|
|
result.add(codepoint)
|
|
|
|
font.close()
|
|
return result
|
|
|
|
|
|
def main():
|
|
if len(sys.argv) < 2:
|
|
print(f"Usage: {sys.argv[0]} font1.ttf [font2.ttf ...]", file=sys.stderr)
|
|
sys.exit(1)
|
|
|
|
paths = sys.argv[1:]
|
|
|
|
# Process each font and intersect.
|
|
common = None
|
|
for path in paths:
|
|
cps = get_vector_codepoints(path)
|
|
print(f"{len(cps):6d} glyphs {path}")
|
|
if common is None:
|
|
common = cps
|
|
else:
|
|
common &= cps
|
|
|
|
if len(paths) > 1:
|
|
print(f"{len(common):6d} glyphs common to all {len(paths)} fonts", file=sys.stderr)
|
|
|
|
# Build the character string, excluding quote and backslash.
|
|
chars = []
|
|
for cp in sorted(common):
|
|
if cp == ord('"') or cp == ord('\\'):
|
|
continue
|
|
chars.append(chr(cp))
|
|
|
|
# Emit C++ file.
|
|
print("// Auto-generated by tools/font-glyphs.py — do not edit by hand.")
|
|
print(f"// {len(chars)} characters common to all {len(paths)} font(s).")
|
|
print()
|
|
print("const TCHAR *CommonChars = TEXT(")
|
|
|
|
# Break into lines of ~70 chars for readability.
|
|
line = ""
|
|
for ch in chars:
|
|
line += ch
|
|
if len(line) >= 70:
|
|
print(f'\t"{line}"')
|
|
line = ""
|
|
if line:
|
|
print(f'\t"{line}"')
|
|
|
|
print(");")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|