Merge "[AT-SPI] Rework intercepting key events" into devel/master
[platform/core/uifw/dali-adaptor.git] / dali / internal / text / text-abstraction / bidirectional-support-impl.cpp
1 /*
2  * Copyright (c) 2015 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17
18 // CLASS  HEADER
19 #include <dali/internal/text/text-abstraction/bidirectional-support-impl.h>
20
21 // EXTERNAL INCLUDES
22 #include <dali/devel-api/common/singleton-service.h>
23 #include <dali/integration-api/debug.h>
24 #include <fribidi/fribidi.h>
25 #include <memory.h>
26
27 namespace Dali
28 {
29 namespace TextAbstraction
30 {
31 namespace Internal
32 {
33 namespace
34 {
35 typedef unsigned char BidiDirection;
36
37 // Internal charcter's direction.
38 const BidiDirection LEFT_TO_RIGHT = 0u;
39 const BidiDirection NEUTRAL       = 1u;
40 const BidiDirection RIGHT_TO_LEFT = 2u;
41
42 /**
43    * @param[in] paragraphDirection The FriBiDi paragraph's direction.
44    *
45    * @return Whether the paragraph is right to left.
46    */
47 bool GetBidiParagraphDirection(FriBidiParType paragraphDirection)
48 {
49   switch(paragraphDirection)
50   {
51     case FRIBIDI_PAR_RTL:  // Right-To-Left paragraph.
52     case FRIBIDI_PAR_WRTL: // Weak Right To Left paragraph.
53     {
54       return true;
55     }
56     case FRIBIDI_PAR_LTR:  // Left-To-Right paragraph.
57     case FRIBIDI_PAR_ON:   // DirectiOn-Neutral paragraph.
58     case FRIBIDI_PAR_WLTR: // Weak Left To Right paragraph.
59     {
60       return false;
61     }
62   }
63
64   return false;
65 }
66 } // namespace
67
68 struct BidirectionalSupport::Plugin
69 {
70   /**
71    * Stores bidirectional info per paragraph.
72    */
73   struct BidirectionalInfo
74   {
75     FriBidiCharType*    characterTypes;     ///< The type of each character (right, left, neutral, ...)
76     FriBidiBracketType* bracketTypes;       ///< Input list of bracket types as returned by fribidi_get_bracket_types().
77     FriBidiLevel*       embeddedLevels;     ///< Embedded levels.
78     FriBidiParType      paragraphDirection; ///< The paragraph's direction.
79   };
80
81   Plugin()
82   : mParagraphBidirectionalInfo(),
83     mFreeIndices()
84   {
85   }
86
87   ~Plugin()
88   {
89     // free all resources.
90     for(Vector<BidirectionalInfo*>::Iterator it    = mParagraphBidirectionalInfo.Begin(),
91                                              endIt = mParagraphBidirectionalInfo.End();
92         it != endIt;
93         ++it)
94     {
95       BidirectionalInfo* info = *it;
96
97       free(info->embeddedLevels);
98       free(info->characterTypes);
99       free(info->bracketTypes);
100       delete info;
101     }
102   }
103
104   BidiInfoIndex CreateInfo(const Character* const paragraph,
105                            Length                 numberOfCharacters,
106                            bool                   matchLayoutDirection,
107                            LayoutDirection::Type  layoutDirection)
108   {
109     // Reserve memory for the paragraph's bidirectional info.
110     BidirectionalInfo* bidirectionalInfo = new BidirectionalInfo();
111
112     bidirectionalInfo->characterTypes = reinterpret_cast<FriBidiCharType*>(malloc(numberOfCharacters * sizeof(FriBidiCharType)));
113     if(DALI_UNLIKELY(!bidirectionalInfo->characterTypes))
114     {
115       DALI_LOG_ERROR("malloc is failed. request malloc size : %zu\n", numberOfCharacters * sizeof(FriBidiCharType));
116       delete bidirectionalInfo;
117       return 0;
118     }
119
120     bidirectionalInfo->embeddedLevels = reinterpret_cast<FriBidiLevel*>(malloc(numberOfCharacters * sizeof(FriBidiLevel)));
121     if(DALI_UNLIKELY(!bidirectionalInfo->embeddedLevels))
122     {
123       DALI_LOG_ERROR("malloc is failed. request malloc size : %zu\n", numberOfCharacters * sizeof(FriBidiLevel));
124       free(bidirectionalInfo->characterTypes);
125       delete bidirectionalInfo;
126       return 0;
127     }
128
129     // Retrieve the type of each character..
130     fribidi_get_bidi_types(paragraph, numberOfCharacters, bidirectionalInfo->characterTypes);
131
132     // Retrieve the paragraph's direction.
133     bidirectionalInfo->paragraphDirection = matchLayoutDirection == true ? (layoutDirection == LayoutDirection::RIGHT_TO_LEFT ? FRIBIDI_PAR_RTL : FRIBIDI_PAR_LTR) : (fribidi_get_par_direction(bidirectionalInfo->characterTypes, numberOfCharacters));
134
135     bidirectionalInfo->bracketTypes = reinterpret_cast<FriBidiBracketType*>(malloc(numberOfCharacters * sizeof(FriBidiBracketType)));
136     if(!bidirectionalInfo->bracketTypes)
137     {
138       DALI_LOG_ERROR("malloc is failed. request malloc size : %zu\n", numberOfCharacters * sizeof(FriBidiBracketType));
139       free(bidirectionalInfo->embeddedLevels);
140       free(bidirectionalInfo->characterTypes);
141       delete bidirectionalInfo;
142       return 0;
143     }
144
145     fribidi_get_bracket_types(paragraph, numberOfCharacters, bidirectionalInfo->characterTypes, bidirectionalInfo->bracketTypes);
146
147     // Retrieve the embedding levels.
148     if(fribidi_get_par_embedding_levels_ex(bidirectionalInfo->characterTypes, bidirectionalInfo->bracketTypes, numberOfCharacters, &bidirectionalInfo->paragraphDirection, bidirectionalInfo->embeddedLevels) == 0)
149     {
150       free(bidirectionalInfo->bracketTypes);
151       free(bidirectionalInfo->embeddedLevels);
152       free(bidirectionalInfo->characterTypes);
153       delete bidirectionalInfo;
154       return 0;
155     }
156
157     // Store the bidirectional info and return the index.
158     BidiInfoIndex index = 0u;
159     if(0u != mFreeIndices.Count())
160     {
161       Vector<BidiInfoIndex>::Iterator it = mFreeIndices.End() - 1u;
162
163       index = *it;
164
165       mFreeIndices.Remove(it);
166
167       *(mParagraphBidirectionalInfo.Begin() + index) = bidirectionalInfo;
168     }
169     else
170     {
171       index = static_cast<BidiInfoIndex>(mParagraphBidirectionalInfo.Count());
172
173       mParagraphBidirectionalInfo.PushBack(bidirectionalInfo);
174     }
175
176     return index;
177   }
178
179   void DestroyInfo(BidiInfoIndex bidiInfoIndex)
180   {
181     if(bidiInfoIndex >= mParagraphBidirectionalInfo.Count())
182     {
183       return;
184     }
185
186     // Retrieve the paragraph's bidirectional info.
187     Vector<BidirectionalInfo*>::Iterator it                = mParagraphBidirectionalInfo.Begin() + bidiInfoIndex;
188     BidirectionalInfo*                   bidirectionalInfo = *it;
189
190     if(NULL != bidirectionalInfo)
191     {
192       // Free resources and destroy the container.
193       free(bidirectionalInfo->embeddedLevels);
194       free(bidirectionalInfo->characterTypes);
195       free(bidirectionalInfo->bracketTypes);
196       delete bidirectionalInfo;
197
198       *it = NULL;
199     }
200
201     // Add the index to the free indices vector.
202     mFreeIndices.PushBack(bidiInfoIndex);
203   }
204
205   void Reorder(BidiInfoIndex   bidiInfoIndex,
206                CharacterIndex  firstCharacterIndex,
207                Length          numberOfCharacters,
208                CharacterIndex* visualToLogicalMap)
209   {
210     const FriBidiFlags flags = FRIBIDI_FLAGS_DEFAULT | FRIBIDI_FLAGS_ARABIC;
211
212     // Retrieve the paragraph's bidirectional info.
213     const BidirectionalInfo* const bidirectionalInfo = *(mParagraphBidirectionalInfo.Begin() + bidiInfoIndex);
214
215     // Initialize the visual to logical mapping table to the identity. Otherwise fribidi_reorder_line fails to retrieve a valid mapping table.
216     for(CharacterIndex index = 0u; index < numberOfCharacters; ++index)
217     {
218       visualToLogicalMap[index] = index;
219     }
220
221     // Copy embedded levels as fribidi_reorder_line() may change them.
222     const size_t  embeddedLevelsSize = static_cast<std::size_t>(numberOfCharacters) * sizeof(FriBidiLevel);
223     FriBidiLevel* embeddedLevels     = reinterpret_cast<FriBidiLevel*>(malloc(embeddedLevelsSize));
224     if(DALI_LIKELY(embeddedLevels))
225     {
226       memcpy(embeddedLevels, bidirectionalInfo->embeddedLevels + firstCharacterIndex, embeddedLevelsSize);
227
228       // Reorder the line.
229       if(fribidi_reorder_line(flags,
230                               bidirectionalInfo->characterTypes + firstCharacterIndex,
231                               numberOfCharacters,
232                               0u,
233                               bidirectionalInfo->paragraphDirection,
234                               embeddedLevels,
235                               NULL,
236                               reinterpret_cast<FriBidiStrIndex*>(visualToLogicalMap)) == 0)
237       {
238         DALI_LOG_ERROR("fribidi_reorder_line is failed\n");
239       }
240
241       // Free resources.
242       free(embeddedLevels);
243     }
244     else
245     {
246       DALI_LOG_ERROR("malloc is failed. request malloc size : %zu\n", embeddedLevelsSize);
247     }
248   }
249
250   bool GetMirroredText(Character*          text,
251                        CharacterDirection* directions,
252                        Length              numberOfCharacters) const
253   {
254     bool updated = false;
255
256     for(CharacterIndex index = 0u; index < numberOfCharacters; ++index)
257     {
258       // Get a reference to the character inside the text.
259       Character& character = *(text + index);
260
261       // Retrieve the mirrored character.
262       FriBidiChar mirroredCharacter = character;
263       bool        mirrored          = false;
264       if(*(directions + index))
265       {
266         mirrored = fribidi_get_mirror_char(character, &mirroredCharacter);
267       }
268       updated = updated || mirrored;
269
270       // Update the character inside the text.
271       character = mirroredCharacter;
272     }
273
274     return updated;
275   }
276
277   bool GetParagraphDirection(BidiInfoIndex bidiInfoIndex) const
278   {
279     // Retrieve the paragraph's bidirectional info.
280     const BidirectionalInfo* const bidirectionalInfo = *(mParagraphBidirectionalInfo.Begin() + bidiInfoIndex);
281
282     return GetBidiParagraphDirection(bidirectionalInfo->paragraphDirection);
283   }
284
285   void GetCharactersDirection(BidiInfoIndex       bidiInfoIndex,
286                               CharacterDirection* directions,
287                               Length              numberOfCharacters)
288   {
289     const BidirectionalInfo* const bidirectionalInfo = *(mParagraphBidirectionalInfo.Begin() + bidiInfoIndex);
290
291     for(CharacterIndex index = 0u; index < numberOfCharacters; ++index)
292     {
293       CharacterDirection& characterDirection = *(directions + index);
294
295       // Checks if the character is rtl oriented.
296       // I.e even a neutral character can become rtl if surrounded by rtl characters.
297       characterDirection = FRIBIDI_IS_RTL(*(bidirectionalInfo->embeddedLevels + index));
298
299       // NOTE
300       // We are discontinuing the previous character direction determination logic.
301       // The previous logic was too heuristic and had many shortcomings in handling various RTL cases.
302       // The key change in this update is that character direction is determined based on embedding levels,
303       // including bracket information.
304       // The character direction determined here will affect the behavior of the GetMirroredText() function.
305       // BTW, Harfbuzz(hb_shape) also supports text mirroring.
306       // To use this, we need to pass the character direction at the embedding level to hb_buffer_set_direction,
307       // which is currently challenging for us.
308       // If this is implemented, we will no longer need to perform GetMirroredText().
309     }
310   }
311
312   Vector<BidirectionalInfo*> mParagraphBidirectionalInfo; ///< Stores the bidirectional info per paragraph.
313   Vector<BidiInfoIndex>      mFreeIndices;                ///< Stores indices of free positions in the bidirectional info vector.
314 };
315
316 BidirectionalSupport::BidirectionalSupport()
317 : mPlugin(NULL)
318 {
319 }
320
321 BidirectionalSupport::~BidirectionalSupport()
322 {
323   delete mPlugin;
324 }
325
326 TextAbstraction::BidirectionalSupport BidirectionalSupport::Get()
327 {
328   TextAbstraction::BidirectionalSupport bidirectionalSupportHandle;
329
330   SingletonService service(SingletonService::Get());
331   if(service)
332   {
333     // Check whether the singleton is already created
334     BaseHandle handle = service.GetSingleton(typeid(TextAbstraction::BidirectionalSupport));
335     if(handle)
336     {
337       // If so, downcast the handle
338       BidirectionalSupport* impl = dynamic_cast<Internal::BidirectionalSupport*>(handle.GetObjectPtr());
339       bidirectionalSupportHandle = TextAbstraction::BidirectionalSupport(impl);
340     }
341     else // create and register the object
342     {
343       bidirectionalSupportHandle = TextAbstraction::BidirectionalSupport(new BidirectionalSupport);
344       service.Register(typeid(bidirectionalSupportHandle), bidirectionalSupportHandle);
345     }
346   }
347
348   return bidirectionalSupportHandle;
349 }
350
351 BidiInfoIndex BidirectionalSupport::CreateInfo(const Character* const      paragraph,
352                                                Length                      numberOfCharacters,
353                                                bool                        matchLayoutDirection,
354                                                Dali::LayoutDirection::Type layoutDirection)
355 {
356   CreatePlugin();
357
358   return mPlugin->CreateInfo(paragraph,
359                              numberOfCharacters,
360                              matchLayoutDirection,
361                              layoutDirection);
362 }
363
364 void BidirectionalSupport::DestroyInfo(BidiInfoIndex bidiInfoIndex)
365 {
366   CreatePlugin();
367
368   mPlugin->DestroyInfo(bidiInfoIndex);
369 }
370
371 void BidirectionalSupport::Reorder(BidiInfoIndex   bidiInfoIndex,
372                                    CharacterIndex  firstCharacterIndex,
373                                    Length          numberOfCharacters,
374                                    CharacterIndex* visualToLogicalMap)
375 {
376   CreatePlugin();
377
378   mPlugin->Reorder(bidiInfoIndex,
379                    firstCharacterIndex,
380                    numberOfCharacters,
381                    visualToLogicalMap);
382 }
383
384 bool BidirectionalSupport::GetMirroredText(Character*          text,
385                                            CharacterDirection* directions,
386                                            Length              numberOfCharacters)
387 {
388   CreatePlugin();
389
390   return mPlugin->GetMirroredText(text, directions, numberOfCharacters);
391 }
392
393 bool BidirectionalSupport::GetParagraphDirection(BidiInfoIndex bidiInfoIndex) const
394 {
395   if(!mPlugin)
396   {
397     return false;
398   }
399
400   return mPlugin->GetParagraphDirection(bidiInfoIndex);
401 }
402
403 void BidirectionalSupport::GetCharactersDirection(BidiInfoIndex       bidiInfoIndex,
404                                                   CharacterDirection* directions,
405                                                   Length              numberOfCharacters)
406 {
407   CreatePlugin();
408
409   mPlugin->GetCharactersDirection(bidiInfoIndex,
410                                   directions,
411                                   numberOfCharacters);
412 }
413
414 void BidirectionalSupport::CreatePlugin()
415 {
416   if(!mPlugin)
417   {
418     mPlugin = new Plugin();
419   }
420 }
421
422 } // namespace Internal
423
424 } // namespace TextAbstraction
425
426 } // namespace Dali