123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198 |
- // Copyright 2023 The Ebitengine Authors
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- package text
- import (
- "errors"
- "unicode/utf8"
- "github.com/hajimehoshi/ebiten/v2/vector"
- )
- var _ Face = (*MultiFace)(nil)
- // MultiFace is a Face that consists of multiple Face objects.
- // The face in the first index is used in the highest priority, and the last the lowest priority.
- //
- // There is a known issue: if the writing directions of the faces don't agree, the rendering result might be messed up.
- type MultiFace struct {
- faces []Face
- }
- // NewMultiFace creates a new MultiFace from the given faces.
- //
- // NewMultiFace returns an error when no faces are given, or the faces' directions don't agree.
- func NewMultiFace(faces ...Face) (*MultiFace, error) {
- if len(faces) == 0 {
- return nil, errors.New("text: no faces are given at NewMultiFace")
- }
- d := faces[0].direction()
- for _, f := range faces[1:] {
- if f.direction() != d {
- return nil, errors.New("text: all the faces' directions must agree at NewMultiFace")
- }
- }
- m := &MultiFace{}
- m.faces = make([]Face, len(faces))
- copy(m.faces, faces)
- return m, nil
- }
- // Metrics implements Face.
- func (m *MultiFace) Metrics() Metrics {
- var mt Metrics
- for _, f := range m.faces {
- mt1 := f.Metrics()
- if mt1.HLineGap > mt.HLineGap {
- mt.HLineGap = mt1.HLineGap
- }
- if mt1.HAscent > mt.HAscent {
- mt.HAscent = mt1.HAscent
- }
- if mt1.HDescent > mt.HDescent {
- mt.HDescent = mt1.HDescent
- }
- if mt1.VLineGap > mt.VLineGap {
- mt.VLineGap = mt1.VLineGap
- }
- if mt1.VAscent > mt.VAscent {
- mt.VAscent = mt1.VAscent
- }
- if mt1.VDescent > mt.VDescent {
- mt.VDescent = mt1.VDescent
- }
- if mt1.XHeight > mt.XHeight {
- mt.XHeight = mt1.XHeight
- }
- if mt1.CapHeight > mt.CapHeight {
- mt.CapHeight = mt1.CapHeight
- }
- }
- return mt
- }
- // advance implements Face.
- func (m *MultiFace) advance(text string) float64 {
- var a float64
- for _, c := range m.splitText(text) {
- if c.faceIndex == -1 {
- continue
- }
- f := m.faces[c.faceIndex]
- a += f.advance(text[c.textStartIndex:c.textEndIndex])
- }
- return a
- }
- // hasGlyph implements Face.
- func (m *MultiFace) hasGlyph(r rune) bool {
- for _, f := range m.faces {
- if f.hasGlyph(r) {
- return true
- }
- }
- return false
- }
- // appendGlyphsForLine implements Face.
- func (m *MultiFace) appendGlyphsForLine(glyphs []Glyph, line string, indexOffset int, originX, originY float64) []Glyph {
- for _, c := range m.splitText(line) {
- if c.faceIndex == -1 {
- continue
- }
- f := m.faces[c.faceIndex]
- t := line[c.textStartIndex:c.textEndIndex]
- glyphs = f.appendGlyphsForLine(glyphs, t, indexOffset, originX, originY)
- if a := f.advance(t); f.direction().isHorizontal() {
- originX += a
- } else {
- originY += a
- }
- indexOffset += len(t)
- }
- return glyphs
- }
- // appendVectorPathForLine implements Face.
- func (m *MultiFace) appendVectorPathForLine(path *vector.Path, line string, originX, originY float64) {
- for _, c := range m.splitText(line) {
- if c.faceIndex == -1 {
- continue
- }
- f := m.faces[c.faceIndex]
- t := line[c.textStartIndex:c.textEndIndex]
- f.appendVectorPathForLine(path, t, originX, originY)
- if a := f.advance(t); f.direction().isHorizontal() {
- originX += a
- } else {
- originY += a
- }
- }
- }
- // direction implements Face.
- func (m *MultiFace) direction() Direction {
- if len(m.faces) == 0 {
- return DirectionLeftToRight
- }
- return m.faces[0].direction()
- }
- // private implements Face.
- func (m *MultiFace) private() {
- }
- type textChunk struct {
- textStartIndex int
- textEndIndex int
- faceIndex int
- }
- func (m *MultiFace) splitText(text string) []textChunk {
- var chunks []textChunk
- for ri, r := range text {
- fi := -1
- _, l := utf8.DecodeRuneInString(text[ri:])
- for i, f := range m.faces {
- if !f.hasGlyph(r) && i < len(m.faces)-1 {
- continue
- }
- fi = i
- break
- }
- if fi == -1 {
- panic("text: a face was not selected correctly")
- }
- var s int
- if len(chunks) > 0 {
- if chunks[len(chunks)-1].faceIndex == fi {
- chunks[len(chunks)-1].textEndIndex += l
- continue
- }
- s = chunks[len(chunks)-1].textEndIndex
- }
- chunks = append(chunks, textChunk{
- textStartIndex: s,
- textEndIndex: s + l,
- faceIndex: fi,
- })
- }
- return chunks
- }
|