#!/usr/bin/env python3 """Generate the four hermetic test faces for the pmacs-gpu font tests. The gpu-set-font acceptance suite (docs/gpu-set-font-framing.md) needs family-routing tests that cannot depend on whatever fonts the host has installed, so these tiny fixture faces are generated and committed: PmacsTestMonoTwo-Regular.ttf "Pmacs Test Mono Two" monospaced, advance 720/1000 (JetBrains Mono is 600/1000, so the measured advance ratio is exactly 1.2), with true "01" and "fi" ligatures whose advances preserve two cells PmacsTestProportional-Regular.ttf "Pmacs Test Proportional" varying advances, not monospaced PmacsTestFamily-Regular.ttf "Pmacs Test Family" monospaced normal face, advance 800/1000 PmacsTestFamily-Bold.ttf "Pmacs Test Family" BOLD and proportional -- the same-family collision the sanitizer removes and the four-style monospace gate must reject Every glyph is a plain rectangle (ink for frame-diff tests); coverage is space, the digits (the ADVANCE_PROBE string), and a-z. fontdb's `monospaced` flag reads the post table's isFixedPitch, so that is the one bit that decides mono vs proportional here. Run from this directory: python3 generate.py Requires fontTools (any recent version). """ from fontTools.feaLib.builder import addOpenTypeFeaturesFromString from fontTools.fontBuilder import FontBuilder from fontTools.pens.ttGlyphPen import TTGlyphPen UPM = 1000 CHARS = " 0123456789abcdefghijklmnopqrstuvwxyz" ASCENT = 800 DESCENT = -200 # Fixed at the first committed fixture generation so re-running this # script changes only intentional font data, not the head timestamps. FIXTURE_TIMESTAMP = 3866975598 def glyph_name(char): return "uni%04X" % ord(char) def rect_glyph(advance): """A filled rectangle spanning most of the advance width.""" pen = TTGlyphPen(None) left = 60 right = max(left + 40, advance - 60) pen.moveTo((left, 0)) pen.lineTo((right, 0)) pen.lineTo((right, 700)) pen.lineTo((left, 700)) pen.closePath() return pen.glyph() def empty_glyph(): return TTGlyphPen(None).glyph() def build( path, family, style, weight, bold, fixed_pitch, advance_for, ligatures=(), ): order = [".notdef"] + [glyph_name(c) for c in CHARS] order += [name for name, _ in ligatures] fb = FontBuilder(UPM, isTTF=True) fb.setupGlyphOrder(order) fb.setupCharacterMap({ord(c): glyph_name(c) for c in CHARS}) glyphs = {".notdef": rect_glyph(600)} metrics = {".notdef": (600, 60)} for c in CHARS: adv = advance_for(c) name = glyph_name(c) glyphs[name] = empty_glyph() if c == " " else rect_glyph(adv) metrics[name] = (adv, 0 if c == " " else 60) for name, components in ligatures: advance = sum(advance_for(c) for c in components) glyphs[name] = rect_glyph(advance) metrics[name] = (advance, 60) fb.setupGlyf(glyphs) fb.setupHorizontalMetrics(metrics) fb.setupHorizontalHeader(ascent=ASCENT, descent=DESCENT) # fontdb refuses faces without a PostScript name (nameID 6). ps_name = (family + "-" + style).replace(" ", "") fb.setupNameTable({"familyName": family, "styleName": style, "psName": ps_name}) fb.setupOS2( sTypoAscender=ASCENT, sTypoDescender=DESCENT, usWinAscent=ASCENT, usWinDescent=-DESCENT, usWeightClass=weight, fsSelection=0x20 if bold else 0x40, # BOLD else REGULAR ) fb.setupPost(isFixedPitch=1 if fixed_pitch else 0) if bold: fb.font["head"].macStyle = 0x01 if ligatures: substitutions = "\n".join( "sub %s by %s;" % (" ".join(glyph_name(c) for c in components), name) for name, components in ligatures ) addOpenTypeFeaturesFromString( fb.font, "feature liga {\n%s\n} liga;" % substitutions, ) fb.font["head"].created = FIXTURE_TIMESTAMP fb.font["head"].modified = FIXTURE_TIMESTAMP fb.font.recalcTimestamp = False fb.save(path) print("wrote", path) def proportional_advance(c): if c == " ": return 250 if c.isdigit(): return 500 # A spread of widths so no two adjacent letters share one. return 300 + (ord(c) - ord("a")) * 15 build( "PmacsTestMonoTwo-Regular.ttf", "Pmacs Test Mono Two", "Regular", 400, bold=False, fixed_pitch=True, advance_for=lambda c: 720, ligatures=( ("zero_one.liga", "01"), ("f_i.liga", "fi"), ), ) build( "PmacsTestProportional-Regular.ttf", "Pmacs Test Proportional", "Regular", 400, bold=False, fixed_pitch=False, advance_for=proportional_advance, ) build( "PmacsTestFamily-Regular.ttf", "Pmacs Test Family", "Regular", 400, bold=False, fixed_pitch=True, advance_for=lambda c: 800, ) build( "PmacsTestFamily-Bold.ttf", "Pmacs Test Family", "Bold", 700, bold=True, fixed_pitch=False, advance_for=proportional_advance, )