[dali_2.1.32] Merge branch 'devel/master'
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / text / emoji-helper.cpp
1
2 /*
3  * Copyright (c) 2021 Samsung Electronics Co., Ltd.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  */
18
19 // FILE HEADER
20 #include <dali-toolkit/internal/text/emoji-helper.h>
21
22 namespace Dali
23 {
24 namespace Toolkit
25 {
26 namespace Text
27 {
28 bool IsTextPresentationSequence(const TextAbstraction::Script&    currentRunScript,
29                                 const TextAbstraction::Character& character)
30 {
31   return (IsSymbolOrEmojiOrTextScript(currentRunScript) && TextAbstraction::IsTextPresentationSelector(character));
32 }
33
34 bool IsEmojiPresentationSequence(const TextAbstraction::Script&    currentRunScript,
35                                  const TextAbstraction::Character& character)
36 {
37   return ((IsSymbolOrEmojiScript(currentRunScript) || IsEmojiColorScript(currentRunScript)) &&
38           TextAbstraction::IsEmojiPresentationSelector(character));
39 }
40
41 bool IsEmojiSequence(const TextAbstraction::Script&    currentRunScript,
42                      const TextAbstraction::Character& character,
43                      const TextAbstraction::Script&    characterScript)
44 {
45   return (!(TextAbstraction::IsNegativeSquaredLatinCapitalLetter(character)) &&
46           (IsOneOfEmojiScripts(currentRunScript) &&
47            (IsOneOfEmojiScripts(characterScript) ||
48             TextAbstraction::IsZeroWidthJoiner(character) ||
49             TextAbstraction::IsZeroWidthNonJoiner(character) ||
50             TextAbstraction::IsEmojiItem(character) ||
51             TextAbstraction::IsMiscellaneousSymbolsAndArrowsEmoji(character) ||
52             TextAbstraction::IsDingbatsEmoji(character))));
53 }
54
55 bool IsNewSequence(const Character* const         textBuffer,
56                    const TextAbstraction::Script& currentRunScript,
57                    const Length&                  currentCharacterIndex,
58                    const Length&                  lastCharacterIndex,
59                    TextAbstraction::Script&       currentCharacterScript)
60 {
61   // Until now we have two cases : VariationSelector & Keycap
62   // In-case there are more cases then should be added in this function
63
64   return IsNewKeycapSequence(textBuffer, currentCharacterIndex, lastCharacterIndex, currentCharacterScript) ||
65          IsNewVariationSelectorSequence(textBuffer, currentRunScript, currentCharacterIndex, lastCharacterIndex, currentCharacterScript);
66 }
67
68 //
69
70 bool IsNewKeycapSequence(const Character* const   textBuffer,
71                          const Length&            currentCharacterIndex,
72                          const Length&            lastCharacterIndex,
73                          TextAbstraction::Script& currentCharacterScript)
74 {
75   // Ref: https://www.unicode.org/Public/emoji/14.0/emoji-sequences.txt Search on "Emoji_Keycap_Sequence"
76   // Ref: https://www.unicode.org/Public/emoji/14.0/emoji-test.txt Search on "subgroup: keycap"
77
78   // Default initialization does not keycap sequence
79   bool isNewKeycapSequence = false;
80
81   if(currentCharacterIndex <= lastCharacterIndex)
82   {
83     Character currentCharacter = *(textBuffer + currentCharacterIndex);
84     if(IsStartForKeycapSequence(currentCharacter))
85     {
86       if(!isNewKeycapSequence && currentCharacterIndex + 2 <= lastCharacterIndex)
87       {
88         Character characterOne = *(textBuffer + currentCharacterIndex + 1);
89         Character characterTwo = *(textBuffer + currentCharacterIndex + 2);
90
91         if(TextAbstraction::IsEmojiPresentationSelector(characterOne) &&
92            TextAbstraction::IsCombiningEnclosingKeycap(characterTwo))
93         {
94           isNewKeycapSequence    = true;
95           currentCharacterScript = TextAbstraction::EMOJI_COLOR;
96         }
97       } // if(!isNewKeycapSequence && currentCharacterIndex + 2 <= lastCharacterIndex)
98     }   // if(IsStartForKeycapSequence(currentCharacter))
99   }     // if(currentCharacterIndex < lastCharacterIndex)
100
101   return isNewKeycapSequence;
102 }
103
104 bool IsNewVariationSelectorSequence(const Character* const         textBuffer,
105                                     const TextAbstraction::Script& currentRunScript,
106                                     const Length&                  currentCharacterIndex,
107                                     const Length&                  lastCharacterIndex,
108                                     TextAbstraction::Script&       currentCharacterScript)
109 {
110   // Ref: Emoji and Text Presentation Selectors: https://www.unicode.org/reports/tr51/#Emoji_Variation_Selectors
111   // Ref: Emoji Variation Sequences for UTS #51: https://www.unicode.org/Public/14.0.0/ucd/emoji/emoji-variation-sequences.txt
112
113   // Default initialization does not VariationSelector sequence
114   bool isNewVariationSelectorSequence = false;
115
116   if(currentCharacterIndex <= lastCharacterIndex)
117   {
118     Character currentCharacter = *(textBuffer + currentCharacterIndex);
119     if(TextAbstraction::IsEmojiVariationSequences(currentCharacter))
120     {
121       if(!isNewVariationSelectorSequence && currentCharacterIndex + 1 <= lastCharacterIndex)
122       {
123         Character characterVS = *(textBuffer + currentCharacterIndex + 1);
124
125         if(TextAbstraction::IsEmojiPresentationSelector(characterVS))
126         {
127           isNewVariationSelectorSequence = currentRunScript != TextAbstraction::EMOJI_COLOR;
128           currentCharacterScript         = TextAbstraction::EMOJI_COLOR;
129         }
130         else if(TextAbstraction::IsTextPresentationSelector(characterVS))
131         {
132           isNewVariationSelectorSequence = currentRunScript != TextAbstraction::EMOJI_TEXT;
133           currentCharacterScript         = TextAbstraction::EMOJI_TEXT;
134         }
135
136       } // if(!isNewVariationSelectorSequence && currentCharacterIndex + 1 <= lastCharacterIndex)
137     }   // if(TextAbstraction::IsEmojiVariationSequences(currentCharacter))
138   }     // if(currentCharacterIndex < lastCharacterIndex)
139
140   return isNewVariationSelectorSequence;
141 }
142
143 bool IsStartForKeycapSequence(const TextAbstraction::Character& character)
144 {
145   return (TextAbstraction::IsASCIIDigits(character) ||
146           TextAbstraction::CHAR_NUMBER_SIGN == character ||
147           TextAbstraction::CHAR_ASTERISK == character);
148 }
149
150 bool IsScriptChangedToFollowSequence(const TextAbstraction::Script&    currentRunScript,
151                                      const TextAbstraction::Character& character,
152                                      TextAbstraction::Script&          currentCharacterScript)
153 {
154   bool isUpdated = false;
155
156   // Keycap cases
157   if(TextAbstraction::IsCombiningEnclosingKeycap(character))
158   {
159     if(TextAbstraction::EMOJI == currentRunScript)
160     {
161       // Keycap and unqualified
162       // Emoji request a default presentation for an emoji character.
163       isUpdated              = (currentCharacterScript != TextAbstraction::EMOJI);
164       currentCharacterScript = TextAbstraction::EMOJI;
165     }
166     else if(TextAbstraction::EMOJI_COLOR == currentRunScript)
167     {
168       // Keycap and fully-qualified
169       // Emoji request an emoji presentation for an emoji character.
170       isUpdated              = (currentCharacterScript != TextAbstraction::EMOJI_COLOR);
171       currentCharacterScript = TextAbstraction::EMOJI_COLOR;
172     }
173   }
174   // Emoji(Text) Presentation cases
175   else if(IsTextPresentationSequence(currentRunScript, character))
176   {
177     // Emoji request a text presentation for an emoji character.
178     isUpdated              = (currentCharacterScript != TextAbstraction::EMOJI_TEXT);
179     currentCharacterScript = TextAbstraction::EMOJI_TEXT;
180   }
181   // Emoji(Color) Presentation cases
182   else if(IsEmojiPresentationSequence(currentRunScript, character))
183   {
184     // Emoji request an emoji presentation for an emoji character.
185     isUpdated              = (currentCharacterScript != TextAbstraction::EMOJI_COLOR);
186     currentCharacterScript = TextAbstraction::EMOJI_COLOR;
187   }
188   // Default Emoji
189   else if(IsEmojiScript(currentRunScript) && IsEmojiScript(currentCharacterScript))
190   {
191     // Emoji request an emoji presentation for an emoji character.
192     isUpdated              = (currentCharacterScript != TextAbstraction::EMOJI);
193     currentCharacterScript = TextAbstraction::EMOJI;
194   }
195   // Emoji sequences
196   else if(IsEmojiSequence(currentRunScript, character, currentCharacterScript))
197   {
198     // Emoji request an emoji presentation for an emoji character.
199     isUpdated              = (currentCharacterScript != TextAbstraction::EMOJI_COLOR);
200     currentCharacterScript = TextAbstraction::EMOJI_COLOR;
201   }
202
203   return isUpdated;
204 }
205
206 Character GetVariationSelectorByScript(const TextAbstraction::Script& script)
207 {
208   Character character = 0u;
209   if(TextAbstraction::EMOJI_COLOR == script)
210   {
211     character = TextAbstraction::CHAR_VARIATION_SELECTOR_16;
212   }
213   else if(TextAbstraction::EMOJI_TEXT == script)
214   {
215     character = TextAbstraction::CHAR_VARIATION_SELECTOR_15;
216   }
217
218   return character;
219 }
220
221 } // namespace Text
222
223 } // namespace Toolkit
224
225 } // namespace Dali