d2lang/d2 · error
create OpenType face: %v
Error message
create OpenType face: %v
What it means
textmeasure's parsedFont.newFace wraps opentype.NewFace; since parseFont already validated the font, NewFace failing is treated as a broken invariant and panics with "create OpenType face". It indicates the sfnt font cannot produce a face at the given size/options — normally impossible for a successfully parsed font.
Source
Thrown at lib/textmeasure/fontface.go:68
return &parsedFont{
font: f,
unitsPerEm: unitsPerEm,
ascentUnits: metrics.Ascent,
descentUnits: metrics.Descent,
kern: kern,
}, nil
}
func (f *parsedFont) newFace(size float64) font.Face {
base, err := opentype.NewFace(f.font, &opentype.FaceOptions{
Size: size,
DPI: 72,
Hinting: font.HintingNone,
})
if err != nil {
// parseFont has already validated the font. NewFace currently cannot
// fail for a parsed font, so an error here indicates an invariant break.
panic(fmt.Sprintf("create OpenType face: %v", err))
}
return &metricPreservingFace{
Face: base,
font: f.font,
scale: fixed.Int26_6(0.5 + size*64),
unitsPerEm: f.unitsPerEm,
ascentUnits: f.ascentUnits,
descentUnits: f.descentUnits,
kern: f.kern,
}
}
type metricPreservingFace struct {
font.Face
font *sfnt.Font
buffer sfnt.Buffer
scale fixed.Int26_6View on GitHub (pinned to 0d69dca6f5)
Solutions
- Ensure the font size passed to textmeasure is a finite, positive number (guard NaN/Inf/<=0)
- Re-validate or replace any custom TTF loaded into textmeasure with a standard font
- Pin golang.org/x/image to the version the project was developed against
- Capture the panic (recover) and fall back to a default font/size if measurement is non-critical
Example fix
// before
ruler.SetText(...) with size := float64(userInput) // NaN possible
// after
if size <= 0 || math.IsNaN(size) || math.IsInf(size, 0) { size = 12 } Defensive patterns
Strategy: validation
Validate before calling
// Guard sizes before text measurement
func validFontSize(size float64) bool {
return !math.IsNaN(size) && !math.IsInf(size, 0) && size > 0 && size < 1000
}
if !validFontSize(size) { size = 12 } Type guard
func validFontSize(f float64) bool {
return !math.IsNaN(f) && !math.IsInf(f, 0) && f > 0
} Try / catch
// Recover around measurement setup
func safeNewFace(f *parsedFont, size float64) (face font.Face, err error) {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("face creation failed: %v", r)
}
}()
return f.newFace(size), nil
} Prevention
- Sanitize font sizes (finite, positive) before any textmeasure call
- Validate custom TTFs with opentype.Parse before registering them
- Pin golang.org/x/image version in go.mod
- Fall back to bundled default fonts for user-supplied fonts
When it happens
Trigger: Calling newFace (via textmeasure ruler/font measurement setup) on a parsedFont whose opentype.NewFace call with Size/DPI 72/HintingNone returns an error — e.g. degenerate size values (NaN, negative, huge) or a font that slipped through parsing in a bad state.
Common situations: Passing NaN or out-of-range font sizes into measurement APIs; custom fonts that parse but have malformed tables; memory exhaustion during face creation; mixing incompatible x/image/opentype versions with the project.
Related errors
- Failed to initialize font faces: %v
- received empty arrow head marker for: %#v
- ruler does not have entire font family %s loaded, is a style
- ruler does not have entire mono font family %s loaded, is a
- Failed to decompress SourceCodePro-Italic: %v
AI-assisted analysis of d2lang/d2@0d69dca6f5 (2026-08-31).
Data as JSON: /api/errors/0f2c52ca8b03a89c.
Report an issue: GitHub.