When drawing multiline text using Core Text (by preparing a paragraph and issuing a call to CTDrawFrame):
- if truncation is enabled via a paragraph setting (spec=
kCTParagraphStyleSpecifierLineBreakMode
and value=kCTLineBreakByTruncatingMiddle
), - and if one of the lines to render contains a line with spaces only (codepoint=U+0020),
- and if the available width for rendering is less than the natural width of the line containing only spaces,
then the rendering is incomplete, all content in the lines following the spaces is omitted.
Many other codepoints cause the same problem: tabs (U+0009), no-break spaces (U+00A0), control characters ...
The issue is quite severe because the rendering can break with a simple ASCII input ! Here is a recording with a yellow text background to help see the spaces :
To reproduce :
CGContextRef ctx = [[NSGraphicsContext currentContext] CGContext];
// String to display: 3 lines, the middle one contains only spaces (40)
CFStringRef str = CFSTR("Hello World!\n \nThis is a multiline example with a long line.");
// Font used for rendering
CTFontRef font = CTFontCreateWithName(CFSTR("Helvetica"), 20, nullptr);
// Paragraph style with middle truncation
CTLineBreakMode lineBreak = kCTLineBreakByTruncatingMiddle;
CTParagraphStyleSetting settings[] = {
{ kCTParagraphStyleSpecifierLineBreakMode, sizeof(CTLineBreakMode), &lineBreak }
};
CTParagraphStyleRef paragraphStyle = CTParagraphStyleCreate(settings, 1);
// Prepare text attributes
NSDictionary *attrs = @{
(__bridge id)kCTFontAttributeName : (__bridge id)font,
(__bridge id)kCTForegroundColorAttributeName : (__bridge id)[NSColor blackColor].CGColor,
(__bridge id)kCTBackgroundColorAttributeName : (__bridge id)[NSColor yellowColor].CGColor,
(__bridge id)kCTParagraphStyleAttributeName : (__bridge id)paragraphStyle,
};
NSAttributedString *attrStr =
[[NSAttributedString alloc] initWithString:(__bridge NSString*)str attributes:attrs];
// Create framesetter
CTFramesetterRef fs = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)attrStr);
CGMutablePathRef path = CGPathCreateMutable();
CGPathAddRect(path, nullptr, self.bounds);
CTFrameRef frame = CTFramesetterCreateFrame(fs, CFRangeMake(0, 0), path, nullptr);
// Draw the paragraph
CTFrameDraw(frame, ctx);
This seems like a bug in Core Text, unless I was missing some additional parameter to specify before calling CTFrameDraw()?