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>
34 void SetBidirectionalInfo( const Vector<Character>& text,
35 const Vector<ScriptRun>& scripts,
36 const Vector<LineBreakInfo>& lineBreakInfo,
37 CharacterIndex startIndex,
38 Length numberOfCharacters,
39 Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo,
40 bool matchSystemLanguageDirection,
41 Dali::LayoutDirection::Type layoutDirection )
43 // Find where to insert the new paragraphs.
44 BidirectionalRunIndex bidiInfoIndex = 0u;
45 for( Vector<BidirectionalParagraphInfoRun>::ConstIterator it = bidirectionalInfo.Begin(),
46 endIt = bidirectionalInfo.End();
50 const BidirectionalParagraphInfoRun& run = *it;
52 if( startIndex < run.characterRun.characterIndex + run.characterRun.numberOfCharacters )
54 // Found where to insert the bidi info.
60 // 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.
61 // From the bidirectional point of view, a paragraph is the piece of text between two LINE_MUST_BREAK.
63 // Index pointing the first character of the current paragraph.
64 CharacterIndex paragraphCharacterIndex = startIndex;
66 // Pointer to the text buffer.
67 const Character* textBuffer = text.Begin();
69 // Pointer to the line break info buffer.
70 const LineBreakInfo* lineBreakInfoBuffer = lineBreakInfo.Begin();
72 // Handle to the bidirectional info module in text-abstraction.
73 TextAbstraction::BidirectionalSupport bidirectionalSupport = TextAbstraction::BidirectionalSupport::Get();
75 const CharacterIndex lastCharacter = startIndex + numberOfCharacters;
77 bool hasRightToLeftScript = false;
79 for( Vector<ScriptRun>::ConstIterator it = scripts.Begin(),
80 endIt = scripts.End();
84 const ScriptRun& scriptRun = *it;
85 const CharacterIndex lastScriptRunIndex = scriptRun.characterRun.characterIndex + scriptRun.characterRun.numberOfCharacters - 1u;
87 if( startIndex > lastScriptRunIndex )
89 // Skip the run as it has already been processed.
93 if( lastCharacter <= scriptRun.characterRun.characterIndex )
95 // Do not get bidirectional info beyond startIndex + numberOfCharacters.
99 if( !hasRightToLeftScript && scriptRun.isRightToLeft )
101 // The script is right to left.
102 hasRightToLeftScript = true;
105 if( TextAbstraction::LINE_MUST_BREAK == *( lineBreakInfoBuffer + lastScriptRunIndex ) )
107 // A new paragraph has been found.
109 if( hasRightToLeftScript )
111 // The Bidirectional run must have the same number of characters than the paragraph.
112 BidirectionalParagraphInfoRun bidirectionalRun;
113 bidirectionalRun.characterRun.characterIndex = paragraphCharacterIndex;
114 bidirectionalRun.characterRun.numberOfCharacters = ( lastScriptRunIndex - paragraphCharacterIndex ) + 1u; // The must break character is part of the paragrah.
116 // Create the bidirectional info for the whole paragraph and store the index to the table with this info in the run.
117 bidirectionalRun.bidirectionalInfoIndex = bidirectionalSupport.CreateInfo( textBuffer + bidirectionalRun.characterRun.characterIndex,
118 bidirectionalRun.characterRun.numberOfCharacters,
119 matchSystemLanguageDirection,
122 bidirectionalRun.direction = bidirectionalSupport.GetParagraphDirection( bidirectionalRun.bidirectionalInfoIndex );
124 bidirectionalInfo.Insert( bidirectionalInfo.Begin() + bidiInfoIndex, bidirectionalRun );
128 // Point to the next paragraph.
129 paragraphCharacterIndex = lastScriptRunIndex + 1u;
131 // Reset whether there is a right to left script.
132 hasRightToLeftScript = false;
136 // Update indices of the bidi runs.
137 for( Vector<BidirectionalParagraphInfoRun>::Iterator it = bidirectionalInfo.Begin() + bidiInfoIndex,
138 endIt = bidirectionalInfo.End();
142 BidirectionalParagraphInfoRun& run = *it;
144 run.characterRun.characterIndex += numberOfCharacters;
148 void ReorderLine( const BidirectionalParagraphInfoRun& bidirectionalParagraphInfo,
149 Vector<BidirectionalLineInfoRun>& lineInfoRuns,
150 BidirectionalLineRunIndex bidiLineIndex,
151 CharacterIndex startIndex,
152 Length numberOfCharacters,
153 CharacterDirection direction )
155 // Handle to the bidirectional info module in text-abstraction.
156 TextAbstraction::BidirectionalSupport bidirectionalSupport = TextAbstraction::BidirectionalSupport::Get();
158 // Creates a bidirectional info for the line run.
159 BidirectionalLineInfoRun lineInfoRun;
160 lineInfoRun.characterRun.characterIndex = startIndex;
161 lineInfoRun.characterRun.numberOfCharacters = numberOfCharacters;
162 lineInfoRun.direction = direction;
163 lineInfoRun.isIdentity = true;
165 // Allocate space for the conversion maps.
166 // The memory is freed after the visual to logical to visual conversion tables are built in the logical model.
167 lineInfoRun.visualToLogicalMap = reinterpret_cast<CharacterIndex*>( malloc( numberOfCharacters * sizeof( CharacterIndex ) ) );
169 if( nullptr != lineInfoRun.visualToLogicalMap )
171 // Reorders the line.
172 bidirectionalSupport.Reorder( bidirectionalParagraphInfo.bidirectionalInfoIndex,
173 lineInfoRun.characterRun.characterIndex - bidirectionalParagraphInfo.characterRun.characterIndex,
174 lineInfoRun.characterRun.numberOfCharacters,
175 lineInfoRun.visualToLogicalMap );
177 // For those LTR lines inside a bidirectional paragraph.
178 // It will save to relayout the line after reordering.
179 for( unsigned int i=0; i<numberOfCharacters; ++i )
181 if( i != *( lineInfoRun.visualToLogicalMap + i ) )
183 lineInfoRun.isIdentity = false;
189 // Push the run into the vector.
190 lineInfoRuns.Insert( lineInfoRuns.Begin() + bidiLineIndex, lineInfoRun );
193 bool GetMirroredText( const Vector<Character>& text,
194 const Vector<CharacterDirection>& directions,
195 const Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo,
196 CharacterIndex startIndex,
197 Length numberOfCharacters,
198 Vector<Character>& mirroredText )
200 bool hasTextMirrored = false;
202 // Handle to the bidirectional info module in text-abstraction.
203 TextAbstraction::BidirectionalSupport bidirectionalSupport = TextAbstraction::BidirectionalSupport::Get();
207 Character* mirroredTextBuffer = mirroredText.Begin();
208 CharacterDirection* directionsBuffer = directions.Begin();
210 CharacterIndex index = startIndex;
211 const CharacterIndex lastCharacter = startIndex + numberOfCharacters;
213 // Traverse the paragraphs and mirror the right to left ones.
214 for( Vector<BidirectionalParagraphInfoRun>::ConstIterator it = bidirectionalInfo.Begin(),
215 endIt = bidirectionalInfo.End();
219 const BidirectionalParagraphInfoRun& paragraph = *it;
221 if( index >= paragraph.characterRun.characterIndex + paragraph.characterRun.numberOfCharacters )
223 // Skip the paragraph as it has already been processed.
227 if( lastCharacter <= paragraph.characterRun.characterIndex )
229 // Do not get mirror characters beyond startIndex + numberOfCharacters.
233 index += paragraph.characterRun.numberOfCharacters;
234 const bool tmpMirrored = bidirectionalSupport.GetMirroredText( mirroredTextBuffer + paragraph.characterRun.characterIndex,
235 directionsBuffer + paragraph.characterRun.characterIndex,
236 paragraph.characterRun.numberOfCharacters );
238 hasTextMirrored = hasTextMirrored || tmpMirrored;
241 return hasTextMirrored;
244 void GetCharactersDirection( const Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo,
245 Length totalNumberOfCharacters,
246 CharacterIndex startIndex,
247 Length numberOfCharacters,
248 Vector<CharacterDirection>& directions )
250 // Handle to the bidirectional info module in text-abstraction.
251 TextAbstraction::BidirectionalSupport bidirectionalSupport = TextAbstraction::BidirectionalSupport::Get();
253 // Resize the vector.
254 directions.Resize( totalNumberOfCharacters );
256 // Whether the current buffer is being updated or is set from scratch.
257 const bool updateCurrentBuffer = numberOfCharacters < totalNumberOfCharacters;
259 CharacterDirection* directionsBuffer = NULL;
260 Vector<CharacterDirection> newDirections;
262 if( updateCurrentBuffer )
264 newDirections.Resize( numberOfCharacters );
265 directionsBuffer = newDirections.Begin();
269 directionsBuffer = directions.Begin();
272 const CharacterIndex lastCharacter = startIndex + numberOfCharacters;
273 CharacterIndex index = startIndex;
275 for( Vector<BidirectionalParagraphInfoRun>::ConstIterator it = bidirectionalInfo.Begin(),
276 endIt = bidirectionalInfo.End();
280 const BidirectionalParagraphInfoRun& paragraph = *it;
282 if( index >= paragraph.characterRun.characterIndex + paragraph.characterRun.numberOfCharacters )
284 // Skip the paragraph as it has already been processed.
288 if( lastCharacter <= paragraph.characterRun.characterIndex )
290 // Do not get the character directions beyond startIndex + numberOfCharacters.
294 // Set the directions of any previous left to right characters.
295 const Length numberOfLeftToRightCharacters = paragraph.characterRun.characterIndex - index;
296 if( numberOfLeftToRightCharacters > 0u )
298 memset( directionsBuffer + index - startIndex, false, numberOfLeftToRightCharacters * sizeof( bool ) );
301 // Set the directions of the bidirectional text.
302 bidirectionalSupport.GetCharactersDirection( paragraph.bidirectionalInfoIndex,
303 directionsBuffer + paragraph.characterRun.characterIndex - startIndex,
304 paragraph.characterRun.numberOfCharacters );
307 index += paragraph.characterRun.numberOfCharacters + numberOfLeftToRightCharacters;
310 // Fills with left to right those paragraphs without right to left characters.
311 memset( directionsBuffer + index - startIndex, false, ( lastCharacter - index ) * sizeof( bool ) );
313 // If the direction info is updated, it needs to be inserted in the model.
314 if( updateCurrentBuffer )
316 // Insert the directions in the given buffer.
317 directions.Insert( directions.Begin() + startIndex,
318 newDirections.Begin(),
319 newDirections.End() );
320 directions.Resize( totalNumberOfCharacters );
326 } // namespace Toolkit