File size: 4,155 Bytes
bc7bdb7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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