[dali_1.3.54] Merge branch 'devel/master'
[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 <memory.h>
23 #include <dali/devel-api/text-abstraction/bidirectional-support.h>
24
25 namespace Dali
26 {
27
28 namespace Toolkit
29 {
30
31 namespace Text
32 {
33
34 namespace
35 {
36
37 /**
38  * @brief Get the lines of a paragraph.
39  *
40  * @param[in] paragraphInfo The paragraph.
41  * @param[in] lines The lines.
42  * @param[in] lineIndex Index pointing the first line to be checked.
43  * @param[out] firstLine Index to the first line of the paragraph.
44  * @param[out] numberOfLines The number of lines.
45  */
46 void GetLines( const BidirectionalParagraphInfoRun& paragraphInfo,
47                const Vector<LineRun>& lines,
48                unsigned int lineIndex,
49                unsigned int& firstLine,
50                unsigned int& numberOfLines )
51 {
52   firstLine = lineIndex;
53   numberOfLines = 0u;
54
55   const CharacterIndex lastCharacterIndex = paragraphInfo.characterRun.characterIndex + paragraphInfo.characterRun.numberOfCharacters;
56   bool firstLineFound = false;
57
58   for( Vector<LineRun>::ConstIterator it = lines.Begin() + lineIndex,
59          endIt = lines.End();
60        it != endIt;
61        ++it )
62   {
63     const LineRun& line = *it;
64
65     if( ( line.characterRun.characterIndex + line.characterRun.numberOfCharacters > paragraphInfo.characterRun.characterIndex ) &&
66         ( lastCharacterIndex > line.characterRun.characterIndex ) )
67     {
68       firstLineFound = true;
69       ++numberOfLines;
70     }
71     else if( lastCharacterIndex <= line.characterRun.characterIndex )
72     {
73       // nothing else to do.
74       break;
75     }
76
77     if( !firstLineFound )
78     {
79       ++firstLine;
80     }
81   }
82 }
83
84 } // namespace
85
86 void SetBidirectionalInfo( const Vector<Character>& text,
87                            const Vector<ScriptRun>& scripts,
88                            const Vector<LineBreakInfo>& lineBreakInfo,
89                            CharacterIndex startIndex,
90                            Length numberOfCharacters,
91                            Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo,
92                            bool matchSystemLanguageDirection,
93                            Dali::LayoutDirection::Type layoutDirection )
94 {
95   // Find where to insert the new paragraphs.
96   BidirectionalRunIndex bidiInfoIndex = 0u;
97   for( Vector<BidirectionalParagraphInfoRun>::ConstIterator it = bidirectionalInfo.Begin(),
98          endIt = bidirectionalInfo.End();
99        it != endIt;
100        ++it )
101   {
102     const BidirectionalParagraphInfoRun& run = *it;
103
104     if( startIndex < run.characterRun.characterIndex + run.characterRun.numberOfCharacters )
105     {
106       // Found where to insert the bidi info.
107       break;
108     }
109     ++bidiInfoIndex;
110   }
111
112   // 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.
113   // From the bidirectional point of view, a paragraph is the piece of text between two LINE_MUST_BREAK.
114
115   // Index pointing the first character of the current paragraph.
116   CharacterIndex paragraphCharacterIndex = startIndex;
117
118   // Pointer to the text buffer.
119   const Character* textBuffer = text.Begin();
120
121   // Pointer to the line break info buffer.
122   const LineBreakInfo* lineBreakInfoBuffer = lineBreakInfo.Begin();
123
124   // Handle to the bidirectional info module in text-abstraction.
125   TextAbstraction::BidirectionalSupport bidirectionalSupport = TextAbstraction::BidirectionalSupport::Get();
126
127   const CharacterIndex lastCharacter = startIndex + numberOfCharacters;
128
129   bool hasRightToLeftScript = false;
130
131   for( Vector<ScriptRun>::ConstIterator it = scripts.Begin(),
132          endIt = scripts.End();
133        it != endIt;
134        ++it )
135   {
136     const ScriptRun& scriptRun = *it;
137     const CharacterIndex lastScriptRunIndex = scriptRun.characterRun.characterIndex + scriptRun.characterRun.numberOfCharacters - 1u;
138
139     if( startIndex > lastScriptRunIndex )
140     {
141       // Skip the run as it has already been processed.
142       continue;
143     }
144
145     if( lastCharacter <= scriptRun.characterRun.characterIndex )
146     {
147       // Do not get bidirectional info beyond startIndex + numberOfCharacters.
148       break;
149     }
150
151     if( !hasRightToLeftScript && TextAbstraction::IsRightToLeftScript( scriptRun.script ) )
152     {
153       // The script is right to left.
154       hasRightToLeftScript = true;
155     }
156
157     if( TextAbstraction::LINE_MUST_BREAK == *( lineBreakInfoBuffer + lastScriptRunIndex ) )
158     {
159       // A new paragraph has been found.
160
161       if( hasRightToLeftScript )
162       {
163         // The Bidirectional run must have the same number of characters than the paragraph.
164         BidirectionalParagraphInfoRun bidirectionalRun;
165         bidirectionalRun.characterRun.characterIndex = paragraphCharacterIndex;
166         bidirectionalRun.characterRun.numberOfCharacters = ( lastScriptRunIndex - paragraphCharacterIndex ) + 1u; // The must break character is part of the paragrah.
167
168         // Create the bidirectional info for the whole paragraph and store the index to the table with this info in the run.
169         bidirectionalRun.bidirectionalInfoIndex = bidirectionalSupport.CreateInfo( textBuffer + bidirectionalRun.characterRun.characterIndex,
170                                                                                    bidirectionalRun.characterRun.numberOfCharacters,
171                                                                                    matchSystemLanguageDirection,
172                                                                                    layoutDirection );
173
174         bidirectionalInfo.Insert( bidirectionalInfo.Begin() + bidiInfoIndex, bidirectionalRun );
175         ++bidiInfoIndex;
176       }
177
178       // Point to the next paragraph.
179       paragraphCharacterIndex = lastScriptRunIndex + 1u;
180
181       // Reset whether there is a right to left script.
182       hasRightToLeftScript = false;
183     }
184   }
185
186   // Update indices of the bidi runs.
187   for( Vector<BidirectionalParagraphInfoRun>::Iterator it = bidirectionalInfo.Begin() + bidiInfoIndex,
188          endIt = bidirectionalInfo.End();
189        it != endIt;
190        ++it )
191   {
192     BidirectionalParagraphInfoRun& run = *it;
193
194     run.characterRun.characterIndex += numberOfCharacters;
195   }
196 }
197
198 void ReorderLines( const Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo,
199                    CharacterIndex startIndex,
200                    Length numberOfCharacters,
201                    Vector<LineRun>& lineRuns,
202                    Vector<BidirectionalLineInfoRun>& lineInfoRuns )
203 {
204   // Find where to insert the new paragraphs.
205   BidirectionalLineRunIndex bidiLineInfoIndex = 0u;
206   for( Vector<BidirectionalLineInfoRun>::ConstIterator it = lineInfoRuns.Begin(),
207          endIt = lineInfoRuns.End();
208        it != endIt;
209        ++it )
210   {
211     const BidirectionalLineInfoRun& run = *it;
212
213     if( startIndex < run.characterRun.characterIndex + run.characterRun.numberOfCharacters )
214     {
215       // Found where to insert the bidi line info.
216       break;
217     }
218     ++bidiLineInfoIndex;
219   }
220
221   // Handle to the bidirectional info module in text-abstraction.
222   TextAbstraction::BidirectionalSupport bidirectionalSupport = TextAbstraction::BidirectionalSupport::Get();
223
224   const CharacterIndex lastCharacter = startIndex + numberOfCharacters;
225
226   // Keep an index to the first line to be checked if it's contained inside the paragraph.
227   // Avoids check the lines from the beginning for each paragraph.
228   unsigned int lineIndex = 0u;
229
230   for( Vector<BidirectionalParagraphInfoRun>::ConstIterator it = bidirectionalInfo.Begin(),
231          endIt = bidirectionalInfo.End();
232        it != endIt;
233        ++it )
234   {
235     const BidirectionalParagraphInfoRun& paragraphInfo = *it;
236
237     if( paragraphInfo.characterRun.characterIndex < startIndex )
238     {
239       // Do not process, the paragraph has already been processed.
240       continue;
241     }
242
243     if( lastCharacter <= paragraphInfo.characterRun.characterIndex )
244     {
245       // Do not process paragraphs beyond startIndex + numberOfCharacters.
246       break;
247     }
248
249     const CharacterDirection direction = bidirectionalSupport.GetParagraphDirection( paragraphInfo.bidirectionalInfoIndex );
250
251     // Get the lines for this paragraph.
252     unsigned int firstLine = 0u;
253     unsigned int numberOfLines = 0u;
254
255     // Get an index to the first line and the number of lines of the current paragraph.
256     GetLines( paragraphInfo,
257               lineRuns,
258               lineIndex,
259               firstLine,
260               numberOfLines );
261
262     lineIndex = firstLine + numberOfLines;
263
264     // Traverse the lines and reorder them
265     for( Vector<LineRun>::Iterator lineIt = lineRuns.Begin() + firstLine,
266            endLineIt = lineRuns.Begin() + firstLine + numberOfLines;
267            lineIt != endLineIt;
268          ++lineIt )
269     {
270       LineRun& line = *lineIt;
271
272       // Sets the paragraph's direction.
273       line.direction = direction;
274
275       // Creates a bidirectional info for the line run.
276       BidirectionalLineInfoRun lineInfoRun;
277       lineInfoRun.characterRun.characterIndex = line.characterRun.characterIndex;
278       lineInfoRun.characterRun.numberOfCharacters = line.characterRun.numberOfCharacters;
279       lineInfoRun.direction = direction;
280
281       // Allocate space for the conversion maps.
282       // The memory is freed after the visual to logical to visual conversion tables are built in the logical model.
283       lineInfoRun.visualToLogicalMap = reinterpret_cast<CharacterIndex*>( malloc( line.characterRun.numberOfCharacters * sizeof( CharacterIndex ) ) );
284
285       if( NULL != lineInfoRun.visualToLogicalMap )
286       {
287         // Reorders the line.
288         bidirectionalSupport.Reorder( paragraphInfo.bidirectionalInfoIndex,
289                                       line.characterRun.characterIndex - paragraphInfo.characterRun.characterIndex,
290                                       line.characterRun.numberOfCharacters,
291                                       lineInfoRun.visualToLogicalMap );
292       }
293
294       // Push the run into the vector.
295       lineInfoRuns.Insert( lineInfoRuns.Begin() + bidiLineInfoIndex, lineInfoRun );
296       ++bidiLineInfoIndex;
297     }
298   }
299
300   // Update indices of the bidi runs.
301   for( Vector<BidirectionalLineInfoRun>::Iterator it = lineInfoRuns.Begin() + bidiLineInfoIndex,
302          endIt = lineInfoRuns.End();
303        it != endIt;
304        ++it )
305   {
306     BidirectionalLineInfoRun& run = *it;
307
308     run.characterRun.characterIndex += numberOfCharacters;
309   }
310 }
311
312 bool GetMirroredText( const Vector<Character>& text,
313                       const Vector<CharacterDirection>& directions,
314                       const Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo,
315                       CharacterIndex startIndex,
316                       Length numberOfCharacters,
317                       Vector<Character>& mirroredText )
318 {
319   bool hasTextMirrored = false;
320
321   // Handle to the bidirectional info module in text-abstraction.
322   TextAbstraction::BidirectionalSupport bidirectionalSupport = TextAbstraction::BidirectionalSupport::Get();
323
324   mirroredText = text;
325
326   Character* mirroredTextBuffer = mirroredText.Begin();
327   CharacterDirection* directionsBuffer = directions.Begin();
328
329   CharacterIndex index = startIndex;
330   const CharacterIndex lastCharacter = startIndex + numberOfCharacters;
331
332   // Traverse the paragraphs and mirror the right to left ones.
333   for( Vector<BidirectionalParagraphInfoRun>::ConstIterator it = bidirectionalInfo.Begin(),
334          endIt = bidirectionalInfo.End();
335        it != endIt;
336        ++it )
337   {
338     const BidirectionalParagraphInfoRun& paragraph = *it;
339
340     if( index >= paragraph.characterRun.characterIndex + paragraph.characterRun.numberOfCharacters )
341     {
342       // Skip the paragraph as it has already been processed.
343       continue;
344     }
345
346     if( lastCharacter <= paragraph.characterRun.characterIndex )
347     {
348       // Do not get mirror characters beyond startIndex + numberOfCharacters.
349       break;
350     }
351
352     index += paragraph.characterRun.numberOfCharacters;
353     const bool tmpMirrored = bidirectionalSupport.GetMirroredText( mirroredTextBuffer + paragraph.characterRun.characterIndex,
354                                                                    directionsBuffer + paragraph.characterRun.characterIndex,
355                                                                    paragraph.characterRun.numberOfCharacters );
356
357     hasTextMirrored = hasTextMirrored || tmpMirrored;
358   }
359
360   return hasTextMirrored;
361 }
362
363 void GetCharactersDirection( const Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo,
364                              Length totalNumberOfCharacters,
365                              CharacterIndex startIndex,
366                              Length numberOfCharacters,
367                              Vector<CharacterDirection>& directions )
368 {
369   // Handle to the bidirectional info module in text-abstraction.
370   TextAbstraction::BidirectionalSupport bidirectionalSupport = TextAbstraction::BidirectionalSupport::Get();
371
372   // Resize the vector.
373   directions.Resize( totalNumberOfCharacters );
374
375   // Whether the current buffer is being updated or is set from scratch.
376   const bool updateCurrentBuffer = numberOfCharacters < totalNumberOfCharacters;
377
378   CharacterDirection* directionsBuffer = NULL;
379   Vector<CharacterDirection> newDirections;
380
381   if( updateCurrentBuffer )
382   {
383     newDirections.Resize( numberOfCharacters );
384     directionsBuffer = newDirections.Begin();
385   }
386   else
387   {
388     directionsBuffer = directions.Begin();
389   }
390
391   const CharacterIndex lastCharacter = startIndex + numberOfCharacters;
392   CharacterIndex index = startIndex;
393
394   for( Vector<BidirectionalParagraphInfoRun>::ConstIterator it = bidirectionalInfo.Begin(),
395          endIt = bidirectionalInfo.End();
396        it != endIt;
397        ++it )
398   {
399     const BidirectionalParagraphInfoRun& paragraph = *it;
400
401     if( index >= paragraph.characterRun.characterIndex + paragraph.characterRun.numberOfCharacters )
402     {
403       // Skip the paragraph as it has already been processed.
404       continue;
405     }
406
407     if( lastCharacter <= paragraph.characterRun.characterIndex )
408     {
409       // Do not get the character directions beyond startIndex + numberOfCharacters.
410       break;
411     }
412
413     // Set the directions of any previous left to right characters.
414     const Length numberOfLeftToRightCharacters = paragraph.characterRun.characterIndex - index;
415     if( numberOfLeftToRightCharacters > 0u )
416     {
417       memset( directionsBuffer + index - startIndex, false, numberOfLeftToRightCharacters * sizeof( bool ) );
418     }
419
420     // Set the directions of the bidirectional text.
421     bidirectionalSupport.GetCharactersDirection( paragraph.bidirectionalInfoIndex,
422                                                  directionsBuffer + paragraph.characterRun.characterIndex - startIndex,
423                                                  paragraph.characterRun.numberOfCharacters );
424
425     // Update the index.
426     index += paragraph.characterRun.numberOfCharacters + numberOfLeftToRightCharacters;
427   }
428
429   // Fills with left to right those paragraphs without right to left characters.
430   memset( directionsBuffer + index - startIndex, false, ( lastCharacter - index ) * sizeof( bool ) );
431
432   // If the direction info is updated, it needs to be inserted in the model.
433   if( updateCurrentBuffer )
434   {
435     // Insert the directions in the given buffer.
436     directions.Insert( directions.Begin() + startIndex,
437                        newDirections.Begin(),
438                        newDirections.End() );
439     directions.Resize( totalNumberOfCharacters );
440   }
441 }
442
443 } // namespace Text
444
445 } // namespace Toolkit
446
447 } // namespace Dali