diff --git a/font/font.go b/font/font.go index c81c517d..d998cf8b 100644 --- a/font/font.go +++ b/font/font.go @@ -34,7 +34,7 @@ type Font struct { // Face is an opaque handle to a typeface. The concrete implementation depends // upon the kind of font and shaper in use. type Face interface { - Face() font.Face + Face() *font.Face } // Typeface identifies a list of font families to attempt to use for displaying diff --git a/font/opentype/opentype.go b/font/opentype/opentype.go index 0e51ee3d..025a1203 100644 --- a/font/opentype/opentype.go +++ b/font/opentype/opentype.go @@ -16,23 +16,21 @@ import ( _ "image/png" giofont "gioui.org/font" - "github.com/go-text/typesetting/font" - fontapi "github.com/go-text/typesetting/opentype/api/font" - "github.com/go-text/typesetting/opentype/api/metadata" - "github.com/go-text/typesetting/opentype/loader" + fontapi "github.com/go-text/typesetting/font" + "github.com/go-text/typesetting/font/opentype" ) // Face is a thread-safe representation of a loaded font. For efficiency, applications // should construct a face for any given font file once, reusing it across different // text shapers. type Face struct { - face font.Font + face *fontapi.Font font giofont.Font } // Parse constructs a Face from source bytes. func Parse(src []byte) (Face, error) { - ld, err := loader.NewLoader(bytes.NewReader(src)) + ld, err := opentype.NewLoader(bytes.NewReader(src)) if err != nil { return Face{}, err } @@ -49,11 +47,11 @@ func Parse(src []byte) (Face, error) { // ParseCollection parse an Opentype font file, with support for collections. // Single font files are supported, returning a slice with length 1. // The returned fonts are automatically wrapped in a text.FontFace with -// inferred font metadata. +// inferred font font. // BUG(whereswaldon): the only Variant that can be detected automatically is // "Mono". func ParseCollection(src []byte) ([]giofont.FontFace, error) { - lds, err := loader.NewLoaders(bytes.NewReader(src)) + lds, err := opentype.NewLoaders(bytes.NewReader(src)) if err != nil { return nil, err } @@ -76,7 +74,7 @@ func ParseCollection(src []byte) ([]giofont.FontFace, error) { return out, nil } -func DescriptionToFont(md metadata.Description) giofont.Font { +func DescriptionToFont(md fontapi.Description) giofont.Font { return giofont.Font{ Typeface: giofont.Typeface(md.Family), Style: gioStyle(md.Aspect.Style), @@ -84,30 +82,30 @@ func DescriptionToFont(md metadata.Description) giofont.Font { } } -func FontToDescription(font giofont.Font) metadata.Description { - return metadata.Description{ +func FontToDescription(font giofont.Font) fontapi.Description { + return fontapi.Description{ Family: string(font.Typeface), - Aspect: metadata.Aspect{ + Aspect: fontapi.Aspect{ Style: mdStyle(font.Style), Weight: mdWeight(font.Weight), }, } } -// parseLoader parses the contents of the loader into a face and its metadata. -func parseLoader(ld *loader.Loader) (font.Font, giofont.Font, error) { +// parseLoader parses the contents of the loader into a face and its font. +func parseLoader(ld *opentype.Loader) (*fontapi.Font, giofont.Font, error) { ft, err := fontapi.NewFont(ld) if err != nil { return nil, giofont.Font{}, err } - data := DescriptionToFont(metadata.Metadata(ld)) + data := DescriptionToFont(ft.Describe()) return ft, data, nil } // Face returns a thread-unsafe wrapper for this Face suitable for use by a single shaper. // Face many be invoked any number of times and is safe so long as each return value is // only used by one goroutine. -func (f Face) Face() font.Face { +func (f Face) Face() *fontapi.Face { return &fontapi.Face{Font: f.face} } @@ -119,74 +117,74 @@ func (f Face) Font() giofont.Font { return f.font } -func gioStyle(s metadata.Style) giofont.Style { +func gioStyle(s fontapi.Style) giofont.Style { switch s { - case metadata.StyleItalic: + case fontapi.StyleItalic: return giofont.Italic - case metadata.StyleNormal: + case fontapi.StyleNormal: fallthrough default: return giofont.Regular } } -func mdStyle(g giofont.Style) metadata.Style { +func mdStyle(g giofont.Style) fontapi.Style { switch g { case giofont.Italic: - return metadata.StyleItalic + return fontapi.StyleItalic case giofont.Regular: fallthrough default: - return metadata.StyleNormal + return fontapi.StyleNormal } } -func gioWeight(w metadata.Weight) giofont.Weight { +func gioWeight(w fontapi.Weight) giofont.Weight { switch w { - case metadata.WeightThin: + case fontapi.WeightThin: return giofont.Thin - case metadata.WeightExtraLight: + case fontapi.WeightExtraLight: return giofont.ExtraLight - case metadata.WeightLight: + case fontapi.WeightLight: return giofont.Light - case metadata.WeightNormal: + case fontapi.WeightNormal: return giofont.Normal - case metadata.WeightMedium: + case fontapi.WeightMedium: return giofont.Medium - case metadata.WeightSemibold: + case fontapi.WeightSemibold: return giofont.SemiBold - case metadata.WeightBold: + case fontapi.WeightBold: return giofont.Bold - case metadata.WeightExtraBold: + case fontapi.WeightExtraBold: return giofont.ExtraBold - case metadata.WeightBlack: + case fontapi.WeightBlack: return giofont.Black default: return giofont.Normal } } -func mdWeight(g giofont.Weight) metadata.Weight { +func mdWeight(g giofont.Weight) fontapi.Weight { switch g { case giofont.Thin: - return metadata.WeightThin + return fontapi.WeightThin case giofont.ExtraLight: - return metadata.WeightExtraLight + return fontapi.WeightExtraLight case giofont.Light: - return metadata.WeightLight + return fontapi.WeightLight case giofont.Normal: - return metadata.WeightNormal + return fontapi.WeightNormal case giofont.Medium: - return metadata.WeightMedium + return fontapi.WeightMedium case giofont.SemiBold: - return metadata.WeightSemibold + return fontapi.WeightSemibold case giofont.Bold: - return metadata.WeightBold + return fontapi.WeightBold case giofont.ExtraBold: - return metadata.WeightExtraBold + return fontapi.WeightExtraBold case giofont.Black: - return metadata.WeightBlack + return fontapi.WeightBlack default: - return metadata.WeightNormal + return fontapi.WeightNormal } } diff --git a/go.mod b/go.mod index 5a0c412b..5380861c 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.21 require ( eliasnaur.com/font v0.0.0-20230308162249-dd43949cb42d gioui.org/shader v1.0.8 - github.com/go-text/typesetting v0.1.2 + github.com/go-text/typesetting v0.2.1 golang.org/x/exp v0.0.0-20240707233637-46b078467d37 golang.org/x/exp/shiny v0.0.0-20240707233637-46b078467d37 golang.org/x/image v0.18.0 diff --git a/go.sum b/go.sum index 9584c5aa..2a75bdcb 100644 --- a/go.sum +++ b/go.sum @@ -3,10 +3,10 @@ eliasnaur.com/font v0.0.0-20230308162249-dd43949cb42d/go.mod h1:OYVuxibdk9OSLX8v gioui.org/cpu v0.0.0-20210808092351-bfe733dd3334/go.mod h1:A8M0Cn5o+vY5LTMlnRoK3O5kG+rH0kWfJjeKd9QpBmQ= gioui.org/shader v1.0.8 h1:6ks0o/A+b0ne7RzEqRZK5f4Gboz2CfG+mVliciy6+qA= gioui.org/shader v1.0.8/go.mod h1:mWdiME581d/kV7/iEhLmUgUK5iZ09XR5XpduXzbePVM= -github.com/go-text/typesetting v0.1.2 h1:KmZOfoxrrYgghohzXgNY7aQPgQ4W+QeKPeRI8yqpDDE= -github.com/go-text/typesetting v0.1.2/go.mod h1:2+owI/sxa73XA581LAzVuEBZ3WEEV2pXeDswCH/3i1I= -github.com/go-text/typesetting-utils v0.0.0-20240317173224-1986cbe96c66 h1:GUrm65PQPlhFSKjLPGOZNPNxLCybjzjYBzjfoBGaDUY= -github.com/go-text/typesetting-utils v0.0.0-20240317173224-1986cbe96c66/go.mod h1:DDxDdQEnB70R8owOx3LVpEFvpMK9eeH1o2r0yZhFI9o= +github.com/go-text/typesetting v0.2.1 h1:x0jMOGyO3d1qFAPI0j4GSsh7M0Q3Ypjzr4+CEVg82V8= +github.com/go-text/typesetting v0.2.1/go.mod h1:mTOxEwasOFpAMBjEQDhdWRckoLLeI/+qrQeBCTGEt6M= +github.com/go-text/typesetting-utils v0.0.0-20241103174707-87a29e9e6066 h1:qCuYC+94v2xrb1PoS4NIDe7DGYtLnU2wWiQe9a1B1c0= +github.com/go-text/typesetting-utils v0.0.0-20241103174707-87a29e9e6066/go.mod h1:DDxDdQEnB70R8owOx3LVpEFvpMK9eeH1o2r0yZhFI9o= golang.org/x/exp v0.0.0-20240707233637-46b078467d37 h1:uLDX+AfeFCct3a2C7uIWBKMJIR3CJMhcgfrUAqjRK6w= golang.org/x/exp v0.0.0-20240707233637-46b078467d37/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= golang.org/x/exp/shiny v0.0.0-20240707233637-46b078467d37 h1:SOSg7+sueresE4IbmmGM60GmlIys+zNX63d6/J4CMtU= diff --git a/text/gotext.go b/text/gotext.go index adc16da6..14a053b5 100644 --- a/text/gotext.go +++ b/text/gotext.go @@ -12,10 +12,9 @@ import ( "github.com/go-text/typesetting/di" "github.com/go-text/typesetting/font" + gotextot "github.com/go-text/typesetting/font/opentype" "github.com/go-text/typesetting/fontscan" "github.com/go-text/typesetting/language" - "github.com/go-text/typesetting/opentype/api" - "github.com/go-text/typesetting/opentype/api/metadata" "github.com/go-text/typesetting/shaping" "golang.org/x/exp/slices" "golang.org/x/image/math/fixed" @@ -201,7 +200,7 @@ type runLayout struct { // Direction is the layout direction of the glyphs. Direction system.TextDirection // face is the font face that the ID of each Glyph in the Layout refers to. - face font.Face + face *font.Face // truncator indicates that this run is a text truncator standing in for remaining // text. truncator bool @@ -211,8 +210,8 @@ type runLayout struct { type shaperImpl struct { // Fields for tracking fonts/faces. fontMap *fontscan.FontMap - faces []font.Face - faceToIndex map[font.Font]int + faces []*font.Face + faceToIndex map[*font.Font]int faceMeta []giofont.Font defaultFaces []string logger interface { @@ -254,7 +253,7 @@ func newShaperImpl(systemFonts bool, collection []FontFace) *shaperImpl { var shaper shaperImpl shaper.logger = newDebugLogger() shaper.fontMap = fontscan.NewFontMap(shaper.logger) - shaper.faceToIndex = make(map[font.Font]int) + shaper.faceToIndex = make(map[*font.Font]int) if systemFonts { str, err := os.UserCacheDir() if err != nil { @@ -282,7 +281,7 @@ func (s *shaperImpl) Load(f FontFace) { s.addFace(f.Face.Face(), f.Font) } -func (s *shaperImpl) addFace(f font.Face, md giofont.Font) { +func (s *shaperImpl) addFace(f *font.Face, md giofont.Font) { if _, ok := s.faceToIndex[f.Font]; ok { return } @@ -377,11 +376,11 @@ func (s *shaperImpl) splitBidi(input shaping.Input) []shaping.Input { // ResolveFace allows shaperImpl to implement shaping.FontMap, wrapping its fontMap // field and ensuring that any faces loaded as part of the search are registered with // ids so that they can be referred to by a GlyphID. -func (s *shaperImpl) ResolveFace(r rune) font.Face { +func (s *shaperImpl) ResolveFace(r rune) *font.Face { face := s.fontMap.ResolveFace(r) if face != nil { family, aspect := s.fontMap.FontMetadata(face.Font) - md := opentype.DescriptionToFont(metadata.Description{ + md := opentype.DescriptionToFont(font.Description{ Family: family, Aspect: aspect, }) @@ -663,7 +662,7 @@ func (s *shaperImpl) Shape(pathOps *op.Ops, gs []Glyph) clip.PathSpec { scaleFactor := fixedToFloat(ppem) / float32(face.Upem()) glyphData := face.GlyphData(gid) switch glyphData := glyphData.(type) { - case api.GlyphOutline: + case font.GlyphOutline: outline := glyphData // Move to glyph position. pos := f32.Point{ @@ -678,9 +677,9 @@ func (s *shaperImpl) Shape(pathOps *op.Ops, gs []Glyph) clip.PathSpec { for _, fseg := range outline.Segments { nargs := 1 switch fseg.Op { - case api.SegmentOpQuadTo: + case gotextot.SegmentOpQuadTo: nargs = 2 - case api.SegmentOpCubeTo: + case gotextot.SegmentOpCubeTo: nargs = 3 } var args [3]f32.Point @@ -695,13 +694,13 @@ func (s *shaperImpl) Shape(pathOps *op.Ops, gs []Glyph) clip.PathSpec { } } switch fseg.Op { - case api.SegmentOpMoveTo: + case gotextot.SegmentOpMoveTo: builder.Move(args[0]) - case api.SegmentOpLineTo: + case gotextot.SegmentOpLineTo: builder.Line(args[0]) - case api.SegmentOpQuadTo: + case gotextot.SegmentOpQuadTo: builder.Quad(args[0], args[1]) - case api.SegmentOpCubeTo: + case gotextot.SegmentOpCubeTo: builder.Cube(args[0], args[1], args[2]) default: panic("unsupported segment op") @@ -742,16 +741,16 @@ func (s *shaperImpl) Bitmaps(ops *op.Ops, gs []Glyph) op.CallOp { } glyphData := face.GlyphData(gid) switch glyphData := glyphData.(type) { - case api.GlyphBitmap: + case font.GlyphBitmap: var imgOp paint.ImageOp var imgSize image.Point bitmapData, ok := s.bitmapGlyphCache.Get(g.ID) if !ok { var img image.Image switch glyphData.Format { - case api.PNG, api.JPG, api.TIFF: + case font.PNG, font.JPG, font.TIFF: img, _, _ = image.Decode(bytes.NewReader(glyphData.Data)) - case api.BlackAndWhite: + case font.BlackAndWhite: // This is a complex family of uncompressed bitmaps that don't seem to be // very common in practice. We can try adding support later if needed. fallthrough @@ -807,7 +806,7 @@ type langConfig struct { } // toInput converts its parameters into a shaping.Input. -func toInput(face font.Face, ppem fixed.Int26_6, lc langConfig, runes []rune) shaping.Input { +func toInput(face *font.Face, ppem fixed.Int26_6, lc langConfig, runes []rune) shaping.Input { var input shaping.Input input.Direction = lc.Direction input.Text = runes @@ -867,7 +866,7 @@ func toGioGlyphs(in []shaping.Glyph, ppem fixed.Int26_6, faceIdx int) []glyph { } // toLine converts the output into a Line with the provided dominant text direction. -func toLine(faceToIndex map[font.Font]int, o shaping.Line, dir system.TextDirection) line { +func toLine(faceToIndex map[*font.Font]int, o shaping.Line, dir system.TextDirection) line { if len(o) < 1 { return line{} } @@ -881,7 +880,7 @@ func toLine(faceToIndex map[font.Font]int, o shaping.Line, dir system.TextDirect if run.Size > maxSize { maxSize = run.Size } - var font font.Font + var font *font.Font if run.Face != nil { font = run.Face.Font }