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,
92 bool matchSystemLanguageDirection,
93 Dali::LayoutDirection::Type layoutDirection )
95 // Find where to insert the new paragraphs.
96 BidirectionalRunIndex bidiInfoIndex = 0u;
97 for( Vector<BidirectionalParagraphInfoRun>::ConstIterator it = bidirectionalInfo.Begin(),
98 endIt = bidirectionalInfo.End();
102 const BidirectionalParagraphInfoRun& run = *it;
104 if( startIndex < run.characterRun.characterIndex + run.characterRun.numberOfCharacters )
106 // Found where to insert the bidi info.
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.
115 // Index pointing the first character of the current paragraph.
116 CharacterIndex paragraphCharacterIndex = startIndex;
118 // Pointer to the text buffer.
119 const Character* textBuffer = text.Begin();
121 // Pointer to the line break info buffer.
122 const LineBreakInfo* lineBreakInfoBuffer = lineBreakInfo.Begin();
124 // Handle to the bidirectional info module in text-abstraction.
125 TextAbstraction::BidirectionalSupport bidirectionalSupport = TextAbstraction::BidirectionalSupport::Get();
127 const CharacterIndex lastCharacter = startIndex + numberOfCharacters;
129 bool hasRightToLeftScript = false;
131 for( Vector<ScriptRun>::ConstIterator it = scripts.Begin(),
132 endIt = scripts.End();
136 const ScriptRun& scriptRun = *it;
137 const CharacterIndex lastScriptRunIndex = scriptRun.characterRun.characterIndex + scriptRun.characterRun.numberOfCharacters - 1u;
139 if( startIndex > lastScriptRunIndex )
141 // Skip the run as it has already been processed.
145 if( lastCharacter <= scriptRun.characterRun.characterIndex )
147 // Do not get bidirectional info beyond startIndex + numberOfCharacters.
151 if( !hasRightToLeftScript && TextAbstraction::IsRightToLeftScript( scriptRun.script ) )
153 // The script is right to left.
154 hasRightToLeftScript = true;
157 if( TextAbstraction::LINE_MUST_BREAK == *( lineBreakInfoBuffer + lastScriptRunIndex ) )
159 // A new paragraph has been found.
161 if( hasRightToLeftScript )
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.
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,
174 bidirectionalInfo.Insert( bidirectionalInfo.Begin() + bidiInfoIndex, bidirectionalRun );
178 // Point to the next paragraph.
179 paragraphCharacterIndex = lastScriptRunIndex + 1u;
181 // Reset whether there is a right to left script.
182 hasRightToLeftScript = false;
186 // Update indices of the bidi runs.
187 for( Vector<BidirectionalParagraphInfoRun>::Iterator it = bidirectionalInfo.Begin() + bidiInfoIndex,
188 endIt = bidirectionalInfo.End();
192 BidirectionalParagraphInfoRun& run = *it;
194 run.characterRun.characterIndex += numberOfCharacters;
198 void ReorderLines( const Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo,
199 CharacterIndex startIndex,
200 Length numberOfCharacters,
201 Vector<LineRun>& lineRuns,
202 Vector<BidirectionalLineInfoRun>& lineInfoRuns )
204 // Find where to insert the new paragraphs.
205 BidirectionalLineRunIndex bidiLineInfoIndex = 0u;
206 for( Vector<BidirectionalLineInfoRun>::ConstIterator it = lineInfoRuns.Begin(),
207 endIt = lineInfoRuns.End();
211 const BidirectionalLineInfoRun& run = *it;
213 if( startIndex < run.characterRun.characterIndex + run.characterRun.numberOfCharacters )
215 // Found where to insert the bidi line info.
221 // Handle to the bidirectional info module in text-abstraction.
222 TextAbstraction::BidirectionalSupport bidirectionalSupport = TextAbstraction::BidirectionalSupport::Get();
224 const CharacterIndex lastCharacter = startIndex + numberOfCharacters;
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;
230 for( Vector<BidirectionalParagraphInfoRun>::ConstIterator it = bidirectionalInfo.Begin(),
231 endIt = bidirectionalInfo.End();
235 const BidirectionalParagraphInfoRun& paragraphInfo = *it;
237 if( paragraphInfo.characterRun.characterIndex < startIndex )
239 // Do not process, the paragraph has already been processed.
243 if( lastCharacter <= paragraphInfo.characterRun.characterIndex )
245 // Do not process paragraphs beyond startIndex + numberOfCharacters.
249 const CharacterDirection direction = bidirectionalSupport.GetParagraphDirection( paragraphInfo.bidirectionalInfoIndex );
251 // Get the lines for this paragraph.
252 unsigned int firstLine = 0u;
253 unsigned int numberOfLines = 0u;
255 // Get an index to the first line and the number of lines of the current paragraph.
256 GetLines( paragraphInfo,
262 lineIndex = firstLine + numberOfLines;
264 // Traverse the lines and reorder them
265 for( Vector<LineRun>::Iterator lineIt = lineRuns.Begin() + firstLine,
266 endLineIt = lineRuns.Begin() + firstLine + numberOfLines;
270 LineRun& line = *lineIt;
272 // Sets the paragraph's direction.
273 line.direction = direction;
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;
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 ) ) );
285 if( NULL != lineInfoRun.visualToLogicalMap )
287 // Reorders the line.
288 bidirectionalSupport.Reorder( paragraphInfo.bidirectionalInfoIndex,
289 line.characterRun.characterIndex - paragraphInfo.characterRun.characterIndex,
290 line.characterRun.numberOfCharacters,
291 lineInfoRun.visualToLogicalMap );
294 // Push the run into the vector.
295 lineInfoRuns.Insert( lineInfoRuns.Begin() + bidiLineInfoIndex, lineInfoRun );
300 // Update indices of the bidi runs.
301 for( Vector<BidirectionalLineInfoRun>::Iterator it = lineInfoRuns.Begin() + bidiLineInfoIndex,
302 endIt = lineInfoRuns.End();
306 BidirectionalLineInfoRun& run = *it;
308 run.characterRun.characterIndex += numberOfCharacters;
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 )
319 bool hasTextMirrored = false;
321 // Handle to the bidirectional info module in text-abstraction.
322 TextAbstraction::BidirectionalSupport bidirectionalSupport = TextAbstraction::BidirectionalSupport::Get();
326 Character* mirroredTextBuffer = mirroredText.Begin();
327 CharacterDirection* directionsBuffer = directions.Begin();
329 CharacterIndex index = startIndex;
330 const CharacterIndex lastCharacter = startIndex + numberOfCharacters;
332 // Traverse the paragraphs and mirror the right to left ones.
333 for( Vector<BidirectionalParagraphInfoRun>::ConstIterator it = bidirectionalInfo.Begin(),
334 endIt = bidirectionalInfo.End();
338 const BidirectionalParagraphInfoRun& paragraph = *it;
340 if( index >= paragraph.characterRun.characterIndex + paragraph.characterRun.numberOfCharacters )
342 // Skip the paragraph as it has already been processed.
346 if( lastCharacter <= paragraph.characterRun.characterIndex )
348 // Do not get mirror characters beyond startIndex + numberOfCharacters.
352 index += paragraph.characterRun.numberOfCharacters;
353 const bool tmpMirrored = bidirectionalSupport.GetMirroredText( mirroredTextBuffer + paragraph.characterRun.characterIndex,
354 directionsBuffer + paragraph.characterRun.characterIndex,
355 paragraph.characterRun.numberOfCharacters );
357 hasTextMirrored = hasTextMirrored || tmpMirrored;
360 return hasTextMirrored;
363 void GetCharactersDirection( const Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo,
364 Length totalNumberOfCharacters,
365 CharacterIndex startIndex,
366 Length numberOfCharacters,
367 Vector<CharacterDirection>& directions )
369 // Handle to the bidirectional info module in text-abstraction.
370 TextAbstraction::BidirectionalSupport bidirectionalSupport = TextAbstraction::BidirectionalSupport::Get();
372 // Resize the vector.
373 directions.Resize( totalNumberOfCharacters );
375 // Whether the current buffer is being updated or is set from scratch.
376 const bool updateCurrentBuffer = numberOfCharacters < totalNumberOfCharacters;
378 CharacterDirection* directionsBuffer = NULL;
379 Vector<CharacterDirection> newDirections;
381 if( updateCurrentBuffer )
383 newDirections.Resize( numberOfCharacters );
384 directionsBuffer = newDirections.Begin();
388 directionsBuffer = directions.Begin();
391 const CharacterIndex lastCharacter = startIndex + numberOfCharacters;
392 CharacterIndex index = startIndex;
394 for( Vector<BidirectionalParagraphInfoRun>::ConstIterator it = bidirectionalInfo.Begin(),
395 endIt = bidirectionalInfo.End();
399 const BidirectionalParagraphInfoRun& paragraph = *it;
401 if( index >= paragraph.characterRun.characterIndex + paragraph.characterRun.numberOfCharacters )
403 // Skip the paragraph as it has already been processed.
407 if( lastCharacter <= paragraph.characterRun.characterIndex )
409 // Do not get the character directions beyond startIndex + numberOfCharacters.
413 // Set the directions of any previous left to right characters.
414 const Length numberOfLeftToRightCharacters = paragraph.characterRun.characterIndex - index;
415 if( numberOfLeftToRightCharacters > 0u )
417 memset( directionsBuffer + index - startIndex, false, numberOfLeftToRightCharacters * sizeof( bool ) );
420 // Set the directions of the bidirectional text.
421 bidirectionalSupport.GetCharactersDirection( paragraph.bidirectionalInfoIndex,
422 directionsBuffer + paragraph.characterRun.characterIndex - startIndex,
423 paragraph.characterRun.numberOfCharacters );
426 index += paragraph.characterRun.numberOfCharacters + numberOfLeftToRightCharacters;
429 // Fills with left to right those paragraphs without right to left characters.
430 memset( directionsBuffer + index - startIndex, false, ( lastCharacter - index ) * sizeof( bool ) );
432 // If the direction info is updated, it needs to be inserted in the model.
433 if( updateCurrentBuffer )
435 // Insert the directions in the given buffer.
436 directions.Insert( directions.Begin() + startIndex,
437 newDirections.Begin(),
438 newDirections.End() );
439 directions.Resize( totalNumberOfCharacters );
445 } // namespace Toolkit