Skip to content

Instantly share code, notes, and snippets.

@ShikiSuen
Created March 9, 2024 14:46
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ShikiSuen/bca950297afb15fdeba6d677647d83c2 to your computer and use it in GitHub Desktop.
Save ShikiSuen/bca950297afb15fdeba6d677647d83c2 to your computer and use it in GitHub Desktop.
NSString.getRenderedDimension().
// (c) 2022 and onwards The vChewing Project (MIT-NTL License).
// ====================
// This code is released under the MIT license (SPDX-License-Identifier: MIT)
// ... with NTL restriction stating that:
// No trademark license is granted to use the trade names, trademarks, service
// marks, or product names of Contributor, except as required to fulfill notice
// requirements defined in MIT License.
import CoreText
import Foundation
public extension String {
func getRenderedDimension(
using givenFont: CTFont? = nil,
size: CGFloat? = 13, /* NSFont.systemFontSize defaults to 13. */
orientation: CTFontOrientation = .horizontal,
locale localeIdentifier: String? = nil
) -> CGSize {
let size = size ?? 13
let baseFont = givenFont ?? CTFont(.system, size: size)
func createMatchedFont(char: Character) -> CTFont {
var cfLocaleIdentifier: CFString?
if let localeIdentifier = localeIdentifier {
cfLocaleIdentifier = localeIdentifier as CFString
}
return CTFontCreateForStringWithLanguage(
baseFont,
char.description as CFString,
CFRange(location: 0, length: char.utf16.count),
cfLocaleIdentifier
)
}
let emojiFallbackSize = size * 1.25
var finalWidth: CGFloat = 0
var finalHeight: CGFloat = 0
switch orientation {
case .horizontal: finalHeight = emojiFallbackSize
case .vertical: finalWidth = emojiFallbackSize
default: break
}
forEach { char in
let uniChars = Array(char.utf16)
let matchedFont = createMatchedFont(char: char)
// The subCount needs to be 1, considering that this is the only literal scalar amount to show.
// Otherwise, incorrect results will be generated while parsing NFD compositions.
let subCount = 1
var fetchedGlyphs = [CGGlyph](repeating: 0, count: subCount)
let result = CTFontGetGlyphsForCharacters(matchedFont, uniChars, &fetchedGlyphs, subCount)
guard result, !fetchedGlyphs.isEmpty else {
switch orientation {
case .horizontal: finalWidth += emojiFallbackSize
case .vertical: finalHeight += emojiFallbackSize
default: break
}
return
}
let advance = CTFontGetAdvancesForGlyphs(matchedFont, orientation, fetchedGlyphs, nil, subCount)
switch orientation {
case .horizontal: finalWidth += advance
case .vertical: finalHeight += advance
default: break
}
}
return .init(width: ceil(finalWidth), height: ceil(finalHeight))
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment