File size: 4,155 Bytes
cbe89a2
d46423a
 
 
 
 
 
cbe89a2
d46423a
 
cbe89a2
 
 
 
 
 
 
 
 
 
 
 
 
d46423a
cbe89a2
9a7e197
 
cbe89a2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d46423a
2aa2819
 
 
 
cbe89a2
2aa2819
cbe89a2
2aa2819
cbe89a2
2aa2819
cbe89a2
2aa2819
cbe89a2
d46423a
2aa2819
cbe89a2
 
 
2aa2819
 
 
cbe89a2
2aa2819
cbe89a2
2aa2819
 
 
 
 
 
 
cbe89a2
2aa2819
 
 
d46423a
 
 
 
 
 
cbe89a2
 
 
 
 
 
9a7e197
2aa2819
cbe89a2
 
 
2aa2819
d46423a
cbe89a2
 
d46423a
 
 
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
99
100
101
102
# 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