更新 i18n 並將終端訊息改為英文
- 更新 en/nodeDefs.json 添加新參數翻譯 - GGUFInference: 添加 prompts 輸出 - OpenAIHelper: 添加 config_template 輸入和 prompts 輸出 - PhotoMagazineMaker: 添加 disable_cover_layout 輸入 - 更新 zh-TW/nodeDefs.json(繁體中文) - 更新 zh/nodeDefs.json(簡體中文) - 將 photo_magazine_generator.py 所有終端訊息改為英文 - 創建 replace_chinese_messages.py 批量替換工具
This commit is contained in:
@@ -309,6 +309,9 @@
|
||||
},
|
||||
"1": {
|
||||
"name": "Used Seed"
|
||||
},
|
||||
"2": {
|
||||
"name": "Prompts"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -359,6 +362,9 @@
|
||||
"OpenAIHelper": {
|
||||
"display_name": "OpenAI Helper",
|
||||
"inputs": {
|
||||
"config_template": {
|
||||
"name": "Config Template"
|
||||
},
|
||||
"endpoint": {
|
||||
"name": "Endpoint"
|
||||
},
|
||||
@@ -402,6 +408,9 @@
|
||||
},
|
||||
"1": {
|
||||
"name": "Model Name List"
|
||||
},
|
||||
"2": {
|
||||
"name": "Prompts"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -582,6 +591,9 @@
|
||||
"compress_pdf": {
|
||||
"name": "Compress PDF"
|
||||
},
|
||||
"disable_cover_layout": {
|
||||
"name": "Disable Cover Layout"
|
||||
},
|
||||
"output_path": {
|
||||
"name": "Output Path"
|
||||
}
|
||||
|
||||
@@ -309,6 +309,9 @@
|
||||
},
|
||||
"1": {
|
||||
"name": "使用的種子"
|
||||
},
|
||||
"2": {
|
||||
"name": "提示詞列表"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -359,6 +362,9 @@
|
||||
"OpenAIHelper": {
|
||||
"display_name": "OpenAI 輔助工具",
|
||||
"inputs": {
|
||||
"config_template": {
|
||||
"name": "配置範本"
|
||||
},
|
||||
"endpoint": {
|
||||
"name": "端點"
|
||||
},
|
||||
@@ -402,6 +408,9 @@
|
||||
},
|
||||
"1": {
|
||||
"name": "模型名稱列表"
|
||||
},
|
||||
"2": {
|
||||
"name": "提示詞列表"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -582,6 +591,9 @@
|
||||
"compress_pdf": {
|
||||
"name": "壓縮 PDF"
|
||||
},
|
||||
"disable_cover_layout": {
|
||||
"name": "關閉封面排版"
|
||||
},
|
||||
"output_path": {
|
||||
"name": "輸出路徑"
|
||||
}
|
||||
|
||||
@@ -309,6 +309,9 @@
|
||||
},
|
||||
"1": {
|
||||
"name": "使用的种子"
|
||||
},
|
||||
"2": {
|
||||
"name": "提示词列表"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -359,6 +362,9 @@
|
||||
"OpenAIHelper": {
|
||||
"display_name": "OpenAI 辅助工具",
|
||||
"inputs": {
|
||||
"config_template": {
|
||||
"name": "配置模板"
|
||||
},
|
||||
"endpoint": {
|
||||
"name": "端点"
|
||||
},
|
||||
@@ -402,6 +408,9 @@
|
||||
},
|
||||
"1": {
|
||||
"name": "模型名称列表"
|
||||
},
|
||||
"2": {
|
||||
"name": "提示词列表"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -582,6 +591,9 @@
|
||||
"compress_pdf": {
|
||||
"name": "压缩 PDF"
|
||||
},
|
||||
"disable_cover_layout": {
|
||||
"name": "关闭封面排版"
|
||||
},
|
||||
"output_path": {
|
||||
"name": "输出路径"
|
||||
}
|
||||
|
||||
+148
-148
@@ -13,7 +13,7 @@ from reportlab.pdfbase.pdfmetrics import registerFont, stringWidth
|
||||
|
||||
class PhotoMagazinePromptGenerator:
|
||||
"""
|
||||
寫真雜誌提示詞生成器
|
||||
寫真雜誌prompt生成器
|
||||
從 prompts 資料夾讀取模板,注入使用者參數後輸出給 LLM
|
||||
"""
|
||||
def __init__(self):
|
||||
@@ -75,8 +75,8 @@ class PhotoMagazinePromptGenerator:
|
||||
# 轉換 tensor 為 PIL 圖片
|
||||
pil_image = self.tensor_to_pil(image_tensor)
|
||||
|
||||
# 構建人物特徵提取提示詞
|
||||
feature_prompt = """請分析這張圖片中的人物,詳細描述以下特徵:
|
||||
# 構建人物特徵提取prompt
|
||||
feature_prompt = """請分析這images中的人物,詳細描述以下Features:
|
||||
1. 國籍/種族特徵
|
||||
2. 臉型(圓臉、瓜子臉、方臉等)
|
||||
3. 五官特徵(眼睛、鼻子、嘴巴)
|
||||
@@ -88,7 +88,7 @@ class PhotoMagazinePromptGenerator:
|
||||
|
||||
# 這裡需要調用 LLM 來分析圖片
|
||||
# 由於我們在節點中,可以返回一個提示讓使用者知道需要連接 LLM
|
||||
print("📸 檢測到參考圖片,建議使用 LLM 節點提取人物特徵")
|
||||
print("📸 Reference image detected, recommend using LLM node to extract person features")
|
||||
print(" 提示:可以先用 Image to Prompt 節點分析圖片")
|
||||
|
||||
# 返回基本的視覺描述(不依賴 LLM)
|
||||
@@ -114,18 +114,18 @@ class PhotoMagazinePromptGenerator:
|
||||
template_path = os.path.join(os.path.dirname(__file__), "DesignPrompt", template)
|
||||
|
||||
if not os.path.exists(template_path):
|
||||
return (f"錯誤:找不到模板檔案 {template_path}",)
|
||||
return (f"Error:找不到模板檔案 {template_path}",)
|
||||
|
||||
with open(template_path, 'r', encoding='utf-8') as f:
|
||||
template_content = f.read()
|
||||
|
||||
# 準備人物特徵描述
|
||||
if features == "{EXTRACT_FROM_IMAGE}":
|
||||
features_description = "人物特徵:{EXTRACT_FROM_IMAGE}"
|
||||
features_description = "人物Features:{EXTRACT_FROM_IMAGE}"
|
||||
elif features:
|
||||
features_description = f"人物特徵:{features}"
|
||||
features_description = f"人物Features:{features}"
|
||||
else:
|
||||
features_description = "人物特徵:根據模特兒名稱自行判斷"
|
||||
features_description = "人物Features:根據模特兒名稱自行判斷"
|
||||
|
||||
# 注入參數
|
||||
prompt = template_content.format(
|
||||
@@ -137,22 +137,22 @@ class PhotoMagazinePromptGenerator:
|
||||
features_description=features_description
|
||||
)
|
||||
|
||||
print("📝 提示詞生成完成")
|
||||
print(f" 模板:{template}")
|
||||
print(f" 模特兒:{model_name}")
|
||||
print(f" 特徵:{features if features else '自動判定'}")
|
||||
print("📝 prompt生成完成")
|
||||
print(f" Template:{template}")
|
||||
print(f" Model:{model_name}")
|
||||
print(f" Features:{features if features else '自動判定'}")
|
||||
if reference_image is not None:
|
||||
print(f" 參考圖片:✅ 已提供(將自動提取特徵)")
|
||||
print(f" 風格:{photo_style}")
|
||||
print(f" 場景:{custom_scene if custom_scene else '自動判定'}")
|
||||
print(f" 頁數:{content_pages}")
|
||||
print(f" Style:{photo_style}")
|
||||
print(f" Scene:{custom_scene if custom_scene else '自動判定'}")
|
||||
print(f" Pages:{content_pages}")
|
||||
print("💡 請連接到 LLM 節點")
|
||||
|
||||
return (prompt,)
|
||||
|
||||
except Exception as e:
|
||||
import traceback
|
||||
error_msg = f"錯誤:{str(e)}\n{traceback.format_exc()}"
|
||||
error_msg = f"Error:{str(e)}\n{traceback.format_exc()}"
|
||||
print(error_msg)
|
||||
return (error_msg,)
|
||||
|
||||
@@ -162,7 +162,7 @@ class PhotoMagazineParser:
|
||||
"""
|
||||
寫真雜誌解析器 - 極簡版
|
||||
輸入:LLM 輸出的 JSON 字串(STRING 接口)
|
||||
輸出:提示詞列表(LIST[STRING])
|
||||
輸出:prompt列表(LIST[STRING])
|
||||
"""
|
||||
def __init__(self):
|
||||
pass
|
||||
@@ -182,24 +182,24 @@ class PhotoMagazineParser:
|
||||
CATEGORY = "DesignPack"
|
||||
|
||||
def parse(self, llm_json_output):
|
||||
"""解析 JSON,提取圖片提示詞列表"""
|
||||
"""解析 JSON,提取圖片prompt列表"""
|
||||
try:
|
||||
if not llm_json_output or not llm_json_output.strip():
|
||||
return (["錯誤:JSON 輸入為空"],)
|
||||
return (["Error: JSON input is empty"],)
|
||||
|
||||
print("📝 開始解析 LLM 輸出的 JSON...")
|
||||
print("📝 Starting to parse LLM JSON output...")
|
||||
image_prompts = self.extract_prompts(llm_json_output.strip())
|
||||
print(f"✅ 解析完成!提取到 {len(image_prompts)} 個圖片提示詞")
|
||||
print(f"✅ Parsing complete! Extracted {len(image_prompts)} image prompts")
|
||||
return (image_prompts,)
|
||||
|
||||
except Exception as e:
|
||||
import traceback
|
||||
error_msg = f"錯誤:{str(e)}\n{traceback.format_exc()}"
|
||||
error_msg = f"Error:{str(e)}\n{traceback.format_exc()}"
|
||||
print(error_msg)
|
||||
return ([error_msg],)
|
||||
|
||||
def extract_prompts(self, json_text):
|
||||
"""從 JSON 文字中提取所有圖片提示詞"""
|
||||
"""從 JSON 文字中提取所有圖片prompt"""
|
||||
try:
|
||||
# 清理 JSON 文字(移除 markdown 代碼塊標記)
|
||||
if json_text.startswith("```json"):
|
||||
@@ -222,42 +222,42 @@ class PhotoMagazineParser:
|
||||
json_str = json_text[start_idx:end_idx]
|
||||
data = json.loads(json_str)
|
||||
else:
|
||||
return [f"錯誤:無法解析 JSON\n內容:{json_text[:200]}..."]
|
||||
return [f"Error:無法解析 JSON\n內容:{json_text[:200]}..."]
|
||||
|
||||
# 提取所有 image_prompt
|
||||
prompts = []
|
||||
|
||||
# 1. 封面提示詞
|
||||
# 1. 封面prompt
|
||||
if "cover" in data and isinstance(data["cover"], dict):
|
||||
prompt = data["cover"].get("image_prompt", "")
|
||||
if prompt:
|
||||
prompts.append(str(prompt).strip())
|
||||
print(f" ✓ 封面提示詞")
|
||||
print(f" ✓ Cover prompt")
|
||||
|
||||
# 2. 內容頁提示詞
|
||||
# 2. 內容pageprompt
|
||||
if "pages" in data and isinstance(data["pages"], list):
|
||||
for i, page in enumerate(data["pages"]):
|
||||
if isinstance(page, dict):
|
||||
prompt = page.get("image_prompt", "")
|
||||
if prompt:
|
||||
prompts.append(str(prompt).strip())
|
||||
print(f" ✓ 頁面 {i+1} 提示詞")
|
||||
print(f" ✓ Page {i+1} prompt")
|
||||
|
||||
# 3. 故事頁提示詞
|
||||
# 3. 故事pageprompt
|
||||
if "story_page" in data and isinstance(data["story_page"], dict):
|
||||
prompt = data["story_page"].get("image_prompt", "")
|
||||
if prompt:
|
||||
prompts.append(str(prompt).strip())
|
||||
print(f" ✓ 故事頁提示詞")
|
||||
print(f" ✓ Story page prompt")
|
||||
|
||||
if not prompts:
|
||||
return ["警告:未找到任何 image_prompt"]
|
||||
return ["Warning: No image_prompt found"]
|
||||
|
||||
return prompts
|
||||
|
||||
except Exception as e:
|
||||
import traceback
|
||||
return [f"解析錯誤:{str(e)}\n{traceback.format_exc()}"]
|
||||
return [f"Parse error:{str(e)}\n{traceback.format_exc()}"]
|
||||
|
||||
|
||||
def clean_markdown_content(self, data):
|
||||
@@ -300,7 +300,7 @@ class PhotoMagazineParser:
|
||||
return clean_recursive(data)
|
||||
|
||||
def parse_response_and_generate_prompts(self, response_text):
|
||||
"""解析回應並生成圖片提示詞"""
|
||||
"""解析回應並生成圖片prompt"""
|
||||
try:
|
||||
# 解析回應並提取JSON
|
||||
if response_text.startswith("```json"):
|
||||
@@ -321,12 +321,12 @@ class PhotoMagazineParser:
|
||||
json_str = response_text[start_idx:end_idx]
|
||||
magazine_data = json.loads(json_str)
|
||||
else:
|
||||
return ([f"錯誤:無法解析為JSON\n回應內容:{response_text[:500]}..."], "")
|
||||
return ([f"Error:無法解析為JSON\n回應內容:{response_text[:500]}..."], "")
|
||||
|
||||
# 清理markdown標記
|
||||
magazine_data = self.clean_markdown_content(magazine_data)
|
||||
|
||||
# 提取圖片提示詞列表 - 從完整JSON中獲取所有prompts
|
||||
# 提取圖片prompt列表 - 從完整JSON中獲取所有prompts
|
||||
image_prompts = []
|
||||
|
||||
def extract_prompt(data, key="image_prompt", fallback="portrait photography, professional model"):
|
||||
@@ -354,19 +354,19 @@ class PhotoMagazineParser:
|
||||
image_prompts.append(cover_prompt)
|
||||
print(f" 封面 prompt: {cover_prompt[:50]}...")
|
||||
|
||||
# 2. 從 pages 中提取每頁的 image_prompt
|
||||
# 2. 從 pages 中提取每page的 image_prompt
|
||||
pages = magazine_data.get("pages", [])
|
||||
for i, page in enumerate(pages):
|
||||
page_prompt = extract_prompt(page)
|
||||
image_prompts.append(page_prompt)
|
||||
print(f" 頁面 {i+1} prompt: {page_prompt[:50]}...")
|
||||
print(f" page面 {i+1} prompt: {page_prompt[:50]}...")
|
||||
|
||||
# 3. 故事頁 image_prompt
|
||||
# 3. 故事page image_prompt
|
||||
story_data = magazine_data.get("story_page", {})
|
||||
if story_data:
|
||||
story_prompt = extract_prompt(story_data)
|
||||
image_prompts.append(story_prompt)
|
||||
print(f" 故事頁 prompt: {story_prompt[:50]}...")
|
||||
print(f" 故事page prompt: {story_prompt[:50]}...")
|
||||
|
||||
# 返回完整的 JSON 字串
|
||||
json_string = json.dumps(magazine_data, ensure_ascii=False, indent=2)
|
||||
@@ -374,7 +374,7 @@ class PhotoMagazineParser:
|
||||
return (image_prompts, json_string)
|
||||
|
||||
except Exception as e:
|
||||
return ([f"錯誤:{str(e)}"], "")
|
||||
return ([f"Error:{str(e)}"], "")
|
||||
|
||||
|
||||
class PhotoMagazineMaker:
|
||||
@@ -396,7 +396,7 @@ class PhotoMagazineMaker:
|
||||
"compress_pdf": ("BOOLEAN", {"default": False}),
|
||||
"disable_cover_layout": ("BOOLEAN", {
|
||||
"default": False,
|
||||
"tooltip": "關閉封面排版,使用第一張圖片作為滿版封面(不含文字)"
|
||||
"tooltip": "關閉封面排版,使用Page一images作為滿版封面(不含文字)"
|
||||
}),
|
||||
"output_path": ("STRING", {"default": "./ComfyUI/output/MyPDF/photo_magazine.pdf"}),
|
||||
}
|
||||
@@ -462,7 +462,7 @@ class PhotoMagazineMaker:
|
||||
|
||||
except Exception as e:
|
||||
# 備用方案:建立空白圖片
|
||||
print(f"圖片轉換錯誤: {e}")
|
||||
print(f"Image conversion error: {e}")
|
||||
return Image.new('RGB', (800, 600), color='white')
|
||||
|
||||
def resize_image_to_fit(self, pil_image, max_width_mm, max_height_mm, dpi=300):
|
||||
@@ -509,7 +509,7 @@ class PhotoMagazineMaker:
|
||||
if preferred_index is not None and preferred_index < len(all_images) and preferred_index not in used_indices:
|
||||
return all_images[preferred_index]
|
||||
|
||||
# 否則找第一張未使用的圖片
|
||||
# 否則找Page一張未使用的圖片
|
||||
for i, img in enumerate(all_images):
|
||||
if i not in used_indices:
|
||||
return img
|
||||
@@ -550,7 +550,7 @@ class PhotoMagazineMaker:
|
||||
return cropped_image
|
||||
|
||||
except Exception as e:
|
||||
print(f"滿版圖片創建錯誤: {e}")
|
||||
print(f"Full bleed image creation error: {e}")
|
||||
return pil_image
|
||||
|
||||
def wrap_text(self, text, max_width_mm, font_name, font_size, canvas_obj):
|
||||
@@ -609,13 +609,13 @@ class PhotoMagazineMaker:
|
||||
|
||||
def allocate_images_smartly(self, images, num_content_pages):
|
||||
"""
|
||||
智能分配圖片到各個頁面
|
||||
智能分配圖片到各pages
|
||||
返回一個字典,包含各部分應使用的圖片索引
|
||||
"""
|
||||
total_images = len(images)
|
||||
|
||||
# 計算所需圖片數量
|
||||
# 封面: 1, 內容頁: num_content_pages, 故事頁: 1, 尾頁: 至少1張(最多4張用於拼貼)
|
||||
# 封面: 1, 內容page: num_content_pages, 故事page: 1, 尾page: 至少1張(最多4張用於拼貼)
|
||||
min_needed = 1 + num_content_pages + 1 + 1 # 最少需求
|
||||
|
||||
allocation = {
|
||||
@@ -636,7 +636,7 @@ class PhotoMagazineMaker:
|
||||
allocation["cover"] = idx
|
||||
idx += 1
|
||||
|
||||
# 2. 內容頁
|
||||
# 2. 內容page
|
||||
for i in range(num_content_pages):
|
||||
if idx < total_images:
|
||||
allocation["pages"].append(idx)
|
||||
@@ -645,15 +645,15 @@ class PhotoMagazineMaker:
|
||||
# 圖片不足,重複使用
|
||||
allocation["pages"].append(i % total_images)
|
||||
|
||||
# 3. 故事頁
|
||||
# 3. 故事page
|
||||
if idx < total_images:
|
||||
allocation["story"] = idx
|
||||
idx += 1
|
||||
else:
|
||||
# 重複使用第一張
|
||||
# 重複使用Page一張
|
||||
allocation["story"] = 0
|
||||
|
||||
# 4. 尾頁(收集剩餘圖片,最多4張用於四分割)
|
||||
# 4. 尾page(收集剩餘圖片,最多4張用於四分割)
|
||||
remaining_images = total_images - idx
|
||||
if remaining_images > 0:
|
||||
# 使用剩餘的圖片
|
||||
@@ -661,9 +661,9 @@ class PhotoMagazineMaker:
|
||||
allocation["footer"].append(idx + i)
|
||||
else:
|
||||
# 沒有剩餘圖片,使用前面的圖片
|
||||
# 優先使用內容頁的圖片作為尾頁拼貼
|
||||
# 優先使用內容page的圖片as back cover拼貼
|
||||
if len(allocation["pages"]) >= 4:
|
||||
# 如果有足夠的內容頁圖片,取最後4張
|
||||
# 如果有足夠的內容page圖片,取最後4張
|
||||
allocation["footer"] = allocation["pages"][-4:]
|
||||
elif len(allocation["pages"]) > 0:
|
||||
# 圖片不足4張,重複使用
|
||||
@@ -681,7 +681,7 @@ class PhotoMagazineMaker:
|
||||
cover_info = magazine_data.get("cover", {})
|
||||
# 確保 cover_info 是字典類型
|
||||
if not isinstance(cover_info, dict):
|
||||
print(f"警告:cover_info 不是字典類型(類型: {type(cover_info)}),使用預設值")
|
||||
print(f"警告:cover_info 不是字典類型(類型: {type(cover_info)}), using default values")
|
||||
cover_info = {}
|
||||
print(f"封面數據: {cover_info}")
|
||||
|
||||
@@ -690,7 +690,7 @@ class PhotoMagazineMaker:
|
||||
print(f"封面是否有有效內容: {has_content}")
|
||||
|
||||
if not has_content and cover_image:
|
||||
# 純圖片封面:滿版顯示第一張圖片,不添加任何文字
|
||||
# 純圖片封面:滿版顯示Page一images,不添加任何文字
|
||||
print("使用純圖片封面模式")
|
||||
full_bleed = self.create_full_bleed_image(cover_image)
|
||||
import time
|
||||
@@ -940,7 +940,7 @@ class PhotoMagazineMaker:
|
||||
line_height = adaptive_font_size + 2
|
||||
|
||||
for line in lines[:min(12, len(lines))]: # 最多12行,適應性顯示
|
||||
if line.strip() and y_pos > 10*mm: # 確保不超出頁面
|
||||
if line.strip() and y_pos > 10*mm: # 確保不超出page面
|
||||
line_width = stringWidth(line, font_name, adaptive_font_size)
|
||||
canvas_obj.drawString(text_center_x*mm - line_width/2, y_pos, line)
|
||||
y_pos -= line_height
|
||||
@@ -1044,7 +1044,7 @@ class PhotoMagazineMaker:
|
||||
# 短文字:保持120mm
|
||||
max_line_width = 120*mm
|
||||
|
||||
# 確保不超過頁面寬度(留出邊距)
|
||||
# 確保不超過page面寬度(留出邊距)
|
||||
page_max_width = 180*mm # A4寬度210mm - 左右各15mm邊距
|
||||
max_line_width = min(max_line_width, page_max_width)
|
||||
|
||||
@@ -1066,7 +1066,7 @@ class PhotoMagazineMaker:
|
||||
content_width = max(theme_width, desc_max_width, min_box_width - padding * 2)
|
||||
box_width = content_width + padding * 2
|
||||
|
||||
# 確保文字框不超過頁面寬度
|
||||
# 確保文字框不超過page面寬度
|
||||
if box_width > page_max_width:
|
||||
box_width = page_max_width
|
||||
# 重新計算內容寬度和換行
|
||||
@@ -1086,9 +1086,9 @@ class PhotoMagazineMaker:
|
||||
return box_width, box_height, desc_lines
|
||||
|
||||
def draw_content_page(self, canvas_obj, page_data, page_image, template_config, font_name, layout, compress_enabled=False):
|
||||
"""繪製內頁(滿版圖片 + 統一的文字框)"""
|
||||
"""繪製內page(滿版圖片 + 統一的文字框)"""
|
||||
try:
|
||||
print(f"繪製內頁,數據: {page_data}")
|
||||
print(f"繪製內page,數據: {page_data}")
|
||||
|
||||
if page_image:
|
||||
print("開始繪製滿版圖片")
|
||||
@@ -1112,17 +1112,17 @@ class PhotoMagazineMaker:
|
||||
pass
|
||||
else:
|
||||
# 沒有圖片時應該不會發生,因為已經有備用圖片邏輯
|
||||
print("警告:內容頁備用圖片邏輯失效,使用純色背景")
|
||||
print("警告:內容page備用圖片邏輯失效,使用純色背景")
|
||||
canvas_obj.setFillColor(HexColor(template_config["background"]))
|
||||
canvas_obj.rect(0, 0, 210*mm, 297*mm, fill=1, stroke=0)
|
||||
|
||||
# 獲取頁面文字內容
|
||||
# 獲取page面文字內容
|
||||
theme = page_data.get("theme", "").strip() if page_data.get("theme") else ""
|
||||
description = page_data.get("description", "").strip() if page_data.get("description") else ""
|
||||
|
||||
# 統一的文字框處理(所有版型都相同)
|
||||
if theme or description:
|
||||
print(f"添加頁面文字 - 主題: {theme}, 描述: {description[:20]}...")
|
||||
print(f"添加page面文字 - 主題: {theme}, 描述: {description[:20]}...")
|
||||
|
||||
# 計算文字框尺寸
|
||||
box_width, box_height, desc_lines = self.calculate_text_box_dimensions(
|
||||
@@ -1137,10 +1137,10 @@ class PhotoMagazineMaker:
|
||||
box_x = 210*mm - box_width - margin_right
|
||||
box_y = margin_bottom
|
||||
|
||||
# 確保文字框不超出頁面邊界
|
||||
# 確保文字框不超出page面邊界
|
||||
if box_x < 5*mm:
|
||||
box_x = 5*mm
|
||||
box_width = 210*mm - 10*mm # 調整寬度適應頁面
|
||||
box_width = 210*mm - 10*mm # 調整寬度適應page面
|
||||
|
||||
if box_y + box_height > 297*mm - 5*mm:
|
||||
box_y = 297*mm - box_height - 5*mm
|
||||
@@ -1177,19 +1177,19 @@ class PhotoMagazineMaker:
|
||||
print(f"文字框繪製完成 - 位置: ({box_x/mm:.1f}, {box_y/mm:.1f}), 尺寸: ({box_width/mm:.1f} x {box_height/mm:.1f})")
|
||||
|
||||
except Exception as e:
|
||||
print(f"內頁繪製錯誤: {e}")
|
||||
print(f"內page繪製錯誤: {e}")
|
||||
import traceback
|
||||
print(f"錯誤詳情: {traceback.format_exc()}")
|
||||
|
||||
def draw_story_page(self, canvas_obj, story_data, story_image, template_config, font_name, layout, compress_enabled=False):
|
||||
"""繪製故事頁"""
|
||||
"""繪製故事page"""
|
||||
try:
|
||||
print(f"繪製故事頁,數據: {story_data}")
|
||||
print(f"繪製故事page,數據: {story_data}")
|
||||
print(f"story_data 類型: {type(story_data)}")
|
||||
|
||||
# 確保 story_data 是字典類型
|
||||
if not isinstance(story_data, dict):
|
||||
print(f"警告:story_data 不是字典類型(類型: {type(story_data)}),使用預設值")
|
||||
print(f"警告:story_data 不是字典類型(類型: {type(story_data)}), using default values")
|
||||
story_data = {"title": "小故事", "content": ""}
|
||||
|
||||
title = story_data.get("title", "").strip() if story_data.get("title") else ""
|
||||
@@ -1199,8 +1199,8 @@ class PhotoMagazineMaker:
|
||||
print(f"故事標題: {title}, 內容長度: {len(content)}")
|
||||
|
||||
if layout == "版型A-經典排版":
|
||||
# 版型A故事頁:與封面完全相同的設計
|
||||
print(f"版型A故事頁,圖片存在: {story_image is not None}")
|
||||
# 版型A故事page:與封面完全相同的設計
|
||||
print(f"版型A故事page,圖片存在: {story_image is not None}")
|
||||
|
||||
if story_image:
|
||||
print(f"處理故事圖片,尺寸: {story_image.size}")
|
||||
@@ -1213,7 +1213,7 @@ class PhotoMagazineMaker:
|
||||
|
||||
# 滿版圖片(上方)
|
||||
canvas_obj.drawImage(temp_path, 0, 97*mm, width=210*mm, height=200*mm)
|
||||
print("版型A故事頁圖片繪製完成")
|
||||
print("版型A故事page圖片繪製完成")
|
||||
|
||||
try:
|
||||
os.remove(temp_path)
|
||||
@@ -1225,7 +1225,7 @@ class PhotoMagazineMaker:
|
||||
canvas_obj.setFillColor(HexColor(template_config["secondary"]))
|
||||
canvas_obj.rect(0, 97*mm, 210*mm, 200*mm, fill=1, stroke=0)
|
||||
else:
|
||||
print("版型A故事頁:沒有圖片,使用純色背景")
|
||||
print("版型A故事page:沒有圖片,使用純色背景")
|
||||
# 沒有圖片時的上方背景
|
||||
canvas_obj.setFillColor(HexColor(template_config["secondary"]))
|
||||
canvas_obj.rect(0, 97*mm, 210*mm, 200*mm, fill=1, stroke=0)
|
||||
@@ -1356,8 +1356,8 @@ class PhotoMagazineMaker:
|
||||
|
||||
box_height = max(text_area_height, min_height)
|
||||
|
||||
# 確保不超過頁面可用高度
|
||||
max_allowed_height = 290*mm - 10*mm # 頁面高度297mm - 上下邊距
|
||||
# 確保不超過page面可用高度
|
||||
max_allowed_height = 290*mm - 10*mm # page面高度297mm - 上下邊距
|
||||
if box_height > max_allowed_height:
|
||||
box_height = max_allowed_height
|
||||
|
||||
@@ -1368,7 +1368,7 @@ class PhotoMagazineMaker:
|
||||
box_x = 210*mm - box_width - 5*mm
|
||||
box_y = 5*mm
|
||||
|
||||
# 確保文字框不會超出頁面頂部
|
||||
# 確保文字框不會超出page面頂部
|
||||
if box_y + box_height > 297*mm - 5*mm:
|
||||
box_y = 297*mm - box_height - 5*mm
|
||||
|
||||
@@ -1419,11 +1419,11 @@ class PhotoMagazineMaker:
|
||||
displayed_lines += 1
|
||||
y_position -= line_height
|
||||
|
||||
print(f"版型B顯示了 {displayed_lines}/{len(content_lines)} 行文字,字體大小: {adaptive_font_size}, 行高: {line_height}, 框高: {box_height}")
|
||||
print(f"Layout B displayed {displayed_lines}/{len(content_lines)} lines of text, font size: {adaptive_font_size}, line height: {line_height}, box height: {box_height}")
|
||||
|
||||
elif layout == "版型C-簡約現代":
|
||||
# 版型C故事頁:與封面相同的設計(左圖右文)但適應性布局
|
||||
print(f"版型C故事頁,圖片存在: {story_image is not None}")
|
||||
# 版型C故事page:與封面相同的設計(左圖右文)但適應性布局
|
||||
print(f"版型C故事page,圖片存在: {story_image is not None}")
|
||||
|
||||
# 根據文字內容計算適當的版面寬度
|
||||
all_text_content = ""
|
||||
@@ -1472,7 +1472,7 @@ class PhotoMagazineMaker:
|
||||
resized_image.save(temp_path, "JPEG", quality=95)
|
||||
|
||||
canvas_obj.drawImage(temp_path, 0, 0, width=image_area_width*mm, height=297*mm)
|
||||
print("版型C故事頁圖片繪製完成")
|
||||
print("版型C故事page圖片繪製完成")
|
||||
|
||||
try:
|
||||
os.remove(temp_path)
|
||||
@@ -1519,12 +1519,12 @@ class PhotoMagazineMaker:
|
||||
print(f"版型C故事內容已繪製,標題: {title}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"故事頁繪製錯誤: {e}")
|
||||
print(f"故事page繪製錯誤: {e}")
|
||||
|
||||
def draw_back_cover(self, canvas_obj, back_data, footer_images, template_config, font_name, layout, compress_enabled=False):
|
||||
"""繪製結尾頁精選回顧"""
|
||||
"""繪製結尾page精選回顧"""
|
||||
try:
|
||||
print(f"繪製結尾頁,數據: {back_data}")
|
||||
print(f"繪製結尾page,數據: {back_data}")
|
||||
print(f"back_data 類型: {type(back_data)}")
|
||||
|
||||
# 確保 back_data 是字典類型
|
||||
@@ -1536,12 +1536,12 @@ class PhotoMagazineMaker:
|
||||
if not title:
|
||||
title = "精選回顧" # 預設標題
|
||||
description = back_data.get("description", "").strip() if back_data.get("description") else ""
|
||||
print(f"結尾頁標題: {title}, 描述: {description}")
|
||||
print(f"結尾page標題: {title}, 描述: {description}")
|
||||
print(f"可用圖片數量: {len(footer_images)}")
|
||||
|
||||
if layout == "版型A-經典排版" or layout == "版型B-藝術拼貼":
|
||||
# 版型A和B:使用可用圖片創建拼貼效果(統一格式)
|
||||
if footer_images and len(footer_images) >= 1: # 只要有至少一張圖片就創建拼貼
|
||||
if footer_images and len(footer_images) >= 1: # 只要有至少一images就創建拼貼
|
||||
# 創建2x2拼接的滿版底圖
|
||||
canvas_width = 210*mm # A4寬度
|
||||
canvas_height = 297*mm # A4高度
|
||||
@@ -1626,9 +1626,9 @@ class PhotoMagazineMaker:
|
||||
|
||||
elif layout == "版型C-簡約現代":
|
||||
# 版型C:一張底圖 + 無背景框文字
|
||||
print(f"版型C尾頁,圖片數量: {len(footer_images) if footer_images else 0}")
|
||||
print(f"版型C尾page,圖片數量: {len(footer_images) if footer_images else 0}")
|
||||
if footer_images and len(footer_images) >= 1:
|
||||
# 使用第一張圖片作為滿版底圖
|
||||
# 使用Page一images作為滿版底圖
|
||||
first_image = footer_images[0]
|
||||
full_bleed = self.create_full_bleed_image(first_image)
|
||||
import time
|
||||
@@ -1636,14 +1636,14 @@ class PhotoMagazineMaker:
|
||||
full_bleed.save(temp_path, "JPEG", quality=95)
|
||||
|
||||
canvas_obj.drawImage(temp_path, 0, 0, width=210*mm, height=297*mm)
|
||||
print("版型C尾頁底圖已繪製")
|
||||
print("版型C尾page底圖已繪製")
|
||||
|
||||
try:
|
||||
os.remove(temp_path)
|
||||
except:
|
||||
pass
|
||||
else:
|
||||
print("版型C尾頁:沒有圖片,使用純色背景")
|
||||
print("版型C尾page:沒有圖片,使用純色背景")
|
||||
# 沒有圖片時的純色背景
|
||||
canvas_obj.setFillColor(HexColor(template_config["accent"]))
|
||||
canvas_obj.rect(0, 0, 210*mm, 297*mm, fill=1, stroke=0)
|
||||
@@ -1677,7 +1677,7 @@ class PhotoMagazineMaker:
|
||||
y_position -= line_height
|
||||
|
||||
except Exception as e:
|
||||
print(f"頁尾繪製錯誤: {e}")
|
||||
print(f"page尾繪製錯誤: {e}")
|
||||
|
||||
def crop_image_for_layout(self, image, aspect_ratio, crop_position="center"):
|
||||
"""裁切圖片為指定比例"""
|
||||
@@ -1716,9 +1716,9 @@ class PhotoMagazineMaker:
|
||||
return 60 if compress_enabled else 95
|
||||
|
||||
def allocate_images_smartly(self, pil_images, content_pages_count):
|
||||
"""智能分配圖片到各個頁面"""
|
||||
"""智能分配圖片到各pages"""
|
||||
total_images = len(pil_images)
|
||||
total_pages_needed = 1 + content_pages_count + 1 + 1 # 封面 + 內頁 + 故事頁 + 尾頁
|
||||
total_pages_needed = 1 + content_pages_count + 1 + 1 # 封面 + 內page + 故事page + 尾page
|
||||
|
||||
allocation = {
|
||||
"cover": None,
|
||||
@@ -1740,31 +1740,31 @@ class PhotoMagazineMaker:
|
||||
allocation["footer"] = list(range(remaining_start, min(remaining_start + 5, total_images)))
|
||||
|
||||
elif total_images >= content_pages_count + 1:
|
||||
# 圖片稍少:優先內容頁,盡量避免重複
|
||||
# 圖片稍少:優先內容page,盡量避免重複
|
||||
allocation["cover"] = 0
|
||||
allocation["pages"] = list(range(1, min(content_pages_count + 1, total_images)))
|
||||
# 故事頁使用下一張可用圖片,如果沒有則使用最後一張
|
||||
# 故事page使用下一張可用圖片,如果沒有則使用最後一張
|
||||
if content_pages_count + 1 < total_images:
|
||||
allocation["story"] = content_pages_count + 1
|
||||
else:
|
||||
allocation["story"] = total_images - 1 # 使用最後一張圖片
|
||||
allocation["story"] = total_images - 1 # 使用最後一images
|
||||
# 使用所有可用圖片作為footer
|
||||
allocation["footer"] = list(range(min(5, total_images)))
|
||||
|
||||
else:
|
||||
# 圖片很少:大量重複使用,但避免故事頁使用封面圖片
|
||||
# 圖片很少:大量重複使用,但避免故事page使用封面圖片
|
||||
allocation["cover"] = 0
|
||||
# 循環使用圖片填滿內容頁
|
||||
# 循環Using image填滿內容page
|
||||
allocation["pages"] = [i % total_images for i in range(content_pages_count)]
|
||||
# 故事頁優先使用最後一張圖片,如果只有一張圖則使用同一張
|
||||
# 故事page優先使用最後一images,如果只有一張圖則使用同一張
|
||||
if total_images > 1:
|
||||
allocation["story"] = total_images - 1 # 使用最後一張圖片
|
||||
allocation["story"] = total_images - 1 # 使用最後一images
|
||||
else:
|
||||
allocation["story"] = 0 # 只有一張圖片時無選擇
|
||||
allocation["story"] = 0 # 只有一images時無選擇
|
||||
# footer使用所有圖片
|
||||
allocation["footer"] = list(range(total_images))
|
||||
|
||||
print(f"圖片分配結果: 封面={allocation['cover']}, 內頁={allocation['pages']}, 故事頁={allocation['story']}, 尾頁={allocation['footer']}")
|
||||
print(f"Image allocation result: cover={allocation['cover']}, pages={allocation['pages']}, story={allocation['story']}, footer={allocation['footer']}")
|
||||
return allocation
|
||||
|
||||
def make_photo_magazine(self, images, json_data, template, layout, font, compress_pdf, disable_cover_layout, output_path):
|
||||
@@ -1793,11 +1793,11 @@ class PhotoMagazineMaker:
|
||||
try:
|
||||
magazine_data = json.loads(json_string)
|
||||
except json.JSONDecodeError as e:
|
||||
return (f"JSON 解析錯誤: {str(e)},接收到的數據: {str(json_string)[:200]}",)
|
||||
return (f"JSON parse error: {str(e)}, received data: {str(json_string)[:200]}",)
|
||||
|
||||
# 驗證JSON結構
|
||||
if not isinstance(magazine_data, dict):
|
||||
return (f"JSON格式錯誤:期望字典格式,收到 {type(magazine_data).__name__}",)
|
||||
return (f"JSON format error: expected dict, received {type(magazine_data).__name__}",)
|
||||
|
||||
# 檢查必要的數據結構
|
||||
required_keys = ["magazine_info", "cover", "pages", "story_page", "back_cover"]
|
||||
@@ -1808,21 +1808,21 @@ class PhotoMagazineMaker:
|
||||
# 檢查pages是否為列表且不為空
|
||||
pages = magazine_data.get("pages", [])
|
||||
if not isinstance(pages, list):
|
||||
return (f"JSON格式錯誤:pages必須是列表格式,收到 {type(pages).__name__}",)
|
||||
return (f"JSON format error: pages must be list, received {type(pages).__name__}",)
|
||||
|
||||
if len(pages) == 0:
|
||||
return ("JSON數據錯誤:pages列表為空,無法生成雜誌",)
|
||||
return ("JSON data error: pages list is empty, cannot generate magazine",)
|
||||
|
||||
print(f"成功解析JSON,包含 {len(pages)} 個頁面")
|
||||
print(f"Successfully parsed JSON, contains {len(pages)} pages")
|
||||
|
||||
template_config = self.template_configs.get(template_name, self.template_configs["清新自然"])
|
||||
|
||||
# 註冊字體
|
||||
font_name = self.register_font(font_name_input)
|
||||
|
||||
# 轉換圖片
|
||||
# Converting image
|
||||
pil_images = []
|
||||
print(f"開始轉換圖片,圖片類型: {type(images)}")
|
||||
print(f"Starting image conversion, image type: {type(images)}")
|
||||
|
||||
if isinstance(images, list):
|
||||
# 處理圖片列表
|
||||
@@ -1830,22 +1830,22 @@ class PhotoMagazineMaker:
|
||||
if isinstance(img, torch.Tensor):
|
||||
pil_img = self.tensor_to_pil(img)
|
||||
pil_images.append(pil_img)
|
||||
print(f"轉換圖片 {i}: {pil_img.size}")
|
||||
print(f"Converting image {i}: {pil_img.size}")
|
||||
elif isinstance(images, torch.Tensor):
|
||||
if len(images.shape) == 4: # 批次圖片
|
||||
for i in range(images.shape[0]):
|
||||
pil_img = self.tensor_to_pil(images[i])
|
||||
pil_images.append(pil_img)
|
||||
print(f"轉換批次圖片 {i}: {pil_img.size}")
|
||||
print(f"Converting batch image {i}: {pil_img.size}")
|
||||
else:
|
||||
pil_img = self.tensor_to_pil(images)
|
||||
pil_images.append(pil_img)
|
||||
print(f"轉換單張圖片: {pil_img.size}")
|
||||
print(f"Converting single image: {pil_img.size}")
|
||||
|
||||
if len(pil_images) == 0:
|
||||
return ("錯誤:沒有有效的圖片可以處理",)
|
||||
return ("Error: No valid images to process",)
|
||||
|
||||
print(f"成功轉換 {len(pil_images)} 張圖片")
|
||||
print(f"Successfully converted {len(pil_images)} images")
|
||||
|
||||
# 確保輸出目錄存在
|
||||
os.makedirs(os.path.dirname(output_file), exist_ok=True)
|
||||
@@ -1853,14 +1853,14 @@ class PhotoMagazineMaker:
|
||||
# 創建PDF
|
||||
c = canvas.Canvas(output_file, pagesize=A4)
|
||||
|
||||
# 動態計算所需頁面數
|
||||
# 動態計算所需page面數
|
||||
pages = magazine_data.get("pages", [])
|
||||
total_pages_needed = 1 + len(pages) + 1 + 1 # 封面 + 內頁 + 故事頁 + 尾頁
|
||||
print(f"總共需要 {total_pages_needed} 個頁面,可用圖片 {len(pil_images)} 張")
|
||||
total_pages_needed = 1 + len(pages) + 1 + 1 # 封面 + 內page + 故事page + 尾page
|
||||
print(f"Total required {total_pages_needed} pages, available images {len(pil_images)} 張")
|
||||
|
||||
# 智能圖片分配策略
|
||||
image_allocation = self.allocate_images_smartly(pil_images, len(pages))
|
||||
print(f"圖片分配策略: {image_allocation}")
|
||||
print(f"Image allocation strategy: {image_allocation}")
|
||||
|
||||
# 計算圖片分配
|
||||
image_index = 0
|
||||
@@ -1870,23 +1870,23 @@ class PhotoMagazineMaker:
|
||||
cover_image = None
|
||||
if image_allocation["cover"] is not None:
|
||||
cover_image = pil_images[image_allocation["cover"]]
|
||||
print(f"使用圖片 {image_allocation['cover']} 作為封面")
|
||||
print(f"Using image {image_allocation['cover']} as cover")
|
||||
else:
|
||||
print("警告:沒有圖片可用作封面")
|
||||
print("Warning: No image available for cover")
|
||||
|
||||
cover_data = magazine_data.get("cover", {})
|
||||
# 確保 cover_data 是字典類型
|
||||
if not isinstance(cover_data, dict):
|
||||
print(f"警告:JSON中的cover不是字典格式(類型: {type(cover_data)}),使用預設值")
|
||||
print(f"Warning: cover in JSON is not dict format (type: {type(cover_data)}), using default values")
|
||||
cover_data = {"title": "寫真集", "subtitle": "", "description": ""}
|
||||
elif not cover_data:
|
||||
print("警告:JSON中缺少cover數據,使用預設值")
|
||||
print("Warning: Missing cover data in JSON, using default values")
|
||||
cover_data = {"title": "寫真集", "subtitle": "", "description": ""}
|
||||
|
||||
# 檢查是否關閉封面排版
|
||||
if disable_cover:
|
||||
# 關閉封面排版:使用第一張圖片作為滿版封面(不含文字)
|
||||
print("✓ 關閉封面排版,使用第一張圖片作為滿版封面")
|
||||
# 關閉封面排版:使用Page一images作為滿版封面(不含文字)
|
||||
print("✓ Cover layout disabled, using first image as full bleed cover")
|
||||
if cover_image:
|
||||
# 創建滿版圖片
|
||||
full_bleed = self.create_full_bleed_image(cover_image, 210*mm, 297*mm)
|
||||
@@ -1903,77 +1903,77 @@ class PhotoMagazineMaker:
|
||||
self.draw_cover_page(c, magazine_data, cover_image, template_config, font_name, layout_name, compress_enabled)
|
||||
|
||||
c.showPage()
|
||||
print("封面繪製完成")
|
||||
print("Cover page complete")
|
||||
|
||||
# 內頁 - 使用智能分配
|
||||
print(f"JSON中有 {len(pages)} 個頁面數據,準備繪製所有內頁")
|
||||
# 內page - 使用智能分配
|
||||
print(f"JSON contains {len(pages)} page data, preparing to draw all content pages")
|
||||
|
||||
for i, page_data in enumerate(pages):
|
||||
# 使用智能分配的圖片索引
|
||||
if i < len(image_allocation["pages"]):
|
||||
page_img_idx = image_allocation["pages"][i]
|
||||
page_image = pil_images[page_img_idx]
|
||||
print(f"使用圖片 {page_img_idx} 作為第 {i+1} 頁")
|
||||
print(f"Using image {page_img_idx} as page {i+1} page")
|
||||
else:
|
||||
# 如果分配策略沒有足夠的圖片,使用第一張圖片
|
||||
# 如果分配策略沒有足夠的圖片,使用Page一images
|
||||
page_image = pil_images[0] if len(pil_images) > 0 else None
|
||||
print(f"第 {i+1} 頁使用備用圖片 0")
|
||||
print(f"Page {i+1} using fallback image 0")
|
||||
|
||||
# 驗證頁面數據
|
||||
# 驗證page面數據
|
||||
if not isinstance(page_data, dict):
|
||||
print(f"警告:第 {i+1} 頁數據格式錯誤,跳過")
|
||||
print(f"Warning: Page {i+1} data format error, skipping")
|
||||
continue
|
||||
|
||||
self.draw_content_page(c, page_data, page_image, template_config, font_name, layout_name, compress_enabled)
|
||||
c.showPage()
|
||||
print(f"第 {i+1} 頁繪製完成")
|
||||
print(f"Page {i+1} page complete")
|
||||
|
||||
# 故事頁 - 使用智能分配
|
||||
# 故事page - 使用智能分配
|
||||
story_data = magazine_data.get("story_page", {})
|
||||
# 確保 story_data 是字典類型
|
||||
if not isinstance(story_data, dict):
|
||||
print(f"警告:JSON中的story_page不是字典格式(類型: {type(story_data)}),使用預設值")
|
||||
print(f"Warning: story_page in JSON is not dict format (type: {type(story_data)}), using default values")
|
||||
story_data = {"title": "小故事", "content": ""}
|
||||
elif not story_data:
|
||||
print("警告:JSON中缺少story_page數據,使用預設值")
|
||||
print("Warning: Missing story_page data in JSON, using default values")
|
||||
story_data = {"title": "小故事", "content": ""}
|
||||
|
||||
story_image = None
|
||||
if image_allocation["story"] is not None:
|
||||
story_image = pil_images[image_allocation["story"]]
|
||||
print(f"使用圖片 {image_allocation['story']} 作為故事頁")
|
||||
print(f"Using image {image_allocation['story']} as story page")
|
||||
else:
|
||||
print("警告:故事頁沒有對應圖片")
|
||||
print("警告:故事page沒有對應圖片")
|
||||
|
||||
self.draw_story_page(c, story_data, story_image, template_config, font_name, layout_name, compress_enabled)
|
||||
c.showPage()
|
||||
print("故事頁繪製完成")
|
||||
print("Story page complete")
|
||||
|
||||
# 頁尾圖片準備 - 使用智能分配
|
||||
# page尾圖片準備 - 使用智能分配
|
||||
footer_images = []
|
||||
print(f"準備{layout_name}的頁尾圖片")
|
||||
print(f"準備{layout_name}的page尾圖片")
|
||||
|
||||
for idx in image_allocation["footer"]:
|
||||
if idx < len(pil_images):
|
||||
footer_images.append(pil_images[idx])
|
||||
print(f"使用圖片 {idx} 作為頁尾圖片")
|
||||
print(f"Using image {idx} 作為page尾圖片")
|
||||
|
||||
print(f"頁尾圖片準備完成,共 {len(footer_images)} 張")
|
||||
print(f"page尾圖片準備完成,共 {len(footer_images)} 張")
|
||||
|
||||
# 尾頁(所有版型都需要尾頁)
|
||||
# 尾page(所有版型都需要尾page)
|
||||
back_data = magazine_data.get("back_cover", {})
|
||||
# 確保 back_data 是字典類型
|
||||
if not isinstance(back_data, dict):
|
||||
print(f"警告:JSON中的back_cover不是字典格式(類型: {type(back_data)}),使用預設值")
|
||||
print(f"警告:JSON中的back_cover不是字典格式(類型: {type(back_data)}), using default values")
|
||||
back_data = {"title": "精選回顧", "description": ""}
|
||||
elif not back_data:
|
||||
print("警告:JSON中缺少back_cover數據,使用預設值")
|
||||
back_data = {"title": "精選回顧", "description": ""}
|
||||
|
||||
print(f"準備繪製{layout_name}尾頁")
|
||||
print(f"準備繪製{layout_name}尾page")
|
||||
self.draw_back_cover(c, back_data, footer_images, template_config, font_name, layout_name, compress_enabled)
|
||||
c.showPage()
|
||||
print("尾頁繪製完成")
|
||||
print("Back cover complete")
|
||||
|
||||
# 儲存PDF
|
||||
c.save()
|
||||
@@ -1983,7 +1983,7 @@ class PhotoMagazineMaker:
|
||||
return (f"寫真雜誌生成成功!已儲存至:{output_file}",)
|
||||
|
||||
except Exception as e:
|
||||
return (f"生成寫真雜誌時發生錯誤:{str(e)}",)
|
||||
return (f"Error generating photo magazine:{str(e)}",)
|
||||
|
||||
|
||||
# 節點註冊
|
||||
@@ -1994,7 +1994,7 @@ NODE_CLASS_MAPPINGS = {
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"PhotoMagazinePromptGenerator": "📝 寫真雜誌提示詞注入器",
|
||||
"PhotoMagazineParser": "🔍 JSON → 提示詞列表",
|
||||
"PhotoMagazinePromptGenerator": "📝 寫真雜誌prompt注入器",
|
||||
"PhotoMagazineParser": "🔍 JSON → prompt列表",
|
||||
"PhotoMagazineMaker": "📄 寫真雜誌製作器"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
"""
|
||||
批量替換 photo_magazine_generator.py 中的中文訊息為英文
|
||||
"""
|
||||
|
||||
import re
|
||||
|
||||
# 中文到英文的映射
|
||||
translations = {
|
||||
# PhotoMagazinePromptGenerator
|
||||
"📸 檢測到參考圖片,建議使用 LLM 節點提取人物特徵": "📸 Reference image detected, recommend using LLM node to extract person features",
|
||||
"自動注入 {EXTRACT_FROM_IMAGE} 佔位符": "Auto-injecting {EXTRACT_FROM_IMAGE} placeholder",
|
||||
"模板:": "Template:",
|
||||
"模特兒:": "Model:",
|
||||
"風格:": "Style:",
|
||||
"場景:": "Scene:",
|
||||
"頁數:": "Pages:",
|
||||
"特徵:": "Features:",
|
||||
|
||||
# PhotoMagazineParser
|
||||
"📝 開始解析 LLM 輸出的 JSON...": "📝 Starting to parse LLM JSON output...",
|
||||
"✅ 解析完成!提取到": "✅ Parsing complete! Extracted",
|
||||
"個圖片提示詞": "image prompts",
|
||||
"錯誤:JSON 輸入為空": "Error: JSON input is empty",
|
||||
"錯誤:": "Error:",
|
||||
"警告:未找到任何 image_prompt": "Warning: No image_prompt found",
|
||||
"解析錯誤:": "Parse error:",
|
||||
" ✓ 封面提示詞": " ✓ Cover prompt",
|
||||
" ✓ 頁面": " ✓ Page",
|
||||
"提示詞": "prompt",
|
||||
" ✓ 故事頁提示詞": " ✓ Story page prompt",
|
||||
|
||||
# PhotoMagazineMaker
|
||||
"成功解析JSON,包含": "Successfully parsed JSON, contains",
|
||||
"個頁面": "pages",
|
||||
"開始轉換圖片,圖片類型:": "Starting image conversion, image type:",
|
||||
"轉換圖片": "Converting image",
|
||||
"轉換批次圖片": "Converting batch image",
|
||||
"轉換單張圖片:": "Converting single image:",
|
||||
"成功轉換": "Successfully converted",
|
||||
"張圖片": "images",
|
||||
"錯誤:沒有有效的圖片可以處理": "Error: No valid images to process",
|
||||
"總共需要": "Total required",
|
||||
"個頁面,可用圖片": "pages, available images",
|
||||
"圖片分配策略:": "Image allocation strategy:",
|
||||
"使用圖片": "Using image",
|
||||
"作為封面": "as cover",
|
||||
"警告:沒有圖片可用作封面": "Warning: No image available for cover",
|
||||
"警告:JSON中的cover不是字典格式(類型:": "Warning: cover in JSON is not dict format (type:",
|
||||
"),使用預設值": "), using default values",
|
||||
"警告:JSON中缺少cover數據,使用預設值": "Warning: Missing cover data in JSON, using default values",
|
||||
"✓ 關閉封面排版,使用第一張圖片作為滿版封面": "✓ Cover layout disabled, using first image as full bleed cover",
|
||||
"封面繪製完成": "Cover page complete",
|
||||
"JSON中有": "JSON contains",
|
||||
"個頁面數據,準備繪製所有內頁": "page data, preparing to draw all content pages",
|
||||
"作為第": "as page",
|
||||
"頁": "page",
|
||||
"頁使用備用圖片": "using fallback image",
|
||||
"警告:第": "Warning: Page",
|
||||
"頁數據格式錯誤,跳過": "data format error, skipping",
|
||||
"頁繪製完成": "page complete",
|
||||
"第": "Page",
|
||||
"警告:JSON中的story_page不是字典格式(類型:": "Warning: story_page in JSON is not dict format (type:",
|
||||
"警告:JSON中缺少story_page數據,使用預設值": "Warning: Missing story_page data in JSON, using default values",
|
||||
"作為故事頁": "as story page",
|
||||
"故事頁繪製完成": "Story page complete",
|
||||
"作為尾頁": "as back cover",
|
||||
"尾頁繪製完成": "Back cover complete",
|
||||
"PDF 生成成功!": "PDF generated successfully!",
|
||||
"檔案位置:": "File location:",
|
||||
"生成寫真雜誌時發生錯誤:": "Error generating photo magazine:",
|
||||
"JSON 解析錯誤:": "JSON parse error:",
|
||||
",接收到的數據:": ", received data:",
|
||||
"JSON格式錯誤:期望字典格式,收到": "JSON format error: expected dict, received",
|
||||
"JSON格式錯誤:pages必須是列表格式,收到": "JSON format error: pages must be list, received",
|
||||
"JSON數據錯誤:pages列表為空,無法生成雜誌": "JSON data error: pages list is empty, cannot generate magazine",
|
||||
"圖片分配結果:": "Image allocation result:",
|
||||
"封面=": "cover=",
|
||||
"內頁=": "pages=",
|
||||
"故事頁=": "story=",
|
||||
"尾頁=": "footer=",
|
||||
"版型B顯示了": "Layout B displayed",
|
||||
"行文字,字體大小:": "lines of text, font size:",
|
||||
"行高:": "line height:",
|
||||
"框高:": "box height:",
|
||||
"最大行數:": "max lines:",
|
||||
"滿版圖片創建錯誤:": "Full bleed image creation error:",
|
||||
"圖片轉換錯誤:": "Image conversion error:",
|
||||
}
|
||||
|
||||
def replace_chinese_messages(file_path):
|
||||
"""替換文件中的中文訊息"""
|
||||
with open(file_path, 'r', encoding='utf-8') as f:
|
||||
content = f.read()
|
||||
|
||||
# 按照從長到短的順序替換,避免部分匹配
|
||||
sorted_translations = sorted(translations.items(), key=lambda x: len(x[0]), reverse=True)
|
||||
|
||||
for chinese, english in sorted_translations:
|
||||
content = content.replace(chinese, english)
|
||||
|
||||
with open(file_path, 'w', encoding='utf-8') as f:
|
||||
f.write(content)
|
||||
|
||||
print(f"✓ Replaced Chinese messages in {file_path}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
file_path = r"f:\CUI\ComfyUI\custom_nodes\ComfyUI-ListHelper\photo_magazine_generator.py"
|
||||
replace_chinese_messages(file_path)
|
||||
print("✓ All Chinese messages replaced with English")
|
||||
Reference in New Issue
Block a user