Fix character direction logic
[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 } // namespace
67
68 struct BidirectionalSupport::Plugin
69 {
70   /**
71    * Stores bidirectional info per paragraph.
72    */
73   struct BidirectionalInfo
74   {
75     FriBidiCharType*    characterTypes;     ///< The type of each character (right, left, neutral, ...)
76     FriBidiBracketType* bracketTypes;       ///< Input list of bracket types as returned by fribidi_get_bracket_types().
77     FriBidiLevel*       embeddedLevels;     ///< Embedded levels.
78     FriBidiParType      paragraphDirection; ///< The paragraph's direction.
79   };
80
81   Plugin()
82   : mParagraphBidirectionalInfo(),
83     mFreeIndices()
84   {
85   }
86
87   ~Plugin()
88   {
89     // free all resources.
90     for(Vector<BidirectionalInfo*>::Iterator it    = mParagraphBidirectionalInfo.Begin(),
91                                              endIt = mParagraphBidirectionalInfo.End();
92         it != endIt;
93         ++it)
94     {
95       BidirectionalInfo* info = *it;
96
97       free(info->embeddedLevels);
98       free(info->characterTypes);
99       free(info->bracketTypes);
100       delete info;
101     }
102   }
103
104   BidiInfoIndex CreateInfo(const Character* const paragraph,
105                            Length                 numberOfCharacters,
106                            bool                   matchLayoutDirection,
107                            LayoutDirection::Type  layoutDirection)
108   {
109     // Reserve memory for the paragraph's bidirectional info.
110     BidirectionalInfo* bidirectionalInfo = new BidirectionalInfo();
111
112     bidirectionalInfo->characterTypes = reinterpret_cast<FriBidiCharType*>(malloc(numberOfCharacters * sizeof(FriBidiCharType)));
113     if(!bidirectionalInfo->characterTypes)
114     {
115       delete bidirectionalInfo;
116       return 0;
117     }
118
119     bidirectionalInfo->embeddedLevels = reinterpret_cast<FriBidiLevel*>(malloc(numberOfCharacters * sizeof(FriBidiLevel)));
120     if(!bidirectionalInfo->embeddedLevels)
121     {
122       free(bidirectionalInfo->characterTypes);
123       delete bidirectionalInfo;
124       return 0;
125     }
126
127     // Retrieve the type of each character..
128     fribidi_get_bidi_types(paragraph, numberOfCharacters, bidirectionalInfo->characterTypes);
129
130     // Retrieve the paragraph's direction.
131     bidirectionalInfo->paragraphDirection = matchLayoutDirection == true ? (layoutDirection == LayoutDirection::RIGHT_TO_LEFT ? FRIBIDI_PAR_RTL : FRIBIDI_PAR_LTR) : (fribidi_get_par_direction(bidirectionalInfo->characterTypes, numberOfCharacters));
132
133     bidirectionalInfo->bracketTypes = reinterpret_cast<FriBidiBracketType*>(malloc(numberOfCharacters * sizeof(FriBidiBracketType)));
134     if(!bidirectionalInfo->bracketTypes)
135     {
136       free(bidirectionalInfo->bracketTypes);
137       delete bidirectionalInfo;
138       return 0;
139     }
140
141     fribidi_get_bracket_types(paragraph, numberOfCharacters, bidirectionalInfo->characterTypes, bidirectionalInfo->bracketTypes);
142
143     // Retrieve the embedding levels.
144     if(fribidi_get_par_embedding_levels_ex(bidirectionalInfo->characterTypes, bidirectionalInfo->bracketTypes, numberOfCharacters, &bidirectionalInfo->paragraphDirection, bidirectionalInfo->embeddedLevels) == 0)
145     {
146       free(bidirectionalInfo->characterTypes);
147       free(bidirectionalInfo->bracketTypes);
148       delete bidirectionalInfo;
149       return 0;
150     }
151
152     // Store the bidirectional info and return the index.
153     BidiInfoIndex index = 0u;
154     if(0u != mFreeIndices.Count())
155     {
156       Vector<BidiInfoIndex>::Iterator it = mFreeIndices.End() - 1u;
157
158       index = *it;
159
160       mFreeIndices.Remove(it);
161
162       *(mParagraphBidirectionalInfo.Begin() + index) = bidirectionalInfo;
163     }
164     else
165     {
166       index = static_cast<BidiInfoIndex>(mParagraphBidirectionalInfo.Count());
167
168       mParagraphBidirectionalInfo.PushBack(bidirectionalInfo);
169     }
170
171     return index;
172   }
173
174   void DestroyInfo(BidiInfoIndex bidiInfoIndex)
175   {
176     if(bidiInfoIndex >= mParagraphBidirectionalInfo.Count())
177     {
178       return;
179     }
180
181     // Retrieve the paragraph's bidirectional info.
182     Vector<BidirectionalInfo*>::Iterator it                = mParagraphBidirectionalInfo.Begin() + bidiInfoIndex;
183     BidirectionalInfo*                   bidirectionalInfo = *it;
184
185     if(NULL != bidirectionalInfo)
186     {
187       // Free resources and destroy the container.
188       free(bidirectionalInfo->embeddedLevels);
189       free(bidirectionalInfo->characterTypes);
190       free(bidirectionalInfo->bracketTypes);
191       delete bidirectionalInfo;
192
193       *it = NULL;
194     }
195
196     // Add the index to the free indices vector.
197     mFreeIndices.PushBack(bidiInfoIndex);
198   }
199
200   void Reorder(BidiInfoIndex   bidiInfoIndex,
201                CharacterIndex  firstCharacterIndex,
202                Length          numberOfCharacters,
203                CharacterIndex* visualToLogicalMap)
204   {
205     const FriBidiFlags flags = FRIBIDI_FLAGS_DEFAULT | FRIBIDI_FLAGS_ARABIC;
206
207     // Retrieve the paragraph's bidirectional info.
208     const BidirectionalInfo* const bidirectionalInfo = *(mParagraphBidirectionalInfo.Begin() + bidiInfoIndex);
209
210     // Initialize the visual to logical mapping table to the identity. Otherwise fribidi_reorder_line fails to retrieve a valid mapping table.
211     for(CharacterIndex index = 0u; index < numberOfCharacters; ++index)
212     {
213       visualToLogicalMap[index] = index;
214     }
215
216     // Copy embedded levels as fribidi_reorder_line() may change them.
217     const size_t  embeddedLevelsSize = static_cast<std::size_t>(numberOfCharacters) * sizeof(FriBidiLevel);
218     FriBidiLevel* embeddedLevels     = reinterpret_cast<FriBidiLevel*>(malloc(embeddedLevelsSize));
219     if(embeddedLevels)
220     {
221       memcpy(embeddedLevels, bidirectionalInfo->embeddedLevels + firstCharacterIndex, embeddedLevelsSize);
222
223       // Reorder the line.
224       if(fribidi_reorder_line(flags,
225                               bidirectionalInfo->characterTypes + firstCharacterIndex,
226                               numberOfCharacters,
227                               0u,
228                               bidirectionalInfo->paragraphDirection,
229                               embeddedLevels,
230                               NULL,
231                               reinterpret_cast<FriBidiStrIndex*>(visualToLogicalMap)) == 0)
232       {
233         DALI_LOG_ERROR("fribidi_reorder_line is failed\n");
234       }
235
236       // Free resources.
237       free(embeddedLevels);
238     }
239   }
240
241   bool GetMirroredText(Character*          text,
242                        CharacterDirection* directions,
243                        Length              numberOfCharacters) const
244   {
245     bool updated = false;
246
247     for(CharacterIndex index = 0u; index < numberOfCharacters; ++index)
248     {
249       // Get a reference to the character inside the text.
250       Character& character = *(text + index);
251
252       // Retrieve the mirrored character.
253       FriBidiChar mirroredCharacter = character;
254       bool        mirrored          = false;
255       if(*(directions + index))
256       {
257         mirrored = fribidi_get_mirror_char(character, &mirroredCharacter);
258       }
259       updated = updated || mirrored;
260
261       // Update the character inside the text.
262       character = mirroredCharacter;
263     }
264
265     return updated;
266   }
267
268   bool GetParagraphDirection(BidiInfoIndex bidiInfoIndex) const
269   {
270     // Retrieve the paragraph's bidirectional info.
271     const BidirectionalInfo* const bidirectionalInfo = *(mParagraphBidirectionalInfo.Begin() + bidiInfoIndex);
272
273     return GetBidiParagraphDirection(bidirectionalInfo->paragraphDirection);
274   }
275
276   void GetCharactersDirection(BidiInfoIndex       bidiInfoIndex,
277                               CharacterDirection* directions,
278                               Length              numberOfCharacters)
279   {
280     const BidirectionalInfo* const bidirectionalInfo = *(mParagraphBidirectionalInfo.Begin() + bidiInfoIndex);
281
282     for(CharacterIndex index = 0u; index < numberOfCharacters; ++index)
283     {
284       CharacterDirection& characterDirection = *(directions + index);
285
286       // Checks if the character is rtl oriented.
287       // I.e even a neutral character can become rtl if surrounded by rtl characters.
288       characterDirection = FRIBIDI_IS_RTL(*(bidirectionalInfo->embeddedLevels + index));
289
290       // NOTE
291       // We are discontinuing the previous character direction determination logic.
292       // The previous logic was too heuristic and had many shortcomings in handling various RTL cases.
293       // The key change in this update is that character direction is determined based on embedding levels,
294       // including bracket information.
295       // The character direction determined here will affect the behavior of the GetMirroredText() function.
296       // BTW, Harfbuzz(hb_shape) also supports text mirroring.
297       // To use this, we need to pass the character direction at the embedding level to hb_buffer_set_direction,
298       // which is currently challenging for us.
299       // If this is implemented, we will no longer need to perform GetMirroredText().
300     }
301   }
302
303   Vector<BidirectionalInfo*> mParagraphBidirectionalInfo; ///< Stores the bidirectional info per paragraph.
304   Vector<BidiInfoIndex>      mFreeIndices;                ///< Stores indices of free positions in the bidirectional info vector.
305 };
306
307 BidirectionalSupport::BidirectionalSupport()
308 : mPlugin(NULL)
309 {
310 }
311
312 BidirectionalSupport::~BidirectionalSupport()
313 {
314   delete mPlugin;
315 }
316
317 TextAbstraction::BidirectionalSupport BidirectionalSupport::Get()
318 {
319   TextAbstraction::BidirectionalSupport bidirectionalSupportHandle;
320
321   SingletonService service(SingletonService::Get());
322   if(service)
323   {
324     // Check whether the singleton is already created
325     BaseHandle handle = service.GetSingleton(typeid(TextAbstraction::BidirectionalSupport));
326     if(handle)
327     {
328       // If so, downcast the handle
329       BidirectionalSupport* impl = dynamic_cast<Internal::BidirectionalSupport*>(handle.GetObjectPtr());
330       bidirectionalSupportHandle = TextAbstraction::BidirectionalSupport(impl);
331     }
332     else // create and register the object
333     {
334       bidirectionalSupportHandle = TextAbstraction::BidirectionalSupport(new BidirectionalSupport);
335       service.Register(typeid(bidirectionalSupportHandle), bidirectionalSupportHandle);
336     }
337   }
338
339   return bidirectionalSupportHandle;
340 }
341
342 BidiInfoIndex BidirectionalSupport::CreateInfo(const Character* const      paragraph,
343                                                Length                      numberOfCharacters,
344                                                bool                        matchLayoutDirection,
345                                                Dali::LayoutDirection::Type layoutDirection)
346 {
347   CreatePlugin();
348
349   return mPlugin->CreateInfo(paragraph,
350                              numberOfCharacters,
351                              matchLayoutDirection,
352                              layoutDirection);
353 }
354
355 void BidirectionalSupport::DestroyInfo(BidiInfoIndex bidiInfoIndex)
356 {
357   CreatePlugin();
358
359   mPlugin->DestroyInfo(bidiInfoIndex);
360 }
361
362 void BidirectionalSupport::Reorder(BidiInfoIndex   bidiInfoIndex,
363                                    CharacterIndex  firstCharacterIndex,
364                                    Length          numberOfCharacters,
365                                    CharacterIndex* visualToLogicalMap)
366 {
367   CreatePlugin();
368
369   mPlugin->Reorder(bidiInfoIndex,
370                    firstCharacterIndex,
371                    numberOfCharacters,
372                    visualToLogicalMap);
373 }
374
375 bool BidirectionalSupport::GetMirroredText(Character*          text,
376                                            CharacterDirection* directions,
377                                            Length              numberOfCharacters)
378 {
379   CreatePlugin();
380
381   return mPlugin->GetMirroredText(text, directions, numberOfCharacters);
382 }
383
384 bool BidirectionalSupport::GetParagraphDirection(BidiInfoIndex bidiInfoIndex) const
385 {
386   if(!mPlugin)
387   {
388     return false;
389   }
390
391   return mPlugin->GetParagraphDirection(bidiInfoIndex);
392 }
393
394 void BidirectionalSupport::GetCharactersDirection(BidiInfoIndex       bidiInfoIndex,
395                                                   CharacterDirection* directions,
396                                                   Length              numberOfCharacters)
397 {
398   CreatePlugin();
399
400   mPlugin->GetCharactersDirection(bidiInfoIndex,
401                                   directions,
402                                   numberOfCharacters);
403 }
404
405 void BidirectionalSupport::CreatePlugin()
406 {
407   if(!mPlugin)
408   {
409     mPlugin = new Plugin();
410   }
411 }
412
413 } // namespace Internal
414
415 } // namespace TextAbstraction
416
417 } // namespace Dali