Skip to content

Commit

Permalink
examples/texti18n: WIP: bug fix: rotate some glyphs for vertical textts
Browse files Browse the repository at this point in the history
Closes #2849
  • Loading branch information
hajimehoshi committed Nov 23, 2023
1 parent b9b365a commit 47909f8
Show file tree
Hide file tree
Showing 6 changed files with 764 additions and 0 deletions.
6 changes: 6 additions & 0 deletions examples/texti18n/LICENSE.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@ Open Font License 1.1

https://fonts.google.com/noto/specimen/Noto+Sans+JP/about

# `NotoSansMongolian-Regular.ttf`

Open Font License 1.1

https://fonts.google.com/noto/specimen/Noto+Sans+Mongolian/about

# `NotoSansThai-Regular.ttf`

Open Font License 1.1
Expand Down
Binary file added examples/texti18n/NotoSansMongolian-Regular.ttf
Binary file not shown.
32 changes: 32 additions & 0 deletions examples/texti18n/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,19 @@ func init() {
thaiFaceSource = s
}

//go:embed NotoSansMongolian-Regular.ttf
var mongolianTTF []byte

var mongolianFaceSource *text.GoTextFaceSource

func init() {
s, err := text.NewGoTextFaceSource(bytes.NewReader(mongolianTTF))
if err != nil {
log.Fatal(err)
}
mongolianFaceSource = s
}

var japaneseFaceSource *text.GoTextFaceSource

func init() {
Expand Down Expand Up @@ -139,6 +152,25 @@ func (g *Game) Draw(screen *ebiten.Image) {
op.GeoM.Translate(float64(x), float64(y))
text.Draw(screen, thaiText, f, op)
}
{
const mongolianText = "ᠬᠦᠮᠦᠨ ᠪᠦᠷ ᠲᠥᠷᠥᠵᠦ ᠮᠡᠨᠳᠡᠯᠡᠬᠦ\nᠡᠷᠬᠡ ᠴᠢᠯᠥᠭᠡ ᠲᠡᠢ᠂ ᠠᠳᠠᠯᠢᠬᠠᠨ"
f := &text.GoTextFace{
Source: mongolianFaceSource,
Direction: text.DirectionTopToBottomAndLeftToRight,
Size: 24,
Language: language.Mongolian,
// language.Mongolian.Script() returns "Cyrl" (Cyrillic), but we want Mongolian script here.
Script: language.MustParseScript("Mong"),
}
const lineSpacing = 48
x, y := 20, 210
w, h := text.Measure(mongolianText, f, lineSpacing)
vector.DrawFilledRect(screen, float32(x), float32(y), float32(w), float32(h), gray, false)
op := &text.DrawOptions{}
op.GeoM.Translate(float64(x), float64(y))
op.LineSpacingInPixels = lineSpacing
text.Draw(screen, mongolianText, f, op)
}
{
const japaneseText = "あのイーハトーヴォの\nすきとおった風、\n夏でも底に冷たさを\nもつ青いそら…"
f := &text.GoTextFace{
Expand Down
179 changes: 179 additions & 0 deletions text/v2/internal/vo/gen.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
// 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.

//go:build ignore

package main

import (
"bufio"
"errors"
"fmt"
"io"
"net/http"
"os"
"regexp"
)

func main() {
if err := run(); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}

type rangeAndValue struct {
start rune
end rune
value string
}

func run() error {
f, err := os.Create("table.go")
if err != nil {
return err
}
defer f.Close()

w := bufio.NewWriter(f)
defer w.Flush()

if _, err := fmt.Fprintf(w, `// 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.
// Code generated by gen.go using 'go generate'. DO NOT EDIT.
package vo
func get(r rune) Value {
switch {
`); err != nil {
return err
}

if err := forEachRange(func(rv rangeAndValue) error {
if rv.start != rv.end {
if _, err := fmt.Fprintf(w, ` case 0x%x <= r && r <= 0x%x:
return %s
`, rv.start, rv.end, rv.value); err != nil {
return err
}
} else {
if _, err := fmt.Fprintf(w, ` case r == 0x%x:
return %s
`, rv.start, rv.value); err != nil {
return err
}
}
return nil
}); err != nil {
return err
}

if _, err := fmt.Fprintf(w, ` }
return U
}
`); err != nil {
return err
}

return nil
}

func forEachRange(f func(rv rangeAndValue) error) error {
const url = `https://www.unicode.org/Public/vertical/revision-17/VerticalOrientation-17.txt`

resp, err := http.Get(url)
if err != nil {
return err
}
defer resp.Body.Close()

reRange := regexp.MustCompile(`^([0-9a-fA-F]{4,})(..([0-9a-fA-F]{4,}))? ; (U|R|Tu|Tr)$`)

var lastRV rangeAndValue
r := bufio.NewReader(resp.Body)
for {
line, err := r.ReadString('\n')
if line != "" {
// Remove the tail '\n'.
line = line[:len(line)-1]
if m := reRange.FindStringSubmatch(line); m != nil {
start := decodeHexToRune(m[1])
end := start
if m[3] != "" {
end = decodeHexToRune(m[3])
}
value := m[4]
if lastRV.value == value {
lastRV.end = end
} else {
if lastRV.value != "" {
if err := f(lastRV); err != nil {
return err
}
}
lastRV = rangeAndValue{
start: start,
end: end,
value: value,
}
}
}
}
if errors.Is(err, io.EOF) {
break
}
if err != nil {
return err
}
}

if err := f(lastRV); err != nil {
return err
}

return nil
}

func decodeHexToRune(str string) rune {
var r rune
for len(str) > 0 {
r *= 16
c := str[0]
str = str[1:]
switch {
case '0' <= c && c <= '9':
r += rune(c - '0')
case 'A' <= c && c <= 'F':
r += rune(c - 'A' + 10)
case 'a' <= c && c <= 'f':
r += rune(c - 'a' + 10)
default:
panic("not reached")
}
}
return r
}
Loading

0 comments on commit 47909f8

Please sign in to comment.