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