السلام عليكم ورحمة الله وبركاته،
نسلط الضوء في هذا المقال على عدة تحديثات في محرك Quran Engine، بهدف تقديم تجربة مثالية لعرض المصحف في مختلف المنصات بأفضل أداء ممكن.
صفحات المصحف في Quran Engine ليست نصًا عاديًا يمكن للمتصفح تقسيمه تلقائيًا، بل الصفحة ككل عبارة عن رسومات متجهة تتكوّن من آلاف العناصر، فهي عبارة عن صورة، لذلك لا يكفي تكبير الصفحة؛ بل كان علينا بناء نظام يعيد ترتيب الكلمات والعلامات مع الحفاظ على رسم المصحف.
في الزوم التقليدي تكبر الصفحة كاملة، ويضطر القارئ إلى التحريك يمينًا ويسارًا. أما في النظام الجديد، فتكبر الكلمات وتُعاد توزيـعها على أسطر تناسب عرض الشاشة، ثم تصبح الصفحة أطول ويمكن تمريرها رأسيًا.
مثال لاستخدام الخاصية في تطبيق الكتاب @ (iPhoneIslam) Tarek Mansour
كيف يعمل؟
يتولى المحرك حساب الزوم وترتيب الصفحة بدل تنفيذ ذلك بشكل مختلف في كل تطبيق. وهو يدعم ثلاثة أوضاع:
- تكبير متدرج بدرجات مناسبة لكل صفحة تحقق أفضل محاذاة للنص الجديد.
- تكبير مرن يسمح باختيار أي درجة.
- تكبير تقليدي للصفحة من دون إعادة ترتيب.
كما يحافظ على درجة التكبير عند الانتقال بين الصفحات، ويميّز بين سحب الصفحة والتنقل إلى الصفحة التالية.
أبرز التحديات
كان التحدي الأساسي هو إعادة ترتيب عناصر الصورة من دون تغيير شكل الكلمات أو العلامات. كذلك احتجنا إلى موازنة الأسطر حتى لا تظهر فراغات كبيرة، واختيار قواعد تحسن من شكل النص بعد إعادة توزيعه، وضبط تكبير عناوين السور والبسملة بشكل مستقل، ومعالجة الفرق بين ارتفاع الشاشة وارتفاع الصفحة الطويلة بعد إعادة ترتيبها.
وللحفاظ على الأداء، لا يعيد التطبيق رسم الصفحة كاملة مع كل حركة، بل يرسم الجزء المطلوب فقط.
كيف تُرتّب الكلمات وتُحاذى الأسطر؟
يتعامل المحرك مع كل كلمة وعلاماتها كوحدة رسومية واحدة. يمكن نقلها إلى سطر جديد، لكن لا يعاد تشكيل حروفها أو تغيير رسمها.
ويراعي أثناء الترتيب عدة قواعد:
- تبقى علامة الآية مع آخر كلمة في الآية.
- تتحرك علامة السجدة وعلامات الأحزاب مع الكلمات المرتبطة بها.
- يبقى اسم السورة والبسملة في سطر مستقل، ولا يعبر النص من خلالهما.
- لا تنتقل أي كلمة إلى صفحة أخرى.
- إذا امتد خط السجدة فوق كلمات انتقلت إلى أكثر من سطر، يعاد رسمه فوق كل سطر في موضعه الصحيح.
المحرك لا يقيس المسافة باستخدام حدود الصورة المستطيلة فقط، بل يقيس الفراغ الحقيقي بين حبر الكلمتين. وهذا مهم لأن بعض الحروف تتداخل بصريًا رغم أن بينها مسافة واضحة.
توجد ثلاث طرق لتقسيم الكلمات إلى أسطر:
- Greedy: يملأ السطر حتى لا تتسع الكلمة التالية.
- Even: يختار التقسيم الذي يجعل أطوال الأسطر متقاربة.
- Fitted: يوازن بين طول السطر، والمسافات التي يمكن توسيعها، ومدى اختلافه عن الأسطر المجاورة. وهو الوضع الافتراضي.
كما يمكن عرض السطر بمحاذاة اليمين، أو تمديد المسافات ليملأ العرض، أو توسيطه. الإعداد الافتراضي هو التوسيط، مع فتح المسافات قليلًا إذا كان أحد الأسطر أقصر بوضوح من الأسطر القريبة، من دون ترك فراغات مبالغ فيها بين الكلمات.
كيف نختار مقاسات الزوم الثلاثة؟
المقاسات الأساسية المستهدفة هي 1.4× و1.8× و2.2× مقارنة بحجم الصفحة الأصلي، لكنها ليست قيمًا ثابتة لجميع الصفحات.
لكل مقاس، يجرّب المحرك عدة قيم قريبة منه ضمن نطاق ±6%، ثم يختار القيمة التي تنتج أفضل توزيع للكلمات. ويقيّم النتيجة بناءً على:
- قِصر الأسطر والفراغات المتبقية فيها.
- الفرق بين طول كل سطر والسطر المجاور.
- عدد الأسطر وارتفاع الصفحة الناتج.
- مدى ابتعاد القيمة المختارة عن المقاس المطلوب.
بعد ذلك تُراجع النتائج على مستوى المصحف كاملًا، حتى لا يتغير حجم الكلمات بشكل ملحوظ عند الانتقال من صفحة إلى أخرى. كما يضمن المحرك أن يكون كل مستوى أكبر من السابق بمقدار واضح، وألا يتجاوز الحد الذي تسمح به كلمات الصفحة.
لذلك يحصل كلٌّ من المقاسات الثلاثة على قيمة محسّنة خاصة بكل صفحة، بدل تطبيق الأرقام نفسها بصورة جامدة على جميع الصفحات. أما حجم الصفحة المطبوع 1× فيبقى المستوى الأساسي قبل مستويات الزوم الثلاثة.
تفاصيل التنفيذ موجودة في PR #81.
أبرز التحديثات
- الزوم الذكي وإعادة ترتيب أسطر الصفحة: #81
- ضبط تكبير اسم السورة والبسملة وحفظ حالة الزوم: #86
- تحسين قياس الصفحة والتمرير الخارجي للصفحات الطويلة: #87
- إصلاح بناء مكتبة Android داخل React Native: #85
- منع رسم العناصر الموجودة خارج حدود الصفحة: #80
- تضمين المكتبات الأصلية داخل حزمة Flutter: #79
- نشر حزم Rust وFlutter باستخدام صلاحيات مؤقتة وآمنة: #78
- تحسين دقة الرسم على Canvas في الشاشات عالية الكثافة: #77
- تثبيت تسمية إصدارات المحرك وبيانات الصفحات: #74، #76
- تحديث جميع الحزم لاستخدام بيانات الصفحات
v0.3.0: #75
- إصلاح عناصر اسم السورة والبسملة في بعض الصفحات: #73
- إصلاح تخطيط الصفحة بعد تدوير الشاشة: #72
- توحيد أسماء حقول نسخة JavaScript الخفيفة وتحسين فحص الأمثلة: #71
- إصلاح بناء تطبيق iOS التجريبي وكشف أخطائه داخل CI: #70
- التحقق من ملفات CDN باستخدام البصمة الرقمية: #69
- تحديث تصميم علامات الآيات لتطابق المصحف المطبوع: #68
- إصلاح إعداد Android بعد إزالة إحدى حزم Google القديمة: #67
- نشر المحرك وملفات الصفحات عبر
cdn.quran.ws: #66
- تحديث توثيق تثبيت الحزم وحالة نشرها: #65
- إتاحة QvpKit عبر Swift Package Manager: #64
- تجهيز إصدار المحرك
v0.2.1: #63
- نشر مكتبة Android على Maven Central: #62
- دعم صفحات الذاكرة بحجم 16 KiB في Android: #61
- تحديث العناصر التفاعلية عند تغير تخطيط الصفحة: #60
- إضافة اختبارات أداء لطرق الرسم في المتصفح: #59
- تصحيح حركة خط السجدة مع الكلمة التابعة له: #57، #58
- إضافة تأثير تدريجي لإخفاء الكلمات وإظهارها: #56
- تصحيح حركة علامات الآيات عند تغيير تباعد الأسطر: #55
- تصحيح توثيق واجهات Dart: #54
- توحيد مصطلحات البحث مع معيار Quran.ws: #53
- إصدار
v0.2.0 وتطبيق معيار التسمية الجديد: #51، #52
- توحيد أنواع القيم المنطقية وفصل إصدار المحرك عن إصدار الصيغة: #50
- توحيد أسماء خصائص تخطيط الصفحة: #49
- استبدال الاختصارات بأسماء واضحة في جميع الواجهات: #48
- توحيد أسماء الأنواع وخيارات البحث: #47
- توحيد أسماء أوامر التلوين والإخفاء والبحث والتحديد: #46
- توحيد واجهات اختبار النقر وحدود العناصر: #45
القادم بإذن الله
- إضافة التفاعل والتلوين على مستوى الحرف.
- تخصيص الإطار وعلامات الآيات وإطار اسم السورة.
- دعم مصاحف وطبعات أخرى.
الشكر بعد الله للصديقين، أبو يامن / طارق منصور @ (iPhoneIslam) Tarek Mansour ، وأبو أيوب / مصطفى الجبالي . على المساهمة والدعم المستمرين
كما أدعو الجميع لتجربة المحرك، وإرسال ملاحظاتكم واقتراحاتكم ومساهماتكم.
ولكم جزيل الشكر