@DTS Engineer
Thanks a lot. Seems this is what I was looking for. This allows me to figure out the best font size for a text to fit in a given rect. The only issue still see is related to drawing the string. For whatever reason half of the string is missing:
Here's the code:
- (void)drawRect:(NSRect)dirtyRect
{
[super drawRect:dirtyRect];
NSDictionary *attributes = [NSDictionary dictionaryWithObject:[NSFont systemFontOfSize:10] forKey:NSFontAttributeName];
NSMutableAttributedString *attrString = [[NSMutableAttributedString alloc] initWithString:@"test" attributes:attributes];
// get the font size to make the string fit into the text view
CGFloat fontSize = [attrString fontSizeToFitInRect:[self bounds]
minimumFontSize:10
maximumFontSize:0
];
// change the attributed string's font size
[attrString addAttribute:NSFontAttributeName
value:[[NSFontManager sharedFontManager] convertFont:[attrString font] toSize:fontSize]
range:NSMakeRange(0, [attrString length])
];
// draw the string
[attrString drawInRect:[self frame]];
}
And here are the extensions I made to the NSAttributedString class to calculate the string's bounds:
- (CGFloat)fontSizeToFitInRect:(NSRect)rect
minimumFontSize:(CGFloat)minFontSize
maximumFontSize:(CGFloat)maxFontSize
{
CGFloat fontSize = (maxFontSize > minFontSize) ? maxFontSize : NSHeight(rect) * 2;
CGFloat textHeight = CGFLOAT_MAX;
CGFloat textWidth = CGFLOAT_MAX;
while ((textHeight > NSHeight(rect) || textWidth > NSWidth(rect)) && fontSize >= minFontSize) {
NSMutableAttributedString *attrString = [[NSMutableAttributedString alloc] initWithAttributedString:self];
[attrString addAttribute:NSFontAttributeName
value:[[NSFontManager sharedFontManager] convertFont:[self font] toSize:--fontSize]
range:NSMakeRange(0, [[self string] length])
];
CGRect usedRect = [attrString bounds];
textHeight = ceil(NSHeight(usedRect));
textWidth = ceil(NSWidth(usedRect));
}
return fontSize;
}
- (CGRect)bounds
{
CGRect usedRect = CGRectZero;
CTLineRef line = CTLineCreateWithAttributedString((CFAttributedStringRef)self);
NSArray *ctRuns = (__bridge NSArray*)CTLineGetGlyphRuns(line);
for (id ctRun in ctRuns) {
NSDictionary *attributes = (__bridge NSDictionary *)CTRunGetAttributes((CTRunRef)ctRun);
id font = attributes[(NSString *)kCTFontAttributeName];
if (font) {
size_t runGlyphsCount = CTRunGetGlyphCount((CTRunRef)ctRun);
CGGlyph glyphs[runGlyphsCount];
CTRunGetGlyphs((CTRunRef)ctRun, CFRangeMake(0, 0), glyphs);
CGRect runRects[runGlyphsCount];
CTFontGetBoundingRectsForGlyphs((CTFontRef)font, kCTFontOrientationDefault, glyphs, runRects, runGlyphsCount);
for (size_t i = 0; i < runGlyphsCount; i++) {
CGRect rect = runRects[i];
CGFloat width = usedRect.size.width + rect.size.width;
CGFloat height = MAX(usedRect.size.height, rect.size.height);
CGFloat y = MIN(usedRect.origin.y, rect.origin.y);
usedRect = CGRectMake(usedRect.origin.x, y, width, height);
}
} else {
break;
}
}
return usedRect;
}
The string rect is a bit smaller than the rectangle of the view it should be drawn into. So I wonder why the string is cut off. With upper and lower case characters it looks like this:
Any idea? Thank you very much.
Regards,
Marc