iosswiftunicodeuifontctfont

Determine if unicode character has a glyph in UIFont


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).


Solution

  • 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!")
    }