(Text Controller) Moved some input properties into a different class & some functiona...
[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                  CharacterIndex                       startIndexInSecondHalfLine,
151                  Length                               numberOfCharactersInSecondHalfLine,
152                  CharacterDirection                   direction)
153 {
154   // Handle to the bidirectional info module in text-abstraction.
155   TextAbstraction::BidirectionalSupport bidirectionalSupport = TextAbstraction::BidirectionalSupport::Get();
156
157   // Creates a bidirectional info for the line run.
158   BidirectionalLineInfoRun lineInfoRun;
159   lineInfoRun.characterRun.characterIndex     = startIndex;
160   lineInfoRun.characterRun.numberOfCharacters = numberOfCharacters;
161   lineInfoRun.direction                       = direction;
162   lineInfoRun.isIdentity                      = true;
163
164   lineInfoRun.characterRunForSecondHalfLine.characterIndex     = startIndexInSecondHalfLine;
165   lineInfoRun.characterRunForSecondHalfLine.numberOfCharacters = numberOfCharactersInSecondHalfLine;
166
167   // Allocate space for the conversion maps.
168   // The memory is freed after the visual to logical to visual conversion tables are built in the logical model.
169   lineInfoRun.visualToLogicalMap = reinterpret_cast<CharacterIndex*>(malloc(numberOfCharacters * sizeof(CharacterIndex)));
170
171   lineInfoRun.visualToLogicalMapSecondHalf = reinterpret_cast<CharacterIndex*>(malloc(numberOfCharactersInSecondHalfLine * sizeof(CharacterIndex)));
172
173   if(nullptr != lineInfoRun.visualToLogicalMap && nullptr != lineInfoRun.visualToLogicalMapSecondHalf)
174   {
175     // Reorders the line.
176     bidirectionalSupport.Reorder(bidirectionalParagraphInfo.bidirectionalInfoIndex,
177                                  lineInfoRun.characterRun.characterIndex - bidirectionalParagraphInfo.characterRun.characterIndex,
178                                  lineInfoRun.characterRun.numberOfCharacters,
179                                  lineInfoRun.visualToLogicalMap);
180
181     bidirectionalSupport.Reorder(bidirectionalParagraphInfo.bidirectionalInfoIndex,
182                                  lineInfoRun.characterRunForSecondHalfLine.characterIndex - bidirectionalParagraphInfo.characterRun.characterIndex,
183                                  lineInfoRun.characterRunForSecondHalfLine.numberOfCharacters,
184                                  lineInfoRun.visualToLogicalMapSecondHalf);
185
186     // For those LTR lines inside a bidirectional paragraph.
187     // It will save to relayout the line after reordering.
188     for(unsigned int i = 0; i < numberOfCharacters; ++i)
189     {
190       if(i != *(lineInfoRun.visualToLogicalMap + i))
191       {
192         lineInfoRun.isIdentity = false;
193         break;
194       }
195     }
196
197     for(unsigned int i = 0; i < numberOfCharactersInSecondHalfLine; ++i)
198     {
199       if(i != *(lineInfoRun.visualToLogicalMapSecondHalf + i))
200       {
201         lineInfoRun.isIdentity = false;
202         break;
203       }
204     }
205   }
206
207   // Push the run into the vector.
208   lineInfoRuns.Insert(lineInfoRuns.Begin() + bidiLineIndex, lineInfoRun);
209 }
210
211 bool GetMirroredText(const Vector<Character>&                     text,
212                      const Vector<CharacterDirection>&            directions,
213                      const Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo,
214                      CharacterIndex                               startIndex,
215                      Length                                       numberOfCharacters,
216                      Vector<Character>&                           mirroredText)
217 {
218   bool hasTextMirrored = false;
219
220   // Handle to the bidirectional info module in text-abstraction.
221   TextAbstraction::BidirectionalSupport bidirectionalSupport = TextAbstraction::BidirectionalSupport::Get();
222
223   mirroredText = text;
224
225   Character*          mirroredTextBuffer = mirroredText.Begin();
226   CharacterDirection* directionsBuffer   = directions.Begin();
227
228   CharacterIndex       index         = startIndex;
229   const CharacterIndex lastCharacter = startIndex + numberOfCharacters;
230
231   // Traverse the paragraphs and mirror the right to left ones.
232   for(Vector<BidirectionalParagraphInfoRun>::ConstIterator it    = bidirectionalInfo.Begin(),
233                                                            endIt = bidirectionalInfo.End();
234       it != endIt;
235       ++it)
236   {
237     const BidirectionalParagraphInfoRun& paragraph = *it;
238
239     if(index >= paragraph.characterRun.characterIndex + paragraph.characterRun.numberOfCharacters)
240     {
241       // Skip the paragraph as it has already been processed.
242       continue;
243     }
244
245     if(lastCharacter <= paragraph.characterRun.characterIndex)
246     {
247       // Do not get mirror characters beyond startIndex + numberOfCharacters.
248       break;
249     }
250
251     index += paragraph.characterRun.numberOfCharacters;
252     const bool tmpMirrored = bidirectionalSupport.GetMirroredText(mirroredTextBuffer + paragraph.characterRun.characterIndex,
253                                                                   directionsBuffer + paragraph.characterRun.characterIndex,
254                                                                   paragraph.characterRun.numberOfCharacters);
255
256     hasTextMirrored = hasTextMirrored || tmpMirrored;
257   }
258
259   return hasTextMirrored;
260 }
261
262 void GetCharactersDirection(const Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo,
263                             Length                                       totalNumberOfCharacters,
264                             CharacterIndex                               startIndex,
265                             Length                                       numberOfCharacters,
266                             Vector<CharacterDirection>&                  directions)
267 {
268   // Handle to the bidirectional info module in text-abstraction.
269   TextAbstraction::BidirectionalSupport bidirectionalSupport = TextAbstraction::BidirectionalSupport::Get();
270
271   // Resize the vector.
272   directions.Resize(totalNumberOfCharacters);
273
274   // Whether the current buffer is being updated or is set from scratch.
275   const bool updateCurrentBuffer = numberOfCharacters < totalNumberOfCharacters;
276
277   CharacterDirection*        directionsBuffer = NULL;
278   Vector<CharacterDirection> newDirections;
279
280   if(updateCurrentBuffer)
281   {
282     newDirections.Resize(numberOfCharacters);
283     directionsBuffer = newDirections.Begin();
284   }
285   else
286   {
287     directionsBuffer = directions.Begin();
288   }
289
290   const CharacterIndex lastCharacter = startIndex + numberOfCharacters;
291   CharacterIndex       index         = startIndex;
292
293   for(Vector<BidirectionalParagraphInfoRun>::ConstIterator it    = bidirectionalInfo.Begin(),
294                                                            endIt = bidirectionalInfo.End();
295       it != endIt;
296       ++it)
297   {
298     const BidirectionalParagraphInfoRun& paragraph = *it;
299
300     if(index >= paragraph.characterRun.characterIndex + paragraph.characterRun.numberOfCharacters)
301     {
302       // Skip the paragraph as it has already been processed.
303       continue;
304     }
305
306     if(lastCharacter <= paragraph.characterRun.characterIndex)
307     {
308       // Do not get the character directions beyond startIndex + numberOfCharacters.
309       break;
310     }
311
312     // Set the directions of any previous left to right characters.
313     const Length numberOfLeftToRightCharacters = paragraph.characterRun.characterIndex - index;
314     if(numberOfLeftToRightCharacters > 0u)
315     {
316       memset(directionsBuffer + index - startIndex, false, numberOfLeftToRightCharacters * sizeof(bool));
317     }
318
319     // Set the directions of the bidirectional text.
320     bidirectionalSupport.GetCharactersDirection(paragraph.bidirectionalInfoIndex,
321                                                 directionsBuffer + paragraph.characterRun.characterIndex - startIndex,
322                                                 paragraph.characterRun.numberOfCharacters);
323
324     // Update the index.
325     index += paragraph.characterRun.numberOfCharacters + numberOfLeftToRightCharacters;
326   }
327
328   // Fills with left to right those paragraphs without right to left characters.
329   memset(directionsBuffer + index - startIndex, false, (lastCharacter - index) * sizeof(bool));
330
331   // If the direction info is updated, it needs to be inserted in the model.
332   if(updateCurrentBuffer)
333   {
334     // Insert the directions in the given buffer.
335     directions.Insert(directions.Begin() + startIndex,
336                       newDirections.Begin(),
337                       newDirections.End());
338     directions.Resize(totalNumberOfCharacters);
339   }
340 }
341
342 } // namespace Text
343
344 } // namespace Toolkit
345
346 } // namespace Dali