| |
| from PIL import Image, ImageDraw, ImageFont |
| import os |
|
|
| def draw_glossing_card(data, output_path): |
| """ |
| 將四行分析畫在畫布上 |
| 包含動態寬度計算,防止長句被截斷(支援雙字型分流防亂碼) |
| """ |
| font_size = 32 |
| font_zh_path = "NotoSansTC-Regular.ttf" |
| |
| |
| |
| font_ind_candidates = [ |
| "DejaVuSans.ttf", |
| "LiberationSans-Regular.ttf", |
| "arial.ttf", |
| "Arial.ttf", |
| "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf" |
| ] |
|
|
| |
| try: |
| font_zh = ImageFont.truetype(font_zh_path, font_size) |
| except Exception as e: |
| print(f"⚠️ 找不到中文字型或載入失敗: {e}") |
| font_zh = ImageFont.load_default() |
|
|
| |
| 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 |
|
|
| |
| 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] |
|
|
| |
| 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 |
| |
| |
| 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) |
|
|
| |
| 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] |
| |
| |
| 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) |
| |
| |
| draw.text((curr_x, y_start + line_spacing * 2), w3, fill=(85,85,85), font=font_zh) |
| |
| |
| 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 |
|
|
| |
| 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 |