Descender — بخشی از حرف کوچک است که زیر خط پایه (baseline) قلم قرار میگیرد. در خط لاتین حروف معمول با descender «g», «j», «p», «q», «y» هستند. طول descender عنصر پایینی قلم را تعیین میکند و برای محاسبه فاصله خطوط حیاتی است: بدون فضای کافی زیر baseline، حروف دارای descender به خط بعدی برخورد خواهند کرد. طبق Material Design Type Scale Guidelines (2025)، عدم توجه کافی به descender یکی از دلایل اصلی برخورد (collision) خطوط در متن چندخطی در دستگاههای موبایل است.
نکات اصلی
Descender بخشی از گلیف است که در زیر خط baseline قرار دارد. در حالی که بدنه اصلی حرف روی baseline قرار میگیرد، descender فراتر از آن امتداد مییابد و سیلوئت مشخصی به قلم میبخشد. در قلمهای لاتین، حروف «g», «j», «p», «q», «y» دارای عناصر پایینی هستند.
عمق descender فاصله از baseline تا مرز پایینی گلیف (descender-line) را توصیف میکند. در قلمهای با کیفیت این فاصله متعادل است: descender خیلی کوتاه باعث میشود حروف دارای descender به سختی قابل تشخیص باشند، و descender خیلی بلند فضای خالی اضافی بین خطوط ایجاد میکند و تراکم متن را کاهش میدهد. فونتهای مختلف تفاوتهای قابل توجهی در طول descender نشان میدهند.
| فونت | Descender / em-size | نمونه حروف با descender |
|---|---|---|
| SF Pro | ~0.22 | g, p — خروجی متعادل |
| Roboto | ~0.24 | g, p — descender متوسط |
| Playfair Display | ~0.30 | g, q — عناصر تزئینی بلند |
| Inter | ~0.26 | g, p — قابل توجه زیر baseline |
| Noto Sans | ~0.20 | g — descender کوتاه، فشرده |
طبق Google Fonts Metrics Guide (2025)، descender زمانی بهینه در نظر گرفته میشود که عمق آن ۲۰-۲۵٪ از اندازه کامل em (۱۰۰۰ FUnits) باشد. مقادیر کمتر از ۱۵٪ باعث میشود حروف دارای descender به سختی قابل تشخیص باشند و مقادیر بالای ۳۰٪ نیاز به افزایش اجباری 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 نمایش داده میشود. تفاوت ۱۰۰ FUnits (حدود ۱.۶ پیکسل در اندازه ۱۶ pt) از نظر بصری قابل توجه است.
طبق Microsoft OpenType Specification v1.9 (2025)، برای رندرینگ صحیح بین پلتفرمی، مقادیر hhea.descent و sTypoDescender باید با دقت ۵۰ FUnits برابر باشند. هنگام انتخاب قلم برای اپلیکیشن موبایل، این موضوع را باید از طریق fontTools یا ابزار مشابه بررسی کرد.
در iOS مقدار descender از طریق ویژگی UIFont.descender در دسترس است. این ویژگی یک عدد منفی برمیگرداند که فاصله از baseline تا لبه پایینی قلم (شامل descender) را نشان میدهد. به عنوان مثال، برای SF Pro در اندازه ۱۷ pt مقدار descender ۴.۲- pt است. هرچه قدر مطلق عدد بیشتر باشد، عناصر پایینی قلم بلندتر هستند.
// دریافت descender در iOS از طریق UIFont
let font = UIFont.systemFont(ofSize: 17)
let descender = font.descender // فاصله ۴.۲- pt برای SF Pro 17pt
let ascender = font.ascender // ۱۶.۲ pt
let lineHeight = font.lineHeight // ۲۰.۴ 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) // ۴.۲ 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 // ۴.۵ px برای 17sp
val bottom = metrics.bottom // ۵.۰ 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 — Typography Implementation (2025)، برای جلوگیری از بریده شدن descender در کانتینرهایی با ارتفاع ثابت، باید padding عمودی حداقل برابر descent قلم اضافه کرد، صرف نظر از وجود حروف دارای descender در متن فعلی. این تضمین میکند که در جایگزینی پویای متن، رابط کاربری خراب نشود.
برخورد خطوط (line collision) — وضعیتی است که descender حرف خط بالایی با ascender حرف خط پایینی تلاقی فیزیکی پیدا میکند. در رابطهای موبایل این موضوع به ویژه در عنوانهای چندخطی، کارتهای محصولات و بلوکهای متنی با فاصله خطوط کم قابل توجه است. مشکل با استفاده از قلمهای دارای descender بلند و line-height کم تشدید میشود.
حداقل line-height برای جلوگیری از برخورد را میتوان با فرمول محاسبه کرد: line-height = ascender + descender + ۲ پیکسل حاشیه. برای SF Pro در اندازه ۱۷ pt این مقدار line-height ۲۲.۴ pt (ضریب ۱.۳۲) میدهد. برای Roboto در اندازه ۱۶ sp — حدود ۱.۳۵. اگر line-height کمتر از این مقدار باشد، در متون حاوی حروف گروه «g», «j», «p», «q», «y» برخورد تضمین شده است.
// 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 آنها میتواند به ۳۵-۴۰٪ از اندازه برسد. چنین قلمهایی به ندرت برای متن اصلی استفاده میشوند اما ممکن است در عنوانها به کار روند. حتی یک بار ظاهر شدن حرف با descender بلند در عنوان میتواند باعث برخورد با عنصر مجاور رابط کاربری شود.
رایجترین خطا بریده شدن descender در دکمهها و فیلدهای متنی است. وقتی ارتفاع دکمه یا فیلد متنی را برابر line-height بدون در نظر گرفتن descender تنظیم میکنیم، حروف دارای descender در لبه پایینی بریده میشوند. این موضوع به ویژه در دکمههای سیستمی با گوشههای گرد قابل توجه است، جایی که descender ممکن است از مرز گرد شدن خارج شود.
طبق Nielsen Norman Group — Mobile Typography Research (2025)، ۴۱٪ از اپلیکیشنهای موبایل حداقل یک صفحه دارند که در آن متن با descender از مرزهای کامپوننت خارج میشود. این باعث کاهش ۱۵٪ خوانایی و افزایش زمان انجام وظیفه توسط کاربر میشود. آزمایش منظم با متنی حاوی حروف «g», «j», «p», «q», «y» به شناسایی چنین مشکلاتی در مراحل اولیه توسعه کمک میکند.
سوالات متداول
Baseline خط افقی است که حروف روی آن قرار میگیرند، در حالی که descender بخشی از حرف است که زیر این خط قرار دارد. Baseline برای یک خط ثابت است، descender ویژگی یک حرف خاص است. این مفاهیم را اشتباه نگیرید: baseline برای تراز استفاده میشود، در حالی که descender بر فاصله خطوط تأثیر میگذارد و هنگام تنظیم ارتفاع کانتینر نیاز به توجه دارد.
از Paint.getFontMetrics().descent برای View-system یا 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 + ۲ پیکسل حاشیه. برای قلم سیستمی ۱۷ pt در iOS این تقریباً ۲۲.۴ pt است. در Android برای ۱۶ sp Roboto — حدود ۲۲ sp. توصیه میشود به نزدیکترین عدد صحیح گرد کنید و با رشته تستی «gpq» بررسی کنید — اگر برخوردی وجود نداشته باشد، line-height کافی است.
بله، اما با شرایطی. قلمهای با descender بلند (Playfair Display، فونتهای تزئینی) برای عنوانها و متنهای خاص قابل قبول هستند، جایی که line-height را میتوان بدون آسیب به طراحی افزایش داد. برای متن اصلی، قلمهایی با descender ۲۰-۲۵٪ از اندازه em (SF Pro, Roboto, Inter) ترجیح داده میشوند تا فضای عمودی اضافی هدر نرود.
نتیجهگیری
ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد
IT Sectr از سال 2017 برنامههای iOS و Android را برای استارتاپها و کسبوکارها ایجاد میکند. ما به شما مشاوره میدهیم و بهترین راهحل را پیشنهاد خواهیم کرد.
همچنین بخوانید