manga / modules /translators /format_bubble.py
sayed555's picture
Upload 140 files
82f073c verified
import math
import re
import logging
class ArabicBubbleFormatter:
def __init__(self):
# النسب المستهدفة لكل عدد أسطر
self.target_ratios = {
2: [0.50, 0.50], # التوزيع المثالي 50-50
3: [0.30, 0.40, 0.30], # شكل الفقاعة المثالي
4: [0.20, 0.30, 0.30, 0.20], # نسب محسنة للأربعة أسطر
5: [0.15, 0.25, 0.30, 0.25, 0.15],
6: [0.12, 0.18, 0.25, 0.25, 0.18, 0.12]
}
# حدود مهمة للتقييم
self.min_single_word_length = 6 # الحد الأدنى لكلمة واحدة في سطر
self.max_line_chars = 30 # الحد الأقصى لعدد الأحرف في السطر
self.optimal_line_chars = 25 # العدد المثالي للأحرف في السطر
self.max_words_per_line = 8
# إعدادات التقييم
self.perfect_score = 100
self.good_score = 85
self.acceptable_score = 70
self.poor_score = 50
def _preprocess_text(self, text: str) -> str:
"""معالجة مسبقة للنص"""
text = re.sub(r'\s+', ' ', text).strip()
return text
def _get_line_character_length(self, words: list) -> int:
"""حساب عدد الأحرف في مجموعة من الكلمات مع المسافات"""
if not words:
return 0
return sum(len(word) for word in words) + len(words) - 1
def _calculate_total_characters(self, words: list) -> int:
"""حساب إجمالي الأحرف في النص"""
return sum(len(word) for word in words) + len(words) - 1
def _evaluate_line_distribution(self, lines: list) -> tuple:
"""تقييم توزيع الأسطر وإرجاع النتيجة مع التفاصيل"""
if not lines:
return 0, "لا توجد أسطر"
num_lines = len(lines)
line_lengths = [len(line) for line in lines]
line_word_counts = [len(line.split()) for line in lines]
total_chars = sum(line_lengths)
score = self.perfect_score
details = []
# فحص الكلمات المفردة القصيرة
for i, line in enumerate(lines):
words = line.split()
if len(words) == 1 and len(words[0]) < self.min_single_word_length:
score -= 80
details.append(f"❌ السطر {i+1}: كلمة واحدة قصيرة ({len(words[0])} أحرف)")
return score, f"فشل: {'; '.join(details)}"
# فحص طول الأسطر
for i, length in enumerate(line_lengths):
if length > self.max_line_chars:
score -= 20
details.append(f"⚠️ السطر {i+1}: طويل جداً ({length} حرف)")
elif length > self.optimal_line_chars:
score -= 10
details.append(f"⚠️ السطر {i+1}: أطول من المثالي ({length} حرف)")
# تقييم خاص حسب عدد الأسطر
if num_lines == 1:
# سطر واحد - مثالي للنصوص القصيرة
if total_chars <= 30:
details.append("✅ سطر واحد مثالي للنص القصير")
return score, f"ممتاز: {'; '.join(details)}"
else:
score -= 30
details.append("⚠️ النص طويل للسطر الواحد")
elif num_lines == 2:
# تقييم السطرين
ratio1 = line_lengths[0] / total_chars
ratio2 = line_lengths[1] / total_chars
# النسبة المثالية 50-50
if abs(ratio1 - 0.5) <= 0.01: # ±1%
score += 10
details.append("✅ توزيع مثالي 50-50")
elif abs(ratio1 - 0.5) <= 0.05: # ±5%
details.append("✅ توزيع جيد متقارب")
elif abs(ratio1 - 0.55) <= 0.01 or abs(ratio1 - 0.45) <= 0.01: # 55-45 أو 45-55
score -= 5
details.append("✅ توزيع جيد 55-45")
elif abs(ratio1 - 0.6) <= 0.05: # حوالي 60-40
score -= 15
details.append("⚠️ توزيع متوسط 60-40")
else:
score -= 25
details.append("❌ توزيع غير متوازن")
elif num_lines == 3:
# تقييم الثلاثة أسطر
first, middle, last = line_lengths
# الوسط يجب أن يكون الأكبر
if middle >= first and middle >= last:
score += 15
details.append("✅ السطر الأوسط هو الأكبر")
# الأطراف متساوية أو متقاربة
if abs(first - last) <= 2:
score += 15
details.append("✅ الأطراف متساوية")
elif abs(first - last) <= 5:
score += 10
details.append("✅ الأطراف متقاربة")
else:
score -= 10
details.append("⚠️ الأطراف مختلفة")
else:
score -= 20
details.append("❌ السطر الأوسط ليس الأكبر")
elif num_lines == 4:
# تقييم الأربعة أسطر
first, second, third, fourth = line_lengths
# السطران الأوسطان أكبر من الأطراف
if second >= first and third >= fourth and second >= 0.8 * max(line_lengths) and third >= 0.8 * max(line_lengths):
score += 15
details.append("✅ السطران الأوسطان أكبر")
# الأطراف متساوية
if abs(first - fourth) <= 2:
score += 10
details.append("✅ الأطراف متساوية")
# الأوسطان متساويان
if abs(second - third) <= 2:
score += 10
details.append("✅ الأوسطان متساويان")
else:
score -= 15
details.append("❌ توزيع غير مثالي")
elif num_lines == 5:
# تقييم الخمسة أسطر
first, second, third, fourth, fifth = line_lengths
# الوسط الأكبر
if third >= max(line_lengths) * 0.9:
score += 10
details.append("✅ السطر الأوسط هو الأكبر")
# الأطراف متساوية
if abs(first - fifth) <= 2:
score += 10
details.append("✅ الأطراف متساوية")
# الثاني والرابع متساويان وأكبر من الأطراف
if abs(second - fourth) <= 2 and second > first and fourth > fifth:
score += 15
details.append("✅ الثاني والرابع متساويان وأكبر من الأطراف")
else:
score -= 15
details.append("❌ توزيع غير مثالي")
elif num_lines == 6:
# تقييم الستة أسطر
first, second, third, fourth, fifth, sixth = line_lengths
# الأوسطان متساويان وأكبر
if abs(third - fourth) <= 2 and third >= max(line_lengths) * 0.9:
score += 10
details.append("✅ الأوسطان متساويان وأكبر")
# الأطراف متساوية
if abs(first - sixth) <= 2:
score += 10
details.append("✅ الأطراف متساوية")
# الثاني والخامس متساويان
if abs(second - fifth) <= 2 and second > first and fifth > sixth:
score += 15
details.append("✅ الثاني والخامس متساويان وأكبر من الأطراف")
else:
score -= 15
details.append("❌ توزيع غير مثالي")
# تحديد مستوى الجودة
if score >= 95:
quality = "ممتاز"
elif score >= 85:
quality = "جيد جداً"
elif score >= 70:
quality = "جيد"
elif score >= 50:
quality = "متوسط"
else:
quality = "ضعيف"
return max(0, score), f"{quality}: {'; '.join(details)}"
def _distribute_words_optimally(self, words: list, num_lines: int) -> list:
"""توزيع الكلمات بشكل مثالي"""
if num_lines == 1:
return [" ".join(words)]
if num_lines not in self.target_ratios:
return self._simple_distribution(words, num_lines)
ratios = self.target_ratios[num_lines]
total_chars = self._calculate_total_characters(words)
n = len(words)
# حساب الأطوال المستهدفة
target_lengths = [int(total_chars * ratio) for ratio in ratios]
# توزيع الكلمات
result_lines = []
current_index = 0
for i, target_len in enumerate(target_lengths):
if current_index >= n:
break
best_end = current_index + 1
best_diff = float('inf')
# تحديد نطاق البحث
max_words_to_try = min(self.max_words_per_line, n - current_index)
for end in range(current_index + 1, current_index + max_words_to_try + 1):
if end > n:
break
line_words = words[current_index:end]
line_length = self._get_line_character_length(line_words)
# فحص الكلمة الواحدة القصيرة
if len(line_words) == 1 and len(line_words[0]) < self.min_single_word_length:
continue
# فحص طول السطر
if line_length > self.max_line_chars:
continue
diff = abs(line_length - target_len)
# تحسين خاص للأسطر الأربعة - جعل الأوسطان أكبر
if num_lines == 4:
if i == 1 or i == 2: # السطر الثاني والثالث (الأوسطان)
# تفضيل الأطوال الأكبر للأوسطان
if line_length >= target_len:
diff -= 20
# مكافأة إضافية للأوسطان المتساويين
if i == 1:
self.temp_second_line_length = line_length
elif i == 2 and hasattr(self, 'temp_second_line_length'):
if abs(line_length - self.temp_second_line_length) <= 2:
diff -= 15
elif i == 0 or i == 3: # السطر الأول والرابع (الأطراف)
# تفضيل الأطوال الأصغر للأطراف
if line_length <= target_len:
diff -= 15
# تفضيل الأطوال المثالية
if line_length <= self.optimal_line_chars:
diff -= 10
# تعامل مع السطر الأخير
if i == num_lines - 1:
remaining_words = n - end
if remaining_words > 0:
diff += remaining_words * 50
if diff < best_diff:
best_diff = diff
best_end = end
# إضافة السطر
if current_index < n:
line_words = words[current_index:best_end]
result_lines.append(" ".join(line_words))
current_index = best_end
# إضافة الكلمات المتبقية
if current_index < n:
remaining_words = words[current_index:]
if result_lines and len(remaining_words) <= 2:
# دمج مع السطر الأخير إذا كان قليل
last_line_words = result_lines[-1].split() + remaining_words
if len(" ".join(last_line_words)) <= self.max_line_chars:
result_lines[-1] = " ".join(last_line_words)
else:
result_lines.append(" ".join(remaining_words))
else:
result_lines.append(" ".join(remaining_words))
return result_lines
def _simple_distribution(self, words: list, num_lines: int) -> list:
"""توزيع بسيط كخيار احتياطي"""
n = len(words)
words_per_line = n // num_lines
remainder = n % num_lines
result = []
index = 0
for i in range(num_lines):
line_len = words_per_line + (1 if i < remainder else 0)
line_words = words[index:index+line_len]
index += line_len
if line_words:
result.append(" ".join(line_words))
return result
def _find_optimal_formatting(self, words: list) -> tuple:
"""البحث عن أفضل تنسيق"""
n = len(words)
total_chars = self._calculate_total_characters(words)
# حالة خاصة للكلمة الواحدة
if n == 1:
return [words[0]], self.perfect_score, "كلمة واحدة"
# حالة خاصة للنصوص القصيرة جداً
if total_chars <= 25:
return [" ".join(words)], self.perfect_score, "نص قصير - سطر واحد"
best_result = None
best_score = 0
best_details = ""
# جرب أعداد مختلفة من الأسطر
max_lines = min(6, n)
for num_lines in range(1, max_lines + 1):
if num_lines > n:
continue
# توزيع الكلمات
lines = self._distribute_words_optimally(words, num_lines)
# تقييم التوزيع
score, details = self._evaluate_line_distribution(lines)
# تفضيل عدد أسطر أقل للنصوص القصيرة
if total_chars <= 50 and num_lines <= 2:
score += 10
elif total_chars <= 80 and num_lines <= 3:
score += 5
# تفضيل الأسطر الثلاثة للنصوص المتوسطة
if 50 <= total_chars <= 120 and num_lines == 3:
score += 15
if score > best_score:
best_score = score
best_result = lines
best_details = details
return best_result, best_score, best_details
def format_text(self, text: str) -> dict:
try:
if not text.strip():
return {"formatted": text, "score": 0, "details": "نص فارغ"}
processed_text = self._preprocess_text(text)
words = processed_text.split()
# البحث عن أفضل تنسيق
best_lines, score, details = self._find_optimal_formatting(words)
if best_lines:
formatted_text = "\n".join(best_lines)
else:
formatted_text = processed_text
return {
"formatted": formatted_text,
"score": score,
"details": details,
"lines_count": len(best_lines) if best_lines else 1,
"original_length": len(processed_text),
"lines_info": [
{
"text": line,
"length": len(line),
"words": len(line.split())
}
for line in best_lines
] if best_lines else []
}
except Exception as e:
logging.error(f"Bubble formatting error: {str(e)}")
return {
"formatted": text,
"score": 0,
"details": f"Formatting failed: {str(e)}"
}
# # اختبار مع أمثلة متنوعة
# if __name__ == "__main__":
# formatter = ArabicBubbleFormatter()
# # أمثلة للاختبار
# test_cases = [
# # نصوص قصيرة - سطر واحد
# "مرحبا",
# "صباح الخير",
# "كيف حالك اليوم؟",
# # نصوص متوسطة - سطرين
# "هذا نص متوسط الطول يناسب السطرين",
# "النص القصير نسبياً للتجربة",
# # نصوص مثالية للثلاثة أسطر
# "هذا نص طويل نسبياً يحتوي على عدة كلمات ومناسب للتوزيع على ثلاثة أسطر",
# "النص المتوسط الطول يبدو أفضل عندما يتم توزيعه على ثلاثة أسطر بشكل جميل",
# # النصوص الإشكالية من الأصل
# "هذا! ضربة أقوى بمئة مرة! هذا الرجل، إنه حقا من عـشـيرة الذهب",
# "لا أذكر، فقد مضى وقت طويل.",
# "هذا إنذاري الأخير.",
# "أمنحك فرصة ثالثة لسماحتي.",
# # نصوص طويلة
# "هذا نص طويل جداً يحتوي على كلمات كثيرة ومناسب للتوزيع على أربعة أو خمسة أسطر حسب الحاجة والتوزيع الأمثل",
# "النصوص الطويلة تحتاج إلى تنسيق خاص لتبدو جميلة ومتوازنة في الفقاعات وهذا مثال على نص طويل نسبياً"
# ]
# print("=" * 100)
# print("نتائج التنسيق مع نظام التقييم المحسن")
# print("=" * 100)
# for i, text in enumerate(test_cases, 1):
# result = formatter.format_text(text)
# print(f"\n{i}. النص الأصلي: {text}")
# print(f"الطول: {result['original_length']} حرف")
# print("\nالنتيجة:")
# print(result['formatted'])
# print(f"\nالتقييم: {result['score']:.1f}/100")
# print(f"التفاصيل: {result['details']}")
# print(f"عدد الأسطر: {result['lines_count']}")
# # تفاصيل كل سطر
# for j, line_info in enumerate(result['lines_info'], 1):
# print(f"السطر {j}: {line_info['length']} حرف، {line_info['words']} كلمة")
# # رمز الجودة
# score = result['score']
# if score >= 95:
# print("🏆 تنسيق ممتاز!")
# elif score >= 85:
# print("✨ تنسيق جيد جداً!")
# elif score >= 70:
# print("✅ تنسيق جيد")
# elif score >= 50:
# print("⚠️ تنسيق متوسط")
# else:
# print("❌ تنسيق ضعيف")
# print("-" * 60)