3

Let's say I'm writing text viewer for the iPhone using Core Text. Every time user changes base font size I need to count how many pages (fixed size CGRects) are needed to display the whole NSAttributedString with given font sizes.

And I would like to do this in separate NSOperation, so that user does not experience unnecessary UI lags.

Unfortunately, to count pages I need to draw my frames (CTFrameDraw) using invisible text drawing mode and then use CTFrameGetVisibleStringRange to count characters. But to draw a text I need a CGContext. And here the problems begin...

I can obtain a CGContext in my drawRect by calling UIGraphicsGetCurrentContext, but in this case:

  1. I have to call any method that operates on the CGContext using performSelectorOnMainThread, right?
  2. The other thread should CFRetain this context. Is it acceptable to use drawRect's CGContext outside drawRect method?

Any other solutions? Creating separate CGContext in the worker thread? How? CGBitmapContext? How can I be sure that all conditions (i don't know, resolution? etc.) will be the same as in drawRect's CGContext, so that pages will be counted correctly?

Łukasz Sromek
  • 3,637
  • 3
  • 30
  • 43
  • Hmm, it seems that in Core Text (unlike Quartz) you don't have to draw invisible text to measure it. So one just needs to call CTFramesetterCreateFrame and then CTFrameGetVisibleStringRange without using CGContext at all! Anyone can confirm this? – Łukasz Sromek Jul 09 '10 at 16:03

3 Answers3

0

use CTFramesetterSuggestFrameSizeWithConstraints ,if you specify the parameter of fitRange,it return the actual range of the string

+ (NSArray*) pagesWithString:(NSString*)string size:(CGSize)size font:(UIFont*)font;
{
  NSMutableArray* result = [[NSMutableArray alloc] initWithCapacity:32];
  CTFontRef fnt = CTFontCreateWithName((CFStringRef)font.fontName, font.pointSize,NULL);
  CFAttributedStringRef str = CFAttributedStringCreate(kCFAllocatorDefault, 
                                                       (CFStringRef)string, 
                                                       (CFDictionaryRef)[NSDictionary dictionaryWithObjectsAndKeys:(id)fnt,kCTFontAttributeName,nil]);
  CTFramesetterRef fs = CTFramesetterCreateWithAttributedString(str);
  CFRange r = {0,0};
  CFRange res = {0,0};
  NSInteger str_len = [string length];
  do {
    CTFramesetterSuggestFrameSizeWithConstraints(fs,r, NULL, size, &res);
    r.location += res.length;
    [result addObject:[NSNumber numberWithInt:res.length]];
  } while(r.location < str_len);

  CFRelease(fs);
  CFRelease(str);
  CFRelease(fnt);
  return result;
}  
jasonhao
  • 2,098
  • 1
  • 14
  • 7
0

You can use CTFramesetterSuggestFrameSizeWithConstraints.

See my question here: How to split long NSString into pages

Community
  • 1
  • 1
Olof Hedman
  • 613
  • 6
  • 17
  • Don't trust CTFramesetterSuggestFrameSizeWithConstraints! See here: http://web.archiveorange.com/archive/v/nagQXwVJ6Gzix0veMh09 – Łukasz Sromek Oct 06 '10 at 13:08
  • As far as I understand, its just the CGSize that is bad. In my code I ignore the returned size, I use the original size I used as constraint, and it seems to work fine so far. Havn't tested it extensively yet though. – Olof Hedman Oct 08 '10 at 08:44
  • I figure I don't need the returned size, since there is other ways to measure the text when you have the range that will fit in the constraint. – Olof Hedman Oct 08 '10 at 08:49
0

You don't need to CTFrameDraw before getting result from CTFrameGetVisibleStringRange

overboming
  • 1,502
  • 1
  • 18
  • 36