How to get the real height of text drawn on a CTFrame

流过昼夜 提交于 2019-12-05 00:10:04


I have a certain amount of text that fill some CTFrame (more than one). To create all frames (one for each page), I'm filling one frame, getting the text that didn't fitted the frame using CTFrameGetVisibleStringRange and repeating this process until all text is processed.

On all frames, except the last, the text occupies the same height of page. On last frame I'd like to know the real height the text occupies, to know where I could start drawing more text.

Is there any way to do this?


As requested on comments, here's my solution using @omz 's suggestion:

I'm using ARC on my project:

CTFrameRef locCTFrame = (__bridge CTFrameRef)ctFrame;

//Save CTLines
lines = (NSArray *) ((__bridge id)CTFrameGetLines(locCTFrame));

//Get line origins
CTFrameGetLineOrigins(locCTFrame, CFRangeMake(0, 0), lOrigins);
CGFloat colHeight = self.frame.size.height;

//Save the amount of the height used by text
percentFull = ((colHeight - lOrigins[[lines count] - 1].y) / colHeight);


You could either get the line origin of the last line in the frame with CTFrameGetLineOrigins or use the CTFramesetterSuggestFrameSizeWithConstraints function to get the size of a rectangular frame for a given range. The latter wouldn't work if you use non-rectangular paths for setting the actual frames though.


+ (CGSize)measureFrame:(CTFrameRef)frame
    // 1. measure width
    CFArrayRef  lines       = CTFrameGetLines(frame);
    CFIndex     numLines    = CFArrayGetCount(lines);
    CGFloat     maxWidth    = 0;

    for(CFIndex index = 0; index < numLines; index++)
        CTLineRef   line = (CTLineRef) CFArrayGetValueAtIndex(lines, index);
        CGFloat     ascent, descent, leading, width;

        width = CTLineGetTypographicBounds(line, &ascent,  &descent, &leading);

        if(width > maxWidth)
            maxWidth = width;

    // 2. measure height
    CGFloat ascent, descent, leading;

    CTLineGetTypographicBounds((CTLineRef) CFArrayGetValueAtIndex(lines, 0), &ascent,  &descent, &leading);
    CGFloat firstLineHeight = ascent + descent + leading;

    CTLineGetTypographicBounds((CTLineRef) CFArrayGetValueAtIndex(lines, numLines - 1), &ascent,  &descent, &leading);
    CGFloat lastLineHeight  = ascent + descent + leading;

    CGPoint firstLineOrigin;
    CTFrameGetLineOrigins(frame, CFRangeMake(0, 1), &firstLineOrigin);

    CGPoint lastLineOrigin;
    CTFrameGetLineOrigins(frame, CFRangeMake(numLines - 1, 1), &lastLineOrigin);

    CGFloat textHeight = ABS(firstLineOrigin.y - lastLineOrigin.y) + firstLineHeight + lastLineHeight;

    return CGSizeMake(maxWidth, textHeight);


Use CTLineGetTypographicBounds.