Commit ·
699416a
1
Parent(s): e93bb43
feat(fact_image): add Bengali text auto-detection and font support
Browse files
.gitattributes
CHANGED
|
@@ -1 +1,2 @@
|
|
| 1 |
*.mp3 filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
| 1 |
*.mp3 filter=lfs diff=lfs merge=lfs -text
|
| 2 |
+
*.ttf filter=lfs diff=lfs merge=lfs -text
|
modules/fact_image/services/text_overlay.py
CHANGED
|
@@ -5,6 +5,7 @@ Supports heading with background + fact text with shadow
|
|
| 5 |
"""
|
| 6 |
import logging
|
| 7 |
import re
|
|
|
|
| 8 |
from pathlib import Path
|
| 9 |
from typing import Tuple, Optional, Dict, Any
|
| 10 |
from PIL import Image, ImageDraw, ImageFont
|
|
@@ -38,6 +39,9 @@ class TextOverlay:
|
|
| 38 |
TEXT_TOP_PERCENT = 0.45 # Fact text starts at 45% from top
|
| 39 |
GAP_HEADING_TEXT = 60 # Gap between heading and fact text (fallback)
|
| 40 |
|
|
|
|
|
|
|
|
|
|
| 41 |
def __init__(self, font_path: Optional[str] = None):
|
| 42 |
"""
|
| 43 |
Initialize text overlay service.
|
|
@@ -48,15 +52,33 @@ class TextOverlay:
|
|
| 48 |
self.font_path = font_path
|
| 49 |
self._font_cache = {}
|
| 50 |
|
| 51 |
-
|
| 52 |
-
|
| 53 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 54 |
if cache_key not in self._font_cache:
|
| 55 |
try:
|
| 56 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 57 |
self._font_cache[cache_key] = ImageFont.truetype(self.font_path, size)
|
| 58 |
else:
|
| 59 |
-
# Try common system fonts
|
| 60 |
if bold:
|
| 61 |
font_names = ['DejaVuSans-Bold.ttf', 'Arial-Bold.ttf', 'Roboto-Bold.ttf', 'FreeSansBold.ttf']
|
| 62 |
else:
|
|
@@ -174,7 +196,12 @@ class TextOverlay:
|
|
| 174 |
Returns:
|
| 175 |
Path to output image
|
| 176 |
"""
|
| 177 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 178 |
|
| 179 |
# Load image
|
| 180 |
img = Image.open(image_path).convert('RGBA')
|
|
@@ -218,9 +245,9 @@ class TextOverlay:
|
|
| 218 |
else:
|
| 219 |
text_font_size = 32 # Minimum for very long text
|
| 220 |
|
| 221 |
-
# Get fonts
|
| 222 |
-
heading_font = self._get_font(heading_font_size, bold=True)
|
| 223 |
-
text_font = self._get_font(text_font_size, bold=True)
|
| 224 |
|
| 225 |
# Word wrap text with dynamic font to fit max_width
|
| 226 |
words = text.split()
|
|
@@ -272,7 +299,8 @@ class TextOverlay:
|
|
| 272 |
|
| 273 |
# Draw heading with background (UPPERCASE)
|
| 274 |
if heading:
|
| 275 |
-
|
|
|
|
| 276 |
# Get text bounding box - returns (left, top, right, bottom)
|
| 277 |
heading_bbox = draw.textbbox((0, 0), heading_upper, font=heading_font)
|
| 278 |
# The bbox includes font metrics offset
|
|
|
|
| 5 |
"""
|
| 6 |
import logging
|
| 7 |
import re
|
| 8 |
+
import unicodedata
|
| 9 |
from pathlib import Path
|
| 10 |
from typing import Tuple, Optional, Dict, Any
|
| 11 |
from PIL import Image, ImageDraw, ImageFont
|
|
|
|
| 39 |
TEXT_TOP_PERCENT = 0.45 # Fact text starts at 45% from top
|
| 40 |
GAP_HEADING_TEXT = 60 # Gap between heading and fact text (fallback)
|
| 41 |
|
| 42 |
+
# Path to Bengali font (bundled in static/fonts/)
|
| 43 |
+
BENGALI_FONT = Path(__file__).parent.parent.parent.parent / "static" / "fonts" / "Bangla_Unicode.ttf"
|
| 44 |
+
|
| 45 |
def __init__(self, font_path: Optional[str] = None):
|
| 46 |
"""
|
| 47 |
Initialize text overlay service.
|
|
|
|
| 52 |
self.font_path = font_path
|
| 53 |
self._font_cache = {}
|
| 54 |
|
| 55 |
+
@staticmethod
|
| 56 |
+
def _has_bengali(text: str) -> bool:
|
| 57 |
+
"""
|
| 58 |
+
Check if text contains any Bengali script characters.
|
| 59 |
+
Bengali Unicode range: U+0980 - U+09FF
|
| 60 |
+
Auto-detects — works for pure Bengali, pure English, or mixed.
|
| 61 |
+
"""
|
| 62 |
+
if not text:
|
| 63 |
+
return False
|
| 64 |
+
for ch in text:
|
| 65 |
+
if '\u0980' <= ch <= '\u09FF':
|
| 66 |
+
return True
|
| 67 |
+
return False
|
| 68 |
+
|
| 69 |
+
def _get_font(self, size: int, bold: bool = False, bengali: bool = False) -> ImageFont.FreeTypeFont:
|
| 70 |
+
"""Get font at specified size (cached). Uses Bengali font when bengali=True."""
|
| 71 |
+
cache_key = (size, bold, bengali)
|
| 72 |
if cache_key not in self._font_cache:
|
| 73 |
try:
|
| 74 |
+
# Bengali font path
|
| 75 |
+
if bengali and self.BENGALI_FONT.exists():
|
| 76 |
+
self._font_cache[cache_key] = ImageFont.truetype(str(self.BENGALI_FONT), size)
|
| 77 |
+
logger.debug(f"Loaded Bengali font at size {size}")
|
| 78 |
+
elif self.font_path and Path(self.font_path).exists():
|
| 79 |
self._font_cache[cache_key] = ImageFont.truetype(self.font_path, size)
|
| 80 |
else:
|
| 81 |
+
# Try common system fonts (Latin)
|
| 82 |
if bold:
|
| 83 |
font_names = ['DejaVuSans-Bold.ttf', 'Arial-Bold.ttf', 'Roboto-Bold.ttf', 'FreeSansBold.ttf']
|
| 84 |
else:
|
|
|
|
| 196 |
Returns:
|
| 197 |
Path to output image
|
| 198 |
"""
|
| 199 |
+
# Detect Bengali INDEPENDENTLY for heading and text
|
| 200 |
+
# So any combination works: BN heading + EN text, EN heading + BN text, etc.
|
| 201 |
+
heading_is_bengali = self._has_bengali(heading or "")
|
| 202 |
+
text_is_bengali = self._has_bengali(text)
|
| 203 |
+
|
| 204 |
+
logger.info(f"Adding text overlay: heading='{heading}' (bn={heading_is_bengali}), text='{text[:30]}...' (bn={text_is_bengali})")
|
| 205 |
|
| 206 |
# Load image
|
| 207 |
img = Image.open(image_path).convert('RGBA')
|
|
|
|
| 245 |
else:
|
| 246 |
text_font_size = 32 # Minimum for very long text
|
| 247 |
|
| 248 |
+
# Get fonts — each uses its OWN Bengali detection
|
| 249 |
+
heading_font = self._get_font(heading_font_size, bold=True, bengali=heading_is_bengali)
|
| 250 |
+
text_font = self._get_font(text_font_size, bold=True, bengali=text_is_bengali)
|
| 251 |
|
| 252 |
# Word wrap text with dynamic font to fit max_width
|
| 253 |
words = text.split()
|
|
|
|
| 299 |
|
| 300 |
# Draw heading with background (UPPERCASE)
|
| 301 |
if heading:
|
| 302 |
+
# Bengali has no uppercase — skip .upper() for Bengali text
|
| 303 |
+
heading_upper = heading if self._has_bengali(heading) else heading.upper()
|
| 304 |
# Get text bounding box - returns (left, top, right, bottom)
|
| 305 |
heading_bbox = draw.textbbox((0, 0), heading_upper, font=heading_font)
|
| 306 |
# The bbox includes font metrics offset
|
static/fonts/Bangla_Italic.ttf
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:245fa5d1b454f0fd63033f9d9893190edca4b82787fac569151b79326ca25ec7
|
| 3 |
+
size 116468
|
static/fonts/Bangla_Unicode.ttf
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:33a515c00aff1bfd401614cc60df1f383bd6fd3733d0fcbaba1b63f4d69f8f71
|
| 3 |
+
size 112140
|