In Part 1, I created a label view, where I draw a text in it. Now let's see what happened there:
Font & Attibuted String
How about font? Font is important part of drawing text. CoreText relies on Attributed String. Simple
NSAttributedString() instance without any attribute, defaults to System font. System font depends on the system (iOS/macOS/tvOS) and system version.
To use set font, I need to create font instance (
UIFont) and set string attribute key (
NSAttributedString.Key.font). Here I set Papyrus (available on macOS) font:
let myfont = CTFontCreateWithName("Papyrus" as CFString, 24, nil) let attributedString = NSAttributedString(string: text, attributes: [NSAttributedString.Key.font: myfont])
CTFont is base CoreText type and is bridged to/from
UIFont. I can use
UIFont directly. This code has the same effect:
let myfont = UIFont(name: "Papyrus", size: 24)! let attributedString = NSAttributedString(string: text, attributes: [NSAttributedString.Key.font: myfont])
When it comes to Attibuted string attributes. String attributes are just a String based keys, and I can define my own attribtued, that I can process later on while drawing (look for
CTRun later on)
CoreText has its own set of String Attributes I can use at any time when dealing with CoreText engine:
Most of it is mapped to/from UIKit/AppKit eg.
CTFont is a reference to the font.
CTFontDescriptor (bridged to NSFontDescriptor/UIFontDescriptor) is where fun with fonts happens. It allows me to adjust point size, and variation that can completely specify a font.
Each font comes with many metrics that font creator (author) specified for the font. Size is just one metric, and the most popular. Font size is a base, however when deal with text drawing (especially with text layout), I need to be aware of every other measurements needed to calculate correct (and expected) position for each letter.
Baseline is the most imporant value. Baseline is the base of the line, and it's not the bottom of the line. Some values (most of it) relates to base line, not to the bottom of the line - something worth remembering.
Baseline marks the Origin.
There's no single "height". Height of what? The height is one of Ascent, Descent, Leading (an more) or sum of it. See image above to find all the values. I can get all the values with CoreText.
Probably the most useful height is a line height
Perfect line height is a sum of Ascent + Descent + Leading. Life would be too easy if that'd be an ultimate answer. Later I'll discuss how that's not necessarily the most used line height.
For the most general metrics, here's the snippet
let fontLineHeight = CTFontGetAscent(font) + CTFontGetDescent(font) + CTFontGetLeading(font)
now, bear in mind we're dealing with a line (
CTLine) that is an Attributed String and fonts or font sizes may vary for the line. To calculate the line height, I should go over all letters and calculate tallest height value. I can do that, or I can use
CTLineGetTypographicBounds that will do all of that
var ascent: CGFloat = 0 var descent: CGFloat = 0 var leading: CGFloat = 0 let width = CTLineGetTypographicBounds(line, &ascent, &descent, &leading)
where width is the width of the line.
Note: If you have an prior experience with CoreText, you may notice at this point that the line height calulated with these values is not what CoreText uses for the layout. I'll discuss that in the next chapter...
Node: this is part of series (2 of many). check here tomorrow for next episode.