Reputation: 234857
I'm developing a custom UIView
that renders text using a number of separate NSLayoutManager
objects. (This view has text positioning requirements beyond what the built-in text view classes can support.) I'm having a hard time getting the correct vertical position for one of the layouts when the text being laid out is Hebrew. I apologize for the length of this post, but I wanted to include as much relevant detail as possible.
The problem
Here's an example of what I'm trying to accomplish:
The baseline for the "2" is nicely aligned with the baseline of the Hebrew letter "ו" (which is rendered in a custom font). However, if I switch the text of the "2" to a Hebrew number (actually the Hebrew letter "ב", which has value 2 in numerical contexts), it comes out like this (where the "ב" is too high):
The only change here is to the text string. Instead of "2"
, it's "\u{05b1}"
. I want everything to be on a common baseline and would appreciate any suggestions for fixing the problem (which happens for all Hebrew numbers, not just "\u{05b1}"
).
The code
(I should mention that for technical reasons, I can't use a single NSLayoutManager
for both the number and the main text.) I'm trying to align the baselines by vertically shifting the rendering position for the number by the difference in font ascents. Here's the code to compute the shift:
numberFont = UIFont.systemFont(ofSize: /* some size */)
offset = fullFont.ascender - numberFont.ascender
Here's how I'm creating the NSLayoutManager
for the number:
var lbl = "2" // or "\u{05b1}"
let paraStyle = NSMutableParagraphStyle()
paraStyle.alignment = .center
let text = NSTextStorage(
string: lbl,
attributes: [NSFontAttributeName: numberFont,
NSParagraphStyleAttributeName: paraStyle]
)
let layout = NSLayoutManager()
text.addLayoutManager(layout)
let container = NSTextContainer(
size: CGSize(width: /* some fixed width */, height: bounds.height - offset)
)
container.lineFragmentPadding = 0
layout.addTextContainer(container)
let x = ... // irrelevant
let origin = CGPoint(x: x, y: bounds.minY + offset)
Later, I render the text using:
let range = layout.glyphRange(for: layout.textContainers[0])
layout.drawGlyphs(forGlyphRange: range, at: origin)
The main text is handled using identical logic but with no offset
(and a different font, of course). This works beautifully for Arabic numerals (e.g., "2"), but fails for Hebrew numbers (e.g., "ב").
Further information
For the images above, the font size (.pointSize
) and ascent (.ascender
) properties are:
sizes: text = 14.3890409469604 ; number = 8.63342465753425
ascents: text = 16.159567469731 ; number = 8.2203017979452
I also generating some diagnostic information about the layouts, but the numbers don't make a lot of sense to me. Here's the code for printing out the information:
size = text.size()
mgr = NSLayoutManager()
text.addLayoutManager(mgr)
container = NSTextContainer(
size: CGSize( width: CGFloat.greatestFiniteMagnitude,
height: CGFloat.greatestFiniteMagnitude))
container.lineFragmentPadding = 0
mgr.addTextContainer(container)
mgr.ensureLayout(for: container)
range = mgr.glyphRange(for: container)
print("label:", lbl)
print(" text size:", size)
print(" bounds:", mgr.boundingRect(forGlyphRange: range, in: container))
And here are the results for the two texts ("2" and "\u{05d1}"):
label: 2
text size: (5.3958904109589, 10.3027782534247)
bounds: (0.0, 0.0, 5.3958904109589, 10.3027782534247)
label: ב
text size: (4.67931616438356, 9.19459726027397)
bounds: (-1.88208657534247, -0.578439452054793, 9.5054005479452, 9.77303671232876)
Several things about this puzzle me:
NSAttributedString.size()
broken?Upvotes: 0
Views: 845