Pygameのデフォルト フォントでは、日本語は表示できない
これは、有名な事実の様です。
次のコードで確認しました。
import pygame
from pygame.locals import *
pygame.init()
screen = pygame.display.set_mode((320, 180))
font = pygame.font.SysFont(None, 80) #⭐️
surface = font.render("文字化け必至", True, 'white')
done = False
while not done:
screen.fill('black')
screen.blit(surface, (10,10))
pygame.display.update()
for event in pygame.event.get():
if event.type == QUIT:
done = True
if event.type == KEYDOWN:
if event.key == K_ESCAPE or event.key == K_q:
done = True
pygame.quit()
ウインドウバーの『閉じる』、ESCキー か Qキーで終了。
日本語が表示できるフォント名を調べる
前段として、
フォント名の一覧を得る
次の関数で、Pygameで扱えるフォント名のリストが取得できます。
pygame.font.get_fonts()
ただし、順不同のため、以降はフォント名をソートしています。
import pygame
from pygame.locals import *
pygame.init()
for index, fontname in enumerate(sorted(pygame.font.get_fonts())):
try:
font = pygame.font.SysFont(fontname, 30)
print(f"{index+1}\t{fontname}")
except:
print(f"{index+1}\t{fontname} is not available!!")
pygame.quit()
pygame.font.SysFont
できないフォントがあったため、try/exceptした。
exceptionの内容から日本語を含むパス名のフォントが扱えない可能性があると想定される。しかし、pygame.font.Font
でそのフルパスを指定したが例外は発生しなかった。ナゾだ。
$ python b.py
pygame 2.5.2 (SDL 2.28.3, Python 3.11.9)
Hello from the pygame community. https://www.pygame.org/contribute.html
1
2 academyengravedlet
3 albayan
4 albayanpua
5 alnile
6 alnilepua
7 altarikh
8 altarikhpua
9 americantypewriter
10 andalemono
11 applebraille
12 applechancery
13 applecoloremoji
14 applecoloremojiui
15 applegothic
16 applemyungjo
17 applesdgothicneo
18 applesdgothicneoi
19 applesymbols
20 aqua
(略)
360 zapfdingbats
361 zapfino
$
先頭にリストアップされた
”フォント名が空白”
フォントは不明。Windows や Ubtntu では存在しない。
自分のMacだと361種類もあった。
使用するOSやインストールされているフォントの種類によって、Pygameにて日本語が表示できるフォントの数や種類は異なります。
日本語が表示できるフォントは、フォント名から判断できない
一部はフォント名から想定できるとしても、実際に表示させてみないと、本当に日本語が表示できるフォントか否かは判断できません。
次のコードでざっと見てみます。
取得したフォントリストを使って、実際に次々に表示させます。
import pygame
from pygame.locals import *
pygame.init()
screen = pygame.display.set_mode((900, 400))
fontList = []
for fontname in sorted(pygame.font.get_fonts()):
try:
font = pygame.font.SysFont(fontname, 30)
fontList.append(fontname)
except:
pass
clock = pygame.time.Clock()
fontIndex = 0
done = False
while not done:
clock.tick(10)
pygame.display.set_caption(f'[{fontIndex + 1}/{len(fontList)}] {fontList[fontIndex]}')
screen.fill('black')
font = pygame.font.SysFont(fontList[fontIndex], 30)
surface1 = font.render(fontList[fontIndex], True, 'white')
surface2 = font.render("1234567890", True, 'white')
surface3 = font.render("!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~", True, 'white')
surface4 = font.render("ABCDEFGHIJKLMNOPQRSTUVWXYZ", True, 'white')
surface5 = font.render("abcdefghijklmnopqrstuvwxyz", True, 'white')
try:
surface6 = font.render("あいうえおアイウエオ漢字、日本語。", True, 'white')
except Exception as e:
surface6 = font.render(f"NIHONGO Exception: {str(e)}", True, 'white')
screen.blit(surface1, [20, 20])
screen.blit(surface2, [20, 70])
screen.blit(surface3, [20, 120])
screen.blit(surface4, [20, 170])
screen.blit(surface5, [20, 220])
screen.blit(surface6, [20, 270])
pygame.display.update()
if fontIndex < len(fontList) - 1:
fontIndex += 1
for event in pygame.event.get():
if event.type == QUIT:
done = True #exit
if event.type == KEYDOWN:
if event.key == K_ESCAPE or event.key == K_q:
done = True #exit
pygame.quit()
漢字文字列をレンダリングしたとき、font.render
で例外が発生するフォントがあったため、例外を拾って対処した。(環境依存だと思われる)
実際に日本語が表示できたフォントの例;
- 1行目:使用したフォント名(上の例では、
"applegothic"
) - 2行目:数字を表示
- 3行目:記号を表示
- 4行目:アルファベット大文字を表示
- 5行目:アルファベット小文字を表示
- 6行目:日本語を表示
普通の英数字でさえ表示できないフォントもある
Symbol用のフォントだと思われます。
日本語が表示できないフォントの場合
6行目(の日本語)だけ文字化けします。
上の2つの画像を見て分かることは、『表示できない文字は□に化ける』ということです(ただし、□の横幅や縦幅はフォントにより まちまち)。
表示可能なフォントを自動的に判断する方法を考察する
Pygameが文字をレンダリングするときの、ビットマップ(点の集合)を見ることで、ある程度の判断はできそうです。
しかし、あらかじめ どの文字が□になるかは分かっていない為、□のビットマップと比較することは、実質無理です。
そこで、レンダリングしたときのビットマップが、絶対に異なるであろう『2つの文字』のビットマップを比較して、完全一致した場合は『表示できない』と判断することを考える。
準備1. フォント名を指定を指定して、特定の文字のビットマップを得る
Pygameのsurfaceが実際にレンダリングするビットマップそのものです。
次のコードは、フォントリストで得たフォントを使い、サイズ30で文字'A'
のビットマップを出力します。(全体が分かるように白黒反転している)
(surface.get_at((x, y))
で指定した座標(x, y)のpixel値が得られる)
import pygame
from pygame.locals import *
pygame.init()
def print_bitmap(font, char):
surface = font.render(char, True, 'white', 'black')
width, height = surface.get_size()
for y in range(height):
buf = ''
for x in range(width):
pixel = '*' if surface.get_at((x, y)) == (0, 0, 0) else ' '
buf += pixel
print(buf)
count = 1
for fontname in sorted(pygame.font.get_fonts()):
try:
font = pygame.font.SysFont(fontname, 30)
print(f"{count}\t{fontname}")
print_bitmap(font, "A")
count += 1
except:
pass
pygame.quit()
$ python d.py
pygame 2.5.2 (SDL 2.28.3, Python 3.11.9)
Hello from the pygame community. https://www.pygame.org/contribute.html
1
***************
***** *****
***** *****
**** *****
**** ****
**** ****
*** ****
*** * ***
** * ***
** ** ***
** **
* **
* *
* ***** *
***** *
******
***************
***************
***************
***************
2 academyengravedlet
***********************
********** ***********
********** **********
********* **********
********* *********
********* * *********
******** ********
******** * * ********
******* ** *******
******* ** * *******
****** **** ******
****** **** ******
***** ***** * ******
***** *****
***** ****** * *****
**** ******** ****
*** ******** * ****
*** ********** ***
** ********** * ***
* ******* * *
*****
***********************
***********************
***********************
***********************
***********************
***********************
***********************
***********************
***********************
***********************
***********************
***********************
***********************
***********************
***********************
3 albayan
**********************
**********************
**********************
**********************
**********************
**********************
**********************
**********************
* *
* *
* **************** *
* **************** *
* **************** *
* **************** *
* **************** *
* **************** *
* **************** *
* **************** *
* **************** *
* **************** *
* **************** *
* **************** *
* **************** *
* **************** *
* **************** *
* **************** *
* **************** *
* **************** *
* *
* *
**********************
**********************
**********************
**********************
**********************
**********************
**********************
**********************
**********************
**********************
**********************
**********************
**********************
**********************
**********************
(以下、省略)
準備2. 文字1と文字2のビットマップ同士を比較する
特定の文字のビットマップ得ることができたので、次に、文字1と別な文字2のビットマップと比較するコードを考える
-
2つの文字のレンダリングサイズが不一致なら、ビットマップ内を比較せずとも、表示できる文字(=表示できるフォント)と判断する
(この判断は本当は違う可能性があるが、表示できないフォントの場合は、同じサイズの□が表示されることを前提とした)
-
2つの文字のレンダリングサイズが同じなら、ビットマップ内のpixelを順に比較する。1pixelでも異なれば 表示できる文字(=表示できるフォント)と判断する。すべてのpixelが一致する場合のみ 表示不可
-
判断に使用する2つの文字は
'i'
と'W'
を、日本語の場合は'あ'
と'漢'
を用いる(明らかにレンダリングイメージが異なる文字なら何でもよい)
表示可能なフォントを自動判定するコード
前項の考察による ビットマップ比較コードは次の通り。
def isSame_bitmap(font, char):
surface1 = font.render(char[0], True, 'white', 'black')
surface2 = font.render(char[1], True, 'white', 'black')
if surface1.get_size() != surface2.get_size(): return False
width, height = surface1.get_size()
for y in range(height):
for x in range(width):
if surface1.get_at((x, y)) != surface2.get_at((x, y)): return False
return True
if isSame_bitmap(font, 'iW'): ・・・
if isSame_bitmap(font, 'あ漢'): ・・・
日本語が表示できるフォントだけに限定
既出のc.py
に、上記の判定コードを加えた結果、期待通り、
すべての文字が表示できるフォントだけになった。
フォント数は21種類に激減
コードは以下の通り。
import pygame
from pygame.locals import *
def isSame_bitmap(font, char):
surface1 = font.render(char[0], True, 'white', 'black')
surface2 = font.render(char[1], True, 'white', 'black')
if surface1.get_size() != surface2.get_size(): return False
width, height = surface1.get_size()
for y in range(height):
for x in range(width):
if surface1.get_at((x, y)) != surface2.get_at((x, y)): return False
return True
pygame.init()
screen = pygame.display.set_mode((900, 400))
fontList = []
for fontname in sorted(pygame.font.get_fonts()):
try:
font = pygame.font.SysFont(fontname, 30)
if isSame_bitmap(font, 'iW'): continue #⭐️
if isSame_bitmap(font, 'あ漢'): continue
fontList.append(fontname)
except:
pass
clock = pygame.time.Clock()
fontIndex, oldIndex = 0, -1
done = False
while not done:
clock.tick(10)
pygame.display.set_caption(f'[{fontIndex + 1}/{len(fontList)}] {fontList[fontIndex]}')
if oldIndex != fontIndex:
print(f'[{fontIndex + 1}] {fontList[fontIndex]}')
oldIndex = fontIndex
screen.fill('black')
font = pygame.font.SysFont(fontList[fontIndex], 30)
surface1 = font.render(fontList[fontIndex], True, 'white')
surface2 = font.render("1234567890", True, 'white')
surface3 = font.render("!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~", True, 'white')
surface4 = font.render("ABCDEFGHIJKLMNOPQRSTUVWXYZ", True, 'white')
surface5 = font.render("abcdefghijklmnopqrstuvwxyz", True, 'white')
try:
surface6 = font.render("あいうえおアイウエオ漢字、日本語。", True, 'white')
except Exception as e:
surface6 = font.render(f"NIHONGO Exception: {str(e)}", True, 'white')
screen.blit(surface1, [20, 20])
screen.blit(surface2, [20, 70])
screen.blit(surface3, [20, 120])
screen.blit(surface4, [20, 170])
screen.blit(surface5, [20, 220])
screen.blit(surface6, [20, 270])
pygame.display.update()
if fontIndex < len(fontList) - 1:
fontIndex += 1
for event in pygame.event.get():
if event.type == QUIT:
done = True #exit
if event.type == KEYDOWN:
if event.key == K_ESCAPE or event.key == K_q:
done = True #exit
pygame.quit()
漢字が表示できても英数字が表示されないフォントがある?
これは環境によると考えられるが、例えば、以下の2パターン。
- 1; 出処不明なフォント
除外するために、英字のチェック(if isSame_bitpattern(font, 'iW')
)が必要。
- 2; 特定の漢字が表示されないフォント
赤丸の位置の文字が表示されない
これを除外するためには、すべてのpixelが背景色であるフォントを除外するコードの追加が必要であるが、ビットマップをチェックする文字以外で文字化け
(今回の現象が発生)する場合は 救えない。よって、最終的には目視
で確認するしかない。
プラットホームによるフォント名の違い
Raspberry Pi OS 12.6(Bookworm)と Ubuntu24.02 の両方で、IPAフォントをインストールしたが、Pygameで取得するフォント名や種類に違いがあった。
$ sudo apt install -y fonts-ipaexfont fonts-ipafont
それぞれのプラットホームでIPAフォントをインストール後、既出のc2.py
を実行したところ、以下のようにフォント名と種類に差が出た。
プラットホーム | スクショ |
---|---|
Ubuntu 24.02 |
![]() ![]() ![]() |
Bookworm 12.6 |
![]() ![]() ![]() ![]() ![]() ![]() |
同じコマンドでインストールしたフォントであるが、OSより扱いに差があるのか?
書体を見ると、Ubuntu の方はゴシック体のみで、明朝体が欠落していると思われる。理由は不明。
Ubuntu24.02 の fc-list
コマンドで確認すると、8種類のフォントが確認できた。もしかして、Pygame
側の問題??
$ fc-list | grep -i ipa
/usr/share/fonts/opentype/ipafont-mincho/ipam.ttf: IPA明朝,IPAMincho:style=Regular
/usr/share/fonts/opentype/ipaexfont-gothic/ipaexg.ttf: IPAexゴシック,IPAexGothic:style=Regular
/usr/share/fonts/opentype/ipaexfont-mincho/ipaexm.ttf: IPAex明朝,IPAexMincho:style=Regular
/usr/share/fonts/opentype/ipafont-gothic/ipag.ttf: IPAゴシック,IPAGothic:style=Regular
/usr/share/fonts/truetype/fonts-japanese-mincho.ttf: IPAex明朝,IPAexMincho:style=Regular
/usr/share/fonts/truetype/fonts-japanese-gothic.ttf: IPAexゴシック,IPAexGothic:style=Regular
/usr/share/fonts/opentype/ipafont-mincho/ipamp.ttf: IPA P明朝,IPAPMincho,䥐䅐䵩湣桯:style=Regular
/usr/share/fonts/opentype/ipafont-gothic/ipagp.ttf: IPA Pゴシック,IPAPGothic,䥐䅐䝯瑨楣:style=Regular
$
プラットホームによって『同じフォントであっても』フォント名が異なると、扱いづらい・・・
Python標準で Bookworm(Debian) と Ubuntu の実行環境を判断する方法があるのか??
最後に、
フォント名の一覧を出力するコードを提示して、今回の記事を締め括ります。
Pygame font List
フォントごとに次のコメントが付きます。
- ・can not draw Japanese.
- 日本語が表示できないフォント(ビットマップ判定)
- ・is not available for font.render.
- 日本語が表示できないフォント(font.render不可)
- ・can not draw alphabet
- 英数字が表示できないフォント(ビットマップ判定)
- ・is not available for font.SysFont.
- 使用できないフォント(font.SysFont不可)
- ・コメントなし
- 英数字も日本語も表示できるフォント
% python pygame_fonts.py
pygame 2.5.2 (SDL 2.28.3, Python 3.11.9)
Hello from the pygame community. https://www.pygame.org/contribute.html
1 can not draw Japanese.
2 academyengravedlet can not draw Japanese.
3 albayan can not draw alphabet.
4 albayanpua can not draw alphabet.
5 alnile can not draw alphabet.
6 alnilepua can not draw alphabet.
7 altarikh can not draw alphabet.
8 altarikhpua can not draw alphabet.
9 americantypewriter can not draw Japanese.
10 andalemono can not draw Japanese.
11 applebraille can not draw alphabet.
12 applechancery can not draw Japanese.
13 applecoloremoji can not draw alphabet.
14 applecoloremojiui can not draw alphabet.
15 applegothic
16 applemyungjo
17 applesdgothicneo
18 applesdgothicneoi
(以下、省略)
import pygame
from pygame.locals import *
pygame.init()
def isSame_bitmap(font, char):
surface1 = font.render(char[0], True, 'white', 'black')
surface2 = font.render(char[1], True, 'white', 'black')
if surface1.get_size() != surface2.get_size(): return False
width, height = surface1.get_size()
for y in range(height):
for x in range(width):
if surface1.get_at((x, y)) != surface2.get_at((x, y)): return False
return True
maxFontLength = 0
fontList = []
for index, fontname in enumerate(sorted(pygame.font.get_fonts())):
maxFontLength = max(maxFontLength, len(fontname))
try:
font = pygame.font.SysFont(fontname, 30)
fontList.append((index+1, fontname, True))
except:
fontList.append((index+1, fontname, False))
for num, fontname, isAvailable in fontList:
print(f"{num}\t{fontname}{' '*(maxFontLength-len(fontname))}\t", end='')
if not isAvailable:
print("is not available for font.SysFont.")
continue
font = pygame.font.SysFont(fontname, 30)
if isSame_bitmap(font, 'iW'):
print("can not draw alphabet.")
continue
try:
surface6 = font.render("あいうえおアイウエオ漢字、日本語。", True, 'white')
if isSame_bitmap(font, 'あ漢'):
print("can not draw Japanese.")
continue
print()
except:
print("is not available for font.render.")
pygame.quit()
Pygame font viewer
おまけで、一つづつのフォントをゆっくり確認できるコードも提示します。
以下のコードは、矢印キーを押すことで、次/前のフォントを表示する。矢印キーが押されるまでは動かないので、表示内容をゆっくり確認することができる。
(長押しすると、キーリピートにより次々にフォントが切り替わる)
もし、英数字や日本語が表示できるフォントに限る場合は、23行目と24行目のif文
を必要により有効にしてもらいたい。
import pygame
from pygame.locals import *
pygame.init()
pygame.key.set_repeat(500, 100)
screen = pygame.display.set_mode((900, 400))
def isSame_bitmap(font, char):
surface1 = font.render(char[0], True, 'white', 'black')
surface2 = font.render(char[1], True, 'white', 'black')
if surface1.get_size() != surface2.get_size(): return False
width, height = surface1.get_size()
for y in range(height):
for x in range(width):
if surface1.get_at((x, y)) != surface2.get_at((x, y)): return False
return True
fontList = []
for fontname in sorted(pygame.font.get_fonts()):
try:
font = pygame.font.SysFont(fontname, 30)
#skip undrawable font
#if isSame_bitmap(font, 'iW'): continue # ⭐️
#if isSame_bitmap(font, 'あ漢'): continue # ⭐️
fontList.append(fontname)
except:
pass
fontIndex = 0
done = False
while not done:
fontname = fontList[fontIndex]
pygame.display.set_caption(f'[{fontIndex + 1}/{len(fontList)}] {fontname}')
screen.fill('black')
font = pygame.font.SysFont(fontname, 30)
surface1 = font.render(fontname, True, 'white')
surface2 = font.render("1234567890", True, 'white')
surface3 = font.render("!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~", True, 'white')
surface4 = font.render("ABCDEFGHIJKLMNOPQRSTUVWXYZ", True, 'white')
surface5 = font.render("abcdefghijklmnopqrstuvwxyz", True, 'white')
try:
surface6 = font.render("あいうえおアイウエオ漢字、日本語。", True, 'white')
except Exception as e:
surface6 = font.render(f"NIHONGO Exception: {str(e)}", True, 'white')
screen.blit(surface1, [20, 20])
screen.blit(surface2, [20, 70])
screen.blit(surface3, [20, 120])
screen.blit(surface4, [20, 170])
screen.blit(surface5, [20, 220])
screen.blit(surface6, [20, 270])
pygame.display.update()
for event in pygame.event.get():
if event.type == QUIT:
done = True #exit
if event.type == KEYDOWN:
if event.key == K_ESCAPE or event.key == K_q:
done = True #exit
if (event.key == K_UP or event.key == K_LEFT) and fontIndex > 0:
fontIndex -= 1
if (event.key == K_DOWN or event.key == K_RIGHT) and fontIndex < len(fontList) - 1:
fontIndex += 1
if event.type == KEYUP:
if event.key == K_HOME or event.key == K_PAGEUP:
fontIndex = 0
if event.key == K_END or event.key == K_PAGEDOWN:
fontIndex = len(fontList) - 1
if event.key == K_SPACE:
print(f'[{fontIndex + 1}] {fontname}')
pygame.quit()
何かの役に立てれば嬉しい。
以上