How to get all characters of the font with CTFontCopyCharacterSet() in Swift?
CFCharacterSet
is toll-free bridged with the Cocoa Foundation counterpart NSCharacterSet
, and can be bridged to the corresponding Swift value type CharacterSet
:
let charset = CTFontCopyCharacterSet(ctFont) as CharacterSet
Then the approach from NSArray from NSCharacterSet can be used to enumerate all Unicode scalar values of that character set (including non-BMP points, i.e. Unicode scalar values greater than U+FFFF).
The CTFontGetGlyphsForCharacters()
expects non-BMP characters as surrogate pair, i.e. as an array of UTF-16 code units.
Putting it together, the function would look like this:
func createUnicodeFontMap(ctFont: CTFont) -> [CGGlyph : UnicodeScalar] {
let charset = CTFontCopyCharacterSet(ctFont) as CharacterSet
var glyphToUnicode = [CGGlyph : UnicodeScalar]() // Start with empty map.
// Enumerate all Unicode scalar values from the character set:
for plane: UInt8 in 0...16 where charset.hasMember(inPlane: plane) {
for unicode in UTF32Char(plane) << 16 ..< UTF32Char(plane + 1) << 16 {
if let uniChar = UnicodeScalar(unicode), charset.contains(uniChar) {
// Get glyph for this `uniChar` ...
let utf16 = Array(uniChar.utf16)
var glyphs = [CGGlyph](repeating: 0, count: utf16.count)
if CTFontGetGlyphsForCharacters(ctFont, utf16, &glyphs, utf16.count) {
// ... and add it to the map.
glyphToUnicode[glyphs[0]] = uniChar
}
}
}
}
return glyphToUnicode
}
Get all available characters from a font
For each UIFont, you have to get characterSet of that font. For example, I take first UIFont.
let firsttFont = UIFont.familyNames.first
let first = UIFont(name: firsttFont!, size: 14)
let fontDescriptor = first!.fontDescriptor
let characterSet : NSCharacterSet = fontDescriptor.object(forKey: UIFontDescriptorCharacterSetAttribute) as! NSCharacterSet
Then, use this extension to get all characters of that NSCharacterSet:
extension NSCharacterSet {
var characters:[String] {
var chars = [String]()
for plane:UInt8 in 0...16 {
if self.hasMemberInPlane(plane) {
let p0 = UInt32(plane) << 16
let p1 = (UInt32(plane) + 1) << 16
for c:UTF32Char in p0..<p1 {
if self.longCharacterIsMember(c) {
var c1 = c.littleEndian
let s = NSString(bytes: &c1, length: 4, encoding: String.Encoding.utf32LittleEndian.rawValue)!
chars.append(String(s))
}
}
}
}
return chars
}
}
(Ref: NSArray from NSCharacterset)
So, at last, just call characterSet.characters
to get all characters (in String)
Total number of characters in a UIFont
I believe you can achieve that by the following way using CoreText
:
import CoreText
extension CharacterSet {
func charCount() -> Int {
var count = 0
for plane:UInt8 in 0...16 where self.hasMember(inPlane: plane) {
for unicode in UInt32(plane) << 16 ..< UInt32(plane+1) << 16 {
if let uniChar = UnicodeScalar(unicode) {
if self.contains(uniChar) {
count += 1
}
}
}
}
return count
}
}
func getCountForFont(font: UIFont) -> Int {
let coreFont: CTFont = font
let characterSet: CharacterSet = CTFontCopyCharacterSet(coreFont) as CharacterSet
let count = characterSet.charCount()
return count
}
CoreText: Get ligature glyph
After some useful advice from the Core Text mailing list, here is the solution:
You can't get ligatures directly from CTFont, as no API exists to do that.
What you do instead is the following:
- Use
CTLineCreateWithAttributedString()
to create a text line from the character sequence whose ligature you want to get. - Make sure to set
kCTLigatureAttributeName
to either 1 or 2 on the attributed string to get ligatures. - If done correctly, with a ligature that exists in the font, you should get a
CTLine
containing oneCTRun
containing oneCGGlyph
which will be the ligated glyph you are looking for!
If you don't want to go down this route, the only other way appears to be parsing the Open - / True Type font tables yourself which I don't recommend.
Hope that helps!
Get unicode character by glyph index in a CTFontRef or CGFontRef object
I think you may end up having to parse the font’s mapping tables yourself. You can obtain access to the tables using CGFontCopyTableForTag()
; the table you're after is the 'cmap
' table, the format of which is documented here:
http://www.microsoft.com/typography/otspec/cmap.htm
and also here:
http://developer.apple.com/fonts/TTRefMan/RM06/Chap6cmap.html
Unfortunately, as you’ll discover by reading through these, the business of mapping characters to glyphs is decidedly non-trivial, and in addition any given font may have more than one mapping table (i.e. the set of characters that use a given glyph may depend on which mapping table format you—or the renderer—chooses).
Furthermore, advanced font technology like OpenType or AAT may result in the existence of glyphs for which there is no direct mapping from characters, but that are nevertheless present in the output as a result of substitutions made by the smart font technology. Inverting the OpenType or AAT substitution mechanisms would be tricky, and might also not lead to a single Unicode code point (or indeed even a single grapheme cluster).
iOS Swift 5, Auto Release CoreText & CoreGraphics memory
Extracting this code from the for loop into another function does the trick of releasing the associated memory with the loaded CGFont and the copied CharacterSet.
OSX: CGGlyph to UniChar
I don't know a direct method to get the Unicode for a given glyph, but you could
build a mapping in the following way:
- Get all characters of the font with
CTFontCopyCharacterSet()
. - Map all these Unicode characters to their glyph with
CTFontGetGlyphsForCharacters()
. - For each Unicode character and its glyph, store the mapping
glyph -> Unicode
in a dictionary.
Determine if unicode character has a glyph in UIFont
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!")
}
Related Topics
Update Firebase Multi-Location with Error: Path Is an Ancestor of Path. Swift
How to Decode Partially Double Serialized JSON String Using 'Codable' Protocol
Swift Alamofire Return Value Is Empty
Swift: Casting a Floatingpoint Conforming Value to Double
How to Compare Two Strings to Check If They Have Same Characters Swift 4
Which Measuring Unit Is Used in Scnvector3 Position for X, Y and Z in Arkit
Comparing Objects in an Array Extension Causing Error in Swift
Swift 3:Delegate Within Tapgesturerecognizer in Generics Doesn't Get Called
Updated Approach to Reauthenticate a User
What Does the Underscore in a Function Declaration Do
Swift Access Array with Index Gives Following Error. Any Idea Why
Swift Decodable, Endpoint Returns Completely Different Types
Advancedby' in Swift Is Unavailable
Parse.Com Pfquery Order by Time (Swift)