main.go 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286
  1. // Copyright 2023 The Ebitengine Authors
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package main
  15. import (
  16. "bytes"
  17. _ "embed"
  18. "image/color"
  19. "log"
  20. "golang.org/x/text/language"
  21. "github.com/hajimehoshi/ebiten/v2"
  22. "github.com/hajimehoshi/ebiten/v2/ebitenutil"
  23. "github.com/hajimehoshi/ebiten/v2/examples/resources/fonts"
  24. "github.com/hajimehoshi/ebiten/v2/inpututil"
  25. "github.com/hajimehoshi/ebiten/v2/text/v2"
  26. "github.com/hajimehoshi/ebiten/v2/vector"
  27. )
  28. //go:embed NotoSansArabic-Regular.ttf
  29. var arabicTTF []byte
  30. var arabicFaceSource *text.GoTextFaceSource
  31. func init() {
  32. s, err := text.NewGoTextFaceSource(bytes.NewReader(arabicTTF))
  33. if err != nil {
  34. log.Fatal(err)
  35. }
  36. arabicFaceSource = s
  37. }
  38. //go:embed NotoSansDevanagari-Regular.ttf
  39. var devanagariTTF []byte
  40. var devanagariFaceSource *text.GoTextFaceSource
  41. func init() {
  42. s, err := text.NewGoTextFaceSource(bytes.NewReader(devanagariTTF))
  43. if err != nil {
  44. log.Fatal(err)
  45. }
  46. devanagariFaceSource = s
  47. }
  48. //go:embed NotoSansThai-Regular.ttf
  49. var thaiTTF []byte
  50. var thaiFaceSource *text.GoTextFaceSource
  51. func init() {
  52. s, err := text.NewGoTextFaceSource(bytes.NewReader(thaiTTF))
  53. if err != nil {
  54. log.Fatal(err)
  55. }
  56. thaiFaceSource = s
  57. }
  58. //go:embed NotoSansMyanmar-Regular.ttf
  59. var myanmarTTF []byte
  60. var myanmarFaceSource *text.GoTextFaceSource
  61. func init() {
  62. s, err := text.NewGoTextFaceSource(bytes.NewReader(myanmarTTF))
  63. if err != nil {
  64. log.Fatal(err)
  65. }
  66. myanmarFaceSource = s
  67. }
  68. //go:embed NotoSansMongolian-Regular.ttf
  69. var mongolianTTF []byte
  70. var mongolianFaceSource *text.GoTextFaceSource
  71. func init() {
  72. s, err := text.NewGoTextFaceSource(bytes.NewReader(mongolianTTF))
  73. if err != nil {
  74. log.Fatal(err)
  75. }
  76. mongolianFaceSource = s
  77. }
  78. var japaneseFaceSource *text.GoTextFaceSource
  79. func init() {
  80. s, err := text.NewGoTextFaceSource(bytes.NewReader(fonts.MPlus1pRegular_ttf))
  81. if err != nil {
  82. log.Fatal(err)
  83. }
  84. japaneseFaceSource = s
  85. }
  86. const (
  87. screenWidth = 640
  88. screenHeight = 640
  89. )
  90. type Game struct {
  91. showOrigins bool
  92. }
  93. func (g *Game) Update() error {
  94. if inpututil.IsKeyJustPressed(ebiten.KeyO) {
  95. g.showOrigins = !g.showOrigins
  96. }
  97. return nil
  98. }
  99. func (g *Game) Draw(screen *ebiten.Image) {
  100. ebitenutil.DebugPrint(screen, "Press O to show/hide origins.\nRed points are the original origin positions.\nThe green points are the origin positions after applying the offset.")
  101. gray := color.RGBA{0x80, 0x80, 0x80, 0xff}
  102. {
  103. const arabicText = "لمّا كان الاعتراف بالكرامة المتأصلة في جميع"
  104. f := &text.GoTextFace{
  105. Source: arabicFaceSource,
  106. Direction: text.DirectionRightToLeft,
  107. Size: 24,
  108. Language: language.Arabic,
  109. }
  110. x, y := screenWidth-20, 50
  111. w, h := text.Measure(arabicText, f, 0)
  112. // The left upper point is not x but x-w, since the text runs in the rigth-to-left direction.
  113. vector.DrawFilledRect(screen, float32(x)-float32(w), float32(y), float32(w), float32(h), gray, false)
  114. op := &text.DrawOptions{}
  115. op.GeoM.Translate(float64(x), float64(y))
  116. text.Draw(screen, arabicText, f, op)
  117. if g.showOrigins {
  118. op := &text.LayoutOptions{}
  119. for _, g := range text.AppendGlyphs(nil, arabicText, f, op) {
  120. vector.DrawFilledCircle(screen, float32(x)+float32(g.OriginX+g.OriginOffsetX), float32(y)+float32(g.OriginY+g.OriginOffsetY), 2, color.RGBA{0, 0xff, 0, 0xff}, true)
  121. vector.DrawFilledCircle(screen, float32(x)+float32(g.OriginX), float32(y)+float32(g.OriginY), 2, color.RGBA{0xff, 0, 0, 0xff}, true)
  122. }
  123. }
  124. }
  125. {
  126. const hindiText = "चूंकि मानव परिवार के सभी सदस्यों के जन्मजात गौरव और समान"
  127. f := &text.GoTextFace{
  128. Source: devanagariFaceSource,
  129. Size: 24,
  130. Language: language.Hindi,
  131. }
  132. x, y := 20, 110
  133. w, h := text.Measure(hindiText, f, 0)
  134. vector.DrawFilledRect(screen, float32(x), float32(y), float32(w), float32(h), gray, false)
  135. op := &text.DrawOptions{}
  136. op.GeoM.Translate(float64(x), float64(y))
  137. text.Draw(screen, hindiText, f, op)
  138. if g.showOrigins {
  139. op := &text.LayoutOptions{}
  140. for _, g := range text.AppendGlyphs(nil, hindiText, f, op) {
  141. vector.DrawFilledCircle(screen, float32(x)+float32(g.OriginX+g.OriginOffsetX), float32(y)+float32(g.OriginY+g.OriginOffsetY), 2, color.RGBA{0, 0xff, 0, 0xff}, true)
  142. vector.DrawFilledCircle(screen, float32(x)+float32(g.OriginX), float32(y)+float32(g.OriginY), 2, color.RGBA{0xff, 0, 0, 0xff}, true)
  143. }
  144. }
  145. }
  146. {
  147. const myanmarText = "လူခပ်သိမ်း၏ မျိုးရိုးဂုဏ်သိက္ခာနှင့်တကွ"
  148. f := &text.GoTextFace{
  149. Source: myanmarFaceSource,
  150. Size: 24,
  151. Language: language.Burmese,
  152. }
  153. x, y := 20, 170
  154. w, h := text.Measure(myanmarText, f, 0)
  155. vector.DrawFilledRect(screen, float32(x), float32(y), float32(w), float32(h), gray, false)
  156. op := &text.DrawOptions{}
  157. op.GeoM.Translate(float64(x), float64(y))
  158. text.Draw(screen, myanmarText, f, op)
  159. if g.showOrigins {
  160. op := &text.LayoutOptions{}
  161. for _, g := range text.AppendGlyphs(nil, myanmarText, f, op) {
  162. vector.DrawFilledCircle(screen, float32(x)+float32(g.OriginX+g.OriginOffsetX), float32(y)+float32(g.OriginY+g.OriginOffsetY), 2, color.RGBA{0, 0xff, 0, 0xff}, true)
  163. vector.DrawFilledCircle(screen, float32(x)+float32(g.OriginX), float32(y)+float32(g.OriginY), 2, color.RGBA{0xff, 0, 0, 0xff}, true)
  164. }
  165. }
  166. }
  167. {
  168. const thaiText = "โดยที่การยอมรับนับถือเกียรติศักดิ์ประจำตัว"
  169. f := &text.GoTextFace{
  170. Source: thaiFaceSource,
  171. Size: 24,
  172. Language: language.Thai,
  173. }
  174. x, y := 20, 230
  175. w, h := text.Measure(thaiText, f, 0)
  176. vector.DrawFilledRect(screen, float32(x), float32(y), float32(w), float32(h), gray, false)
  177. op := &text.DrawOptions{}
  178. op.GeoM.Translate(float64(x), float64(y))
  179. text.Draw(screen, thaiText, f, op)
  180. if g.showOrigins {
  181. op := &text.LayoutOptions{}
  182. for _, g := range text.AppendGlyphs(nil, thaiText, f, op) {
  183. vector.DrawFilledCircle(screen, float32(x)+float32(g.OriginX+g.OriginOffsetX), float32(y)+float32(g.OriginY+g.OriginOffsetY), 2, color.RGBA{0, 0xff, 0, 0xff}, true)
  184. vector.DrawFilledCircle(screen, float32(x)+float32(g.OriginX), float32(y)+float32(g.OriginY), 2, color.RGBA{0xff, 0, 0, 0xff}, true)
  185. }
  186. }
  187. }
  188. {
  189. const mongolianText = "ᠬᠦᠮᠦᠨ ᠪᠦᠷ ᠲᠥᠷᠥᠵᠦ ᠮᠡᠨᠳᠡᠯᠡᠬᠦ\nᠡᠷᠬᠡ ᠴᠢᠯᠥᠭᠡ ᠲᠡᠢ᠂ ᠠᠳᠠᠯᠢᠬᠠᠨ"
  190. f := &text.GoTextFace{
  191. Source: mongolianFaceSource,
  192. Direction: text.DirectionTopToBottomAndLeftToRight,
  193. Size: 24,
  194. Language: language.Mongolian,
  195. // language.Mongolian.Script() returns "Cyrl" (Cyrillic), but we want Mongolian script here.
  196. Script: language.MustParseScript("Mong"),
  197. }
  198. const lineSpacing = 48
  199. x, y := 20, 290
  200. w, h := text.Measure(mongolianText, f, lineSpacing)
  201. vector.DrawFilledRect(screen, float32(x), float32(y), float32(w), float32(h), gray, false)
  202. op := &text.DrawOptions{}
  203. op.GeoM.Translate(float64(x), float64(y))
  204. op.LineSpacing = lineSpacing
  205. text.Draw(screen, mongolianText, f, op)
  206. if g.showOrigins {
  207. op := &text.LayoutOptions{}
  208. op.LineSpacing = lineSpacing
  209. for _, g := range text.AppendGlyphs(nil, mongolianText, f, op) {
  210. vector.DrawFilledCircle(screen, float32(x)+float32(g.OriginX+g.OriginOffsetX), float32(y)+float32(g.OriginY+g.OriginOffsetY), 2, color.RGBA{0, 0xff, 0, 0xff}, true)
  211. vector.DrawFilledCircle(screen, float32(x)+float32(g.OriginX), float32(y)+float32(g.OriginY), 2, color.RGBA{0xff, 0, 0, 0xff}, true)
  212. }
  213. }
  214. }
  215. {
  216. const japaneseText = "あのイーハトーヴォの\nすきとおった風、\n夏でも底に冷たさを\nもつ青いそら…\nあHello World.あ"
  217. f := &text.GoTextFace{
  218. Source: japaneseFaceSource,
  219. Direction: text.DirectionTopToBottomAndRightToLeft,
  220. Size: 24,
  221. Language: language.Japanese,
  222. }
  223. const lineSpacing = 48
  224. x, y := screenWidth-20, 290
  225. w, h := text.Measure(japaneseText, f, lineSpacing)
  226. // The left upper point is not x but x-w, since the text runs in the rigth-to-left direction as the secondary direction.
  227. vector.DrawFilledRect(screen, float32(x)-float32(w), float32(y), float32(w), float32(h), gray, false)
  228. op := &text.DrawOptions{}
  229. op.GeoM.Translate(float64(x), float64(y))
  230. op.LineSpacing = lineSpacing
  231. text.Draw(screen, japaneseText, f, op)
  232. if g.showOrigins {
  233. op := &text.LayoutOptions{}
  234. op.LineSpacing = lineSpacing
  235. for _, g := range text.AppendGlyphs(nil, japaneseText, f, op) {
  236. vector.DrawFilledCircle(screen, float32(x)+float32(g.OriginX), float32(y)+float32(g.OriginY), 2, color.RGBA{0xff, 0, 0, 0xff}, true)
  237. vector.DrawFilledCircle(screen, float32(x)+float32(g.OriginX+g.OriginOffsetX), float32(y)+float32(g.OriginY+g.OriginOffsetY), 2, color.RGBA{0, 0xff, 0, 0xff}, true)
  238. }
  239. }
  240. }
  241. }
  242. func (g *Game) Layout(outsideWidth, outsideHeight int) (int, int) {
  243. return screenWidth, screenHeight
  244. }
  245. func main() {
  246. ebiten.SetWindowSize(screenWidth, screenHeight)
  247. ebiten.SetWindowTitle("Text I18N (Ebitengine Demo)")
  248. if err := ebiten.RunGame(&Game{}); err != nil {
  249. log.Fatal(err)
  250. }
  251. }