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