CTFramesetterSuggestFrameSizeWithConstraints sometimes returns incorrect size?

In the code below, CTFramesetterSuggestFrameSizeWithConstraints sometimes returns a CGSize with a height that is not big enough to contain all the text that is being passed into it. I did look at this answer. But in my case the width of the text box needs to be constant. Is there any other/better way to figure out the correct height for my attributed string? Thanks!

CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString(attributedString);
CGSize tmpSize = CTFramesetterSuggestFrameSizeWithConstraints(framesetter, CFRangeMake(0,0), NULL, CGSizeMake(self.view.bounds.size.width, CGFLOAT_MAX), NULL); 
CGSize textBoxSize = CGSizeMake((int)tmpSize.width + 1, (int)tmpSize.height + 1);

CTFramesetterSuggestFrameSizeWithConstraints() is broken. I filed a bug on this a while back. Your alternative is to use CTFramesetterCreateFrame() with a path that is sufficiently high. Then you can measure the rect of the CTFrame that you get back. Note that you cannot use CGFLOAT_MAX for the height, as CoreText uses a flipped coordinate system from the iPhone and will locate its text at the "top" of the box. This means that if you use CGFLOAT_MAX, you won't have enough precision to actually tell the height of the box. I recommend using something like 10,000 as your height, as that's 10x taller than the screen itself and yet gives enough precision for the resulting rectangle. If you need to lay out even taller text, you can do this multiple times for each section of text (you can ask CTFrameRef for the range in the original string that it was able to lay out).

Core Text's CTFramesetterSuggestFrameSizeWithConstraints , Search. CTFramesetterSuggestFrameSizeWithConstraints sometimes returns incorrect size? Question. In the code below,  According to the docs, CTFramesetterSuggestFrameSizeWithConstraints () "determines the frame size needed for a string range". Unfortunately the size returned by this function is never accurate.


CTFramesetterSuggestFrameSizeWithConstraints works correctly. The reason that you get a height that is too short is because of the leading in the default paragraph style attached to attributed strings. If you don't attach a paragraph style to the string then CoreText returns the height needed to render the text, but with no space between the lines. This took me forever to figure out. Nothing in the documentation spells it out. I just happened to notice that my heights were short by an amount equal to (number of lines x expected leading). To get the height result you expect you can use code like the following:

NSString  *text = @"This\nis\nsome\nmulti-line\nsample\ntext."
UIFont    *uiFont = [UIFont fontWithName:@"Helvetica" size:17.0];
CTFontRef ctFont = CTFontCreateWithName((CFStringRef) uiFont.fontName, uiFont.pointSize, NULL);

//  When you create an attributed string the default paragraph style has a leading 
//  of 0.0. Create a paragraph style that will set the line adjustment equal to
//  the leading value of the font.
CGFloat leading = uiFont.lineHeight - uiFont.ascender + uiFont.descender;
CTParagraphStyleSetting paragraphSettings[1] = { kCTParagraphStyleSpecifierLineSpacingAdjustment, sizeof (CGFloat), &leading };

CTParagraphStyleRef  paragraphStyle = CTParagraphStyleCreate(paragraphSettings, 1);
CFRange textRange = CFRangeMake(0, text.length);

//  Create an empty mutable string big enough to hold our test
CFMutableAttributedStringRef string = CFAttributedStringCreateMutable(kCFAllocatorDefault, text.length);

//  Inject our text into it
CFAttributedStringReplaceString(string, CFRangeMake(0, 0), (CFStringRef) text);

//  Apply our font and line spacing attributes over the span
CFAttributedStringSetAttribute(string, textRange, kCTFontAttributeName, ctFont);
CFAttributedStringSetAttribute(string, textRange, kCTParagraphStyleAttributeName, paragraphStyle);

CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString(string);
CFRange fitRange;

CGSize frameSize = CTFramesetterSuggestFrameSizeWithConstraints(framesetter, textRange, NULL, bounds, &fitRange);

CFRelease(framesetter);
CFRelease(string);

CTFramesetterSuggestFrameSizeWithConstraints, On return, contains the range of the string that actually fit in the constrained size. Return Value. The actual dimensions for the given string range and constraints. I have encountered a number of directories that now show incorrect “properties” information after the recent Windows 10 upgrade to the 1803 Feature Update. Most directories that I have looked at still show valid information. But in some specific directories the calculated size, number of files, and number of folders is wildly inaccurate.


Thanks to Chris DeSalvo for the excellent answer! Finally ending 16 hours of debugging. I had a little trouble figuring out the Swift 3 syntax. So sharing the Swift 3 version of setting the paragraph style.

let leading = uiFont.lineHeight - uiFont.ascender + uiFont.descender
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.lineSpacing = leading      
mutableAttributedString.addAttribute(NSParagraphStyleAttributeName, value: paragraphStyle, range: textRange)

Re: CTFramesetterSuggestFrameSizeWithConstraints - Lists, a while back, and after a lot of anguish I think I worked out what was going wrong. 'CTFramesetterSuggestFrameSizeWithConstraints' sometimes returns a width that's *larger* than the width in the 'constraints' it when you got the suggested frame size, and the text wraps with (generally) one extra line. Why does fread sometimes return incorrect values? Follow 20 views (last 30 days) Harry on 26 Mar 2019. Vote. 0 ⋮ Vote. 0. Commented: Walter Roberson on 27 Mar 2019


Working on this problem and following a lot of different answers from several posters, I had implemented a solution for the all mighty problem of correct text size, for me CTFramesetterSuggestFrameSizeWithConstraints is not working properly so we need to use CTFramesetterCreateFrame and then measure the size for that frame, (this is a UIFont extension) this is swift 100%

References

CTFramesetterSuggestFrameSizeWithConstraints sometimes returns incorrect size?

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

Using CFArrayGetValueAtIndex in Swift with UnsafePointer (AUPreset)

func sizeOfString (string: String, constrainedToWidth width: Double) -> CGSize {
        let attributes = [NSAttributedString.Key.font:self]
        let attString = NSAttributedString(string: string,attributes: attributes)
        let framesetter = CTFramesetterCreateWithAttributedString(attString)

        let frame = CTFramesetterCreateFrame(framesetter,CFRange(location: 0,length: 0),CGPath.init(rect: CGRect(x: 0, y: 0, width: width, height: 10000), transform: nil),nil)

        return UIFont.measure(frame: frame)
    }

Then we measure our CTFrame

static func measure(frame:CTFrame) ->CGSize {

        let lines = CTFrameGetLines(frame)
        let numOflines = CFArrayGetCount(lines)
        var maxWidth : Double = 0

        for index in 0..<numOflines {
            let line : CTLine = unsafeBitCast(CFArrayGetValueAtIndex(lines, index), to: CTLine.self)
            var ascent : CGFloat = 0
            var descent : CGFloat = 0
            var leading : CGFloat = 0
            let width = CTLineGetTypographicBounds(line, &ascent, &descent, &leading)

            if(width > maxWidth) {
                maxWidth = width
            }
        }

        var ascent : CGFloat = 0
        var descent : CGFloat = 0
        var leading : CGFloat = 0


        CTLineGetTypographicBounds(unsafeBitCast(CFArrayGetValueAtIndex(lines, 0), to: CTLine.self), &ascent, &descent, &leading)
        let firstLineHeight = ascent + descent + leading

        CTLineGetTypographicBounds(unsafeBitCast(CFArrayGetValueAtIndex(lines, numOflines - 1), to: CTLine.self), &ascent, &descent, &leading)
        let lastLineHeight = ascent + descent + leading

        var firstLineOrigin : CGPoint = CGPoint(x: 0, y: 0)
        CTFrameGetLineOrigins(frame, CFRangeMake(0, 1), &firstLineOrigin);

        var lastLineOrigin : CGPoint = CGPoint(x: 0, y: 0)
        CTFrameGetLineOrigins(frame, CFRangeMake(numOflines - 1, 1), &lastLineOrigin);

        let textHeight = abs(firstLineOrigin.y - lastLineOrigin.y) + firstLineHeight + lastLineHeight

        return CGSize(width: maxWidth, height: Double(textHeight))
    }

fix a bug about compute string size · Issue #142 · AliSoftware , and max size is (width=255.5, height=3.40282e+38); This is an Apple bug in CTFramesetterSuggestFrameSizeWithConstraints . size the string would take when drawn, you instead return a totally wrong size with your fix. (You can still tweak the size returned by this method by yourself in your own app  Excel calculates wrong. Yes, in some cases, Excel will return wrong results. You don’t believe me? Then type the following formula into an empty Excel cell: =1*(0.5-0.4-0.1). The result should be 0. But what does Excel show? -2,77556E-17. This is just a simple example, but when it comes to larger Excel models it can be quite annoying.


I finally find out.. When uses CTFramesetterSuggestFrameSizeWithConstraints returning CGSize to draw text, the size is considered not big enough(sometimes) for the whole text, so last line is not drew.. I just add 1 to size.height for return, it appears to be right now.

Something like this:

suggestedSize = CGSizeMake(suggestedSize.width, suggestedSize.height + 1);

boundingRectWithSize for NSAttributedString returning wrong size, boundingRectWithSize function as well as CTFramesetterSuggestFrameSizeWithConstraints (and many other methods) will calculate size and text portion correct  We hope you love what you've ordered, but we know that sometimes things don’t work out: Initiate your return within 90 days of the purchase date on your receipt or packing slip. If you want to replace an item in a different size or color, we recommend processing a purchase quickly as items do sell out. Damaged or missing items?


subject:"Re\: boundingRectWithSize gives wrong size", I must confess that I made the mistake of calculating the size of the from the font that CTFramesetterSuggestFrameSizeWithConstraints() On Sep 10, 2019, at 9:​54 AM, Gabriel Zachmann wrote: > > The excess width is sometimes more, The problem is that this method does not return the correct width. Returns support Return, replace, or exchange items. Start a return by going to Your Orders. Manage recent returns. Print return labels and check the status of your recent returns. Return a gift. Start a gift return by using your order number (e.g. 123-1234567-1234567) on the Gift Returns page.


iphone boundingRectWithSize for NSAttributedString returning , iphone boundingRectWithSize for NSAttributedString returning wrong size? For some reason, boundingRectWithSize always returns wrong size. Sometimes the returned width is larger than the width of the size parameter. fitSize = CTFramesetterSuggestFrameSizeWithConstraints(framesetter, CFRangeMake(0,  We understand that people sometimes change their minds or may inadvertently confirm an unapproved purchase. If you place your order online and decide it’s not for you, please give our Customer Service department a call at 1-800-TRAEGER (1-800-872-3437) within 24 hours of purchasing.


A simple way to check for this error is to use the MATCH formula by itself and see if it returns a value. Doing so is useful when you’re still learning Excel because it isolates a smaller portion of the INDEX MATCH formula, and the shorter length reduces the chances of making additional errors.