{"record":{"id":"0f2c52ca8b03a89c","repo":"d2lang/d2","slug":"create-opentype-face-v","errorCode":null,"errorMessage":"create OpenType face: %v","messagePattern":"create OpenType face: (.+?)","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"lib/textmeasure/fontface.go","lineNumber":68,"sourceCode":"\treturn &parsedFont{\n\t\tfont:         f,\n\t\tunitsPerEm:   unitsPerEm,\n\t\tascentUnits:  metrics.Ascent,\n\t\tdescentUnits: metrics.Descent,\n\t\tkern:         kern,\n\t}, nil\n}\n\nfunc (f *parsedFont) newFace(size float64) font.Face {\n\tbase, err := opentype.NewFace(f.font, &opentype.FaceOptions{\n\t\tSize:    size,\n\t\tDPI:     72,\n\t\tHinting: font.HintingNone,\n\t})\n\tif err != nil {\n\t\t// parseFont has already validated the font. NewFace currently cannot\n\t\t// fail for a parsed font, so an error here indicates an invariant break.\n\t\tpanic(fmt.Sprintf(\"create OpenType face: %v\", err))\n\t}\n\n\treturn &metricPreservingFace{\n\t\tFace:         base,\n\t\tfont:         f.font,\n\t\tscale:        fixed.Int26_6(0.5 + size*64),\n\t\tunitsPerEm:   f.unitsPerEm,\n\t\tascentUnits:  f.ascentUnits,\n\t\tdescentUnits: f.descentUnits,\n\t\tkern:         f.kern,\n\t}\n}\n\ntype metricPreservingFace struct {\n\tfont.Face\n\tfont         *sfnt.Font\n\tbuffer       sfnt.Buffer\n\tscale        fixed.Int26_6","sourceCodeStart":50,"sourceCodeEnd":86,"githubUrl":"https://github.com/d2lang/d2/blob/0d69dca6f532ceaeacd615d35d1eaa41a238ffdb/lib/textmeasure/fontface.go#L50-L86","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nruler.SetText(...) with size := float64(userInput) // NaN possible\n// after\nif size <= 0 || math.IsNaN(size) || math.IsInf(size, 0) { size = 12 }","handlingStrategy":"validation","validationCode":"// Guard sizes before text measurement\nfunc validFontSize(size float64) bool {\n    return !math.IsNaN(size) && !math.IsInf(size, 0) && size > 0 && size < 1000\n}\nif !validFontSize(size) { size = 12 }","typeGuard":"func validFontSize(f float64) bool {\n    return !math.IsNaN(f) && !math.IsInf(f, 0) && f > 0\n}","tryCatchPattern":"// Recover around measurement setup\nfunc safeNewFace(f *parsedFont, size float64) (face font.Face, err error) {\n    defer func() {\n        if r := recover(); r != nil {\n            err = fmt.Errorf(\"face creation failed: %v\", r)\n        }\n    }()\n    return f.newFace(size), nil\n}","preventionTips":["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"],"tags":["go","panic","fonts","textmeasure","opentype","invariant-violation"],"backgroundTag":"opentype-face-creation-failed","analyzedSha":"0d69dca6f532ceaeacd615d35d1eaa41a238ffdb","analyzedAt":"2026-08-31T12:19:21.182Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}