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