e6b9cd87c489d1e0aec6a0422c6bcc7916bfbc38
[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
67 BidiDirection GetBidiCharacterDirection(FriBidiCharType characterDirection)
68 {
69   switch(characterDirection)
70   {
71     case FRIBIDI_TYPE_LTR: // Left-To-Right letter.
72     {
73       return LEFT_TO_RIGHT;
74     }
75     case FRIBIDI_TYPE_AL:  // Arabic Letter.
76     case FRIBIDI_TYPE_RTL: // Right-To-Left letter.
77     {
78       return RIGHT_TO_LEFT;
79     }
80     case FRIBIDI_TYPE_AN: // Arabic Numeral.
81     case FRIBIDI_TYPE_ES: // European number Separator.
82     case FRIBIDI_TYPE_ET: // European number Terminator.
83     case FRIBIDI_TYPE_EN: // European Numeral.
84     default:
85     {
86       return NEUTRAL;
87     }
88   }
89 }
90 } // namespace
91
92 struct BidirectionalSupport::Plugin
93 {
94   /**
95    * Stores bidirectional info per paragraph.
96    */
97   struct BidirectionalInfo
98   {
99     FriBidiCharType* characterTypes;     ///< The type of each character (right, left, neutral, ...)
100     FriBidiLevel*    embeddedLevels;     ///< Embedded levels.
101     FriBidiParType   paragraphDirection; ///< The paragraph's direction.
102   };
103
104   Plugin()
105   : mParagraphBidirectionalInfo(),
106     mFreeIndices()
107   {
108   }
109
110   ~Plugin()
111   {
112     // free all resources.
113     for(Vector<BidirectionalInfo*>::Iterator it    = mParagraphBidirectionalInfo.Begin(),
114                                              endIt = mParagraphBidirectionalInfo.End();
115         it != endIt;
116         ++it)
117     {
118       BidirectionalInfo* info = *it;
119
120       free(info->embeddedLevels);
121       free(info->characterTypes);
122       delete info;
123     }
124   }
125
126   BidiInfoIndex CreateInfo(const Character* const paragraph,
127                            Length                 numberOfCharacters,
128                            bool                   matchSystemLanguageDirection,
129                            LayoutDirection::Type  layoutDirection)
130   {
131     // Reserve memory for the paragraph's bidirectional info.
132     BidirectionalInfo* bidirectionalInfo = new BidirectionalInfo();
133
134     bidirectionalInfo->characterTypes = reinterpret_cast<FriBidiCharType*>(malloc(numberOfCharacters * sizeof(FriBidiCharType)));
135     if(!bidirectionalInfo->characterTypes)
136     {
137       delete bidirectionalInfo;
138       return 0;
139     }
140
141     bidirectionalInfo->embeddedLevels = reinterpret_cast<FriBidiLevel*>(malloc(numberOfCharacters * sizeof(FriBidiLevel)));
142     if(!bidirectionalInfo->embeddedLevels)
143     {
144       free(bidirectionalInfo->characterTypes);
145       delete bidirectionalInfo;
146       return 0;
147     }
148
149     // Retrieve the type of each character..
150     fribidi_get_bidi_types(paragraph, numberOfCharacters, bidirectionalInfo->characterTypes);
151
152     // Retrieve the paragraph's direction.
153     bidirectionalInfo->paragraphDirection = matchSystemLanguageDirection == true ? (layoutDirection == LayoutDirection::RIGHT_TO_LEFT ? FRIBIDI_PAR_RTL : FRIBIDI_PAR_LTR) : (fribidi_get_par_direction(bidirectionalInfo->characterTypes, numberOfCharacters));
154
155     // Retrieve the embedding levels.
156     if(fribidi_get_par_embedding_levels(bidirectionalInfo->characterTypes, numberOfCharacters, &bidirectionalInfo->paragraphDirection, bidirectionalInfo->embeddedLevels) == 0)
157     {
158       free(bidirectionalInfo->characterTypes);
159       delete bidirectionalInfo;
160       return 0;
161     }
162
163     // Store the bidirectional info and return the index.
164     BidiInfoIndex index = 0u;
165     if(0u != mFreeIndices.Count())
166     {
167       Vector<BidiInfoIndex>::Iterator it = mFreeIndices.End() - 1u;
168
169       index = *it;
170
171       mFreeIndices.Remove(it);
172
173       *(mParagraphBidirectionalInfo.Begin() + index) = bidirectionalInfo;
174     }
175     else
176     {
177       index = static_cast<BidiInfoIndex>(mParagraphBidirectionalInfo.Count());
178
179       mParagraphBidirectionalInfo.PushBack(bidirectionalInfo);
180     }
181
182     return index;
183   }
184
185   void DestroyInfo(BidiInfoIndex bidiInfoIndex)
186   {
187     if(bidiInfoIndex >= mParagraphBidirectionalInfo.Count())
188     {
189       return;
190     }
191
192     // Retrieve the paragraph's bidirectional info.
193     Vector<BidirectionalInfo*>::Iterator it                = mParagraphBidirectionalInfo.Begin() + bidiInfoIndex;
194     BidirectionalInfo*                   bidirectionalInfo = *it;
195
196     if(NULL != bidirectionalInfo)
197     {
198       // Free resources and destroy the container.
199       free(bidirectionalInfo->embeddedLevels);
200       free(bidirectionalInfo->characterTypes);
201       delete bidirectionalInfo;
202
203       *it = NULL;
204     }
205
206     // Add the index to the free indices vector.
207     mFreeIndices.PushBack(bidiInfoIndex);
208   }
209
210   void Reorder(BidiInfoIndex   bidiInfoIndex,
211                CharacterIndex  firstCharacterIndex,
212                Length          numberOfCharacters,
213                CharacterIndex* visualToLogicalMap)
214   {
215     const FriBidiFlags flags = FRIBIDI_FLAGS_DEFAULT | FRIBIDI_FLAGS_ARABIC;
216
217     // Retrieve the paragraph's bidirectional info.
218     const BidirectionalInfo* const bidirectionalInfo = *(mParagraphBidirectionalInfo.Begin() + bidiInfoIndex);
219
220     // Initialize the visual to logical mapping table to the identity. Otherwise fribidi_reorder_line fails to retrieve a valid mapping table.
221     for(CharacterIndex index = 0u; index < numberOfCharacters; ++index)
222     {
223       visualToLogicalMap[index] = index;
224     }
225
226     // Copy embedded levels as fribidi_reorder_line() may change them.
227     const uint32_t embeddedLevelsSize = numberOfCharacters * sizeof(FriBidiLevel);
228     FriBidiLevel*  embeddedLevels     = reinterpret_cast<FriBidiLevel*>(malloc(embeddedLevelsSize));
229     if(embeddedLevels)
230     {
231       memcpy(embeddedLevels, bidirectionalInfo->embeddedLevels + firstCharacterIndex, embeddedLevelsSize);
232
233       // Reorder the line.
234       if(fribidi_reorder_line(flags,
235                               bidirectionalInfo->characterTypes + firstCharacterIndex,
236                               numberOfCharacters,
237                               0u,
238                               bidirectionalInfo->paragraphDirection,
239                               embeddedLevels,
240                               NULL,
241                               reinterpret_cast<FriBidiStrIndex*>(visualToLogicalMap)) == 0)
242       {
243         DALI_LOG_ERROR("fribidi_reorder_line is failed\n");
244       }
245
246       // Free resources.
247       free(embeddedLevels);
248     }
249   }
250
251   bool GetMirroredText(Character*          text,
252                        CharacterDirection* directions,
253                        Length              numberOfCharacters) const
254   {
255     bool updated = false;
256
257     for(CharacterIndex index = 0u; index < numberOfCharacters; ++index)
258     {
259       // Get a reference to the character inside the text.
260       Character& character = *(text + index);
261
262       // Retrieve the mirrored character.
263       FriBidiChar mirroredCharacter = character;
264       bool        mirrored          = false;
265       if(*(directions + index))
266       {
267         mirrored = fribidi_get_mirror_char(character, &mirroredCharacter);
268       }
269       updated = updated || mirrored;
270
271       // Update the character inside the text.
272       character = mirroredCharacter;
273     }
274
275     return updated;
276   }
277
278   bool GetParagraphDirection(BidiInfoIndex bidiInfoIndex) const
279   {
280     // Retrieve the paragraph's bidirectional info.
281     const BidirectionalInfo* const bidirectionalInfo = *(mParagraphBidirectionalInfo.Begin() + bidiInfoIndex);
282
283     return GetBidiParagraphDirection(bidirectionalInfo->paragraphDirection);
284   }
285
286   void GetCharactersDirection(BidiInfoIndex       bidiInfoIndex,
287                               CharacterDirection* directions,
288                               Length              numberOfCharacters)
289   {
290     const BidirectionalInfo* const bidirectionalInfo = *(mParagraphBidirectionalInfo.Begin() + bidiInfoIndex);
291
292     const CharacterDirection paragraphDirection = GetBidiParagraphDirection(bidirectionalInfo->paragraphDirection);
293     CharacterDirection       previousDirection  = paragraphDirection;
294
295     for(CharacterIndex index = 0u; index < numberOfCharacters; ++index)
296     {
297       CharacterDirection& characterDirection = *(directions + index);
298       CharacterDirection  nextDirection      = false;
299
300       characterDirection = false;
301
302       // Get the bidi direction.
303       const BidiDirection bidiDirection = GetBidiCharacterDirection(*(bidirectionalInfo->characterTypes + index));
304
305       if(RIGHT_TO_LEFT == bidiDirection)
306       {
307         characterDirection = true;
308         nextDirection      = true;
309       }
310       else if(NEUTRAL == bidiDirection)
311       {
312         // For neutral characters it check's the next and previous directions.
313         // If they are equals set that direction. If they are not, sets the paragraph's direction.
314         // If there is no next, sets the paragraph's direction.
315         nextDirection = paragraphDirection;
316
317         // Look for the next non-neutral character.
318         Length nextIndex = index + 1u;
319         for(; nextIndex < numberOfCharacters; ++nextIndex)
320         {
321           BidiDirection nextBidiDirection = GetBidiCharacterDirection(*(bidirectionalInfo->characterTypes + nextIndex));
322           if(nextBidiDirection != NEUTRAL)
323           {
324             nextDirection = RIGHT_TO_LEFT == nextBidiDirection;
325             break;
326           }
327         }
328
329         // Calculate the direction for all the neutral characters.
330         characterDirection = previousDirection == nextDirection ? previousDirection : paragraphDirection;
331
332         // Set the direction to all the neutral characters.
333         // The indices from currentIndex + 1u to nextIndex - 1u are neutral characters.
334         ++index;
335
336         for(; index < nextIndex; ++index)
337         {
338           CharacterDirection& nextCharacterDirection = *(directions + index);
339           nextCharacterDirection                     = characterDirection;
340         }
341
342         // Set the direction of the next non-neutral character.
343         if(nextIndex < numberOfCharacters)
344         {
345           *(directions + nextIndex) = nextDirection;
346         }
347       }
348
349       previousDirection = nextDirection;
350     }
351   }
352
353   Vector<BidirectionalInfo*> mParagraphBidirectionalInfo; ///< Stores the bidirectional info per paragraph.
354   Vector<BidiInfoIndex>      mFreeIndices;                ///< Stores indices of free positions in the bidirectional info vector.
355 };
356
357 BidirectionalSupport::BidirectionalSupport()
358 : mPlugin(NULL)
359 {
360 }
361
362 BidirectionalSupport::~BidirectionalSupport()
363 {
364   delete mPlugin;
365 }
366
367 TextAbstraction::BidirectionalSupport BidirectionalSupport::Get()
368 {
369   TextAbstraction::BidirectionalSupport bidirectionalSupportHandle;
370
371   SingletonService service(SingletonService::Get());
372   if(service)
373   {
374     // Check whether the singleton is already created
375     BaseHandle handle = service.GetSingleton(typeid(TextAbstraction::BidirectionalSupport));
376     if(handle)
377     {
378       // If so, downcast the handle
379       BidirectionalSupport* impl = dynamic_cast<Internal::BidirectionalSupport*>(handle.GetObjectPtr());
380       bidirectionalSupportHandle = TextAbstraction::BidirectionalSupport(impl);
381     }
382     else // create and register the object
383     {
384       bidirectionalSupportHandle = TextAbstraction::BidirectionalSupport(new BidirectionalSupport);
385       service.Register(typeid(bidirectionalSupportHandle), bidirectionalSupportHandle);
386     }
387   }
388
389   return bidirectionalSupportHandle;
390 }
391
392 BidiInfoIndex BidirectionalSupport::CreateInfo(const Character* const      paragraph,
393                                                Length                      numberOfCharacters,
394                                                bool                        matchSystemLanguageDirection,
395                                                Dali::LayoutDirection::Type layoutDirection)
396 {
397   CreatePlugin();
398
399   return mPlugin->CreateInfo(paragraph,
400                              numberOfCharacters,
401                              matchSystemLanguageDirection,
402                              layoutDirection);
403 }
404
405 void BidirectionalSupport::DestroyInfo(BidiInfoIndex bidiInfoIndex)
406 {
407   CreatePlugin();
408
409   mPlugin->DestroyInfo(bidiInfoIndex);
410 }
411
412 void BidirectionalSupport::Reorder(BidiInfoIndex   bidiInfoIndex,
413                                    CharacterIndex  firstCharacterIndex,
414                                    Length          numberOfCharacters,
415                                    CharacterIndex* visualToLogicalMap)
416 {
417   CreatePlugin();
418
419   mPlugin->Reorder(bidiInfoIndex,
420                    firstCharacterIndex,
421                    numberOfCharacters,
422                    visualToLogicalMap);
423 }
424
425 bool BidirectionalSupport::GetMirroredText(Character*          text,
426                                            CharacterDirection* directions,
427                                            Length              numberOfCharacters)
428 {
429   CreatePlugin();
430
431   return mPlugin->GetMirroredText(text, directions, numberOfCharacters);
432 }
433
434 bool BidirectionalSupport::GetParagraphDirection(BidiInfoIndex bidiInfoIndex) const
435 {
436   if(!mPlugin)
437   {
438     return false;
439   }
440
441   return mPlugin->GetParagraphDirection(bidiInfoIndex);
442 }
443
444 void BidirectionalSupport::GetCharactersDirection(BidiInfoIndex       bidiInfoIndex,
445                                                   CharacterDirection* directions,
446                                                   Length              numberOfCharacters)
447 {
448   CreatePlugin();
449
450   mPlugin->GetCharactersDirection(bidiInfoIndex,
451                                   directions,
452                                   numberOfCharacters);
453 }
454
455 void BidirectionalSupport::CreatePlugin()
456 {
457   if(!mPlugin)
458   {
459     mPlugin = new Plugin();
460   }
461 }
462
463 } // namespace Internal
464
465 } // namespace TextAbstraction
466
467 } // namespace Dali