From 0a0b9b8cc9cdc0ffe1819de0266dd1e3c3eb564f Mon Sep 17 00:00:00 2001 From: Sam Anthony Date: Tue, 3 Mar 2026 14:22:43 -0500 Subject: style: unit conversion Implemented the golang.org/x/exp/shiny/unit.Converter interface on style.Style. --- style/style.go | 109 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 106 insertions(+), 3 deletions(-) (limited to 'style/style.go') diff --git a/style/style.go b/style/style.go index 5b74952..fe10833 100644 --- a/style/style.go +++ b/style/style.go @@ -1,22 +1,125 @@ package style import ( + "fmt" + "golang.org/x/exp/shiny/unit" + "golang.org/x/image/font" + "golang.org/x/image/font/opentype" "golang.org/x/image/math/fixed" ) -// TODO +const ( + DefaultFontSize = 12.0 // points (1/72") + DefaultDpi = 72.0 // dots per inch + DefaultHinting = font.HintingFull +) + +// Style defines the colors and font faces that widgets are drawn with. type Style struct { + font *opentype.Font + faceOpts opentype.FaceOptions + face font.Face // for measurements + + // TODO +} + +// New returns a new Style with the given options. +// The Style should be closed after use. +func New(opts ...Option) (*Style, error) { + o := parseOpts(opts...) + + fnt, err := opentype.Parse(o.font) + if err != nil { + return nil, err + } + face, err := opentype.NewFace(fnt, &o.FaceOptions) + if err != nil { + return nil, err + } + + return &Style{ + fnt, + o.FaceOptions, + face, + }, nil +} + +func (s *Style) Close() error { + return s.face.Close() +} + +// NewFace returns a new font.Face using the Style's Font and FaceOptions. +func (s *Style) NewFace() (font.Face, error) { + return opentype.NewFace(s.font, &s.faceOpts) } // Convert implements the golang.org/x/exp/shiny/unit.Converter // interface. func (s *Style) Convert(v unit.Value, to unit.Unit) unit.Value { - // TODO + px := fixed26ToFloat(s.Pixels(v)) + var f float64 + switch to { + case unit.Px: + f = px + case unit.Dp: + f = s.pixelsToInches(px) * unit.DensityIndependentPixelsPerInch + case unit.Pt: + f = s.pixelsToInches(px) * unit.PointsPerInch + case unit.Mm: + f = s.pixelsToInches(px) * unit.MillimetresPerInch + case unit.In: + f = s.pixelsToInches(px) + case unit.Em: + f = px / fixed26ToFloat(s.face.Metrics().Height) + case unit.Ex: + f = px / fixed26ToFloat(s.face.Metrics().XHeight) + case unit.Ch: + f = px / fixed26ToFloat(s.zeroWidth()) + default: + panic(fmt.Sprintf("unreachable: impossible %T: %v", to, to)) + } + return unit.Value{f, to} } // Pixels implements the golang.org/x/exp/shiny/unit.Converter // interface. func (s *Style) Pixels(v unit.Value) fixed.Int26_6 { - // TODO + f := floatToFixed26(v.F) + switch v.U { + case unit.Px: + return f + case unit.Dp: + return s.inchesToPixels(v.F / unit.DensityIndependentPixelsPerInch) + case unit.Pt: + return s.inchesToPixels(v.F / unit.PointsPerInch) + case unit.Mm: + return s.inchesToPixels(v.F / unit.MillimetresPerInch) + case unit.In: + return s.inchesToPixels(v.F) + case unit.Em: + return f.Mul(s.face.Metrics().Height) + case unit.Ex: + return f.Mul(s.face.Metrics().XHeight) + case unit.Ch: + return f.Mul(s.zeroWidth()) + default: + panic(fmt.Sprintf("unreachable: impossible %T: %v", v.U, v.U)) + } +} + +func (s *Style) pixelsToInches(px float64) (in float64) { + return px / s.faceOpts.DPI +} + +func (s *Style) inchesToPixels(in float64) (px fixed.Int26_6) { + return floatToFixed26(in * s.faceOpts.DPI) +} + +func (s *Style) zeroWidth() (px fixed.Int26_6) { + if advance, ok := s.face.GlyphAdvance('0'); ok { + return advance + } else { + return floatToFixed26(0.5).Mul(s.face.Metrics().Height) // 0.5em + } } -- cgit v1.2.3