CoreText With Too Long Arabic Text
Asked Answered
T

1

6

I was trying to follow a tutorial on CoreText and how to draw the text, and I implemented this function in my customView.

override func draw(_ rect: CGRect) {         
 guard let context = UIGraphicsGetCurrentContext() else { return }       
 let path = CGMutablePath()
 path.addRect(bounds)
 let attrString = NSAttributedString(string: "Hello World")
 let framesetter = CTFramesetterCreateWithAttributedString(attrString as CFAttributedString)
 let frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, attrString.length), path, nil) 
 CTFrameDraw(frame, context)
}

It works fine when the text is short but when the text becomes more than 10k letters, it renders in a wrong way. Is there any solution for that?

NOTE: this happen when the text in Arabic not English.

Here is the render when the text is small: enter image description here

Here is the text render when the text count is too big above 10kb, it appears disjointed and reversed:

enter image description here

Trier answered 14/12, 2020 at 17:20 Comment(5)
That's quite a bug. I've reproduced it just as you describe, somewhere around 10k letters. I see the same problem if you skip the CTFrameSetter and use CTLine and CTTypesetter directly. Other than "make your strings shorter," I'm not sure what to do here other than talk with Apple. I'd probably use a DTS for this if you have any left this year. (click "Code-Level Support" in the account pane at developer.apple.com) This is really interesting bug.Bellbird
Hi @RobNapier, Thanks a lot for your replay, As you recommended I sent the issue to DTS, and they respond with the following: In the particular sample that you provided, it might not actually be the size of the text so much as the single english word in the middle of the text that could be demanding additional Unicode processing. But, by default, those parts of CoreText that draw complex Unicode are turned off by default to avoid slow performance and other potential problems. This is controlled by the kCTTypesetterOptionAllowUnboundedLayout (default false) Typesetter Option.Trier
@RobNapier, So How can I enable such value, and as you know i didn't use Typesetter class?Trier
I reproduced this issue using text from istizada.com/arabic-lorem-ipsum (copying a paragraph repeatedly). It definitely should not have had mixed-bidi text (ltr and rtl). And it definitely happens when I cross a specific size, so I don't buy their "single english word" theory. But kCTTypesetterOptionAllowUnboundedLayout does seem to fix it. I hacked together my test case, and it doesn't do proper layout, but I'll write up correct example later this morning. (Note that I think using attrString.draw(in: rect) instead of Core Text will work here, but I get that you want to learn CT)Bellbird
Yeah I don't agree with them about this line "single english word" too , Thanks a lot @RobNapierTrier
B
3

Based on your feedback from Apple, it seems that CTFramesetter turns off advanced layout when the string is longer than a certain size. (Their suggestion that this is because there is mixed-direction text doesn't sound right; I've definitely reproduced this with only Arabic.)

To fix this, you need to create a framesetter with a custom typesetter that always does advanced layout (kCTTypesetterOptionAllowUnboundedLayout). Luckily that's straightforward.

Create a custom typesetter first, and then use that to create the framesetter:

let typesetterOptions = [kCTTypesetterOptionAllowUnboundedLayout: true] as CFDictionary
let typesetter = CTTypesetterCreateWithAttributedStringAndOptions(attrString,
                                                                  typesetterOptions)!
let framesetter = CTFramesetterCreateWithTypesetter(typesetter)
Bellbird answered 16/12, 2020 at 15:16 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.