ILRDF-AI-Translator / visual_renderer.py
ILRDF-Lowking's picture
Update visual_renderer.py
cbe89a2 verified
Raw
History Blame Contribute Delete
4.16 kB
# visual_renderer.py (滿血防豆腐塊、雙字型分流完全體)
from PIL import Image, ImageDraw, ImageFont
import os
def draw_glossing_card(data, output_path):
"""
將四行分析畫在畫布上
包含動態寬度計算,防止長句被截斷(支援雙字型分流防亂碼)
"""
font_size = 32
font_zh_path = "NotoSansTC-Regular.ttf"
# 💡 核心改動:定義高階拉丁符號(族語專用)的字型候補清單
# Linux 伺服器通常內建 DejaVuSans 或 LiberationSans,地端 Windows 則有 arial
font_ind_candidates = [
"DejaVuSans.ttf",
"LiberationSans-Regular.ttf",
"arial.ttf",
"Arial.ttf",
"/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf"
]
# 1. 載入中文大腦
try:
font_zh = ImageFont.truetype(font_zh_path, font_size)
except Exception as e:
print(f"⚠️ 找不到中文字型或載入失敗: {e}")
font_zh = ImageFont.load_default()
# 2. 載入族語拉丁大腦(尋找認得 ʉ 和 ’ 的字型)
font_ind = None
for f_path in font_ind_candidates:
try:
font_ind = ImageFont.truetype(f_path, font_size)
print(f"✅ 族語圖卡成功啟用特殊符號防彈字型: {f_path}")
break
except:
continue
if font_ind is None:
print("⚠️ 未找到特殊拉丁字型,自動降級使用中文字型(可能出現豆腐塊)")
font_ind = font_zh
# 建立一個臨時畫布來精準測量文字寬度 (相容不同版本的 Pillow)
temp_img = Image.new('RGB', (1, 1))
temp_draw = ImageDraw.Draw(temp_img)
def get_text_width(text, font_obj):
if hasattr(temp_draw, 'textlength'):
return temp_draw.textlength(text, font=font_obj)
elif hasattr(temp_draw, 'textbbox'):
return temp_draw.textbbox((0,0), text, font=font_obj)[2]
else:
return font_obj.getsize(text)[0]
# 💡 3. 動態計算畫布所需的總寬度(依據各自使用的字型精準測量)
x_offset = 80
for i in range(len(data['glossing']['line1'])):
w1_len = get_text_width(data['glossing']['line1'][i], font_ind) # 族語用拉丁字型量
w2_len = get_text_width(data['glossing']['line2'][i], font_ind) # 構詞用拉丁字型量
w3_len = get_text_width(data['glossing']['line3'][i], font_zh) # 中文用中文字型量
x_offset += max(w1_len, w2_len, w3_len) + 50 # 每個單詞群組保留 50px 間距
# 計算第四行(翻譯)的寬度
trans_w = get_text_width(data['translation'], font_zh)
# 決定最終畫布寬度
canvas_width = int(max(1200, x_offset + 80, trans_w + 160))
canvas_height = 500 # 固定高度
bg_color = (253, 248, 241) # 米白色紙張感
img = Image.new('RGB', (canvas_width, canvas_height), color=bg_color)
draw = ImageDraw.Draw(img)
# 💡 4. 開始分流繪製
curr_x = 80
y_start = 100
line_spacing = 80 # 四行等距
for i in range(len(data['glossing']['line1'])):
w1 = data['glossing']['line1'][i]
w2 = data['glossing']['line2'][i]
w3 = data['glossing']['line3'][i]
# 🚀 第一、二行強行注入 font_ind,徹底封殺 ʉ 與 ’ 的豆腐塊
draw.text((curr_x, y_start), w1, fill=(0,0,0), font=font_ind)
draw.text((curr_x, y_start + line_spacing), w2, fill=(139,0,0), font=font_ind)
# 🚀 第三行回歸 font_zh,確保中文解說完美顯示
draw.text((curr_x, y_start + line_spacing * 2), w3, fill=(85,85,85), font=font_zh)
# 推進 X 座標到下一個單詞的起點
bbox_w1 = get_text_width(w1, font_ind)
bbox_w2 = get_text_width(w2, font_ind)
bbox_w3 = get_text_width(w3, font_zh)
curr_x += max(bbox_w1, bbox_w2, bbox_w3) + 50
# 🚀 第四行回歸 font_zh 畫出整句意譯
draw.text((80, y_start + line_spacing * 3.5), data['translation'], fill=(0,0,0), font=font_zh)
img.save(output_path)
return output_path