Post

Replies

Boosts

Views

Activity

Reply to NSAttributedString draw in rect
Ok, I figured it out. The following code gives me the correct bounds of the attributed string: - (CGRect)typographicBounds { CGRect usedRect = CGRectZero; CTLineRef line = CTLineCreateWithAttributedString((CFAttributedStringRef)self); if (line) { CGContextRef context = [[NSGraphicsContext currentContext] CGContext]; usedRect = CTLineGetImageBounds(line, context); CFRelease(line); } return usedRect; } and the following code draws it exactly into the view: NSRect stringRect = [_attributedString typographicBounds]; // draw the string CTLineRef line = CTLineCreateWithAttributedString((CFAttributedStringRef)_attributedString); if (line) { CGContextRef context = [[NSGraphicsContext currentContext] CGContext]; CGContextSetTextPosition( context, ((NSWidth([self frame]) - NSWidth(stringRect)) / 2) - stringRect.origin.x, ((NSHeight([self frame]) - NSHeight(stringRect)) / 2) - stringRect.origin.y ); CTLineDraw(line, context); CFRelease(line); } @DTS Engineer Thank you for your patience and for showing me the right direction.
Aug ’24
Reply to NSAttributedString draw in rect
This is what I have so far: CTLineRef line = CTLineCreateWithAttributedString((CFAttributedStringRef)self); if (line) { CGContextRef context = [[NSGraphicsContext currentContext] CGContext]; CTLineDraw(line, context); CFRelease(line); } Looks quite good… Just have to figure out how to align it horizontally and vertically in the rect.
Aug ’24
Reply to NSAttributedString draw in rect
@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
Aug ’24
Reply to NSAttributedString draw in rect
@DTS Engineer Thanks, again. I tried your code but it returns more or less the same rect as boundingRectWithSize:options:context:. So it returns the exact same height for a lower case "x" and an upper case "X": x -> {{-66, 0}, {132, 303.63671875}} X -> {{-82.88671875, 0}, {165.7734375, 303.63671875}} Any idea? Thanks…
Aug ’24
Reply to NSAttributedString draw in rect
@DTS Engineer Thank you! This looks very promising. In the sample code it works as expected but if I try to create a NSTextLineFragment from an attributed string,typographicBounds always returns {{0, 0}, {0, 0}}… NSMutableAttributedString *attrString = [[NSMutableAttributedString alloc] initWithString:@"test"]; [attrString addAttribute:NSFontAttributeName value:[NSFont systemFontOfSize:90] range:NSMakeRange(0, [[attrString string] length]) ]; NSTextLineFragment *fragment = [[NSTextLineFragment alloc] initWithAttributedString:attrString range:NSMakeRange(0, [[attrString string] length]) ]; CGRect usedRect = [fragment typographicBounds];
Aug ’24
Reply to NSAttributedString draw in rect
For me it looks like boundingRectWithSize:options:context: just uses the attributed string's font (and its attributes) to calculate a bounding rectangle that could hold any combination of characters with the specific font and font size. The height of the bounding rect is more or less the same, regardless of what combinations of characters I use in the string. So the question is how to get the real bounding rect… 🤷🏻‍♂️
Aug ’24
Reply to NSAttributedString draw in rect
This is how I try to get the font size at the moment but it seems the value is always a bit too small… - (CGFloat)fontSizeToFitInRect:(NSRect)rect minimumFontSize:(CGFloat)minFontSize maximumFontSize:(CGFloat)maxFontSize { CGFloat fontSize = (maxFontSize > minFontSize) ? maxFontSize : NSHeight(rect); 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 boundingRectWithSize:NSMakeSize(CGFLOAT_MAX, CGFLOAT_MAX) options:0 context:nil ]; textHeight = ceil(NSHeight(usedRect)); textWidth = ceil(NSWidth(usedRect)); } return fontSize; }
Aug ’24
Reply to User defaults not updating
@DTS Engineer I don't think that I misuse NSUserDefaults. In the app the user can save different configurations and they will stored in NSUserDefaults as an array of dictionaries. This works as expected. But now I got the request to allow the user to set one of the configurations as the default configuration. So I just wanted to add an additional key to one of the dictionaries in the array to mark it as the default. I also tried "mutableArrayForKeyPath" but even if using this, the user defaults are not updated…
Aug ’24
Reply to Launch constraints disappear
@DTS Engineer It fails in Xcode 16 beta 3. Also tried your sample project from here: https://developer.apple.com/documentation/security/constraining_a_tool_s_launch_environment but as soon as I upload it for notarizing the constraints also disappear. So the issue seems not to be related to my project.
Jul ’24
Reply to Launch constraints to prevent an agent from being launched manually by a user?
Thank you, @DTS Engineer. The correct syntax seems to be: <dict> <key>$or-array</key> <array> <array> <string>$or</string> <dict> <key>launch-type</key> <dict> <key>$lt</key> <integer>3</integer> </dict> </dict> </array> <array> <string>$or</string> <dict> <key>launch-type</key> <dict> <key>$gt</key> <integer>3</integer> </dict> </dict> </array> </array> </dict> Regards, Marc
Jul ’24