iPhone iOS UIFont conversion to CTFontRef causes size misalignment

3.5k views Asked by At

I know that UIFont and CTFont are different things, but I was able to kinda make it work.

I'm trying to generate a PDF and use a UIView as a template, however to draw editable PDF, I need to have a CTFontRef render itself in a certain frame. Below is my method that draws a UILabel in PDF Context. The method uses UILabel as a template for font name, font size and position, while CTFontRef renders characters into PDF.

The method works, but has quirks.

I noticed that the code below may fail to render in a frame that is big enough to fit a text written with UIFont. For example I have a 80x21 UILabel which fits a certain font. If I try to render that font with CTFontRef, I get a blank space, unless I increase the label's height to let's say 80x30. Simply put, CTFontRef will not render text that does not clips the frame either horizontally or vertically.

Additionally, I noticed that some fonts have different width, so an 80x21 label that fits 20 characters of UIFont may only fit like 15 characters of CTFontRef. My guess is this is because CTFontRef does not truncate text and simply does not render words that are sticking out in either direction from the frame.

A few questions: What could be causing my font sizes to misalign? Do I need to copy some other property over? Is there a way to ask CTFontRef to truncate long words at the end of a label?

//xOriginOffset, yOriginOffset is the X of the origin of the container view that holds the label.

+(void)drawTextLabel:(UILabel*)label WithXOriginOffset:(int)xOriginOffset YOffset:(int)yOriginOffset
{

    NSString* textToDraw = label.text;
    CFStringRef stringRef = (__bridge CFStringRef)textToDraw;

    UIFont* uiFont = label.font;


    // Prepare font

   CTFontRef font = CTFontCreateWithName((__bridge CFStringRef)uiFont.fontName, uiFont.pointSize, NULL);

    // Create an attributed string
    CFStringRef keys[] = { kCTFontAttributeName,kCTForegroundColorAttributeName };
    CFTypeRef values[] = { font,[[UIColor redColor]CGColor] };
    CFDictionaryRef attr = CFDictionaryCreate(NULL, (const void **)&keys, (const void **)&values,
                                              sizeof(keys) / sizeof(keys[0]), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);

    // Prepare the text using a Core Text Framesetter
    CFAttributedStringRef currentText = CFAttributedStringCreate(NULL, stringRef, attr);

    NSAssert(currentText!=nil,@"current text is nil!");
    CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString(currentText);

    //account for the view's superview
    CGRect frameRect = CGRectMake(xOriginOffset+label.frame.origin.x,
                                  yOriginOffset+label.frame.origin.y,
                                  label.frame.size.width,
                                  label.frame.size.height);


    CGMutablePathRef framePath = CGPathCreateMutable();
    CGPathAddRect(framePath, NULL, frameRect);

    // Get the frame that will do the rendering.
    CFRange currentRange = CFRangeMake(0, 0);
    CTFrameRef frameRef = CTFramesetterCreateFrame(framesetter, currentRange, framePath, NULL);
    CGPathRelease(framePath);

    // Get the graphics context.
    CGContextRef currentContext = UIGraphicsGetCurrentContext();

     NSAssert(currentContext!=nil,@"currentContext is nil!");


    // Draw the frame.
    CTFrameDraw(frameRef, currentContext);

    CFRelease(frameRef);
    CFRelease(stringRef);
    CFRelease(framesetter);
}

UPDATE:

I refactored the code and am using this method now, to produce full featured PDF text.

+(void)drawTextLabel:(UILabel*)label WithXOriginOffset:(int)xOriginOffset YOffset:(int)yOriginOffset
{

    NSString* textToDraw = label.text;

    UIFont* uiFont = label.font;
        // Prepare font
    CGRect frameRect = CGRectMake(xOriginOffset+label.frame.origin.x,
                                  yOriginOffset+label.frame.origin.y,
                                  label.frame.size.width,
                                  label.frame.size.height);

    [textToDraw drawInRect:frameRect withFont:uiFont lineBreakMode:UILineBreakModeTailTruncation];
  }
1

There are 1 answers

1
idz On BEST ANSWER

1) You can't mix text drawn by UIKit with text drawn by CoreText. This is mentioned in the Apple docs (See Text, Web and Editing Programming for iOS in the section "Core Text and the UIKit Framework" ). (What appears to be going on is that UIKit does some rounding and takes some shortcuts to boost performance and also does not do kerning by default.)

2) Nope, you're not missing some property.

3) CoreText does not provide a simple means of truncating the text, you would have to write your own code to do it.