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_6

View on GitHub (pinned to 0d69dca6f5)

Solutions

  1. Ensure the font size passed to textmeasure is a finite, positive number (guard NaN/Inf/<=0)
  2. Re-validate or replace any custom TTF loaded into textmeasure with a standard font
  3. Pin golang.org/x/image to the version the project was developed against
  4. 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

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


AI-assisted analysis of d2lang/d2@0d69dca6f5 (2026-08-31). Data as JSON: /api/errors/0f2c52ca8b03a89c. Report an issue: GitHub.