How to Get All Characters of the Font with Ctfontcopycharacterset() in Swift

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:

  1. Use CTLineCreateWithAttributedString() to create a text line from the character sequence whose ligature you want to get.
  2. Make sure to set kCTLigatureAttributeName to either 1 or 2 on the attributed string to get ligatures.
  3. If done correctly, with a ligature that exists in the font, you should get a CTLine containing one CTRun containing one CGGlyph 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



Leave a reply



Submit