2 * Copyright (c) 2015 Samsung Electronics Co., Ltd.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 #include <dali-toolkit/internal/text/bidirectional-support.h>
23 #include <dali/devel-api/text-abstraction/bidirectional-support.h>
38 * @brief Get the lines of a paragraph.
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.
46 void GetLines( const BidirectionalParagraphInfoRun& paragraphInfo,
47 const Vector<LineRun>& lines,
48 unsigned int lineIndex,
49 unsigned int& firstLine,
50 unsigned int& numberOfLines )
52 firstLine = lineIndex;
55 const CharacterIndex lastCharacterIndex = paragraphInfo.characterRun.characterIndex + paragraphInfo.characterRun.numberOfCharacters;
56 bool firstLineFound = false;
58 for( Vector<LineRun>::ConstIterator it = lines.Begin() + lineIndex,
63 const LineRun& line = *it;
65 if( ( line.characterRun.characterIndex + line.characterRun.numberOfCharacters > paragraphInfo.characterRun.characterIndex ) &&
66 ( lastCharacterIndex > line.characterRun.characterIndex ) )
68 firstLineFound = true;
71 else if( lastCharacterIndex <= line.characterRun.characterIndex )
73 // nothing else to do.
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 )
93 // Find where to insert the new paragraphs.
94 BidirectionalRunIndex bidiInfoIndex = 0u;
95 for( Vector<BidirectionalParagraphInfoRun>::ConstIterator it = bidirectionalInfo.Begin(),
96 endIt = bidirectionalInfo.End();
100 const BidirectionalParagraphInfoRun& run = *it;
102 if( startIndex < run.characterRun.characterIndex + run.characterRun.numberOfCharacters )
104 // Found where to insert the bidi info.
110 // 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.
111 // From the bidirectional point of view, a paragraph is the piece of text between two LINE_MUST_BREAK.
113 // Index pointing the first character of the current paragraph.
114 CharacterIndex paragraphCharacterIndex = startIndex;
116 // Pointer to the text buffer.
117 const Character* textBuffer = text.Begin();
119 // Pointer to the line break info buffer.
120 const LineBreakInfo* lineBreakInfoBuffer = lineBreakInfo.Begin();
122 // Handle to the bidirectional info module in text-abstraction.
123 TextAbstraction::BidirectionalSupport bidirectionalSupport = TextAbstraction::BidirectionalSupport::Get();
125 const CharacterIndex lastCharacter = startIndex + numberOfCharacters;
127 bool hasRightToLeftScript = false;
129 for( Vector<ScriptRun>::ConstIterator it = scripts.Begin(),
130 endIt = scripts.End();
134 const ScriptRun& scriptRun = *it;
135 const CharacterIndex lastScriptRunIndex = scriptRun.characterRun.characterIndex + scriptRun.characterRun.numberOfCharacters - 1u;
137 if( startIndex > lastScriptRunIndex )
139 // Skip the run as it has already been processed.
143 if( lastCharacter <= scriptRun.characterRun.characterIndex )
145 // Do not get bidirectional info beyond startIndex + numberOfCharacters.
149 if( !hasRightToLeftScript && TextAbstraction::IsRightToLeftScript( scriptRun.script ) )
151 // The script is right to left.
152 hasRightToLeftScript = true;
155 if( TextAbstraction::LINE_MUST_BREAK == *( lineBreakInfoBuffer + lastScriptRunIndex ) )
157 // A new paragraph has been found.
159 if( hasRightToLeftScript )
161 // The Bidirectional run must have the same number of characters than the paragraph.
162 BidirectionalParagraphInfoRun bidirectionalRun;
163 bidirectionalRun.characterRun.characterIndex = paragraphCharacterIndex;
164 bidirectionalRun.characterRun.numberOfCharacters = ( lastScriptRunIndex - paragraphCharacterIndex ) + 1u; // The must break character is part of the paragrah.
166 // Create the bidirectional info for the whole paragraph and store the index to the table with this info in the run.
167 bidirectionalRun.bidirectionalInfoIndex = bidirectionalSupport.CreateInfo( textBuffer + bidirectionalRun.characterRun.characterIndex,
168 bidirectionalRun.characterRun.numberOfCharacters );
170 bidirectionalInfo.Insert( bidirectionalInfo.Begin() + bidiInfoIndex, bidirectionalRun );
174 // Point to the next paragraph.
175 paragraphCharacterIndex = lastScriptRunIndex + 1u;
177 // Reset whether there is a right to left script.
178 hasRightToLeftScript = false;
182 // Update indices of the bidi runs.
183 for( Vector<BidirectionalParagraphInfoRun>::Iterator it = bidirectionalInfo.Begin() + bidiInfoIndex,
184 endIt = bidirectionalInfo.End();
188 BidirectionalParagraphInfoRun& run = *it;
190 run.characterRun.characterIndex += numberOfCharacters;
194 void ReorderLines( const Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo,
195 CharacterIndex startIndex,
196 Length numberOfCharacters,
197 Vector<LineRun>& lineRuns,
198 Vector<BidirectionalLineInfoRun>& lineInfoRuns )
200 // Find where to insert the new paragraphs.
201 BidirectionalLineRunIndex bidiLineInfoIndex = 0u;
202 for( Vector<BidirectionalLineInfoRun>::ConstIterator it = lineInfoRuns.Begin(),
203 endIt = lineInfoRuns.End();
207 const BidirectionalLineInfoRun& run = *it;
209 if( startIndex < run.characterRun.characterIndex + run.characterRun.numberOfCharacters )
211 // Found where to insert the bidi line info.
217 // Handle to the bidirectional info module in text-abstraction.
218 TextAbstraction::BidirectionalSupport bidirectionalSupport = TextAbstraction::BidirectionalSupport::Get();
220 const CharacterIndex lastCharacter = startIndex + numberOfCharacters;
222 // Keep an index to the first line to be checked if it's contained inside the paragraph.
223 // Avoids check the lines from the beginning for each paragraph.
224 unsigned int lineIndex = 0u;
226 for( Vector<BidirectionalParagraphInfoRun>::ConstIterator it = bidirectionalInfo.Begin(),
227 endIt = bidirectionalInfo.End();
231 const BidirectionalParagraphInfoRun& paragraphInfo = *it;
233 if( paragraphInfo.characterRun.characterIndex < startIndex )
235 // Do not process, the paragraph has already been processed.
239 if( lastCharacter <= paragraphInfo.characterRun.characterIndex )
241 // Do not process paragraphs beyond startIndex + numberOfCharacters.
245 const CharacterDirection direction = bidirectionalSupport.GetParagraphDirection( paragraphInfo.bidirectionalInfoIndex );
247 // Get the lines for this paragraph.
248 unsigned int firstLine = 0u;
249 unsigned int numberOfLines = 0u;
251 // Get an index to the first line and the number of lines of the current paragraph.
252 GetLines( paragraphInfo,
258 lineIndex = firstLine + numberOfLines;
260 // Traverse the lines and reorder them
261 for( Vector<LineRun>::Iterator lineIt = lineRuns.Begin() + firstLine,
262 endLineIt = lineRuns.Begin() + firstLine + numberOfLines;
266 LineRun& line = *lineIt;
268 // Sets the paragraph's direction.
269 line.direction = direction;
271 // Creates a bidirectional info for the line run.
272 BidirectionalLineInfoRun lineInfoRun;
273 lineInfoRun.characterRun.characterIndex = line.characterRun.characterIndex;
274 lineInfoRun.characterRun.numberOfCharacters = line.characterRun.numberOfCharacters;
275 lineInfoRun.direction = direction;
277 // Allocate space for the conversion maps.
278 // The memory is freed after the visual to logical to visual conversion tables are built in the logical model.
279 lineInfoRun.visualToLogicalMap = reinterpret_cast<CharacterIndex*>( malloc( line.characterRun.numberOfCharacters * sizeof( CharacterIndex ) ) );
281 if( NULL != lineInfoRun.visualToLogicalMap )
283 // Reorders the line.
284 bidirectionalSupport.Reorder( paragraphInfo.bidirectionalInfoIndex,
285 line.characterRun.characterIndex - paragraphInfo.characterRun.characterIndex,
286 line.characterRun.numberOfCharacters,
287 lineInfoRun.visualToLogicalMap );
290 // Push the run into the vector.
291 lineInfoRuns.Insert( lineInfoRuns.Begin() + bidiLineInfoIndex, lineInfoRun );
296 // Update indices of the bidi runs.
297 for( Vector<BidirectionalLineInfoRun>::Iterator it = lineInfoRuns.Begin() + bidiLineInfoIndex,
298 endIt = lineInfoRuns.End();
302 BidirectionalLineInfoRun& run = *it;
304 run.characterRun.characterIndex += numberOfCharacters;
308 bool GetMirroredText( const Vector<Character>& text,
309 const Vector<CharacterDirection>& directions,
310 const Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo,
311 CharacterIndex startIndex,
312 Length numberOfCharacters,
313 Vector<Character>& mirroredText )
315 bool hasTextMirrored = false;
317 // Handle to the bidirectional info module in text-abstraction.
318 TextAbstraction::BidirectionalSupport bidirectionalSupport = TextAbstraction::BidirectionalSupport::Get();
322 Character* mirroredTextBuffer = mirroredText.Begin();
323 CharacterDirection* directionsBuffer = directions.Begin();
325 CharacterIndex index = startIndex;
326 const CharacterIndex lastCharacter = startIndex + numberOfCharacters;
328 // Traverse the paragraphs and mirror the right to left ones.
329 for( Vector<BidirectionalParagraphInfoRun>::ConstIterator it = bidirectionalInfo.Begin(),
330 endIt = bidirectionalInfo.End();
334 const BidirectionalParagraphInfoRun& paragraph = *it;
336 if( index >= paragraph.characterRun.characterIndex + paragraph.characterRun.numberOfCharacters )
338 // Skip the paragraph as it has already been processed.
342 if( lastCharacter <= paragraph.characterRun.characterIndex )
344 // Do not get mirror characters beyond startIndex + numberOfCharacters.
348 index += paragraph.characterRun.numberOfCharacters;
349 const bool tmpMirrored = bidirectionalSupport.GetMirroredText( mirroredTextBuffer + paragraph.characterRun.characterIndex,
350 directionsBuffer + paragraph.characterRun.characterIndex,
351 paragraph.characterRun.numberOfCharacters );
353 hasTextMirrored = hasTextMirrored || tmpMirrored;
356 return hasTextMirrored;
359 void GetCharactersDirection( const Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo,
360 Length totalNumberOfCharacters,
361 CharacterIndex startIndex,
362 Length numberOfCharacters,
363 Vector<CharacterDirection>& directions )
365 // Handle to the bidirectional info module in text-abstraction.
366 TextAbstraction::BidirectionalSupport bidirectionalSupport = TextAbstraction::BidirectionalSupport::Get();
368 // Resize the vector.
369 directions.Resize( totalNumberOfCharacters );
371 // Whether the current buffer is being updated or is set from scratch.
372 const bool updateCurrentBuffer = numberOfCharacters < totalNumberOfCharacters;
374 CharacterDirection* directionsBuffer = NULL;
375 Vector<CharacterDirection> newDirections;
377 if( updateCurrentBuffer )
379 newDirections.Resize( numberOfCharacters );
380 directionsBuffer = newDirections.Begin();
384 directionsBuffer = directions.Begin();
387 const CharacterIndex lastCharacter = startIndex + numberOfCharacters;
388 CharacterIndex index = startIndex;
390 for( Vector<BidirectionalParagraphInfoRun>::ConstIterator it = bidirectionalInfo.Begin(),
391 endIt = bidirectionalInfo.End();
395 const BidirectionalParagraphInfoRun& paragraph = *it;
397 if( index >= paragraph.characterRun.characterIndex + paragraph.characterRun.numberOfCharacters )
399 // Skip the paragraph as it has already been processed.
403 if( lastCharacter <= paragraph.characterRun.characterIndex )
405 // Do not get the character directions beyond startIndex + numberOfCharacters.
409 // Set the directions of any previous left to right characters.
410 const Length numberOfLeftToRightCharacters = paragraph.characterRun.characterIndex - index;
411 if( numberOfLeftToRightCharacters > 0u )
413 memset( directionsBuffer + index - startIndex, false, numberOfLeftToRightCharacters * sizeof( bool ) );
416 // Set the directions of the bidirectional text.
417 bidirectionalSupport.GetCharactersDirection( paragraph.bidirectionalInfoIndex,
418 directionsBuffer + paragraph.characterRun.characterIndex - startIndex,
419 paragraph.characterRun.numberOfCharacters );
422 index += paragraph.characterRun.numberOfCharacters + numberOfLeftToRightCharacters;
425 // Fills with left to right those paragraphs without right to left characters.
426 memset( directionsBuffer + index - startIndex, false, ( lastCharacter - index ) * sizeof( bool ) );
428 // If the direction info is updated, it needs to be inserted in the model.
429 if( updateCurrentBuffer )
431 // Insert the directions in the given buffer.
432 directions.Insert( directions.Begin() + startIndex,
433 newDirections.Begin(),
434 newDirections.End() );
435 directions.Resize( totalNumberOfCharacters );
441 } // namespace Toolkit