3

I am running into a problem while using Core Text, where the first line of the text I display in a CTFrame is cut off at the top, as seen in the screenshot below, with the character "B":

Example of CTFrame clipping first line

I think I'm doing something wrong while setting the leading in the CTFrame. My code is below:

- (void)drawRect:(CGRect)rect
{
    [super drawRect:rect];
    CGContextRef context = UIGraphicsGetCurrentContext();

    NSAttributedString *myString;

    //Create the rectangle into which we'll draw the text
    CGMutablePathRef path = CGPathCreateMutable();
    CGPathAddRect(path, NULL, self.bounds);

    //Flip the coordinate system
    CGContextSetTextMatrix(context, CGAffineTransformIdentity);
    CGContextTranslateCTM(context, 0, self.bounds.size.height);
    CGContextScaleCTM(context, 1.0, -1.0);

    //Setup leading (line height)
    CGFloat lineHeight = 25;
    CTParagraphStyleSetting settings[] = {
        { kCTParagraphStyleSpecifierMinimumLineHeight, sizeof(CGFloat), &lineHeight },
        { kCTParagraphStyleSpecifierMaximumLineHeight, sizeof(CGFloat), &lineHeight },
    };

    CTParagraphStyleRef paragraphStyle = CTParagraphStyleCreate(settings, sizeof(settings) / sizeof(settings[0]));
    NSDictionary * attrs = [[NSDictionary alloc] initWithObjectsAndKeys:

                                (__bridge id)CTFontCreateWithName((__bridge CFStringRef) font.fontName, font.pointSize, NULL) ,
                                (NSString*)kCTFontAttributeName,

                                (id)textColor.CGColor,
                                (NSString*)kCTForegroundColorAttributeName,

                                (__bridge id) paragraphStyle,
                                kCTParagraphStyleAttributeName,
                                nil];

    myString = [[NSAttributedString alloc] initWithString:text attributes:attrs];
    CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((__bridge CFAttributedStringRef)myString);
    CTFrameRef frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0,[myString length]), path, NULL);

    CTFrameDraw(frame, context);
    CFRelease(frame);
    CFRelease(framesetter);
    CFRelease(path);
}

Other SO posts (this one and this one) are not really helpful.

How can I prevent the CTFrame from clipping the first line?

--EDIT--

Reducing lineheight to 20:

lineheight from the second line onwards is respected, but the baseline of the first line of text is less than 20 below the top.

enter image description here

Community
  • 1
  • 1
Dominic Mortlock
  • 284
  • 3
  • 11

2 Answers2

1

I managed to fix the problem. In the end it turned out to be the location at which I flipped the coordinate system, and by flipping it earlier on in the code the problem fixed itself.

Dominic Mortlock
  • 284
  • 3
  • 11
  • I noticed that you got your solution years ago. But could you help me elaborate more what do you mean by "flipping it earlier"? Because from what I read through your code in the question section, it directly do the flipping once the `CGMutablePathRef` is created. – Willy Jun 12 '15 at 09:44
0

Here CGFloat lineHeight = 25; I am sure (from your screen shot) its less than font size you are currently using (font.pointSize). This is the only reason of clipping top of text, as text size not able to fit in 25 points of height.

You have two options:

1) Remove paragraphStyle and its related code. Replace your NSDictionary * attrs = … by following code.

NSDictionary * attrs = [[NSDictionary alloc] initWithObjectsAndKeys:
                               (__bridge id)CTFontCreateWithName((__bridge CFStringRef) font.fontName, font.pointSize, NULL) ,
                                (NSString*)kCTFontAttributeName,
                                (id)textColor.CGColor, (NSString*)kCTForegroundColorAttributeName,
                                nil];

CTFrameDraw() will take care for line height automatically.

2) Or always provide lineHeight greater than your max font size. Like lineHeight = font.pointSize + somemargine;

To understand above explanation try this: In your current code set your lineHeight = 20;; you can see more text will be clipped from top line and second line text will be more closed to bottom of its top line.

atrane
  • 398
  • 6
  • 20
  • I originally thought this, but measuring from the top to the baseline shows that the first line is much less than the specified `lineheight` while the second line onwards was exactly as specified. See the edit in the question. – Dominic Mortlock Oct 29 '12 at 10:48
  • Second and third lines are using standard space between two lines. if you go below line-height too much (eg. 10) you can see clipping of them also. Do one thing, comment out (__bridge id) paragraphStyle, kCTParagraphStyleAttributeName, And try without line spacing and paragraphStyle. – atrane Oct 29 '12 at 12:17