2 years ago
#116550
Peter W.
NSFont autoscaling via NSStringDrawingContext does not work
I'm building a small test app that draws text into rectangles on images. Said text might sometimes be too long to be drawn into said rectangles, but I thought I would simply use NSStringDrawingContext
and NSAttributedString.boundingRect(with:options:context:)
in order to generate a scale factor that I'd use to scale the font down to make it fit.
The problem I'm now facing is that this straight up is not working, as actualScaleFactor
is always 1.0, even though it should be less than that. To demonstrate, I've prepared a minimal reproducible example.
The image used as input variable image
can be found here: https://i.imgur.com/VNd2y8r.png. The text target rectangle rect
is marked by the black rectangle in the image.
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.alignment = .left
paragraphStyle.lineBreakMode = .byWordWrapping
let textAttributes: [NSAttributedString.Key: Any] = [
.font: NSFont(name: "Futura-Bold", size: 100)!,
.foregroundColor: NSColor.white,
.paragraphStyle: paragraphStyle,
]
let text = "A very long string that'll be auto-wrapped when it gets drawn"
let attrStr = NSAttributedString(string: text, attributes: textAttributes)
let drawingOptions: NSString.DrawingOptions = [.usesLineFragmentOrigin, .usesFontLeading]
let context = NSStringDrawingContext()
// counter-intuitively, 1.0 is the "maximum" minimum scale factor
// see also https://developer.apple.com/documentation/uikit/nsstringdrawingcontext/1534020-minimumscalefactor
context.minimumScaleFactor = 1.0
let position = NSPoint(x: 80, y: 140)
let size = NSSize(width: 640, height: 200)
let rect = NSRect(origin: position, size: size)
let newImage = NSImage(size: image.size, flipped: true) { imageRect in
image.draw(in: imageRect)
// let NSStringDrawingContext do its thing
attrStr.boundingRect(with: size, options: drawingOptions, context: context)
print("Actual Scale Factor: \(context.actualScaleFactor)") // is always 1.0
attrStr.setAttributes([.font: font.withSize(font.pointSize * context.actualScaleFactor)], range: NSRangeFromString(text))
attrStr.draw(with: rect, options: drawingOptions, context: context)
return true
}
At this point, newImage
should be an image that looks something like this:
(recreated in an image editing tool)
This is obviously wrong.
While I was looking for a solution, I found this related SO question from 2013 with a conclusion of "it's broken", but surely this has to be possible somehow by now. iOS and macOS label controls both have this feature, SwiftUI even makes it available as a modifier entitled simply minimumScaleFactor
. Is there a replacement API I'm not aware of?
swift
nsattributedstring
appkit
core-text
0 Answers
Your Answer