90e8cafe59d7918aed8cb741c1ebf4b6dbec2cde
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / text / bidirectional-support.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 // FILE HEADER
19 #include <dali-toolkit/internal/text/bidirectional-support.h>
20
21 // EXTERNAL INCLUDES
22 #include <dali/devel-api/text-abstraction/bidirectional-support.h>
23 #include <memory.h>
24
25 namespace Dali
26 {
27 namespace Toolkit
28 {
29 namespace Text
30 {
31 void SetBidirectionalInfo(const Vector<Character>&               text,
32                           const Vector<ScriptRun>&               scripts,
33                           const Vector<LineBreakInfo>&           lineBreakInfo,
34                           CharacterIndex                         startIndex,
35                           Length                                 numberOfCharacters,
36                           Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo,
37                           bool                                   matchLayoutDirection,
38                           Dali::LayoutDirection::Type            layoutDirection)
39 {
40   // Find where to insert the new paragraphs.
41   BidirectionalRunIndex bidiInfoIndex = 0u;
42   for(Vector<BidirectionalParagraphInfoRun>::ConstIterator it    = bidirectionalInfo.Begin(),
43                                                            endIt = bidirectionalInfo.End();
44       it != endIt;
45       ++it)
46   {
47     const BidirectionalParagraphInfoRun& run = *it;
48
49     if(startIndex < run.characterRun.characterIndex + run.characterRun.numberOfCharacters)
50     {
51       // Found where to insert the bidi info.
52       break;
53     }
54     ++bidiInfoIndex;
55   }
56
57   // Traverse the script runs. If there is one with a right to left script, create the bidirectional info for the paragraph containing that script is needed.
58   // From the bidirectional point of view, a paragraph is the piece of text between two LINE_MUST_BREAK.
59
60   // Index pointing the first character of the current paragraph.
61   CharacterIndex paragraphCharacterIndex = startIndex;
62
63   // Pointer to the text buffer.
64   const Character* textBuffer = text.Begin();
65
66   // Pointer to the line break info buffer.
67   const LineBreakInfo* lineBreakInfoBuffer = lineBreakInfo.Begin();
68
69   // Handle to the bidirectional info module in text-abstraction.
70   TextAbstraction::BidirectionalSupport bidirectionalSupport = TextAbstraction::BidirectionalSupport::Get();
71
72   const CharacterIndex lastCharacter = startIndex + numberOfCharacters;
73
74   bool hasRightToLeftScript = false;
75
76   for(Vector<ScriptRun>::ConstIterator it    = scripts.Begin(),
77                                        endIt = scripts.End();
78       it != endIt;
79       ++it)
80   {
81     const ScriptRun&     scriptRun          = *it;
82     const CharacterIndex lastScriptRunIndex = scriptRun.characterRun.characterIndex + scriptRun.characterRun.numberOfCharacters - 1u;
83
84     if(startIndex > lastScriptRunIndex)
85     {
86       // Skip the run as it has already been processed.
87       continue;
88     }
89
90     if(lastCharacter <= scriptRun.characterRun.characterIndex)
91     {
92       // Do not get bidirectional info beyond startIndex + numberOfCharacters.
93       break;
94     }
95
96     if(!hasRightToLeftScript && scriptRun.isRightToLeft)
97     {
98       // The script is right to left.
99       hasRightToLeftScript = true;
100     }
101
102     if(TextAbstraction::LINE_MUST_BREAK == *(lineBreakInfoBuffer + lastScriptRunIndex))
103     {
104       // A new paragraph has been found.
105
106       if(hasRightToLeftScript)
107       {
108         // The Bidirectional run must have the same number of characters than the paragraph.
109         BidirectionalParagraphInfoRun bidirectionalRun;
110         bidirectionalRun.characterRun.characterIndex     = paragraphCharacterIndex;
111         bidirectionalRun.characterRun.numberOfCharacters = (lastScriptRunIndex - paragraphCharacterIndex) + 1u; // The must break character is part of the paragrah.
112
113         // Create the bidirectional info for the whole paragraph and store the index to the table with this info in the run.
114         bidirectionalRun.bidirectionalInfoIndex = bidirectionalSupport.CreateInfo(textBuffer + bidirectionalRun.characterRun.characterIndex,
115                                                                                   bidirectionalRun.characterRun.numberOfCharacters,
116                                                                                   matchLayoutDirection,
117                                                                                   layoutDirection);
118
119         bidirectionalRun.direction = bidirectionalSupport.GetParagraphDirection(bidirectionalRun.bidirectionalInfoIndex);
120
121         bidirectionalInfo.Insert(bidirectionalInfo.Begin() + bidiInfoIndex, bidirectionalRun);
122         ++bidiInfoIndex;
123       }
124
125       // Point to the next paragraph.
126       paragraphCharacterIndex = lastScriptRunIndex + 1u;
127
128       // Reset whether there is a right to left script.
129       hasRightToLeftScript = false;
130     }
131   }
132
133   // Update indices of the bidi runs.
134   for(Vector<BidirectionalParagraphInfoRun>::Iterator it    = bidirectionalInfo.Begin() + bidiInfoIndex,
135                                                       endIt = bidirectionalInfo.End();
136       it != endIt;
137       ++it)
138   {
139     BidirectionalParagraphInfoRun& run = *it;
140
141     run.characterRun.characterIndex += numberOfCharacters;
142   }
143 }
144
145 void ReorderLine(const BidirectionalParagraphInfoRun& bidirectionalParagraphInfo,
146                  Vector<BidirectionalLineInfoRun>&    lineInfoRuns,
147                  BidirectionalLineRunIndex            bidiLineIndex,
148                  CharacterIndex                       startIndex,
149                  Length                               numberOfCharacters,
150                  CharacterDirection                   direction)
151 {
152   // Handle to the bidirectional info module in text-abstraction.
153   TextAbstraction::BidirectionalSupport bidirectionalSupport = TextAbstraction::BidirectionalSupport::Get();
154
155   // Creates a bidirectional info for the line run.
156   BidirectionalLineInfoRun lineInfoRun;
157   lineInfoRun.characterRun.characterIndex     = startIndex;
158   lineInfoRun.characterRun.numberOfCharacters = numberOfCharacters;
159   lineInfoRun.direction                       = direction;
160   lineInfoRun.isIdentity                      = true;
161
162   // Allocate space for the conversion maps.
163   // The memory is freed after the visual to logical to visual conversion tables are built in the logical model.
164   lineInfoRun.visualToLogicalMap = reinterpret_cast<CharacterIndex*>(malloc(numberOfCharacters * sizeof(CharacterIndex)));
165
166   if(nullptr != lineInfoRun.visualToLogicalMap)
167   {
168     // Reorders the line.
169     bidirectionalSupport.Reorder(bidirectionalParagraphInfo.bidirectionalInfoIndex,
170                                  lineInfoRun.characterRun.characterIndex - bidirectionalParagraphInfo.characterRun.characterIndex,
171                                  lineInfoRun.characterRun.numberOfCharacters,
172                                  lineInfoRun.visualToLogicalMap);
173
174     // For those LTR lines inside a bidirectional paragraph.
175     // It will save to relayout the line after reordering.
176     for(unsigned int i = 0; i < numberOfCharacters; ++i)
177     {
178       if(i != *(lineInfoRun.visualToLogicalMap + i))
179       {
180         lineInfoRun.isIdentity = false;
181         break;
182       }
183     }
184   }
185
186   // Push the run into the vector.
187   lineInfoRuns.Insert(lineInfoRuns.Begin() + bidiLineIndex, lineInfoRun);
188 }
189
190 bool GetMirroredText(const Vector<Character>&                     text,
191                      const Vector<CharacterDirection>&            directions,
192                      const Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo,
193                      CharacterIndex                               startIndex,
194                      Length                                       numberOfCharacters,
195                      Vector<Character>&                           mirroredText)
196 {
197   bool hasTextMirrored = false;
198
199   // Handle to the bidirectional info module in text-abstraction.
200   TextAbstraction::BidirectionalSupport bidirectionalSupport = TextAbstraction::BidirectionalSupport::Get();
201
202   mirroredText = text;
203
204   Character*          mirroredTextBuffer = mirroredText.Begin();
205   CharacterDirection* directionsBuffer   = directions.Begin();
206
207   CharacterIndex       index         = startIndex;
208   const CharacterIndex lastCharacter = startIndex + numberOfCharacters;
209
210   // Traverse the paragraphs and mirror the right to left ones.
211   for(Vector<BidirectionalParagraphInfoRun>::ConstIterator it    = bidirectionalInfo.Begin(),
212                                                            endIt = bidirectionalInfo.End();
213       it != endIt;
214       ++it)
215   {
216     const BidirectionalParagraphInfoRun& paragraph = *it;
217
218     if(index >= paragraph.characterRun.characterIndex + paragraph.characterRun.numberOfCharacters)
219     {
220       // Skip the paragraph as it has already been processed.
221       continue;
222     }
223
224     if(lastCharacter <= paragraph.characterRun.characterIndex)
225     {
226       // Do not get mirror characters beyond startIndex + numberOfCharacters.
227       break;
228     }
229
230     index += paragraph.characterRun.numberOfCharacters;
231     const bool tmpMirrored = bidirectionalSupport.GetMirroredText(mirroredTextBuffer + paragraph.characterRun.characterIndex,
232                                                                   directionsBuffer + paragraph.characterRun.characterIndex,
233                                                                   paragraph.characterRun.numberOfCharacters);
234
235     hasTextMirrored = hasTextMirrored || tmpMirrored;
236   }
237
238   return hasTextMirrored;
239 }
240
241 void GetCharactersDirection(const Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo,
242                             Length                                       totalNumberOfCharacters,
243                             CharacterIndex                               startIndex,
244                             Length                                       numberOfCharacters,
245                             Vector<CharacterDirection>&                  directions)
246 {
247   // Handle to the bidirectional info module in text-abstraction.
248   TextAbstraction::BidirectionalSupport bidirectionalSupport = TextAbstraction::BidirectionalSupport::Get();
249
250   // Resize the vector.
251   directions.Resize(totalNumberOfCharacters);
252
253   // Whether the current buffer is being updated or is set from scratch.
254   const bool updateCurrentBuffer = numberOfCharacters < totalNumberOfCharacters;
255
256   CharacterDirection*        directionsBuffer = NULL;
257   Vector<CharacterDirection> newDirections;
258
259   if(updateCurrentBuffer)
260   {
261     newDirections.Resize(numberOfCharacters);
262     directionsBuffer = newDirections.Begin();
263   }
264   else
265   {
266     directionsBuffer = directions.Begin();
267   }
268
269   const CharacterIndex lastCharacter = startIndex + numberOfCharacters;
270   CharacterIndex       index         = startIndex;
271
272   for(Vector<BidirectionalParagraphInfoRun>::ConstIterator it    = bidirectionalInfo.Begin(),
273                                                            endIt = bidirectionalInfo.End();
274       it != endIt;
275       ++it)
276   {
277     const BidirectionalParagraphInfoRun& paragraph = *it;
278
279     if(index >= paragraph.characterRun.characterIndex + paragraph.characterRun.numberOfCharacters)
280     {
281       // Skip the paragraph as it has already been processed.
282       continue;
283     }
284
285     if(lastCharacter <= paragraph.characterRun.characterIndex)
286     {
287       // Do not get the character directions beyond startIndex + numberOfCharacters.
288       break;
289     }
290
291     // Set the directions of any previous left to right characters.
292     const Length numberOfLeftToRightCharacters = paragraph.characterRun.characterIndex - index;
293     if(numberOfLeftToRightCharacters > 0u)
294     {
295       memset(directionsBuffer + index - startIndex, false, numberOfLeftToRightCharacters * sizeof(bool));
296     }
297
298     // Set the directions of the bidirectional text.
299     bidirectionalSupport.GetCharactersDirection(paragraph.bidirectionalInfoIndex,
300                                                 directionsBuffer + paragraph.characterRun.characterIndex - startIndex,
301                                                 paragraph.characterRun.numberOfCharacters);
302
303     // Update the index.
304     index += paragraph.characterRun.numberOfCharacters + numberOfLeftToRightCharacters;
305   }
306
307   // Fills with left to right those paragraphs without right to left characters.
308   memset(directionsBuffer + index - startIndex, false, (lastCharacter - index) * sizeof(bool));
309
310   // If the direction info is updated, it needs to be inserted in the model.
311   if(updateCurrentBuffer)
312   {
313     // Insert the directions in the given buffer.
314     directions.Insert(directions.Begin() + startIndex,
315                       newDirections.Begin(),
316                       newDirections.End());
317     directions.Resize(totalNumberOfCharacters);
318   }
319 }
320
321 } // namespace Text
322
323 } // namespace Toolkit
324
325 } // namespace Dali