Fix bidirection text issue
[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   {
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 = 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 {
395   CreatePlugin();
396
397   return mPlugin->CreateInfo( paragraph,
398                               numberOfCharacters );
399 }
400
401 void BidirectionalSupport::DestroyInfo( BidiInfoIndex bidiInfoIndex )
402 {
403   CreatePlugin();
404
405   mPlugin->DestroyInfo( bidiInfoIndex );
406 }
407
408 void BidirectionalSupport::Reorder( BidiInfoIndex bidiInfoIndex,
409                                     CharacterIndex firstCharacterIndex,
410                                     Length numberOfCharacters,
411                                     CharacterIndex* visualToLogicalMap )
412 {
413   CreatePlugin();
414
415   mPlugin->Reorder( bidiInfoIndex,
416                     firstCharacterIndex,
417                     numberOfCharacters,
418                     visualToLogicalMap );
419 }
420
421 bool BidirectionalSupport::GetMirroredText( Character* text,
422                                             CharacterDirection* directions,
423                                             Length numberOfCharacters )
424 {
425   CreatePlugin();
426
427   return mPlugin->GetMirroredText( text, directions, numberOfCharacters );
428 }
429
430 bool BidirectionalSupport::GetParagraphDirection( BidiInfoIndex bidiInfoIndex ) const
431 {
432   if( !mPlugin )
433   {
434     return false;
435   }
436
437   return mPlugin->GetParagraphDirection( bidiInfoIndex );
438 }
439
440 void BidirectionalSupport::GetCharactersDirection( BidiInfoIndex bidiInfoIndex,
441                                                    CharacterDirection* directions,
442                                                    Length numberOfCharacters )
443 {
444   CreatePlugin();
445
446   mPlugin->GetCharactersDirection( bidiInfoIndex,
447                                    directions,
448                                    numberOfCharacters );
449 }
450
451 void BidirectionalSupport::CreatePlugin()
452 {
453   if( !mPlugin )
454   {
455     mPlugin = new Plugin();
456   }
457 }
458
459 } // namespace Internal
460
461 } // namespace TextAbstraction
462
463 } // namespace Dali