Descender คือส่วนของตัวพิมพ์เล็กที่ยื่นลงไปต่ำกว่าเส้นฐาน (baseline) ของแบบอักษร ในอักษรละติน ตัวอักษรทั่วไปที่มี descender คือ “g”, “j”, “p”, “q”, “y” ความยาวของ descender กำหนดองค์ประกอบส่วนห้อยลงด้านล่างของแบบอักษรและมีความสำคัญอย่างยิ่งต่อการคำนวณระยะห่างระหว่างบรรทัด: หากไม่มีพื้นที่เพียงพอใต้ baseline ตัวอักษรที่มี descender จะชนกับบรรทัดถัดไป ตาม Material Design Type Scale Guidelines (2025) การพิจารณา descender ที่ไม่เพียงพอเป็นหนึ่งในสาเหตุหลักของการชนกันของบรรทัดในข้อความหลายบรรทัดบนอุปกรณ์เคลื่อนที่
ประเด็นสำคัญ
Descender คือส่วนของกลิฟที่อยู่ต่ำกว่าเส้นฐาน ขณะที่ตัวหลักของตัวอักษรอยู่บน baseline descender ยื่นออกไปเกิน สร้างรูปทรงลักษณะเฉพาะของแบบอักษร ในอักษรละติน ตัวอักษรที่มี descender ได้แก่ “g”, “j”, “p”, “q”, “y” — องค์ประกอบด้านล่างของพวกมันตกลงไปใต้บรรทัด
ความลึกของ descender อธิบายระยะทางจาก baseline ถึงขอบล่างของกลิฟ (descender-line) ในแบบอักษรที่มีคุณภาพ ระยะนี้สมดุล: descender ที่สั้นเกินไปทำให้ตัวอักษรที่มี 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 Fonts Metrics Guide (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 px ที่ขนาดแบบอักษร 16 pt) สังเกตเห็นได้ทางสายตาแล้ว
ตาม Microsoft OpenType Specification v1.9 (2025) เพื่อการเรนเดอร์ข้ามแพลตฟอร์มที่ถูกต้อง ค่า hhea.descent และ sTypoDescender ควรเท่ากันด้วยความแม่นยำ 50 FUnits เมื่อเลือกแบบอักษรสำหรับแอปพลิเคชันมือถือ ควรตรวจสอบผ่าน fontTools หรือยูทิลิตี้ที่คล้ายกัน
ใน iOS ค่า descender สามารถเข้าถึงได้ผ่านคุณสมบัติ UIFont.descender คุณสมบัตินี้ส่งคืนตัวเลขติดลบที่ระบุระยะทางจาก baseline ถึงขอบล่างของแบบอักษร (รวมถึง descender) ตัวอย่างเช่น สำหรับ SF Pro ที่ 17 pt ค่า descender ≈ -4.2 pt ยิ่งค่าสัมบูรณ์มากเท่าใด ส่วนยื่นด้านล่างของแบบอักษรก็ยิ่งยาวมากขึ้นเท่านั้น
// การรับ 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: การรับ bounding box พร้อม 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 — Typography Implementation (2025) เพื่อป้องกันการตัด descender ในคอนเทนเนอร์ที่มีความสูงคงที่ คุณต้องเพิ่ม padding แนวตั้งอย่างน้อยเท่ากับ descent ของแบบอักษร โดยไม่คำนึงว่าข้อความปัจจุบันมีตัวอักษรที่มี descender หรือไม่ ซึ่งรับประกันว่าอินเทอร์เฟซจะไม่เสียหายเมื่อข้อความถูกแทนที่แบบไดนามิก
การชนกันของบรรทัดคือสถานการณ์ที่ descender ของตัวอักษรในบรรทัดบนตัดกับ ascender ของตัวอักษรในบรรทัดล่างทางกายภาพ ในอินเทอร์เฟซมือถือ เห็นได้ชัดเจนเป็นพิเศษในหัวข้อหลายบรรทัด การ์ดสินค้า และบล็อกข้อความที่มีระยะห่างระหว่างบรรทัดน้อย ปัญหารุนแรงขึ้นเมื่อใช้แบบอักษรที่มี descender ยาวและ line-height เล็ก
line-height ขั้นต่ำที่ป้องกันการชนกันสามารถคำนวณได้ด้วยสูตร: line-height = ascender + descender + 2 px ระยะเผื่อ สำหรับ SF Pro ที่ 17 pt ให้ line-height ≈ 16.2 + 4.2 + 2 = 22.4 pt (ค่าสัมประสิทธิ์ ~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 — Mobile Typography Research (2025) 41% ของแอปพลิเคชันมือถือมีหน้าจออย่างน้อยหนึ่งหน้าที่ข้อความที่มี descender เกินขอบเขตของคอมโพเนนต์ ส่งผลให้ความสามารถในการอ่านลดลง 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 px ระยะเผื่อ สำหรับแบบอักษรระบบ 17 pt บน iOS ประมาณ 22.4 pt บน 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 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ
อ่านเพิ่มเติม