Files
freezelib/font/font.go
2025-07-18 19:02:23 +08:00

166 lines
4.1 KiB
Go

package font
import (
"embed"
"fmt"
formatter "github.com/alecthomas/chroma/v2/formatters/svg"
)
//go:embed *.ttf
var fonts embed.FS
// JetBrainsMonoTTF contains the JetBrains Mono font data
var JetBrainsMonoTTF []byte
// JetBrainsMonoNLTTF contains the JetBrains Mono NL font data
var JetBrainsMonoNLTTF []byte
func init() {
var err error
JetBrainsMonoTTF, err = fonts.ReadFile("JetBrainsMono-Regular.ttf")
if err != nil {
// If embedded font is not available, use empty slice
JetBrainsMonoTTF = []byte{}
}
JetBrainsMonoNLTTF, err = fonts.ReadFile("JetBrainsMonoNL-Regular.ttf")
if err != nil {
// If embedded font is not available, use empty slice
JetBrainsMonoNLTTF = []byte{}
}
}
// FontOptions creates formatter options for the given font configuration
func FontOptions(family string, size float64, ligatures bool, fontFile string) ([]formatter.Option, error) {
var options []formatter.Option
// Set font family
if family != "" {
options = append(options, formatter.FontFamily(family))
}
// Embed font file if specified
if fontFile != "" {
option, err := formatter.EmbedFontFile(family, fontFile)
if err != nil {
return nil, fmt.Errorf("failed to embed font file: %w", err)
}
options = append(options, option)
}
return options, nil
}
// GetDefaultFontFamily returns the default font family
func GetDefaultFontFamily() string {
return "JetBrains Mono"
}
// GetDefaultFontSize returns the default font size
func GetDefaultFontSize() float64 {
return 14.0
}
// IsMonospaceFont checks if a font family is monospace
func IsMonospaceFont(family string) bool {
monospaceFonts := map[string]bool{
"JetBrains Mono": true,
"Fira Code": true,
"Source Code Pro": true,
"Monaco": true,
"Menlo": true,
"Consolas": true,
"Courier New": true,
"monospace": true,
"SF Mono": true,
"Cascadia Code": true,
"Ubuntu Mono": true,
"DejaVu Sans Mono": true,
"Liberation Mono": true,
"Inconsolata": true,
"Roboto Mono": true,
}
return monospaceFonts[family]
}
// ValidateFontFamily validates if a font family name is valid
func ValidateFontFamily(family string) error {
if family == "" {
return fmt.Errorf("font family cannot be empty")
}
return nil
}
// ValidateFontSize validates if a font size is valid
func ValidateFontSize(size float64) error {
if size <= 0 {
return fmt.Errorf("font size must be positive, got %.2f", size)
}
if size > 100 {
return fmt.Errorf("font size too large, got %.2f", size)
}
return nil
}
// GetFontHeightToWidthRatio returns the typical height to width ratio for monospace fonts
func GetFontHeightToWidthRatio() float64 {
return 1.68
}
// CalculateTextWidth estimates the width of text in pixels
func CalculateTextWidth(text string, fontSize float64) float64 {
return float64(len(text)) * (fontSize / GetFontHeightToWidthRatio())
}
// CalculateLineHeight calculates the line height in pixels
func CalculateLineHeight(fontSize, lineHeightRatio float64) float64 {
return fontSize * lineHeightRatio
}
// GetEmbeddedFontData returns embedded font data if available
func GetEmbeddedFontData(fontName string) []byte {
switch fontName {
case "JetBrains Mono", "JetBrainsMono":
return JetBrainsMonoTTF
case "JetBrains Mono NL", "JetBrainsMonoNL":
return JetBrainsMonoNLTTF
default:
return nil
}
}
// FontConfig represents font configuration
type FontConfig struct {
Family string
Size float64
Ligatures bool
File string
}
// NewFontConfig creates a new font configuration with defaults
func NewFontConfig() *FontConfig {
return &FontConfig{
Family: GetDefaultFontFamily(),
Size: GetDefaultFontSize(),
Ligatures: true,
File: "",
}
}
// Validate validates the font configuration
func (fc *FontConfig) Validate() error {
if err := ValidateFontFamily(fc.Family); err != nil {
return err
}
if err := ValidateFontSize(fc.Size); err != nil {
return err
}
return nil
}
// ToFormatterOptions converts font config to formatter options
func (fc *FontConfig) ToFormatterOptions() ([]formatter.Option, error) {
return FontOptions(fc.Family, fc.Size, fc.Ligatures, fc.File)
}