overlay: ルビ表示の上下パディングを対称化(outer_padding導入)しテキスト周囲にUIパディングを追加、ルビ機能のドキュメントを追加
This commit is contained in:
41
src-python/docs/overlay_ruby.md
Normal file
41
src-python/docs/overlay_ruby.md
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
# 小型ログ ルビ表示機能 (Ruby Overlay for Small Log)
|
||||||
|
|
||||||
|
## 概要
|
||||||
|
小型ログ (Small Log Overlay) に日本語原文が含まれる場合、ローマ字(hepburn) を上段、ひらがな(hira) を下段として原文メッセージの上に 2 段のルビを表示できます。翻訳行には現段階ではルビを付与しません。
|
||||||
|
|
||||||
|
## 有効化条件
|
||||||
|
- 原文 `message` が存在し空文字列でない。
|
||||||
|
- `model.createOverlayImageSmallLog` 内で自動的に `convertMessageToTransliteration(..., hiragana=True, romaji=True)` を呼び出しトークン生成。
|
||||||
|
- 生成されたトークンに `hepburn` または `hira` が含まれる。
|
||||||
|
|
||||||
|
## 設定キー (`config.OVERLAY_SMALL_LOG_SETTINGS`)
|
||||||
|
| キー | 型 | 初期値 | 説明 |
|
||||||
|
| ---- | --- | ------ | ---- |
|
||||||
|
| ruby_font_scale | float | 0.5 | ルビ文字サイズ倍率 (原文フォントサイズ * 倍率)。安全範囲 0.05〜3.0 |
|
||||||
|
| ruby_line_spacing | int | 4 | ローマ字行とひらがな行の垂直スペース (px)。0〜200 |
|
||||||
|
|
||||||
|
## レイアウト仕様
|
||||||
|
1. ルビブロック (romaji 上 / hiragana 下) を中央揃えで描画。
|
||||||
|
2. その下に従来の本文テキストボックスを縦方向に連結。
|
||||||
|
3. フォントファミリは本文と同一 (言語に対応する NotoSans 系)。
|
||||||
|
4. ルビが存在しない場合は従来表示のみ。
|
||||||
|
|
||||||
|
## フォールバック
|
||||||
|
- ルビ生成中に例外が発生した場合はログを記録し、ルビ無しで本文のみ表示。
|
||||||
|
- トークンが空の場合(両方 False など)は従来表示。
|
||||||
|
|
||||||
|
## 今後の拡張候補
|
||||||
|
- 翻訳行へのルビ付与オプション。
|
||||||
|
- トークン単位での幅センタリングと折り返し。
|
||||||
|
- 高度な幅計測 (可変幅フォント対応改善)。
|
||||||
|
|
||||||
|
## 簡易テスト
|
||||||
|
`src-python/overlay_ruby_test.py` を実行すると `overlay_small_ruby_test.png` が生成され、縦順と配置を確認できます。
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# PowerShell (仮想環境有効化後)
|
||||||
|
python src-python/overlay_ruby_test.py
|
||||||
|
```
|
||||||
|
|
||||||
|
## 注意
|
||||||
|
UI スケーリングは OpenVR 側の表示サイズのみ変更し、画像内部フォントサイズは直接変更しません。ルビの視認性が低い場合は `ruby_font_scale` を調整してください。
|
||||||
@@ -115,18 +115,20 @@ class OverlayImage:
|
|||||||
font_family = self.LANGUAGES.get(language, self.LANGUAGES["Default"])
|
font_family = self.LANGUAGES.get(language, self.LANGUAGES["Default"])
|
||||||
ruby_size = max(1, int(base_font_size * ruby_font_scale))
|
ruby_size = max(1, int(base_font_size * ruby_font_scale))
|
||||||
font_ruby = self._get_font(font_family, ruby_size)
|
font_ruby = self._get_font(font_family, ruby_size)
|
||||||
|
# Symmetric outer padding so ruby block has breathing room top/bottom
|
||||||
|
outer_padding = 10
|
||||||
# Measure widths to center lines independently.
|
# Measure widths to center lines independently.
|
||||||
img_tmp = Image.new("RGBA", (base_width, ruby_size * 2 + ruby_line_spacing + 10), (0, 0, 0, 0))
|
img_tmp = Image.new("RGBA", (base_width, ruby_size * 2 + ruby_line_spacing + outer_padding * 2), (0, 0, 0, 0))
|
||||||
draw_tmp = ImageDraw.Draw(img_tmp)
|
draw_tmp = ImageDraw.Draw(img_tmp)
|
||||||
romaji_width = draw_tmp.textlength(romaji_line, font_ruby) if romaji_line else 0
|
romaji_width = draw_tmp.textlength(romaji_line, font_ruby) if romaji_line else 0
|
||||||
hira_width = draw_tmp.textlength(hira_line, font_ruby) if hira_line else 0
|
hira_width = draw_tmp.textlength(hira_line, font_ruby) if hira_line else 0
|
||||||
romaji_x = (base_width - romaji_width) // 2
|
romaji_x = (base_width - romaji_width) // 2
|
||||||
hira_x = (base_width - hira_width) // 2
|
hira_x = (base_width - hira_width) // 2
|
||||||
# Construct final ruby image.
|
# Construct final ruby image with symmetric padding
|
||||||
ruby_height = ruby_size * (2 if hira_line and romaji_line else 1) + (ruby_line_spacing if hira_line and romaji_line else 0) + 10
|
ruby_height = outer_padding + ruby_size * (2 if hira_line and romaji_line else 1) + (ruby_line_spacing if hira_line and romaji_line else 0) + outer_padding
|
||||||
ruby_img = Image.new("RGBA", (base_width, ruby_height), (0, 0, 0, 0))
|
ruby_img = Image.new("RGBA", (base_width, ruby_height), (0, 0, 0, 0))
|
||||||
draw = ImageDraw.Draw(ruby_img)
|
draw = ImageDraw.Draw(ruby_img)
|
||||||
current_y = 5 + ruby_size // 2
|
current_y = outer_padding + ruby_size // 2
|
||||||
if romaji_line:
|
if romaji_line:
|
||||||
draw.text((romaji_x + romaji_width // 2, current_y), romaji_line, text_color, anchor="mm", font=font_ruby)
|
draw.text((romaji_x + romaji_width // 2, current_y), romaji_line, text_color, anchor="mm", font=font_ruby)
|
||||||
current_y += ruby_size + (ruby_line_spacing if hira_line else 0)
|
current_y += ruby_size + (ruby_line_spacing if hira_line else 0)
|
||||||
@@ -325,6 +327,10 @@ class OverlayImage:
|
|||||||
for textbox_img in textbox_images[1:]:
|
for textbox_img in textbox_images[1:]:
|
||||||
img = self.concatenateImagesVertically(img, textbox_img)
|
img = self.concatenateImagesVertically(img, textbox_img)
|
||||||
|
|
||||||
|
# 画像周囲にUIパディングを追加して、文字が端に張り付かないようにする
|
||||||
|
ui_outer_padding = 50
|
||||||
|
img = self.addImageMargin(img, ui_outer_padding, ui_outer_padding, ui_outer_padding, ui_outer_padding, (0, 0, 0, 0))
|
||||||
|
|
||||||
# 角丸背景を作成
|
# 角丸背景を作成
|
||||||
background = Image.new("RGBA", img.size, (0, 0, 0, 0))
|
background = Image.new("RGBA", img.size, (0, 0, 0, 0))
|
||||||
draw = ImageDraw.Draw(background)
|
draw = ImageDraw.Draw(background)
|
||||||
|
|||||||
Reference in New Issue
Block a user