Source code

Revision control

Copy as Markdown

Other Tools

#!/usr/bin/env python3
"""
Generate FloatPrecisionTest.ttf, a minimal variable font with unusual floating
point precision on all axes to test font variation axis rounding behavior.
Run this script with:
> ./mach python -m pip install fonttools
> ./mach python devtools/client/inspector/fonts/test/FloatPrecisionTest_generate.py
"""
import os
import shutil
from fontTools.designspaceLib import (
AxisDescriptor,
DesignSpaceDocument,
InstanceDescriptor,
SourceDescriptor,
)
from fontTools.fontBuilder import FontBuilder
from fontTools.pens.ttGlyphPen import TTGlyphPen
from fontTools.varLib import build as varLib_build
FAMILY_NAME = "FloatPrecisionTest"
def create_base_font(name, width=600):
"""Create a simple base font with minimal glyph set.
This creates a "master" font that will be used as one of the sources
for building a variable font. The width parameter allows us to create
different masters with different glyph widths.
"""
# Initialize font builder with 1000 units per em (standard resolution)
fb = FontBuilder(unitsPerEm=1000, isTTF=True)
# Set the font family and style names
fb.setupNameTable({
"familyName": FAMILY_NAME,
"styleName": name,
})
# Define two glyphs: .notdef (fallback glyph) and "A"
glyphOrder = [".notdef", "A"]
# Draw .notdef as a simple rectangle (standard fallback glyph)
def draw_notdef(pen):
pen.moveTo((50, 0))
pen.lineTo((50, 700))
pen.lineTo((450, 700))
pen.lineTo((450, 0))
pen.closePath()
# Draw "A" as a rectangle with variable width
# The width parameter controls how wide the glyph is
def draw_A(pen):
pen.moveTo((100, 0))
pen.lineTo((100, 700))
pen.lineTo((width - 100, 700))
pen.lineTo((width - 100, 0))
pen.closePath()
# Set the glyph order and map character "A" to the A glyph
fb.setupGlyphOrder(glyphOrder)
fb.setupCharacterMap({ord("A"): "A"})
# Create the .notdef glyph by drawing with a pen
pen = TTGlyphPen(None)
draw_notdef(pen)
notdef_glyph = pen.glyph()
# Create the A glyph
pen = TTGlyphPen(None)
draw_A(pen)
a_glyph = pen.glyph()
# Add the glyphs to the font
fb.setupGlyf({".notdef": notdef_glyph, "A": a_glyph})
# Set horizontal metrics: (advance width, left side bearing)
fb.setupHorizontalMetrics({
".notdef": (500, 50),
"A": (width, 100),
})
# Set font metrics for vertical positioning
fb.setupHorizontalHeader(ascent=800, descent=-200)
fb.setupOS2(
sTypoAscender=800, sTypoDescender=-200, usWinAscent=800, usWinDescent=200
)
fb.setupPost()
fb.setupHead()
return fb.font
def create_precision_test_font():
"""Create variable font with floating point precision issues.
This function generates a variable font specifically designed to test
how the font inspector handles unusual floating point precision in
font variation axes. The axis values have many decimal places that
can expose rounding errors or precision issues in the UI.
"""
AXIS_VALUES = {
"Weight": {
"minimum": 300.3333282470703,
"default": 400.6666717529297,
"maximum": 699.4444580078125,
},
"Width": {
"minimum": 62.142856597900391,
"default": 87.555557250976562,
"maximum": 137.77777099609375,
},
"Slant": {
"minimum": -12.345678329467773,
"default": -0.111111111111111,
"maximum": 5.432109832763672,
},
"Optical Size": {
"minimum": 8.888888359069824,
"default": 14.285714149475098,
"maximum": 96.969696044921875,
},
}
AXES = [
("Weight", "wght"),
("Width", "wdth"),
("Slant", "slnt"),
("Optical Size", "opsz"),
]
SOURCES = [
("Light", "minimum", 400),
("Regular", "default", 600),
("Bold", "maximum", 800),
]
INSTANCES = [
("Regular", "default"),
("Bold", "maximum"),
]
# Determine the script's directory so all output goes to the same location
script_dir = os.path.dirname(os.path.abspath(__file__))
tmp_masters = os.path.join(script_dir, "tmp_masters")
os.makedirs(tmp_masters, exist_ok=True)
# Create and save master fonts with different glyph widths.
# Variable fonts interpolate between these masters to create variations.
# One master must be at the default location for all axes.
for source_name, _, width in SOURCES:
font = create_base_font(source_name, width=width)
font.save(os.path.join(tmp_masters, f"{source_name}.ttf"))
# Create a designspace document that describes the variable font.
# A designspace defines: the variation axes, the master fonts (sources),
# and named instances (predefined styles).
doc = DesignSpaceDocument()
# Define variation axes with intentionally unusual floating point precision.
# These values have many decimal places to test how the font inspector
# handles rounding and display of precise floating point numbers.
for axis_name, axis_tag in AXES:
axis = AxisDescriptor()
axis.name = axis_name
axis.tag = axis_tag
axis.minimum = AXIS_VALUES[axis_name]["minimum"]
axis.default = AXIS_VALUES[axis_name]["default"]
axis.maximum = AXIS_VALUES[axis_name]["maximum"]
doc.addAxis(axis)
# Add sources (the master fonts) to the designspace.
# Sources define the "control points" in the variation space that the
# variable font will interpolate between. Each source has a location
# in the design space defined by its coordinates on each axis.
for source_name, location_type, _ in SOURCES:
source = SourceDescriptor()
source.path = os.path.join(tmp_masters, f"{source_name}.ttf")
source.familyName = FAMILY_NAME
source.styleName = source_name
source.location = {
axis_name: AXIS_VALUES[axis_name][location_type]
for axis_name in AXIS_VALUES
}
doc.addSource(source)
# Add named instances to the designspace.
# Instances are predefined style names (like "Regular" or "Bold") that
# correspond to specific locations in the variation space. Users can
# select these by name rather than manually adjusting axis values.
for instance_name, location_type in INSTANCES:
instance = InstanceDescriptor()
instance.familyName = FAMILY_NAME
instance.styleName = instance_name
instance.location = {
axis_name: AXIS_VALUES[axis_name][location_type]
for axis_name in AXIS_VALUES
}
doc.addInstance(instance)
# Build the variable font from the designspace document.
# This generates a single .ttf file containing all the variation data,
# allowing continuous interpolation between the master fonts.
output_path = os.path.join(script_dir, "FloatPrecisionTest.ttf")
varfont, _, _ = varLib_build(doc)
varfont.save(output_path)
# Clean up temporary master font files (no longer needed)
shutil.rmtree(tmp_masters)
print(f"Created: {output_path}")
if __name__ == "__main__":
create_precision_test_font()