Skip to content

Instantly share code, notes, and snippets.

@yossan
Last active September 28, 2021 06:43
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save yossan/c34e6850c542bda747ab4d6fc68a7eaf to your computer and use it in GitHub Desktop.
Save yossan/c34e6850c542bda747ab4d6fc68a7eaf to your computer and use it in GitHub Desktop.
Making CGlyph of an extended grapheme cluster.
// CTFontGetGlyphsForCharacters(_:_:_:_:)
// [UniChar] β†’ [CGGlyph]
import Foundation
func makeUnichars(from str: NSString) -> [UInt16] {
let range = NSRange(location:0, length: str.length)
let encoding = String.Encoding.utf16.rawValue
let maxLength = str.maximumLengthOfBytes(using: encoding)
let rawPointer = UnsafeMutableRawPointer.allocate(byteCount: maxLength, alignment: 0)
var usedLength: Int = 0
str.getBytes(rawPointer,
maxLength: maxLength,
usedLength: &usedLength,
encoding: encoding,
range: range,
remaining: nil)
let rawBufferPointer = UnsafeRawBufferPointer(start: rawPointer, count: usedLength)
let bufferPointer = rawBufferPointer.bindMemory(to: UInt16.self)
return Array(bufferPointer)
}
// EXAMPLE
let str: NSString = "\u{0001F469}\u{200D}\u{0001F469}\u{200D}\u{0001F467}\u{200D}\u{0001F466}"
print("Inputed: \(str)")
let unichars = makeUnichars(from: str)
var font = CTFontCreateWithName("AppleColorEmoji" as CFString, 17.0, nil)
font = CTFontCreateForString(font, str, CFRange(location: 0, length: str.length))
let fontName = CTFontCopyDisplayName(font)
print("font = \(fontName)")
var glyphs: [CGGlyph] = Array(repeating: 0, count: str.length)
if (CTFontGetGlyphsForCharacters(font, unichars, &glyphs, str.length)) {
print("succeed in generating glyph")
print("glyphs = \(glyphs)")
} else {
print("failed in generating glyph")
}
/* output
Inputed: πŸ‘©β€πŸ‘©β€πŸ‘§β€πŸ‘¦
font = Apple Color Emoji
succeed in generating glyph
glyphs = [1164, 0, 43, 1164, 0, 43, 1056, 0, 43, 1050, 0]
*/
// CTFrameSetter β†’ CTFrame β†’ [CTLine] β†’ [CTRun] β†’ [CGGlyph]
import Foundation
func makeRuns(from str: String) -> [CTRun] {
let attStr = NSAttributedString(string: str)
let range = CFRange(location: 0, length: attStr.length)
let size = CGSize(width: 100, height: 100)
let path = CGPath(rect: CGRect(origin: .zero, size: size), transform: nil)
let framesetter = CTFramesetterCreateWithAttributedString(attStr)
let frame = CTFramesetterCreateFrame(framesetter, range, path, nil)
let lines = CTFrameGetLines(frame) as! [CTLine]
return lines.flatMap {
return CTLineGetGlyphRuns($0) as! [CTRun]
}
}
func makeGlyphs(from run: CTRun) -> [CGGlyph] {
let glyphsCount = CTRunGetGlyphCount(run)
let range = CFRange(location: 0, length: glyphsCount)
var glyphs: [CGGlyph] = Array(repeating: 0, count: glyphsCount)
CTRunGetGlyphs(run, range, &glyphs)
return glyphs
}
// EXAMPLE
// πŸ‘©β€πŸ‘©β€πŸ‘§β€πŸ‘¦
let str = "\u{0001F469}\u{200D}\u{0001F469}\u{200D}\u{0001F467}\u{200D}\u{0001F466}"
print("Inputed: \(str)")
let run = makeRuns(from: str)[0]
let attributes = CTRunGetAttributes(run) as! [String :Any]
let font = attributes["NSFont"] as! CTFont
let fontName = CTFontCopyDisplayName(font)
print("font = \(fontName)")
let glyphs = makeGlyphs(from: run)
print(glyphs) // [1289]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment