Tokenizer is now done, we also have the new InternalizeID and ExternalizeID
This commit is contained in:
96
tools/font-glyphs.py
Executable file
96
tools/font-glyphs.py
Executable file
@@ -0,0 +1,96 @@
|
||||
#!/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()
|
||||
Reference in New Issue
Block a user