-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathfont_util.py
More file actions
98 lines (78 loc) · 3.31 KB
/
font_util.py
File metadata and controls
98 lines (78 loc) · 3.31 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
import pygame
# CJK Unicode ranges for character detection
CJK_RANGES = [
(0x4E00, 0x9FFF), # CJK Unified Ideographs
(0x3400, 0x4DBF), # CJK Unified Ideographs Extension A
(0xFF00, 0xFFEF), # Halfwidth and Fullwidth Forms
]
def is_cjk(char):
"""Check if a character is in the CJK ranges."""
char_ord = ord(char)
for start, end in CJK_RANGES:
if start <= char_ord <= end:
return True
return False
class CompositeFont:
"""
A wrapper for pygame.font.Font that uses a fallback font for CJK characters.
It mimics the interface of pygame.font.Font for methods like render() and size().
"""
def __init__(self, primary_path, primary_size, fallback_path, fallback_size=None):
if fallback_size is None:
fallback_size = primary_size
self.primary_font = pygame.font.Font(primary_path, primary_size)
self.fallback_font = pygame.font.Font(fallback_path, fallback_size)
self.size_cache = {}
def render(self, text, antialias, color, background=None):
if background:
raise NotImplementedError("CompositeFont does not support background color in render.")
# If no CJK characters, use the primary font for performance
if not any(is_cjk(char) for char in text):
return self.primary_font.render(text, antialias, color)
surfaces = []
total_width = 0
max_height = 0
i = 0
while i < len(text):
use_fallback = is_cjk(text[i])
j = i
while j < len(text) and (is_cjk(text[j]) == use_fallback):
j += 1
segment = text[i:j]
font_to_use = self.fallback_font if use_fallback else self.primary_font
rendered_segment = font_to_use.render(segment, antialias, color)
surfaces.append(rendered_segment)
total_width += rendered_segment.get_width()
max_height = max(max_height, rendered_segment.get_height())
i = j
final_surface = pygame.Surface((total_width, max_height), pygame.SRCALPHA)
x = 0
for surf in surfaces:
# Align to bottom to handle different font metrics
y = max_height - surf.get_height()
final_surface.blit(surf, (x, y))
x += surf.get_width()
return final_surface
def size(self, text):
if text in self.size_cache:
return self.size_cache[text]
# If no CJK characters, use the primary font for performance
if not any(is_cjk(char) for char in text):
return self.primary_font.size(text)
total_width = 0
max_height = 0
i = 0
while i < len(text):
use_fallback = is_cjk(text[i])
j = i
while j < len(text) and (is_cjk(text[j]) == use_fallback):
j += 1
segment = text[i:j]
font_to_use = self.fallback_font if use_fallback else self.primary_font
size_w, size_h = font_to_use.size(segment)
total_width += size_w
max_height = max(max_height, size_h)
i = j
result = (total_width, max_height)
self.size_cache[text] = result
return result