#!/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()