I want to find out if a certain unicode character has a glyph representation even if by a cascading font. For example, let's say I am using UIFont.systemFont(withSize:18)
and a string \u{1CDA}
and would like to find out if this font will display the graphical representation of this character, and not a default question mark representation (ie there's no graphical representation, even by the supporting cascade fonts).
Asked
Active
Viewed 1,847 times
4

andrewz
- 4,729
- 5
- 49
- 67
-
1Possible duplicate of [Detect when a unicode character cannot be displayed correctly](http://stackoverflow.com/questions/31363211/detect-when-a-unicode-character-cannot-be-displayed-correctly) – Martin R Mar 01 '17 at 19:38
-
1@MartinR why not use your hammer? – JAL Mar 01 '17 at 19:39
-
@JAL: Because I have no experience with that problem myself. I'll dupe hammer it as soon as some confirmation from OP or someone else comes. – Martin R Mar 01 '17 at 19:42
-
@MartinR I saw that question and am using it's solution, but it returns only a glyph of that specific font. – andrewz Mar 01 '17 at 21:24
3 Answers
3
This works for me. Swift 3, XCode 8.6 version:
import UIKit
import CoreText
extension Font {
public func hasGlyph(utf32 character:UInt32) -> Bool {
var code_point: [UniChar] = [
UniChar.init(truncatingBitPattern: character),
UniChar.init(truncatingBitPattern: character >> 16)
]
var glyphs: [CGGlyph] = [0,0]
let result = CTFontGetGlyphsForCharacters(self as CTFont, &code_point, &glyphs, glyphs.count)
return result
}
}
public class Glypher {
let font:UIFont
var support:[CTFont] = []
public init(for font:UIFont, languages:[String] = ["en"]) {
self.font = font
let languages = languages as CFArray
let result = CTFontCopyDefaultCascadeListForLanguages(font as CTFont, languages)
let array = result as! Array<CTFontDescriptor>
for descriptor in array {
support.append(CTFontCreateWithFontDescriptor(descriptor,18,nil))
}
}
public func isGlyph(_ point:UInt32) -> Bool {
return font.hasGlyph(utf32:point) || isGlyphSupported(point)
}
public func isGlyphSupported(_ point:UInt32) -> Bool {
for font in support {
var code_point: [UniChar] = [
UniChar.init(truncatingBitPattern: point),
UniChar.init(truncatingBitPattern: point >> 16)
]
var glyphs: [CGGlyph] = [0, 0]
let result = CTFontGetGlyphsForCharacters(font as CTFont, &code_point, &glyphs, glyphs.count)
if result {
return true
}
}
return false
}
}
let glypher = Glypher(for:UIFont.systemFont(ofSize:18))
if glypher.isGlyph(0x1CDA) {
print("bingo!")
}

andrewz
- 4,729
- 5
- 49
- 67
-
When testing this code using Ingra-Book and the iPhone 7 simulator CTFontGetGlyphsForCharacters returns false for a number of test cases across the UTF-8 BMP space. The font has 1628 glyphs (CTFontGetGlyphCount). The backup support fonts are used for all matches. Could the font not be truly installed? Swift 3.1, Xcode 8.3.3., iPhone 7 simulator. Any suggestions would be helpful. – Jason Harrison Nov 17 '17 at 18:07
1
This may work too, it doesn't check the glyph, but it checks the character set
import CoreText
func isSupported(unicode: UnicodeScalar, font: UIFont) -> Bool {
let coreFont: CTFont = font
let characterSet: CharacterSet = CTFontCopyCharacterSet(coreFont) as CharacterSet
return characterSet.contains(unicode)
}
Example test:
let testString = "R"
let font = UIFont.boldSystemFont(ofSize: 10.0)
print("\(isSupported(unicode: testString.unicodeScalars.first!, font: font))")

Jason Harrison
- 922
- 9
- 27
0
This is heavily based on the answer by @andrewz, so most credit to them! I found some issues with certain emojis giving false negatives (including ✋) so I've got some modifications, including passing the emoji in as a String
:
(note: I've got Font
instead of UIFont
as I'm using this on both iOS and macOS so have a typealias to switch between the two font classes, you can just prefix it with NS
or UI
for your platform of choice!)
extension Font {
func canRender(emoji: String) -> Bool {
var code_points = Array(emoji.utf16)
var glyphs: [CGGlyph] = Array(repeating: 0, count: code_points.count)
return CTFontGetGlyphsForCharacters(self as CTFont, &code_points, &glyphs, glyphs.count)
}
}
class Glypher {
private let font: Font
private var support: [CTFont] = []
init(for font: Font, languages: [String] = ["en"]) {
self.font = font
let languages = languages as CFArray
let result = CTFontCopyDefaultCascadeListForLanguages(font as CTFont, languages)
let array = result as! Array<CTFontDescriptor>
for descriptor in array {
support.append(CTFontCreateWithFontDescriptor(descriptor, 18, nil))
}
}
func isRenderable(emoji: String) -> Bool {
return font.canRender(emoji: emoji) || renderSupported(emoji: emoji)
}
func renderSupported(emoji: String) -> Bool {
var code_points = Array(emoji.utf16)
var glyphs: [CGGlyph] = Array(repeating: 0, count: code_points.count)
for font in support where CTFontGetGlyphsForCharacters(font as CTFont, &code_points, &glyphs, glyphs.count) {
return true
}
return false
}
}

CMash
- 1,987
- 22
- 35