如何验证支持多个代码点表情符号?



对于上下文,我试图创建从字符串代码点到表情符号的映射,需要知道系统是否支持表情符号:

("1F9AE") -> "🦮"
("1FAE0") -> "🫠" (iOS 15.4+) / nil (if below 15.4, since it would show as "򪪺")
("1F415-200D-1F9BA") -> "🐕‍🦺"
("1F415-1F9BA") -> nil (since it would normally be "🐕🦺", which isn't a single emoji)

我已经得到了这个工作与单一代码点的情况下:

func emoji(for codepoint: String) -> String? {
guard let int = Int(codepoint, radix: 16),
let scalar = UnicodeScalar(int),
scalar.properties.isEmoji
else { return nil }
return String(scalar)
}

但是,我不知道isEmoji有多个代码点的相应检查是什么。

// assume I had to make these scalars via a `String`
let scalars = [UnicodeScalar(0x1F415)!, UnicodeScalar(0x200D)!, UnicodeScalar(0x1F9BA)!]
let scalarView = String.UnicodeScalarView(scalars)
// How can I check that this `UnicodeScalarView` is for single, supported emoji, since I can't check `isEmoji`?
print(String(scalarView))

例如,"1FAE0-1F3FD"应该是nil,因为它不是一个表情符号("🫠🏽")。然而,在未来的版本中,融化的脸可能会与皮肤变化一起工作,在这种情况下,它应该返回单个有效的表情符号。

根据Emoji 14.0的数据文件,一个Emoji要么是一个基本的Emoji,一个keycap序列,一个标志,一个修饰符序列,或者一个ZWJ序列。在每种情况下,序列中至少有一个代码点isEmoji返回true,并且该序列将形成单个字形。

所以,你应该首先从unicode标量中生成一个字符串:
let scalars = [UnicodeScalar(0x1F415)!, UnicodeScalar(0x200D)!, UnicodeScalar(0x1F9BA)!]
let scalarView = String.UnicodeScalarView(scalars)
let string = String(scalarView)

然后,你可以检查它是否是这样的表情符号:

CTLineGetGlyphCount(CTLineCreateWithAttributedString(
NSAttributedString(string: string)
)) == 1 && 
string.unicodeScalars.contains { $0.properties.isEmoji }

或者,因为你只是想检查表情符号是否可以正确显示,你可以使用CTFontGetGlyphsForCharacters看看苹果颜色表情符号是否支持字符。

let font = UIFont(name: "AppleColorEmoji", size: 20)! as CTFont
var text = Array(string.utf16)
var glyphs = Array(repeating: 0 as CGGlyph, count: text.count)
let isEmoji = CTFontGetGlyphsForCharacters(font, &text, &glyphs, text.count) && 
CTLineGetGlyphCount(CTLineCreateWithAttributedString(
NSAttributedString(string: string)
)) == 1

请注意,这两个方法都将返回假阳性(非表情符号,如ASCII字母被报告为表情符号),但不会返回假阴性。