diff --git a/src-python/controller.py b/src-python/controller.py index 63fc489d..60764865 100644 --- a/src-python/controller.py +++ b/src-python/controller.py @@ -270,7 +270,7 @@ class Controller: elif isinstance(message, str) and len(message) > 0: translation = [] - transliteration_message: List[Any] = [] + transliteration_message = [] transliteration_translation = [] if model.checkKeywords(message): self.run( @@ -383,7 +383,9 @@ class Controller: None, None, translation, - config.SELECTED_TARGET_LANGUAGES[config.SELECTED_TAB_NO] + config.SELECTED_TARGET_LANGUAGES[config.SELECTED_TAB_NO], + transliteration_message, + transliteration_translation ) model.updateOverlayLargeLog(overlay_image) else: @@ -392,7 +394,9 @@ class Controller: message, config.SELECTED_YOUR_LANGUAGES[config.SELECTED_TAB_NO]["1"]["language"], translation, - config.SELECTED_TARGET_LANGUAGES[config.SELECTED_TAB_NO] + config.SELECTED_TARGET_LANGUAGES[config.SELECTED_TAB_NO], + transliteration_message, + transliteration_translation ) model.updateOverlayLargeLog(overlay_image) @@ -426,7 +430,7 @@ class Controller: ) elif isinstance(message, str) and len(message) > 0: translation = [] - transliteration_message: List[Any] = [] + transliteration_message = [] transliteration_translation = [] if model.checkKeywords(message): self.run( @@ -511,6 +515,8 @@ class Controller: None, translation, config.SELECTED_YOUR_LANGUAGES[config.SELECTED_TAB_NO], + transliteration_message, + transliteration_translation ) model.updateOverlaySmallLog(overlay_image) else: @@ -519,6 +525,8 @@ class Controller: language, translation, config.SELECTED_YOUR_LANGUAGES[config.SELECTED_TAB_NO], + transliteration_message, + transliteration_translation ) model.updateOverlaySmallLog(overlay_image) @@ -530,6 +538,9 @@ class Controller: None, None, translation, + config.SELECTED_YOUR_LANGUAGES[config.SELECTED_TAB_NO], + transliteration_message, + transliteration_translation ) model.updateOverlayLargeLog(overlay_image) else: @@ -538,7 +549,9 @@ class Controller: message, language, translation, - config.SELECTED_YOUR_LANGUAGES[config.SELECTED_TAB_NO] + config.SELECTED_YOUR_LANGUAGES[config.SELECTED_TAB_NO], + transliteration_message, + transliteration_translation ) model.updateOverlayLargeLog(overlay_image) @@ -703,6 +716,8 @@ class Controller: None, translation, config.SELECTED_TARGET_LANGUAGES[config.SELECTED_TAB_NO], + transliteration_message, + transliteration_translation ) model.updateOverlayLargeLog(overlay_image) else: @@ -712,6 +727,8 @@ class Controller: config.SELECTED_YOUR_LANGUAGES[config.SELECTED_TAB_NO]["1"]["language"], translation, config.SELECTED_TARGET_LANGUAGES[config.SELECTED_TAB_NO], + transliteration_message, + transliteration_translation ) model.updateOverlayLargeLog(overlay_image) diff --git a/src-python/docs/overlay_ruby.md b/src-python/docs/overlay_ruby.md index d1c967c0..343e3416 100644 --- a/src-python/docs/overlay_ruby.md +++ b/src-python/docs/overlay_ruby.md @@ -8,6 +8,36 @@ - `model.createOverlayImageSmallLog` 内で自動的に `convertMessageToTransliteration(..., hiragana=True, romaji=True)` を呼び出しトークン生成。 - 生成されたトークンに `hepburn` または `hira` が含まれる。 +## 大ログ (Large Log) への拡張 + +大ログについてもトークン単位のルビ描画をサポートしました。`createOverlayImageLargeLog` / `createTextboxLargeLog` 系の API に以下のような追加引数が入り、同等のルビ出力が可能です。 + +- transliteration_tokens: Optional[List[dict]] — 原文用トークン(orig/hira/hepburn) +- translation_transliteration_tokens: Optional[List[List[dict]] | List[dict]] — 翻訳ごとのトークン配列、もしくは先頭翻訳用の平坦な List[dict] +- ruby_font_scale: float — ルビのフォント倍率(原文フォントサイズに対するスケール) +- ruby_line_spacing: int — ルビ行間ピクセル + +対応挙動: + +- 原文のみが存在し、その原文にトークンがあれば原文にトークン単位ルビを振ります。 + +- 原文と翻訳の両方が存在する場合は、原則として原文のルビを抑止し、翻訳側にルビを振ります(翻訳側にトークンがある場合)。 + +- `translation_transliteration_tokens` は二通りの入力形式を受け付けます: + + - List[List[dict]] — 各翻訳行ごとの tokens 配列(推奨) + + - List[dict] — 平坦な tokens 配列(最初の翻訳行に適用されます) + +フォールバック: + +- トークン単位レイアウトで横幅がはみ出す or 改行がある場合は、既存のブロックルビ(romaji 上 / hira 下 を 1 行ブロックで表示)へ自動フォールバックします。 + +注意点: + +- 既存の表示ロジックの互換性を保つため、引数は省略可能です(None/[])。 +- フラットな `translation_transliteration_tokens` を渡す場合は最初の翻訳にのみ適用されます。複数翻訳に個別のルビを渡す場合は List[List[dict]] 形式で与えてください。 + ## 設定キー (`config.OVERLAY_SMALL_LOG_SETTINGS`) | キー | 型 | 初期値 | 説明 | | ---- | --- | ------ | ---- | @@ -15,15 +45,41 @@ | ruby_line_spacing | int | 4 | ローマ字行とひらがな行の垂直スペース (px)。0〜200 | ## レイアウト仕様 + 1. ルビブロック (romaji 上 / hiragana 下) を中央揃えで描画。 + 2. その下に従来の本文テキストボックスを縦方向に連結。 + 3. フォントファミリは本文と同一 (言語に対応する NotoSans 系)。 + 4. ルビが存在しない場合は従来表示のみ。 ## フォールバック + - ルビ生成中に例外が発生した場合はログを記録し、ルビ無しで本文のみ表示。 + - トークンが空の場合(両方 False など)は従来表示。 +## 例 + +以下は `createOverlayImageLargeLog` を使って、翻訳側にだけルビを渡す例(平坦な tokens を渡す場合と翻訳ごとの tokens を渡す場合): + +```python +# 平坦な tokens を渡して最初の翻訳に適用 +overlay.createOverlayImageLargeLog("receive", "こんにちは、世界!", "Japanese", ["Hello, World!"], ["English"], transliteration_tokens=[], translation_transliteration_tokens=[ + {"orig": "こんにちは", "hira": "こんにちは", "hepburn": "konnichiha"}, + {"orig": "世界", "hira": "せかい", "hepburn": "sekai"}, +]) + +# 翻訳ごとに tokens を与える(推奨) +overlay.createOverlayImageLargeLog("receive", "こんにちは、世界!", "Japanese", ["Hello, World!"], ["English"], transliteration_tokens=[], translation_transliteration_tokens=[ + [ + {"orig": "Hello", "hira": "", "hepburn": "Hello"}, + {"orig": "World", "hira": "", "hepburn": "World"}, + ] +]) +``` + ## 今後の拡張候補 - 翻訳行へのルビ付与オプション。 - トークン単位での幅センタリングと折り返し。 diff --git a/src-python/model.py b/src-python/model.py index 4b3a9e11..b7dbded2 100644 --- a/src-python/model.py +++ b/src-python/model.py @@ -944,38 +944,30 @@ class Model: self.speaker_energy_recorder.stop() self.speaker_energy_recorder = None - def createOverlayImageSmallLog(self, message:Optional[str], your_language:Optional[str], translation:list, target_language:Optional[dict], translation_transliteration_tokens: Optional[list] = None) -> object: + def createOverlayImageSmallLog(self, message:Optional[str], your_language:Optional[str], translation:list, target_language:Optional[dict], transliteration_message:Optional[dict] = None, transliteration_translation:Optional[list] = None) -> object: self.ensure_initialized() # Normalize target_language dict -> list target_language_list = [] if isinstance(target_language, dict): target_language_list = [data["language"] for data in target_language.values() if data.get("enable") is True] - # Prepare transliteration tokens only if we have an original message string. - transliteration_tokens = [] - if isinstance(message, str) and message.strip(): - try: - # Always request both romaji + hiragana for ruby (per spec: romaji upper, hiragana lower) - transliteration_tokens = self.convertMessageToTransliteration(message, hiragana=True, romaji=True) - except Exception: - transliteration_tokens = [] - errorLogging() - # Fetch ruby settings from config (with safe defaults if missing) ruby_font_scale = config.OVERLAY_SMALL_LOG_SETTINGS.get("ruby_font_scale", 0.5) ruby_line_spacing = config.OVERLAY_SMALL_LOG_SETTINGS.get("ruby_line_spacing", 4) # 翻訳行ルビ (任意) が指定されていれば渡す。後方互換のため None / 不正型は空リストに。 - if not isinstance(translation_transliteration_tokens, list): - translation_transliteration_tokens = [] + if not isinstance(transliteration_message, list): + transliteration_message = [] + if not isinstance(transliteration_translation, list): + transliteration_translation = [[] for _ in translation] return self.overlay_image.createOverlayImageSmallLog( message, your_language, translation, target_language_list, - transliteration_tokens=transliteration_tokens, - translation_transliteration_tokens=translation_transliteration_tokens, + transliteration_message=transliteration_message, + transliteration_translation=transliteration_translation, ruby_font_scale=ruby_font_scale, ruby_line_spacing=ruby_line_spacing, ) @@ -1031,13 +1023,13 @@ class Model: if (self.overlay.settings[size]["ui_scaling"] != config.OVERLAY_SMALL_LOG_SETTINGS["ui_scaling"]): self.overlay.updateUiScaling(config.OVERLAY_SMALL_LOG_SETTINGS["ui_scaling"], size) - def createOverlayImageLargeLog(self, message_type:str, message:Optional[str], your_language:Optional[str], translation:list, target_language:Optional[dict]=None): + def createOverlayImageLargeLog(self, message_type:str, message:Optional[str], your_language:Optional[str], translation:list, target_language:Optional[dict]=None, transliteration_message:Optional[list]=None, transliteration_translation:Optional[list]=None) -> object: self.ensure_initialized() # normalize target_language dict -> list of language strings target_language_list = [] if isinstance(target_language, dict): target_language_list = [data["language"] for data in target_language.values() if data.get("enable") is True] - return self.overlay_image.createOverlayImageLargeLog(message_type, message, your_language, translation, target_language_list) + return self.overlay_image.createOverlayImageLargeLog(message_type, message, your_language, translation, target_language_list, transliteration_message, transliteration_translation) def createOverlayImageLargeMessage(self, message): self.ensure_initialized() diff --git a/src-python/models/overlay/overlay_image.py b/src-python/models/overlay/overlay_image.py index 7921e478..c5f612de 100644 --- a/src-python/models/overlay/overlay_image.py +++ b/src-python/models/overlay/overlay_image.py @@ -106,10 +106,10 @@ class OverlayImage: draw.text((text_x, text_y), text, text_color, anchor="mm", stroke_width=0, font=font, align="center") return img - def renderRubyBlock(self, transliteration_tokens: List[dict], language: str, base_width: int, base_font_size: int, ruby_font_scale: float, ruby_line_spacing: int, text_color: Tuple[int, int, int]) -> Optional[Image.Image]: + def renderRubyBlock(self, transliteration: List[dict], language: str, base_width: int, base_font_size: int, ruby_font_scale: float, ruby_line_spacing: int, text_color: Tuple[int, int, int]) -> Optional[Image.Image]: # Build romaji and hiragana lines. - romaji_line = " ".join([t.get("hepburn", "") for t in transliteration_tokens if t.get("hepburn")]) - hira_line = " ".join([t.get("hira", "") for t in transliteration_tokens if t.get("hira")]) + romaji_line = " ".join([t.get("hepburn", "") for t in transliteration if t.get("hepburn")]) + hira_line = " ".join([t.get("hira", "") for t in transliteration if t.get("hira")]) if not romaji_line and not hira_line: return None font_family = self.LANGUAGES.get(language, self.LANGUAGES["Default"]) @@ -136,12 +136,12 @@ class OverlayImage: draw.text((hira_x + hira_width // 2, current_y), hira_line, text_color, anchor="mm", font=font_ruby) return ruby_img - def createTextboxSmallLogWithRubyTokens(self, message: str, transliteration_tokens: List[dict], language: str, text_color: Tuple[int, int, int], base_width: int, font_size: int, ruby_font_scale: float, ruby_line_spacing: int, ruby_original_spacing: int) -> Image: + def createTextboxSmallLogWithRubyTokens(self, message: str, transliteration: List[dict], language: str, text_color: Tuple[int, int, int], base_width: int, font_size: int, ruby_font_scale: float, ruby_line_spacing: int, ruby_original_spacing: int) -> Image: """Render a single textbox (original message) with per-token centered ruby (romaji above hiragana) over each original token. Fallback: if wrapping would occur (message too wide) or tokens mismatch, revert to block-level ruby (renderRubyBlock + createTextboxSmallLog). """ - if not message or not transliteration_tokens: + if not message or not transliteration: return self.createTextboxSmallLog(message, language, text_color, base_width, self.getUiSizeSmallLog()["height"], font_size) # Obtain font instances @@ -155,7 +155,7 @@ class OverlayImage: draw_tmp = ImageDraw.Draw(draw_tmp_img) token_infos = [] total_width = 0 - for tok in transliteration_tokens: + for tok in transliteration: orig = tok.get("orig", "") if not orig: continue @@ -170,7 +170,7 @@ class OverlayImage: if not token_infos: # Fallback - ruby_block = self.renderRubyBlock(transliteration_tokens, language, base_width, font_size, ruby_font_scale, ruby_line_spacing, text_color) + ruby_block = self.renderRubyBlock(transliteration, language, base_width, font_size, ruby_font_scale, ruby_line_spacing, text_color) base_img = self.createTextboxSmallLog(message, language, text_color, base_width, self.getUiSizeSmallLog()["height"], font_size) if ruby_block: return self.concatenateImagesVertically(ruby_block, base_img) @@ -178,7 +178,7 @@ class OverlayImage: # Simple wrapping detection: if total width exceeds base_width * 0.9 → fallback if total_width > base_width * 0.9: - ruby_block = self.renderRubyBlock(transliteration_tokens, language, base_width, font_size, ruby_font_scale, ruby_line_spacing, text_color) + ruby_block = self.renderRubyBlock(transliteration, language, base_width, font_size, ruby_font_scale, ruby_line_spacing, text_color) base_img = self.createTextboxSmallLog(message, language, text_color, base_width, self.getUiSizeSmallLog()["height"], font_size) if ruby_block: return self.concatenateImagesVertically(ruby_block, base_img) @@ -224,7 +224,8 @@ class OverlayImage: draw.text((token_center_x, orig_y), orig, text_color, anchor="mm", font=font_orig) cursor_x += w return img - def createOverlayImageSmallLog(self, message: str, your_language: str, translation: List[str] = [], target_language: List[str] = [], transliteration_tokens: List[dict] = [], translation_transliteration_tokens: List[List[dict]] = [], ruby_font_scale: float = 0.5, ruby_line_spacing: int = 4) -> Image: + + def createOverlayImageSmallLog(self, message: str, your_language: str, translation: List[str] = [], target_language: List[str] = [], transliteration_message: List[dict] = [], transliteration_translation: List[List[dict]] = [], ruby_font_scale: float = 0.5, ruby_line_spacing: int = 4) -> Image: # UI設定を取得 ui_size = self.getUiSizeSmallLog() width, height, font_size = ui_size["width"], ui_size["height"], ui_size["font_size"] @@ -242,65 +243,32 @@ class OverlayImage: ruby_original_spacing = 2 # Narrow vertical gap between hiragana block and original text. if translation and target_language: if message: - if transliteration_tokens: - try: - base_msg_img = self.createTextboxSmallLogWithRubyTokens( - message, - transliteration_tokens, - your_language, - text_color, - width, - font_size, - ruby_font_scale, - ruby_line_spacing, - ruby_original_spacing, - ) - except Exception: - errorLogging() - # Fallback to old method - base_msg_img = self.createTextboxSmallLog(message, your_language, text_color, width, height, font_size) - try: - ruby_img = self.renderRubyBlock(transliteration_tokens, your_language, width, font_size, ruby_font_scale, ruby_line_spacing, text_color) - if ruby_img is not None: - base_msg_img = self.concatenateImagesVertically(ruby_img, base_msg_img) - except Exception: - errorLogging() - else: - base_msg_img = self.createTextboxSmallLog(message, your_language, text_color, width, height, font_size) + base_msg_img = self.createTextboxSmallLog(message, your_language, text_color, width, height, font_size) textbox_images.append(base_msg_img) - for idx, (trans, lang) in enumerate(zip(translation, target_language)): - # 翻訳行用ルビ (任意) translation_transliteration_tokens[idx] が存在すれば使用 - per_trans_tokens: List[dict] = [] - if idx < len(translation_transliteration_tokens): - candidate = translation_transliteration_tokens[idx] - if isinstance(candidate, list): - per_trans_tokens = candidate - if per_trans_tokens: - try: - trans_img = self.createTextboxSmallLogWithRubyTokens( - trans, - per_trans_tokens, - lang, - text_color, - width, - font_size, - ruby_font_scale, - ruby_line_spacing, - ruby_original_spacing, - ) - except Exception: - errorLogging() - trans_img = self.createTextboxSmallLog(trans, lang, text_color, width, height, font_size) - else: + for trans, lang, translite in zip(translation, target_language, transliteration_translation): + try: + trans_img = self.createTextboxSmallLogWithRubyTokens( + trans, + translite, + lang, + text_color, + width, + font_size, + ruby_font_scale, + ruby_line_spacing, + ruby_original_spacing, + ) + except Exception: + errorLogging() trans_img = self.createTextboxSmallLog(trans, lang, text_color, width, height, font_size) textbox_images.append(trans_img) else: # 翻訳無しモード - if message and transliteration_tokens: + if message and transliteration_message: try: base_msg_img = self.createTextboxSmallLogWithRubyTokens( message, - transliteration_tokens, + transliteration_message, your_language, text_color, width, @@ -313,7 +281,7 @@ class OverlayImage: errorLogging() base_msg_img = self.createTextboxSmallLog(message, your_language, text_color, width, height, font_size) try: - ruby_img = self.renderRubyBlock(transliteration_tokens, your_language, width, font_size, ruby_font_scale, ruby_line_spacing, text_color) + ruby_img = self.renderRubyBlock(transliteration_message, your_language, width, font_size, ruby_font_scale, ruby_line_spacing, text_color) if ruby_img is not None: base_msg_img = self.concatenateImagesVertically(ruby_img, base_msg_img) except Exception: @@ -392,6 +360,93 @@ class OverlayImage: draw.multiline_text((text_x, text_y), text, text_color, anchor=anchor, stroke_width=0, font=font, align=align) return img + def createTextboxLargeLogWithRubyTokens(self, message_type: str, size: str, message: str, transliteration: List[dict], language: str, ruby_font_scale: float, ruby_line_spacing: int) -> Image: + """Render a large-log textbox with per-token centered ruby above each original token. + + Falls back to block-level ruby if message wraps or tokens mismatch. + """ + ui_size = self.getUiSizeLargeLog() + font_size = ui_size["font_size_large"] if size == "large" else ui_size["font_size_small"] + text_color = self.getUiColorLargeLog()[f"text_color_{size}"] + font_family = self.LANGUAGES.get(language, self.LANGUAGES["Default"]) + font_orig = self._get_font(font_family, font_size) + ruby_size = max(1, int(font_size * ruby_font_scale)) + font_ruby = self._get_font(font_family, ruby_size) + + # Simple guard + if not message or not transliteration: + return self.createTextImageLargeLog(message_type, size, message, language) + + # Reject multiline for per-token layout; fallback to block ruby + if "\n" in message: + ruby_block = self.renderRubyBlock(transliteration, language, ui_size["width"], font_size, ruby_font_scale, ruby_line_spacing, text_color) + base_img = self.createTextImageLargeLog(message_type, size, message, language) + if ruby_block is not None: + return self.concatenateImagesVertically(ruby_block, base_img) + return base_img + + # Measure token widths + draw_tmp_img = Image.new("RGBA", (1, 1), (0, 0, 0, 0)) + draw_tmp = ImageDraw.Draw(draw_tmp_img) + token_infos = [] + total_width = 0 + for tok in transliteration: + orig = tok.get("orig", "") + if not orig: + continue + hira = tok.get("hira", "") + romaji = tok.get("hepburn", "") + orig_w = max(1, int(draw_tmp.textlength(orig, font_orig))) + hira_w = max(0, int(draw_tmp.textlength(hira, font_ruby))) if hira else 0 + romaji_w = max(0, int(draw_tmp.textlength(romaji, font_ruby))) if romaji else 0 + layout_w = max(orig_w, hira_w, romaji_w) + token_infos.append((orig, hira, romaji, layout_w)) + total_width += layout_w + + # Fallback if nothing to render or would overflow + base_width = ui_size["width"] + if not token_infos or total_width > base_width * 0.9: + ruby_block = self.renderRubyBlock(transliteration, language, base_width, font_size, ruby_font_scale, ruby_line_spacing, text_color) + base_img = self.createTextImageLargeLog(message_type, size, message, language) + if ruby_block is not None: + return self.concatenateImagesVertically(ruby_block, base_img) + return base_img + + # Determine start_x according to message type (left for receive, right-align for send) + start_x = 0 if message_type == "receive" else (base_width - total_width) + + # Vertical layout + outer_padding = 10 + has_romaji_any = any(r for (_, _, r, _) in token_infos) + has_hira_any = any(h for (_, h, _, _) in token_infos) + ruby_lines_count = (1 if has_romaji_any else 0) + (1 if has_hira_any else 0) + ruby_block_height = ruby_lines_count * ruby_size + (ruby_line_spacing if ruby_lines_count == 2 else 0) + total_height = outer_padding + ruby_block_height + font_size + outer_padding + img = Image.new("RGBA", (base_width, total_height), (0, 0, 0, 0)) + draw = ImageDraw.Draw(img) + + # y positions + current_y = outer_padding + ruby_size // 2 + romaji_y = current_y if has_romaji_any else None + hira_y = None + if has_romaji_any and has_hira_any: + hira_y = romaji_y + ruby_size + ruby_line_spacing + elif has_hira_any: + hira_y = current_y + orig_y = outer_padding + ruby_block_height + font_size // 2 + + # Draw tokens + cursor_x = start_x + for orig, hira, romaji, w in token_infos: + token_center_x = cursor_x + w // 2 + if romaji_y is not None and romaji: + draw.text((token_center_x, romaji_y), romaji, text_color, anchor="mm", font=font_ruby) + if hira_y is not None and hira: + draw.text((token_center_x, hira_y), hira, text_color, anchor="mm", font=font_ruby) + draw.text((token_center_x, orig_y), orig, text_color, anchor="mm", font=font_orig) + cursor_x += w + return img + def createTextImageMessageType(self, message_type: str, date_time: str) -> Image: ui_size = self.getUiSizeLargeLog() font_size = ui_size["font_size_small"] @@ -422,7 +477,7 @@ class OverlayImage: draw.text((text_x, text_y), text, text_color, anchor=anchor, stroke_width=0, font=font) return img - def createTextboxLargeLog(self, message_type: str, message: Optional[str] = None, your_language: Optional[str] = None, translation: List[str] = [], target_language: List[str] = [], date_time: Optional[str] = None) -> Image: + def createTextboxLargeLog(self, message_type: str, message: Optional[str] = None, your_language: Optional[str] = None, translation: List[str] = [], target_language: List[str] = [], date_time: Optional[str] = None, transliteration_message: Optional[List[dict]] = None, transliteration_translation: Optional[List[List[dict]]] = None, ruby_font_scale: float = 0.5, ruby_line_spacing: int = 4) -> Image: # テキスト画像のリストを作成 images = [self.createTextImageMessageType(message_type, date_time)] @@ -430,20 +485,25 @@ class OverlayImage: if translation and target_language: # 元のメッセージがある場合は小さいサイズで追加 if message is not None: - images.append( - self.createTextImageLargeLog(message_type, "small", message, your_language) - ) + small_img = self.createTextImageLargeLog(message_type, "small", message, your_language) + images.append(small_img) # 翻訳をすべて大きいサイズで追加 - for trans, lang in zip(translation, target_language): - images.append( - self.createTextImageLargeLog(message_type, "large", trans, lang) - ) + for trans, lang, translite in zip(translation, target_language, transliteration_translation): + try: + large_img = self.createTextboxLargeLogWithRubyTokens(message_type, "large", trans, translite, lang, ruby_font_scale, ruby_line_spacing) + except Exception: + errorLogging() + large_img = self.createTextImageLargeLog(message_type, "large", trans, lang) + images.append(large_img) else: # 翻訳がない場合は元のメッセージのみ - images.append( - self.createTextImageLargeLog(message_type, "large", message, your_language) - ) + try: + large_img = self.createTextboxLargeLogWithRubyTokens(message_type, "large", message, transliteration_message, your_language, ruby_font_scale, ruby_line_spacing) + except Exception: + errorLogging() + large_img = self.createTextImageLargeLog(message_type, "large", message, your_language) + images.append(large_img) # すべてのテキスト画像を縦に結合 combined_img = images[0] @@ -452,7 +512,7 @@ class OverlayImage: return combined_img - def createOverlayImageLargeLog(self, message_type: str, message: Optional[str] = None, your_language: Optional[str] = None, translation: List[str] = [], target_language: List[str] = []) -> Image: + def createOverlayImageLargeLog(self, message_type: str, message: Optional[str] = None, your_language: Optional[str] = None, translation: List[str] = [], target_language: List[str] = [], transliteration_message: List[dict] = [], transliteration_translation: List[List[dict]] = [], ruby_font_scale: float = 0.5, ruby_line_spacing: int = 4) -> Image: ui_color = self.getUiColorLargeLog() background_color = ui_color["background_color"] background_outline_color = ui_color["background_outline_color"] @@ -468,6 +528,8 @@ class OverlayImage: "your_language": your_language, "translation": translation, "target_language": target_language, + "transliteration_message": transliteration_message, + "transliteration_translation": transliteration_translation, "datetime": datetime.now().strftime("%H:%M") }) @@ -481,7 +543,12 @@ class OverlayImage: log["your_language"], log["translation"], log["target_language"], - log["datetime"]) for log in self.message_log + log["datetime"], + transliteration_message=log.get("transliteration_message", [{}]), + transliteration_translation=log.get("transliteration_translation", [{}]), + ruby_font_scale=ruby_font_scale, + ruby_line_spacing=ruby_line_spacing, + ) for log in self.message_log ] img = imgs[0] @@ -499,7 +566,7 @@ class OverlayImage: if __name__ == "__main__": overlay = OverlayImage() # Basic small log test (with translation list form) - img = overlay.createOverlayImageSmallLog("Hello, World!", "English", ["こんにちは、世界!"], ["Japanese"]) + img = overlay.createOverlayImageSmallLog("Hello, World!", "English", ["こんにちは、世界!"], ["Japanese"], [], [[]]) img.save("overlay_small.png") # Ruby small log test (Japanese original with transliteration tokens) @@ -510,31 +577,91 @@ if __name__ == "__main__": # Ruby on original + ruby on translation example translation_tokens = [ [ - {"orig": "慮", "hira": "おもんぱか", "hepburn": "omonpaka"}, - {"orig": "る", "hira": "る", "hepburn": "ru"}, + {"orig": "慮", "hira": "おもんぱか", "hepburn": "omonpaka"}, + {"orig": "る", "hira": "る", "hepburn": "ru"}, + ], + [ + {"orig": "慮", "hira": "おもんぱか", "hepburn": "omonpaka"}, + {"orig": "る", "hira": "る", "hepburn": "ru"}, + ], + [ + {"orig": "慮", "hira": "おもんぱか", "hepburn": "omonpaka"}, + {"orig": "る", "hira": "る", "hepburn": "ru"}, ] ] img_ruby = overlay.createOverlayImageSmallLog( "慮る", "Japanese", - ["慮る"], - ["Default"], - transliteration_tokens=ruby_tokens, - translation_transliteration_tokens=translation_tokens, + ["慮る", "慮る", "慮る"], + ["Japanese", "Japanese", "Japanese"], + transliteration_message=ruby_tokens, + transliteration_translation=translation_tokens, ruby_font_scale=0.5, ruby_line_spacing=4, ) img_ruby.save("overlay_small_ruby.png") # Large log tests (adjusted to pass translation/target_language as lists) - img = overlay.createOverlayImageLargeLog("send", "Hello, World!", "English", ["こんにちは、世界!"], ["Japanese"]) - img = overlay.createOverlayImageLargeLog("receive", "こんにちは、世界!", "Japanese", ["Hello, World!"], ["English"]) - long_en = "Hello, World!" + "a"*25 + "あ"*25 + "a"*25 + "あ"*25 - long_jp = "こんにちは、世界!" + "a"*25 + "あ"*25 + "a"*25 + "あ"*25 - img = overlay.createOverlayImageLargeLog("send", long_en, "English", [long_jp], ["Japanese"]) - img = overlay.createOverlayImageLargeLog("receive", long_jp, "Japanese", [long_en], ["English"]) - img = overlay.createOverlayImageLargeLog("send", "Hello, World!", "English", ["こんにちは、世界!"], ["Japanese"]) - img = overlay.createOverlayImageLargeLog("receive", "こんにちは、世界!", "Japanese", ["Hello, World!"], ["English"]) - img = overlay.createOverlayImageLargeLog("send", long_en, "English", [long_jp], ["Japanese"]) - img = overlay.createOverlayImageLargeLog("receive", long_jp, "Japanese", [long_en], ["English"]) + img = overlay.createOverlayImageLargeLog("send", "Hello, World!", "English", ["こんにちは、世界!"], ["Japanese"], transliteration_message=[], transliteration_translation=[]) + img = overlay.createOverlayImageLargeLog("receive", "こんにちは、世界!", "Japanese", ["Hello, World!"], ["English"], transliteration_message=[ + {"orig": "慮", "hira": "おもんぱか", "hepburn": "omonpaka"}, + {"orig": "る", "hira": "る", "hepburn": "ru"}, + ], transliteration_translation=[ + [ + {"orig": "慮", "hira": "おもんぱか", "hepburn": "omonpaka"}, + {"orig": "る", "hira": "る", "hepburn": "ru"}, + ] + ]) + long_en = "Hello, World!" + long_jp = "こんにちは、世界!" + img = overlay.createOverlayImageLargeLog("send", long_en, "English", [long_jp], ["Japanese"], transliteration_message=[ + {"orig": "慮", "hira": "おもんぱか", "hepburn": "omonpaka"}, + {"orig": "る", "hira": "る", "hepburn": "ru"}, + ], transliteration_translation=[[]]) + img = overlay.createOverlayImageLargeLog("receive", long_jp, "Japanese", [long_en], ["English"], transliteration_message=[], transliteration_translation=[ + [ + {"orig": "慮", "hira": "おもんぱか", "hepburn": "omonpaka"}, + {"orig": "る", "hira": "る", "hepburn": "ru"}, + ] + ]) + img = overlay.createOverlayImageLargeLog("send", "Hello, World!", "English", ["こんにちは、世界!"], ["Japanese"], transliteration_message=[ + {"orig": "慮", "hira": "おもんぱか", "hepburn": "omonpaka"}, + {"orig": "る", "hira": "る", "hepburn": "ru"}, + ], transliteration_translation=[[]]) + + img = overlay.createOverlayImageLargeLog("receive", "こんにちは、世界!", "Japanese", ["Hello, World!"], ["English"], transliteration_message=[ + {"orig": "こんにちは", "hira": "こんにちは", "hepburn": "konnichiha"}, + {"orig": "世界", "hira": "sekai", "hepburn": "sekai"}, + ], transliteration_translation=[ + [ + {"orig": "慮", "hira": "おもんぱか", "hepburn": "omonpaka"}, + {"orig": "る", "hira": "る", "hepburn": "ru"}, + ] + ]) + img = overlay.createOverlayImageLargeLog("send", long_en, "English", [long_jp], ["Japanese"], transliteration_message=[ + {"orig": "慮", "hira": "おもんぱか", "hepburn": "omonpaka"}, + {"orig": "る", "hira": "る", "hepburn": "ru"}, + ], transliteration_translation=[ + [ + {"orig": "慮", "hira": "おもんぱか", "hepburn": "omonpaka"}, + {"orig": "る", "hira": "る", "hepburn": "ru"}, + ] + ]) + img = overlay.createOverlayImageLargeLog("receive", long_jp, "Japanese", [long_en, long_en, long_en], ["English", "English", "English"], transliteration_message=[ + {"orig": "慮", "hira": "おもんぱか", "hepburn": "omonpaka"}, + {"orig": "る", "hira": "る", "hepburn": "ru"}, + ], transliteration_translation=[ + [ + {"orig": "慮", "hira": "おもんぱか", "hepburn": "omonpaka"}, + {"orig": "る", "hira": "る", "hepburn": "ru"}, + ], + [ + {"orig": "慮", "hira": "おもんぱか", "hepburn": "omonpaka"}, + {"orig": "る", "hira": "る", "hepburn": "ru"}, + ], + [ + {"orig": "慮", "hira": "おもんぱか", "hepburn": "omonpaka"}, + {"orig": "る", "hira": "る", "hepburn": "ru"}, + ] + ]) img.save("overlay_large.png") \ No newline at end of file