gemoji icon indicating copy to clipboard operation
gemoji copied to clipboard

How should users apply skin tone modifiers?

Open wearhere opened this issue 4 years ago • 4 comments

I've searched through issues and examined the DB and from what I can tell, the library currently reports what emojis can accept a skin tone modifier but doesn't actually provide the modified variants, but rather, only/always the base emoji. That's fine, it seems like it should be easy to produce the variants by appending the modifiers to the base.

Unfortunately, this fails for all of the emojis that have U+FE0F VARIATION SELECTOR-16 appended. As an example, when copying "✋" and "🖐️" out of emojis.json, I can do "✋" + "🏻" and get "✋🏻", but "🖐️" + "🏻" results in "🖐️🏻".

It looks like this library intentionally includes the selector for certain emojis. Moreover, trying to drop the selector, then append the modifier to the remaining unicode scalars, results in correctly modified emoji for only some of the results ("🖐️" yes; "👨‍🍳" no).

Do you folks have recommendations on how to apply the modifiers?

wearhere avatar May 28 '20 08:05 wearhere

Ok I deleted my previous comment talking about trial-and-error since I found the spec. In particular, it says:

an emoji modifier must immediately follow [the] base emoji character

and

Emoji presentation selectors are neither needed nor recommended for emoji characters when they are followed by emoji modifiers, and should not be used in newly generated emoji modifier sequences; the emoji modifier automatically implies the emoji presentation style.

I think that suggests processing emojis like this. You're welcome to adopt that implementation for this library if you like, tried to heavily comment and test it both from the perspective of "does this format correctly" and "does this render correctly (on Apple platforms, at least)".

One note about non-Swift implementations is that you'll probably need to look up the scalar (codepoint) properties (isVariationSelector, isEmojiModifier, isEmojiModifierBase) from an external lookup table. I'm pretty sure that unicode.org provides such and I believe that this library already parses some of them.

wearhere avatar May 29 '20 00:05 wearhere

The Gist linked from my previous comment doesn't support applying different modifiers to each person in a multi-person grouping. I'm currently adding support for that and will probably publish the result as a Swift package rather than continue to add to the Gist.

In the meantime, I'd like to call out a limitation of the algorithm in the Gist: it won't work for 👭, 👫, and 👬. That's because those emojis consist of a single scalar, whereas applying different modifiers to each person in the grouping would require representing the groupings as a ZWJ sequence. This means that we have to convert those emojis into a sequence before applying the algorithm:

extension String {
    var modifiableBySkinTone: String {
        switch self {
        case "👭": return "\u{0001F469}\u{200D}\u{0001F91D}\u{200D}\u{0001F469}"
        case "👫": return "\u{0001F469}\u{200D}\u{0001F91D}\u{200D}\u{0001F468}"
        case "👬": return "\u{0001F468}\u{200D}\u{0001F91D}\u{200D}\u{0001F468}"
        default: return self
        }
    }
}

I hoped to submit some sort of PR to add these "sequence forms" to this project's database, the idea being that a capable platform¹ could only ever store and display those versions of 👭, 👫, and 👬 and so avoid having to convert the single-scalar versions in the process of applying skin tones.

Unfortunately, these sequences only render as single characters when they contain modifiers, at least on macOS 10.15.4. Apple probably didn't bother adding code to make the base sequences render given that they can display the single-scalar versions instead.


¹Supporting Emoji 12.0, which I think is when the sequence forms were added, per the spec—whereas the single-scalar emoji date back to Emoji 6.0, says the database.

wearhere avatar May 29 '20 20:05 wearhere

Two more gotchas I've found:

Firstly, the basic algorithm may return results that won't actually render in cases where platforms haven't generally added support for rendering all the different variants. I'm specifically talking about many of the multi-person groupings. In these cases, the database correctly omits skin_tones and so the fix is to simply skip trying to modify such emoji.

More problematic is that skin_tones may be true in cases where only some platforms will render the different variants. This is because "in some circumstances, display of an emoji modifier… should be suppressed", per the spec. As an example, Apple depicts 🏂 without any visible skin whereas Android shows skin. This means that my Swift implementation (for use in iOS) should return no variants for 🏂 whereas implementations for other platforms would need to behave differently.

wearhere avatar May 30 '20 05:05 wearhere

Thanks for sharing your finding so far!

I am unable to provide advice on how to implement support for skin tone modifiers on top of gemoji since I've never done it. The library does report which emoji supports skin tone modifiers, but intentionally does not yet provide any facilities to generate or parse emoji with skin tones because we haven't figured out proper support for this yet, so the implementation is left to the users.

mislav avatar Jun 22 '20 12:06 mislav

With https://github.com/github/gemoji/pull/165 merged, you can now call the raw_skin_tone_variants method on an emoji character to get a set of 5 unicode sequences that have each of the skin tone modifiers inserted at the right place.

mislav avatar Nov 15 '22 20:11 mislav