やってみる

アウトプットすべく己を導くためのブログ。その試行錯誤すらたれ流す。

OpenGLで日本語を描画する(FTGL)

 なんとOpenGLでは不可能。別のライブラリFTGLを使う。

成果物

demo2 demo

調査

 どうやらFTGLというのがそれっぽい。Pythonバインディングも発見。

 FTGLはfreetypeというライブラリを使ったものっぽい。

インストール

freetype

 freetypeはすでにインストール済みだった。

$ apt search freetype
libfreetype6/stable,stable,now 2.9.1-3+deb10u1 armhf [インストール済み、自動]
  FreeType 2 font engine, shared library files

libfreetype6-dev/stable,stable,now 2.9.1-3+deb10u1 armhf [インストール済み]
  FreeType 2 font engine, development files

FTGL

 FTGLは以下の名前っぽい。

$ apt search ftgl
ソート中... 完了
全文検索... 完了  
libftgl-dev/stable,stable 2.4.0-2 armhf
  development files for libftgl

libftgl2/stable,stable 2.4.0-2 armhf
  library to render text in OpenGL using FreeType
sudo apt -y install libftgl-dev

python-FTGL

 pipでftglを調査するとPyFTGLというのがあった。

$ pip search ftgl
sippy-ftgl (0.1.0)  - Python binding for FTGL using SIP
PyFTGL (0.4b)       - Python FTGL binding
$ pip3 search ftgl
sippy-ftgl (0.1.0)  - Python binding for FTGL using SIP
PyFTGL (0.4b)       - Python FTGL binding
pip install PyFTGL
pip3 install PyFTGL
ERROR: Could not find a version that satisfies the requirement PyFTGL (from versions: none)
ERROR: No matching distribution found for PyFTGL
WARNING: You are using pip version 19.3.1; however, version 20.0.2 is available.
You should consider upgrading via the 'pip install --upgrade pip' command.

 インストールできなかった……。

 GitHubで見つけたヤツを使ってみる。

git clone https://github.com/mugwort-rc/python-ftgl
cd python-ftgl
python setup.py build
python3 setup.py build

 ビルドはエラー無く終了。成功したっぽい。

 公式サイトには書いてなかったが、以下のようにインストールする必要がある。

sudo python setup.py install
sudo python3 setup.py install

フォント

 使うフォントのパスを探す。

fc-list
$ fc-list | grep vl
/usr/share/fonts/truetype/vlgothic/VL-PGothic-Regular.ttf: VL Pゴシック,VL PGothic:style=regular
/usr/share/fonts/truetype/vlgothic/VL-Gothic-Regular.ttf: VL ゴシック,VL Gothic:style=regular

コード

main.py

import ftgl
font = ftgl.FTGLPixmapFont("/usr/share/fonts/truetype/vlgothic/VL-Gothic-Regular.ttf")
font.FaceSize(72)
font.Render("Hello World!")
python main.py
python3 main.py
Traceback (most recent call last):
  File "main.py_", line 8, in <module>
    font = ftgl.FTGLPixmapFont("Arial.ttf")
AttributeError: 'module' object has no attribute 'FTGLPixmapFont'

 FTGLPixmapFontがないだと?

 APIを確認しよう。

import ftgl
print(dir(ftgl))
['ALIGN_CENTER', 'ALIGN_FRONT', 'ALIGN_JUSTIFY', 'ALIGN_RIGHT', 'FTBBox', 'FTBitmapFont', 'FTBitmapGlyph', 'FTBuffer', 'FTBufferFont', 'FTBufferGlyph', 'FTExtrudeFont', 'FTExtrudeGlyph', 'FTFont', 'FTGlyph', 'FTLayout', 'FTOutlineFont', 'FTOutlineGlyph', 'FTPixmapFont', 'FTPixmapGlyph', 'FTPoint', 'FTPolygonFont', 'FTPolygonGlyph', 'FTSimpleLayout', 'FTTextureFont', 'FTTextureGlyph', 'RENDER_ALL', 'RENDER_BACK', 'RENDER_FRONT', 'RENDER_SIDE', 'RenderMode', 'TextAlignment', '__builtins__', '__doc__', '__file__', '__ftgl', '__name__', '__package__', '__path__']

 いや、名前ちゃうやん。公式に書いてあることはメチャクチャだ。ReadMeに騙された。もうお前なんて読まない。

import ftgl
print(dir(ftgl))
font = ftgl.FTGLPixmapFont("Arial.ttf")
#font = ftgl.FTPixmapFont("/usr/share/fonts/truetype/vlgothic/VL-Gothic-Regular.ttf")
font.FaceSize(72)
font.Render("Hello World!")

 いや、名前ちゃうやん。公式に書いてあることはメチャクチャすぎ。ReadMeに騙された。もうお前なんて読まない。

python main.py

 エラーは出ない。けどなにも表示されない。OpenGLで表示するよう処理せねばならないのだろう。

 ぐぐってみた。

 なんか類似だけど別のリポジトリ。こいつのコードをパクって修正する。

example2.py

#!/usr/bin/python
# -*- coding: utf-8 -*-
import sys

from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.GLUT import *

import ftgl
#import ftgl

fonts = []
width = 600
height = 600

def do_ortho():
    w, h = width, height
    glViewport(0, 0, w, h)
    glMatrixMode(GL_PROJECTION)
    glLoadIdentity()
    size = max(w, h) / 2.0
    aspect = float(w) / float(h)
    if w <= h:
        aspect = float(h) / float(w)
        glOrtho(-size, size, -size*aspect, size*aspect, -100000.0, 100000.0)
    else:
        glOrtho(-size*aspect, size*aspect, -size, size, -100000.0, 100000.0)
    glScaled(aspect, aspect, 1.0)
    glMatrixMode(GL_MODELVIEW)
    glLoadIdentity()

def draw_scene():
    string = []
    chars = []
    
    string.append(u'日本語も描画できる!')

    chars[:] = []
    for i in range(1, 32):
        chars.append(chr(i))
    string.append("".join(chars))

    chars[:] = []
    for i in range(32, 64):
        chars.append(chr(i))
    string.append("".join(chars))

    chars[:] = []
    for i in range(64, 96):
        chars.append(chr(i))
    string.append("".join(chars))

    chars[:] = []
    for i in range(96, 128):
        chars.append(chr(i))
    string.append("".join(chars))

    chars[:] = []
    for i in range(128, 160):
        chars.append(chr(i))
    string.append("".join(chars))

    chars[:] = []
    for i in range(160, 192):
        chars.append(chr(i))
    string.append("".join(chars))

    chars[:] = []
    for i in range(192, 224):
        chars.append(chr(i))
    string.append("".join(chars))

    chars[:] = []
    for i in range(224, 256):
        chars.append(chr(i))
    string.append("".join(chars))
    

    glColor3f(1.0, 1.0, 1.0)

    for i, font in enumerate(fonts):
        x = -250.0
        yild = 20.0
        for j in range(0, 4):
            y = 275.0 - i * 120.0 - j * yild
            if i >= 3:
                glRasterPos(x, y)
                font.Render(string[j])
            elif i == 2:
                glEnable(GL_TEXTURE_2D)
                glEnable(GL_BLEND)
                glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
                glPushMatrix()
                glTranslatef(x, y, 0.0)
                font.Render(string[j])
                glPopMatrix()
                glDisable(GL_TEXTURE_2D)
                glDisable(GL_BLEND)
            else:
                glPushMatrix()
                glTranslatef(x, y, 0.0)
                font.Render(string[j])
                glPopMatrix()
            
  
def on_display():
    glClear(GL_COLOR_BUFFER_BIT)
    do_ortho()
    draw_scene()
    glutSwapBuffers()

def on_reshape(w, h):
    width, height = w, h

def on_key(key, x, y):
    if key == '\x1b':
        sys.exit(1)

if __name__ == '__main__':
    glutInitWindowSize(width, height)
    glutInit(sys.argv)
    glutInitDisplayMode(GLUT_RGBA|GLUT_DOUBLE)
    glutCreateWindow("Pyftgl Demo")
    glClearColor(0.0, 0.0, 0.0, 0.0)

    try:
        fonts = [
#            ftgl.OutlineFont(sys.argv[1]),
#            ftgl.PolygonFont(sys.argv[1]),
#            ftgl.TextureFont(sys.argv[1]),
#            ftgl.BitmapFont(sys.argv[1]),
#            ftgl.PixmapFont(sys.argv[1]),
            ftgl.FTOutlineFont(sys.argv[1]),
            ftgl.FTPolygonFont(sys.argv[1]),
            ftgl.FTTextureFont(sys.argv[1]),
            ftgl.FTBitmapFont(sys.argv[1]),
            ftgl.FTPixmapFont(sys.argv[1]),
            ]
        for font in fonts:
            font.FaceSize(24, 72)
    except:
        import traceback
        traceback.print_exc()
        print("usage:", sys.argv[0], "font_filename.ttf")
#        print "usage:", sys.argv[0], "font_filename.ttf"
        sys.exit(0)

    glutDisplayFunc(on_display)
    glutReshapeFunc(on_reshape)
    glutKeyboardUpFunc(on_key)

    glutMainLoop()

実行

python example2.py /usr/share/fonts/truetype/vlgothic/VL-Gothic-Regular.ttf

 第1引数は使いたいフォントパス。

demo2

所感

 疲れた……。なんでこんなツギハギせにゃならんの? 文字くらい書けるようにしてくれや。

対象環境

$ uname -a
Linux raspberrypi 4.19.97-v7l+ #1294 SMP Thu Jan 30 13:21:14 GMT 2020 armv7l GNU/Linux