Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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?

UPDATE

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
CGPoint lOrigins[MAXLINESPERPAGE];
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);
like image 502
javsmo Avatar asked Dec 04 '11 18:12

javsmo


2 Answers

+ (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);
}
like image 73
abuharsky Avatar answered Nov 15 '22 17:11

abuharsky


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.

like image 20
omz Avatar answered Nov 15 '22 17:11

omz