Skip to content

Commit 23a8d62

Browse files
HenryQuanpadtrack
andauthored
Support Unicode in chat & frag (#14)
* support CJK characters with dynamic font loading in chat and frag layers --------- Co-authored-by: padtrack <60636294+padtrack@users.noreply.github.com>
1 parent 98f12e6 commit 23a8d62

36 files changed

+118
-63
lines changed

replays/languages.wowsreplay

46.3 KB
Binary file not shown.

requirements.txt

+3-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ filelock==3.8.0
99
flake8==5.0.4
1010
imageio-ffmpeg==0.4.7
1111
iniconfig==1.1.1
12-
lxml==4.9.1
12+
lxml==4.9.4
1313
mccabe==0.7.0
1414
mypy-extensions==0.4.3
1515
numpy==1.23.2
@@ -32,3 +32,5 @@ tomli==2.0.1
3232
tox==3.25.1
3333
tqdm==4.64.0
3434
virtualenv==20.16.3
35+
hanzidentifier==1.2.0
36+
langdetect==1.0.9

src/renderer/layers/chat.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
from renderer.render import Renderer
77
from functools import lru_cache
88

9-
109
class LayerChatBase(LayerBase):
1110
"""The class for handling in-game chat messages.
1211
@@ -21,9 +20,7 @@ def __init__(
2120
self._replay_data = (
2221
replay_data if replay_data else self._renderer.replay_data
2322
)
24-
self._font = self._renderer.resman.load_font(
25-
filename="warhelios_bold.ttf", size=12
26-
)
23+
self._font = None # to be decided per message
2724
self._players = self._replay_data.player_info
2825
self._generated_lines: dict[int, Image.Image] = {}
2926
self._messages: list[Message] = []
@@ -65,6 +62,9 @@ def build(self, message: Message) -> Image.Image:
6562
# if image := self._lines.get(m_hash, None):
6663
# return image
6764

65+
self._font = self._renderer.resman.load_font_with_text(
66+
message.message, size=12
67+
)
6868
base = Image.new("RGBA", (560, 17))
6969
draw = ImageDraw.Draw(base)
7070
player = self._players[message.player_id]

src/renderer/layers/frag.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,9 @@ def __init__(
2525
self._replay_data = (
2626
replay_data if replay_data else self._renderer.replay_data
2727
)
28-
self._font = self._renderer.resman.load_font(
29-
filename="warhelios_bold.ttf", size=12
30-
)
28+
realm = next(iter(self._replay_data.player_info.values())).realm
29+
font_name = "warhelios_bold_zh.ttf" if realm == "CN" else "warhelios_bold.ttf"
30+
self._font = self._renderer.resman.load_font(filename=font_name, size=12)
3131
self._frags: list[Frag] = []
3232
self._ships = self._renderer.resman.load_json("ships.json")
3333
self._players = self._replay_data.player_info

src/renderer/resman.py

+52-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@
55
from PIL import Image
66
from typing import Optional
77
from PIL import ImageFont
8-
8+
from langdetect import detect, LangDetectException
9+
from hanzidentifier import has_chinese
10+
from renderer.data import Message
911

1012
class ResourceManager:
1113
"""A resource manager."""
@@ -70,6 +72,55 @@ def load_font(
7072
self._cache[key] = data
7173
return data
7274

75+
def load_default_font(self, size=12) -> ImageFont.FreeTypeFont:
76+
"""Loads the default font.
77+
78+
Returns:
79+
ImageFont.FreeTypeFont: The font.
80+
"""
81+
return self.load_font(filename="warhelios_bold.ttf", size=size)
82+
83+
def load_font_with_text(self, text: str, size=12) -> ImageFont.FreeTypeFont:
84+
"""Pick the font based on the message language.
85+
86+
Args:
87+
text (str): The pure text.
88+
89+
Returns:
90+
ImageFont.FreeTypeFont: The font.
91+
"""
92+
return self._select_font_by_text(text, size)
93+
94+
def _select_font_by_text(self, text: str, size: int) -> ImageFont.FreeTypeFont:
95+
"""Select the font based on the text language.
96+
97+
Args:
98+
text (str): The text.
99+
100+
Returns:
101+
ImageFont.FreeTypeFont: The font.
102+
"""
103+
try:
104+
language = detect(text) # this can detect Chinese as Korean
105+
if has_chinese(text):
106+
return self.load_font(
107+
filename="warhelios_bold_zh.ttf", size=size
108+
)
109+
110+
if language == "ja":
111+
return self.load_font(
112+
filename="warhelios_bold_jp.ttf", size=size
113+
)
114+
elif language == "ko":
115+
return self.load_font(
116+
filename="warhelios_bold_ko.ttf", size=size
117+
)
118+
except LangDetectException:
119+
LOGGER.warn(f"Unable to detect language of message \"{text}\"")
120+
121+
# fallback to the default font
122+
return self.load_default_font(size=size)
123+
73124
def load_image(
74125
self,
75126
filename: str,
6.4 MB
Binary file not shown.
5.08 MB
Binary file not shown.
10.8 MB
Binary file not shown.

src/replay_unpack/clients/wows/versions/0_11_10/battle_controller.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -721,7 +721,7 @@ def _create_player_vehicle_data(self):
721721
clan_id=player["clanID"],
722722
clan_tag=player["clanTag"],
723723
max_health=player["maxHealth"],
724-
name=player["name"],
724+
name=player["name"].encode('ISO8859-1').decode('UTF-8'),
725725
realm=player["realm"],
726726
ship_id=player["shipId"],
727727
team_id=player["teamId"],
@@ -761,7 +761,7 @@ def _create_player_vehicle_data(self):
761761
is_alive=player["isAlive"],
762762
is_hidden=player["isHidden"],
763763
is_suppressed=player["isSuppressed"],
764-
name=player["name"],
764+
name=player["name"].encode('ISO8859-1').decode('UTF-8'),
765765
params_id=player["paramsId"],
766766
team_id=player["teamId"],
767767
unique_id=player["uniqueId"],

src/replay_unpack/clients/wows/versions/0_11_11/battle_controller.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -732,7 +732,7 @@ def _create_player_vehicle_data(self):
732732
clan_id=player["clanID"],
733733
clan_tag=player["clanTag"],
734734
max_health=player["maxHealth"],
735-
name=player["name"],
735+
name=player["name"].encode('ISO8859-1').decode('UTF-8'),
736736
realm=player["realm"],
737737
ship_id=player["shipId"],
738738
team_id=player["teamId"],
@@ -772,7 +772,7 @@ def _create_player_vehicle_data(self):
772772
is_alive=player["isAlive"],
773773
is_hidden=player["isHidden"],
774774
is_suppressed=player["isSuppressed"],
775-
name=player["name"],
775+
name=player["name"].encode('ISO8859-1').decode('UTF-8'),
776776
params_id=player["paramsId"],
777777
team_id=player["teamId"],
778778
unique_id=player["uniqueId"],

src/replay_unpack/clients/wows/versions/0_11_6/battle_controller.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -701,7 +701,7 @@ def _create_player_vehicle_data(self):
701701
clan_id=player["clanID"],
702702
clan_tag=player["clanTag"],
703703
max_health=player["maxHealth"],
704-
name=player["name"],
704+
name=player["name"].encode('ISO8859-1').decode('UTF-8'),
705705
realm=player["realm"],
706706
ship_id=player["shipId"],
707707
team_id=player["teamId"],
@@ -741,7 +741,7 @@ def _create_player_vehicle_data(self):
741741
is_alive=player["isAlive"],
742742
is_hidden=player["isHidden"],
743743
is_suppressed=player["isSuppressed"],
744-
name=player["name"],
744+
name=player["name"].encode('ISO8859-1').decode('UTF-8'),
745745
params_id=player["paramsId"],
746746
team_id=player["teamId"],
747747
unique_id=player["uniqueId"],

src/replay_unpack/clients/wows/versions/0_11_7/battle_controller.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -701,7 +701,7 @@ def _create_player_vehicle_data(self):
701701
clan_id=player["clanID"],
702702
clan_tag=player["clanTag"],
703703
max_health=player["maxHealth"],
704-
name=player["name"],
704+
name=player["name"].encode('ISO8859-1').decode('UTF-8'),
705705
realm=player["realm"],
706706
ship_id=player["shipId"],
707707
team_id=player["teamId"],
@@ -741,7 +741,7 @@ def _create_player_vehicle_data(self):
741741
is_alive=player["isAlive"],
742742
is_hidden=player["isHidden"],
743743
is_suppressed=player["isSuppressed"],
744-
name=player["name"],
744+
name=player["name"].encode('ISO8859-1').decode('UTF-8'),
745745
params_id=player["paramsId"],
746746
team_id=player["teamId"],
747747
unique_id=player["uniqueId"],

src/replay_unpack/clients/wows/versions/0_11_8/battle_controller.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -721,7 +721,7 @@ def _create_player_vehicle_data(self):
721721
clan_id=player["clanID"],
722722
clan_tag=player["clanTag"],
723723
max_health=player["maxHealth"],
724-
name=player["name"],
724+
name=player["name"].encode('ISO8859-1').decode('UTF-8'),
725725
realm=player["realm"],
726726
ship_id=player["shipId"],
727727
team_id=player["teamId"],
@@ -761,7 +761,7 @@ def _create_player_vehicle_data(self):
761761
is_alive=player["isAlive"],
762762
is_hidden=player["isHidden"],
763763
is_suppressed=player["isSuppressed"],
764-
name=player["name"],
764+
name=player["name"].encode('ISO8859-1').decode('UTF-8'),
765765
params_id=player["paramsId"],
766766
team_id=player["teamId"],
767767
unique_id=player["uniqueId"],

src/replay_unpack/clients/wows/versions/0_11_9/battle_controller.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -721,7 +721,7 @@ def _create_player_vehicle_data(self):
721721
clan_id=player["clanID"],
722722
clan_tag=player["clanTag"],
723723
max_health=player["maxHealth"],
724-
name=player["name"],
724+
name=player["name"].encode('ISO8859-1').decode('UTF-8'),
725725
realm=player["realm"],
726726
ship_id=player["shipId"],
727727
team_id=player["teamId"],
@@ -761,7 +761,7 @@ def _create_player_vehicle_data(self):
761761
is_alive=player["isAlive"],
762762
is_hidden=player["isHidden"],
763763
is_suppressed=player["isSuppressed"],
764-
name=player["name"],
764+
name=player["name"].encode('ISO8859-1').decode('UTF-8'),
765765
params_id=player["paramsId"],
766766
team_id=player["teamId"],
767767
unique_id=player["uniqueId"],

src/replay_unpack/clients/wows/versions/12_0_0/battle_controller.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -735,7 +735,7 @@ def _create_player_vehicle_data(self):
735735
clan_id=player["clanID"],
736736
clan_tag=player["clanTag"],
737737
max_health=player["maxHealth"],
738-
name=player["name"],
738+
name=player["name"].encode('ISO8859-1').decode('UTF-8'),
739739
realm=player["realm"],
740740
ship_id=player["shipId"],
741741
team_id=player["teamId"],
@@ -775,7 +775,7 @@ def _create_player_vehicle_data(self):
775775
is_alive=player["isAlive"],
776776
is_hidden=player["isHidden"],
777777
is_suppressed=player["isSuppressed"],
778-
name=player["name"],
778+
name=player["name"].encode('ISO8859-1').decode('UTF-8'),
779779
params_id=player["paramsId"],
780780
team_id=player["teamId"],
781781
unique_id=player["uniqueId"],

src/replay_unpack/clients/wows/versions/12_10_0/battle_controller.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -738,7 +738,7 @@ def _create_player_vehicle_data(self):
738738
clan_id=player["clanID"],
739739
clan_tag=player["clanTag"],
740740
max_health=player["maxHealth"],
741-
name=player["name"],
741+
name=player["name"].encode('ISO8859-1').decode('UTF-8'),
742742
realm=player["realm"],
743743
ship_id=player["shipId"],
744744
team_id=player["teamId"],
@@ -778,7 +778,7 @@ def _create_player_vehicle_data(self):
778778
is_alive=player["isAlive"],
779779
is_hidden=player["isHidden"],
780780
is_suppressed=player["isSuppressed"],
781-
name=player["name"],
781+
name=player["name"].encode('ISO8859-1').decode('UTF-8'),
782782
params_id=player["paramsId"],
783783
team_id=player["teamId"],
784784
unique_id=player["uniqueId"],

src/replay_unpack/clients/wows/versions/12_11_1/battle_controller.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -738,7 +738,8 @@ def _create_player_vehicle_data(self):
738738
clan_id=player["clanID"],
739739
clan_tag=player["clanTag"],
740740
max_health=player["maxHealth"],
741-
name=player["name"],
741+
# fix issues for chinese names
742+
name=player["name"].encode('ISO8859-1').decode('UTF-8'),
742743
realm=player["realm"],
743744
ship_id=player["shipId"],
744745
team_id=player["teamId"],
@@ -778,7 +779,7 @@ def _create_player_vehicle_data(self):
778779
is_alive=player["isAlive"],
779780
is_hidden=player["isHidden"],
780781
is_suppressed=player["isSuppressed"],
781-
name=player["name"],
782+
name=player["name"].encode('ISO8859-1').decode('UTF-8'),
782783
params_id=player["paramsId"],
783784
team_id=player["teamId"],
784785
unique_id=player["uniqueId"],

src/replay_unpack/clients/wows/versions/12_1_0/battle_controller.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -735,7 +735,7 @@ def _create_player_vehicle_data(self):
735735
clan_id=player["clanID"],
736736
clan_tag=player["clanTag"],
737737
max_health=player["maxHealth"],
738-
name=player["name"],
738+
name=player["name"].encode('ISO8859-1').decode('UTF-8'),
739739
realm=player["realm"],
740740
ship_id=player["shipId"],
741741
team_id=player["teamId"],
@@ -775,7 +775,7 @@ def _create_player_vehicle_data(self):
775775
is_alive=player["isAlive"],
776776
is_hidden=player["isHidden"],
777777
is_suppressed=player["isSuppressed"],
778-
name=player["name"],
778+
name=player["name"].encode('ISO8859-1').decode('UTF-8'),
779779
params_id=player["paramsId"],
780780
team_id=player["teamId"],
781781
unique_id=player["uniqueId"],

src/replay_unpack/clients/wows/versions/12_2_0/battle_controller.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -735,7 +735,7 @@ def _create_player_vehicle_data(self):
735735
clan_id=player["clanID"],
736736
clan_tag=player["clanTag"],
737737
max_health=player["maxHealth"],
738-
name=player["name"],
738+
name=player["name"].encode('ISO8859-1').decode('UTF-8'),
739739
realm=player["realm"],
740740
ship_id=player["shipId"],
741741
team_id=player["teamId"],
@@ -775,7 +775,7 @@ def _create_player_vehicle_data(self):
775775
is_alive=player["isAlive"],
776776
is_hidden=player["isHidden"],
777777
is_suppressed=player["isSuppressed"],
778-
name=player["name"],
778+
name=player["name"].encode('ISO8859-1').decode('UTF-8'),
779779
params_id=player["paramsId"],
780780
team_id=player["teamId"],
781781
unique_id=player["uniqueId"],

src/replay_unpack/clients/wows/versions/12_3_1/battle_controller.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -735,7 +735,7 @@ def _create_player_vehicle_data(self):
735735
clan_id=player["clanID"],
736736
clan_tag=player["clanTag"],
737737
max_health=player["maxHealth"],
738-
name=player["name"],
738+
name=player["name"].encode('ISO8859-1').decode('UTF-8'),
739739
realm=player["realm"],
740740
ship_id=player["shipId"],
741741
team_id=player["teamId"],
@@ -775,7 +775,7 @@ def _create_player_vehicle_data(self):
775775
is_alive=player["isAlive"],
776776
is_hidden=player["isHidden"],
777777
is_suppressed=player["isSuppressed"],
778-
name=player["name"],
778+
name=player["name"].encode('ISO8859-1').decode('UTF-8'),
779779
params_id=player["paramsId"],
780780
team_id=player["teamId"],
781781
unique_id=player["uniqueId"],

src/replay_unpack/clients/wows/versions/12_4_0/battle_controller.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -735,7 +735,7 @@ def _create_player_vehicle_data(self):
735735
clan_id=player["clanID"],
736736
clan_tag=player["clanTag"],
737737
max_health=player["maxHealth"],
738-
name=player["name"],
738+
name=player["name"].encode('ISO8859-1').decode('UTF-8'),
739739
realm=player["realm"],
740740
ship_id=player["shipId"],
741741
team_id=player["teamId"],
@@ -775,7 +775,7 @@ def _create_player_vehicle_data(self):
775775
is_alive=player["isAlive"],
776776
is_hidden=player["isHidden"],
777777
is_suppressed=player["isSuppressed"],
778-
name=player["name"],
778+
name=player["name"].encode('ISO8859-1').decode('UTF-8'),
779779
params_id=player["paramsId"],
780780
team_id=player["teamId"],
781781
unique_id=player["uniqueId"],

src/replay_unpack/clients/wows/versions/12_5_0/battle_controller.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -738,7 +738,7 @@ def _create_player_vehicle_data(self):
738738
clan_id=player["clanID"],
739739
clan_tag=player["clanTag"],
740740
max_health=player["maxHealth"],
741-
name=player["name"],
741+
name=player["name"].encode('ISO8859-1').decode('UTF-8'),
742742
realm=player["realm"],
743743
ship_id=player["shipId"],
744744
team_id=player["teamId"],
@@ -778,7 +778,7 @@ def _create_player_vehicle_data(self):
778778
is_alive=player["isAlive"],
779779
is_hidden=player["isHidden"],
780780
is_suppressed=player["isSuppressed"],
781-
name=player["name"],
781+
name=player["name"].encode('ISO8859-1').decode('UTF-8'),
782782
params_id=player["paramsId"],
783783
team_id=player["teamId"],
784784
unique_id=player["uniqueId"],

src/replay_unpack/clients/wows/versions/12_6_0/battle_controller.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -738,7 +738,7 @@ def _create_player_vehicle_data(self):
738738
clan_id=player["clanID"],
739739
clan_tag=player["clanTag"],
740740
max_health=player["maxHealth"],
741-
name=player["name"],
741+
name=player["name"].encode('ISO8859-1').decode('UTF-8'),
742742
realm=player["realm"],
743743
ship_id=player["shipId"],
744744
team_id=player["teamId"],
@@ -778,7 +778,7 @@ def _create_player_vehicle_data(self):
778778
is_alive=player["isAlive"],
779779
is_hidden=player["isHidden"],
780780
is_suppressed=player["isSuppressed"],
781-
name=player["name"],
781+
name=player["name"].encode('ISO8859-1').decode('UTF-8'),
782782
params_id=player["paramsId"],
783783
team_id=player["teamId"],
784784
unique_id=player["uniqueId"],

0 commit comments

Comments
 (0)