Descender هو جزء من الحرف الصغير يمتد تحت خط الأساس (baseline) للخط. في السيريلية، الأحرف النموذجية ذات descender هي «er» و«u» و«ef»، وفي اللاتينية — «g» و«j» و«p» و«q» و«y». يحدد طول descender العنصر السفلي الممتد للخط وهو مهم بشكل حاسم لحساب تباعد الأسطر: بدون مساحة كافية أسفل baseline، ستصطدم الأحرف ذات descender بالسطر التالي. وفقاً لإرشادات Material Design Type Scale Guidelines (2025)، فإن عدم مراعاة descender بشكل كافٍ هو أحد الأسباب الرئيسية لتصادم الأسطر في النص متعدد الأسطر على الأجهزة المحمولة.
النقاط الرئيسية
Descender هو جزء من الرسم الحرفي (glyph) الموجود أسفل خط الأساس. بينما يجلس الجسم الرئيسي للحرف على baseline، يمتد descender إلى ما وراءها، مما يخلق الشكل المميز للخط. في الأبجدية اللاتينية، تشمل الأحرف ذات descender «g» و«j» و«p» و«q» و«y» — حيث تنخفض عناصرها السفلية أسفل السطر.
يصف عمق descender المسافة من baseline إلى الحافة السفلية للرسم الحرفي (descender-line). في الخطوط عالية الجودة، تكون هذه المسافة متوازنة: فdescender قصير جداً يجعل الأحرف ذات descender صعبة التمييز، بينما طويل جداً يخلق مساحة فارغة زائدة بين الأسطر ويقلل كثافة النص. تظهر أنواع الخطوط المختلفة اختلافات كبيرة في طول descender.
| نوع الخط | Descender / em-size | أمثلة على أحرف ذات descender |
|---|---|---|
| SF Pro | ~0.22 | g, j, p, q, y — امتداد متوازن |
| Roboto | ~0.24 | g, j, p — descender معتدل |
| Playfair Display | ~0.30 | g, j, p, q — عناصر زخرفية طويلة |
| Inter | ~0.26 | g, j, p — بشكل ملحوظ أسفل baseline |
| Noto Sans | ~0.20 | g, j — descender قصير، مضغوط |
وفقاً لدليل مقاييس خطوط Google (2025)، يعتبر descender مثالياً عندما يكون عمقه 20–25% من حجم em الكامل (1000 FUnits). القيم الأقل من 15% تجعل الأحرف ذات descender صعبة التمييز، بينما القيم الأعلى من 30% تتطلب زيادة إجبارية في line-height لمنع تصادم الأسطر.
في الخطوط الرقمية، يتم تخزين descender كقيمة سالبة في جداول المقاييس. في تنسيق OpenType، هذا هو الحقل hhea.descent (جدول hhea) وsTypoDescender (جدول OS/2). كلا القيمتين سالبتان لأنهما تقاسان من baseline إلى الأسفل. بالنسبة لـ TrueType، يُستخدم جدول OS/2 مع الحقل usWinDescent — قيمته موجبة لكنها تشير إلى نفس المقياس.
# قراءة descender من الخط عبر fontTools
from fontTools.ttLib import TTFont
font = TTFont('Roboto-Regular.ttf')
hhea = font['hhea']
os2 = font['OS/2']
descent_hhea = hhea.descent # -500 FUnits (Roboto)
typo_descender = os2.sTypoDescender # -500 FUnits
win_descent = os2.usWinDescent # 500 (positive value)
# تحويل إلى بكسلات لحجم خط 16pt
px_per_em = 16
descent_px = abs(descent_hhea) * px_per_em / 1000 # 8 px
الفرق الحاسم بين المنصات: iOS يستخدم hhea.descent للعرض، بينما Android يستخدم sTypoDescender من OS/2. إذا اختلفت هذه القيم (وهو ما يحدث في الخطوط غير المهيأة جيداً)، فسيتم عرض نفس النص بتباعد أسطر مختلف على iOS وAndroid. فارق 100 FUnits (حوالي 1.6 بكسل بحجم خط 16 نقطة) ملحوظ بالفعل بصرياً.
وفقاً لمواصفات Microsoft OpenType v1.9 (2025)، للعرض الصحيح عبر المنصات، يجب أن تكون قيم hhea.descent وsTypoDescender متساوية بدقة 50 FUnits. عند اختيار خط لتطبيق محمول، يجب التحقق من ذلك عبر fontTools أو أداة مشابهة.
في iOS، قيمة descender متاحة عبر الخاصية UIFont.descender. ترجع هذه الخاصية رقماً سالباً يشير إلى المسافة من baseline إلى الحافة السفلية للخط (بما في ذلك descender). على سبيل المثال، لـ SF Pro بحجم 17 نقطة، قيمة descender حوالي -4.2 نقطة. كلما زادت القيمة المطلقة، زاد طول العناصر السفلية الممتدة للخط.
// الحصول على descender في iOS عبر UIFont
let font = UIFont.systemFont(ofSize: 17)
let descender = font.descender // ~ -4.2 pt لـ SF Pro 17pt
let ascender = font.ascender // ~ 16.2 pt
let lineHeight = font.lineHeight // ~ 20.4 pt
// عرض مخصص مع إزاحة descender
let attrString = NSAttributedString(
string: "Sample text with letter p and y",
attributes: [.font: font]
)
// Core Text: الحصول على المربع المحيط مع descender
let ctFont = CTFontCreateWithName(
"SF Pro Text" as CFString, 17, nil
)
let descent = CTFontGetDescent(ctFont) // ~4.2 pt
عند استخدام TextKit (NSTextStorage, NSLayoutManager)، يتم أخذ descender في الاعتبار تلقائياً في lineFragmentPadding وlineFragmentRect. ومع ذلك، عند القيام بالعرض المخصص عبر Core Graphics (draw(in:))، يجب عليك ضبط الإحداثيات يدوياً بإضافة القيمة المطلقة لـ descender إلى الهامش السفلي للحاوية. إذا لم يتم ذلك، فستتجاوز الأحرف ذات descender حدود العرض وسيتم قصها.
في Android، مقاييس descender متاحة عبر Paint.FontMetrics.descent. على عكس iOS، قيمة descent موجبة — تمثل المسافة من baseline إلى الحافة السفلية للنص. الخاصية FontMetrics.bottom تشمل ليس فقط descender ولكن أيضاً المساحة الإضافية الموصى بها من قبل مصمم الخط (leading). للحساب الدقيق لـ descender فقط، استخدم descent بدلاً من bottom.
// الحصول على descender في Android عبر Paint
val paint = Paint().apply {
textSize = 17 * density
}
val metrics = paint.fontMetrics
val descent = metrics.descent // ~4.5 px لـ 17sp
val bottom = metrics.bottom // ~5.0 px مع leading
// عرض مخصص مع إزاحة descender
val baseline = y
canvas.drawText("مثال: gpq", x, baseline, paint)
// الحد السفلي مع descender
val bottomBound = baseline + descent // الحد السفلي الصحيح
في Jetpack Compose، يمكن الحصول على descender عبر TextLayoutResult. ترجع طريقة getLineBottom الإحداثي Y للحافة السفلية للسطر، والذي يتضمن بالفعل descender. عند القيام بتخطيط مخصص لسلاسل بأحجام خطوط مختلفة (على سبيل المثال، سعر مخفض وسعر كامل)، فإن المحاذاة على baseline مع مراعاة descender تعطي نتيجة أكثر دقة من المحاذاة على الحافة السفلية.
// Compose: التحقق من الحد السفلي للنص
val text = "Text with descenders: gpq"
var layoutResult by remember { mutableStateOf<TextLayoutResult?>(null) }
Text(
text = text,
onTextLayout = { layoutResult = it },
modifier = Modifier.drawBehind {
layoutResult?.let { result ->
val lastLine = result.lineCount - 1
val bottom = result.getLineBottom(lastLine)
val top = result.getLineTop(lastLine)
// التحقق من عدم تجاوز descender لحدود الحاوية
}
}
)
وفقاً لتنفيذ الطباعة من Google Material Design (2025)، لمنع قص descender في الحاويات ذات الارتفاع الثابت، يجب إضافة حشوة رأسية تساوي على الأقل descent للخط، بغض النظر عن وجود أحرف ذات descender في النص الحالي. هذا يضمن أن الواجهة لن تنكسر عند استبدال النص ديناميكياً.
تصادم الأسطر هو حالة يتقاطع فيها descender حرف من السطر العلوي جسدياً مع ascender حرف من السطر السفلي. في واجهات المحمول، يكون هذا ملحوظاً بشكل خاص في العناوين متعددة الأسطر وبطاقات المنتجات وكتل النص ذات التباعد الصغير بين الأسطر. تتفاقم المشكلة عند استخدام خطوط ذات descenders طويلة وline-height صغير.
يمكن حساب الحد الأدنى لـ line-height الذي يمنع التصادم باستخدام الصيغة: line-height = ascender + descender + 2 بكسل هامش. لـ SF Pro بحجم 17 نقطة، هذا يعطي line-height حوالي 16.2 + 4.2 + 2 = 22.4 نقطة (معامل ~1.32). لـ Roboto بحجم 16 sp، حوالي 1.35. إذا كان line-height أقل من هذه القيمة، فالتصادم مضمون في النصوص التي تحتوي على أحرف ذات descender.
// iOS: حساب الحد الأدنى لـ line-height لمنع التصادمات
let font = UIFont.systemFont(ofSize: 17)
let minLineHeight = abs(font.ascender) + abs(font.descender) + 2.0
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.minimumLineHeight = minLineHeight
paragraphStyle.maximumLineHeight = minLineHeight
let attributedText = NSAttributedString(
string: "Text with p on first line\nand y on second line",
attributes: [
.font: font,
.paragraphStyle: paragraphStyle
]
)
يجب توخي الحذر بشكل خاص عند العمل مع الخطوط الزخرفية والخطوط اليدوية — يمكن أن يصل descender فيها إلى 35–40% من حجم em. نادراً ما تستخدم هذه الخطوط للنص الأساسي ولكن يمكن تطبيقها في العناوين. حتى ظهور واحد لحرف ذو descender طويل في عنوان يمكن أن يسبب تصادماً مع عنصر واجهة مجاور.
الخطأ الأكثر شيوعاً هو قص descender في الأزرار وحقول النص. عندما نضبط ارتفاع زر أو حقل نص مساوياً لـ line-height دون مراعاة descender، يتم قص الأحرف ذات descender عند الحافة السفلية. هذا ملحوظ بشكل خاص في أزرار النظام ذات الزوايا المستديرة، حيث يمكن أن يتجاوز descender حد نصف قطر الزاوية.
وفقاً لأبحاث الطباعة المحمولة من Nielsen Norman Group (2025)، 41% من التطبيقات المحمولة لديها شاشة واحدة على الأقل حيث يتجاوز النص ذو descenders حدود المكون. يؤدي هذا إلى انخفاض بنسبة 15% في قابلية القراءة وزيادة في وقت إنجاز المهمة من قبل المستخدم. يساعد الاختبار المنتظم بنص يحتوي على أحرف ذات descender في تحديد هذه المشكلات في المراحل المبكرة من التطوير.
الأسئلة الشائعة
Baseline هو الخط الأفقي الذي تستقر عليه الأحرف، بينما descender هو جزء الحرف الموجود أسفل هذا الخط. baseline ثابت للسطر، descender هو خاصية لحرف معين. لا تخلط بين هذه المفاهيم: baseline يُستخدم للمحاذاة، بينما descender يؤثر على تباعد الأسطر ويتطلب مراعاة عند ضبط ارتفاع الحاوية.
استخدم Paint.getFontMetrics().descent لنظام View أو TextLayoutResult في Jetpack Compose. على عكس iOS، قيمة descent في Android موجبة وتشير إلى المسافة من baseline إلى الحافة السفلية للرسم الحرفي. لحساب الحد السفلي الكامل للسطر، أضف descent إلى الإحداثي Y لـ baseline.
تستخدم المنصات جداول مقاييس مختلفة من ملف الخط: iOS يستخدم hhea.descent، Android يستخدم os/2.sTypoDescender. إذا اختلفت هذه القيم في الخط، سيختلف العرض. تحقق دائماً من كلا القيمتين عبر fontTools. خطوط النظام عالية الجودة (SF Pro, Roboto, Noto) لها مقاييس متسقة لكلتا المنصتين.
الحد الأدنى line-height = ascender + descender + 2 بكسل هامش. لخط نظام 17 نقطة على iOS هذا حوالي 22.4 نقطة. على Android لـ Roboto 16 sp — حوالي 22 sp. يُوصى بالتقريب إلى أقرب عدد صحيح والاختبار بسلسلة اختبارية من الأحرف ذات descender — إذا لم يكن هناك تصادم، فإن line-height كافٍ.
نعم، ولكن مع تحفظات. الخطوط ذات descender الطويل (Playfair Display، الخطوط الزخرفية) مقبولة للعناوين والنص المميز حيث يمكن زيادة line-height دون المساس بالتصميم. للنص الأساسي، يُفضل استخدام خطوط ذات descender بنسبة 20–25% من حجم em (SF Pro, Roboto, Inter) لتجنب إهدار المساحة الرأسية.
الخلاصة
سنقوم بتطوير تطبيق جوال جاهز
تقدم IT Sectr تطبيقات iOS وAndroid للشركات الناشئة والشركات منذ عام 2017. سوف نقدم لك النصح ونقترح أفضل حل.
اقرأ أيضًا