Getting all available emojis on iOS version [duplicate] - ios

Some unicode characters cannot be displayed on iOS but are displayed correctly on macOS. Similarly, some unicode characters that iOS can display cannot be displayed on watchOS. This is due to different built-in fonts installed on these platforms.
When a character cannot be displayed it appears as a ? inside a box, like so:
I've also seen some characters display as an alien instead (not sure why the difference):
Is there a way to know when a specific unicode character will not be displayed properly given a string of the unicode character such as "ᄥ"?
I am in need of a solution that works for both iOS and watchOS.

You can use CTFontGetGlyphsForCharacters() to determine if a font has a glyph for a particular code point (note that supplementary characters need to be checked as surrogate pairs):
CTFontRef font = CTFontCreateWithName(CFSTR("Helvetica"), 12, NULL);
const UniChar code_point[] = { 0xD83C, 0xDCA1 }; // U+1F0A1
CGGlyph glyph[] = { 0, 0 };
bool has_glyph = CTFontGetGlyphsForCharacters(font, code_point, glyph, 2);
Or, in Swift:
let font = CTFontCreateWithName("Helvetica", 12, nil)
var code_point: [UniChar] = [0xD83C, 0xDCA1]
var glyphs: [CGGlyph] = [0, 0]
let has_glyph = CTFontGetGlyphsForCharacters(font, &code_point, &glyph, 2)
If you want to check the complete set of fallback fonts that the system will try to load a glyph from, you will need to check all of the fonts returned by CTFontCopyDefaultCascadeListForLanguages(). Check the answer to this question for information on how the fallback font list is created.

Compare against the known, undefined character U+1FFF:
/// - Parameter font: a UIFont
/// - Returns: true if glyph exists
func glyphAvailable(forFont font:UIFont) -> Bool {
if let refUnicodePng = Character("\u{1fff}").png(forFont: font),
let myPng = self.png(forFont: font) {
return refUnicodePng != myPng
}
return false
}
using a png bitmap:
/// - Parameter font: a UIFont
/// - Returns: an optional png representation
func png(forFont font: UIFont) -> Data? {
let attributes = [NSAttributedStringKey.font: font]
let charStr = "\(self)" as NSString
let size = charStr.size(withAttributes: attributes)
UIGraphicsBeginImageContext(size)
charStr.draw(at: CGPoint(x: 0,y :0), withAttributes: attributes)
var png:Data? = nil
if let charImage = UIGraphicsGetImageFromCurrentImageContext() {
png = UIImagePNGRepresentation(charImage)
}
UIGraphicsEndImageContext()
return png
}
Answered here.

Related

Get all Glyphs of a UIFont that have a descender

Is there a way to get all the glyphs of a UIFont that contain a true descender? It seems that using CTLineGetTypographicBounds is not accurate and returns the exact same descent value for every line. I thought it would provide the information that I needed but it did not. So now I am looking to see if I can build a character set from the glyphs that contain true descenders unless there is another way. The ultimate goal would be able to see if a line of text is below the baseline.
let line = CTLineCreateWithAttributedString(NSAttributedString(string: s, attributes: attr))
//let's get the real descent test
var a : CGFloat = 0
var d : CGFloat = 0
var l : CGFloat = 0
let bounds = CTLineGetTypographicBounds(line, &a, &d, &l)
print("the descent is \(d)")
print("the ascent is \(a)")
print("the leading is \(l)")
Since it seems that your actual goal is to determine whether a string contains a character with a descender, you can use Core Text to look at the bounding rect of each glyph. If the bounding rect's origin in negative, this means the glyph starts below the baseline. This will be true for characters such as y but also ,.
func checkDescender(string: String) {
let uiFont = UIFont.systemFont(ofSize: 14) // Pick your font
let font = CTFontCreateWithName(uiFont.fontName as CFString, uiFont.pointSize, nil)
for ch in string.unicodeScalars {
let utf16codepoints = Array(ch.utf16)
var glyphs: [CGGlyph] = [0, 0]
let hasGlyph = CTFontGetGlyphsForCharacters(font, utf16codepoints, &glyphs, utf16codepoints.count)
if hasGlyph {
let rect = CTFontGetBoundingRectsForGlyphs(font, .default, glyphs, nil, 1)
// print("\(ch) has bounding box of \(rect)")
if rect.origin.y < 0 {
print("\(ch) goes below the baseline by \(-rect.origin.y)")
}
}
}
}
checkDescender(string: "Ymy,")
You might want to add additional checks to only look at letters depending on your needs.

How to get emoji path in iOS

My purpose : Draw outline of every glyph
example1:
input: text= "666棒"
display:
Attach:In the figure above, 1 is displayView,2 is inputView.
example2:
input: text= "666😁棒"
display:
Attach: In the figure above, 1 is displayView,2 is inputView,3 is nothing rendered.
Main ideas is :
Use CoreText obtained every CGglyph.
Get every glyph's CGPath
Use CAShapeLayer display the glyph on screen.
Main method:
let letters = CGMutablePath()
let font = CTFontCreateWithName(fontName as CFString?, fontSize, nil)
let attrString = NSAttributedString(string: text, attributes: [kCTFontAttributeName as String : font])
let line = CTLineCreateWithAttributedString(attrString)
let runArray = CTLineGetGlyphRuns(line)
for runIndex in 0..<CFArrayGetCount(runArray) {
let run : CTRun = unsafeBitCast(CFArrayGetValueAtIndex(runArray, runIndex), to: CTRun.self)
let dictRef : CFDictionary = unsafeBitCast(CTRunGetAttributes(run), to: CFDictionary.self)
let dict : NSDictionary = dictRef as NSDictionary
let runFont = dict[kCTFontAttributeName as String] as! CTFont
for runGlyphIndex in 0..<CTRunGetGlyphCount(run) {
let thisGlyphRange = CFRangeMake(runGlyphIndex, 1)
var glyph = CGGlyph()
var position = CGPoint.zero
CTRunGetGlyphs(run, thisGlyphRange, &glyph)
CTRunGetPositions(run, thisGlyphRange, &position)
let letter = CTFontCreatePathForGlyph(runFont, glyph, nil)
let t = CGAffineTransform(translationX: position.x, y: position.y)
if let letter = letter {
letters.addPath(letter, transform: t)
}
}
}
let path = UIBezierPath()
path.move(to: CGPoint.zero)
path.append(UIBezierPath(cgPath: letters))
let pathLayer = CAShapeLayer()
pathLayer.path = path.cgPath
self.layer.addSubLayer(pathLayer)
...
Question:
How to get emoji path ,in this case I can draw the emoji outline instead of draw the whole emoji? Another benefit is I can draw emoji path animated if I need.
Any help is appreciate!
************************ update 2.15.2017 ***********************
Thanks #KrishnaCA 's suggest.
I used bool supports = CTFontGetGlyphWithName(myFont, "😀") find that no font is support emoji.
Fortunately is Google's Noto provide good support for emoji fonts
You can find it there :google's Noto
I used font Noto Emoji
Display:
Only Noto Emoji and Noto Color Emoji support Emoji (I guess)
Hope to help people who come here!
I believe you need to check whether a glyph for an unicode corresponding to the CTFont exist or not. If it doesn't exist, fall back to any default CTFont that has a glpyh for an unicode
You can check that using the following code.
bool supports = CTFontGetGlyphWithName(myFont, "😀")
here, myFont is a CTFontRef object.
Please let me know if this is what you're not looking for
I believe you'll need CATextLayers to help you out.
I know it's a bit late, but sadly you can not - emojis are actually bitmaps drawn into the same context as shapes representing regular characters. You best bet is probably to draw emoji characters separately at needed scale into the context. This won't give you access to the actual vector data.
If you really need it in a vector form:
I'd go with finding Apple Emoji font redrawn in vector (I remember seeing it on the internet, not sure if it contains all the latest emojis though)
Mapping names of the individual vector images you found to the characters and then drawing the vector images

Is there a way to know if an Emoji is supported in iOS?

I'm building an iOS App, and Emojis play a big part in it.
In iOS 10.2, new emojis were released.
I'm pretty sure that if someone has iOS 8, for example, they wouldn't actually be able to see these emojis. Is there a way to detect this? I'm trying to dynamically build a list of all the Emojis that are supported on the user's iOS version, but I'm having a bit of trouble.
Clarification: an Emoji is merely a character in the Unicode Character space, so the present solution works for all characters, not just Emoji.
Synopsis
To know if a Unicode character (including an Emoji) is available on a given device or OS, run the unicodeAvailable() method below.
It works by comparing a given character image against a known undefined Unicode character U+1FFF.
unicodeAvailable(), a Character extension
private static let refUnicodeSize: CGFloat = 8
private static let refUnicodePng =
Character("\u{1fff}").png(ofSize: Character.refUnicodeSize)
func unicodeAvailable() -> Bool {
if let refUnicodePng = Character.refUnicodePng,
let myPng = self.png(ofSize: Character.refUnicodeSize) {
return refUnicodePng != myPng
}
return false
}
Discussion
All characters will be rendered as a png at the same size (8) as defined once in
static let refUnicodeSize: CGFloat = 8
The undefined character U+1FFF image is calculated once in
static let refUnicodePng = Character("\u{1fff}").png(ofSize: Character.refUnicodeSize)
A helper method optionally creates a png from a Character
func png(ofSize fontSize: CGFloat) -> Data?
1. Example: Test against 3 emoji
let codes:[Character] = ["\u{2764}","\u{1f600}","\u{1F544}"] // ❤️, 😀, undefined
for unicode in codes {
print("\(unicode) : \(unicode.unicodeAvailable())")
}
2. Example: Test a range of Unicode characters
func unicodeRange(from: Int, to: Int) {
for unicodeNumeric in from...to {
if let scalar = UnicodeScalar(unicodeNumeric) {
let unicode = Character(scalar)
let avail = unicode.unicodeAvailable()
let hex = String(format: "0x%x", unicodeNumeric)
print("\(unicode) \(hex) is \(avail ? "" : "not ")available")
}
}
}
Helper function: Character to png
func png(ofSize fontSize: CGFloat) -> Data? {
let attributes = [NSAttributedStringKey.font:
UIFont.systemFont(ofSize: fontSize)]
let charStr = "\(self)" as NSString
let size = charStr.size(withAttributes: attributes)
UIGraphicsBeginImageContext(size)
charStr.draw(at: CGPoint(x: 0,y :0), withAttributes: attributes)
var png:Data? = nil
if let charImage = UIGraphicsGetImageFromCurrentImageContext() {
png = UIImagePNGRepresentation(charImage)
}
UIGraphicsEndImageContext()
return png
}
► Find this solution on GitHub and a detailed article on Swift Recipes.
Just for future reference, after discovering my app had 13.2 emojis not supported in 12.x versions, I used the answer from here: How can I determine if a specific emoji character can be rendered by an iOS device? which worked really well for me.

How can I determine if a specific emoji character can be rendered by an iOS device?

With the release of iOS 9.1, we got a lot of cool new emoji like taco (🌮)! I'm working on an application where I'd like to show the new emoji characters on devices where they are supported but keep them hidden on devices where they are not. Is there a way to determine if a given emoji character (contained in an NSString) can be rendered on the device the app is running on?
After quite a bit of digging on SO and experimenting I've come up with a solution which I will post as an answer below, but please let me know if there's a better way.
In response to the Swift question above, here's a Swift version based largely on an answer in this question:
Get surrogate pairs from an emoji
func isEmojiSupported(emoji: String) -> Bool {
let uniChars = Array(emoji.utf16)
let font = CTFontCreateWithName("AppleColorEmoji", 0.0, nil)
var glyphs: [CGGlyph] = [0, 0]
return CTFontGetGlyphsForCharacters(font, uniChars, &glyphs, uniChars.count)
}
// Determine if the emoji character provided in the string can be rendered on this OS version
+ (BOOL)isEmojiSupported:(NSString *)emoji
{
NSData *data = [string dataUsingEncoding:NSUTF32LittleEndianStringEncoding];
UTF32Char emojiValue;
[data getBytes:&emojiValue length:sizeof(emojiValue)];
// Convert UTF32Char to UniChar surrogate pair.
// Found here: http://stackoverflow.com/questions/13005091/how-to-tell-if-a-particular-font-has-a-specific-glyph-64k#
UniChar characters[2] = { };
CFIndex length = (CFStringGetSurrogatePairForLongCharacter(emojiValue, characters) ? 2 : 1);
CGGlyph glyphs[2] = { };
CTFontRef ctFont = CTFontCreateWithName((CFStringRef)#"AppleColorEmoji", 0.0, NULL);
// If we don't get back any glyphs for the characters array, it's not supported
BOOL ret = CTFontGetGlyphsForCharacters(ctFont, characters, glyphs, length);
CFRelease(ctFont);
return ret;
}

Detect when a unicode character cannot be displayed correctly

Some unicode characters cannot be displayed on iOS but are displayed correctly on macOS. Similarly, some unicode characters that iOS can display cannot be displayed on watchOS. This is due to different built-in fonts installed on these platforms.
When a character cannot be displayed it appears as a ? inside a box, like so:
I've also seen some characters display as an alien instead (not sure why the difference):
Is there a way to know when a specific unicode character will not be displayed properly given a string of the unicode character such as "ᄥ"?
I am in need of a solution that works for both iOS and watchOS.
You can use CTFontGetGlyphsForCharacters() to determine if a font has a glyph for a particular code point (note that supplementary characters need to be checked as surrogate pairs):
CTFontRef font = CTFontCreateWithName(CFSTR("Helvetica"), 12, NULL);
const UniChar code_point[] = { 0xD83C, 0xDCA1 }; // U+1F0A1
CGGlyph glyph[] = { 0, 0 };
bool has_glyph = CTFontGetGlyphsForCharacters(font, code_point, glyph, 2);
Or, in Swift:
let font = CTFontCreateWithName("Helvetica", 12, nil)
var code_point: [UniChar] = [0xD83C, 0xDCA1]
var glyphs: [CGGlyph] = [0, 0]
let has_glyph = CTFontGetGlyphsForCharacters(font, &code_point, &glyph, 2)
If you want to check the complete set of fallback fonts that the system will try to load a glyph from, you will need to check all of the fonts returned by CTFontCopyDefaultCascadeListForLanguages(). Check the answer to this question for information on how the fallback font list is created.
Compare against the known, undefined character U+1FFF:
/// - Parameter font: a UIFont
/// - Returns: true if glyph exists
func glyphAvailable(forFont font:UIFont) -> Bool {
if let refUnicodePng = Character("\u{1fff}").png(forFont: font),
let myPng = self.png(forFont: font) {
return refUnicodePng != myPng
}
return false
}
using a png bitmap:
/// - Parameter font: a UIFont
/// - Returns: an optional png representation
func png(forFont font: UIFont) -> Data? {
let attributes = [NSAttributedStringKey.font: font]
let charStr = "\(self)" as NSString
let size = charStr.size(withAttributes: attributes)
UIGraphicsBeginImageContext(size)
charStr.draw(at: CGPoint(x: 0,y :0), withAttributes: attributes)
var png:Data? = nil
if let charImage = UIGraphicsGetImageFromCurrentImageContext() {
png = UIImagePNGRepresentation(charImage)
}
UIGraphicsEndImageContext()
return png
}
Answered here.

Resources