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>
22 #include <dali/devel-api/text-abstraction/bidirectional-support.h>
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 matchSystemLanguageDirection,
38 Dali::LayoutDirection::Type layoutDirection)
40 // Find where to insert the new paragraphs.
41 BidirectionalRunIndex bidiInfoIndex = 0u;
42 for(Vector<BidirectionalParagraphInfoRun>::ConstIterator it = bidirectionalInfo.Begin(),
43 endIt = bidirectionalInfo.End();
47 const BidirectionalParagraphInfoRun& run = *it;
49 if(startIndex < run.characterRun.characterIndex + run.characterRun.numberOfCharacters)
51 // Found where to insert the bidi info.
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.
60 // Index pointing the first character of the current paragraph.
61 CharacterIndex paragraphCharacterIndex = startIndex;
63 // Pointer to the text buffer.
64 const Character* textBuffer = text.Begin();
66 // Pointer to the line break info buffer.
67 const LineBreakInfo* lineBreakInfoBuffer = lineBreakInfo.Begin();
69 // Handle to the bidirectional info module in text-abstraction.
70 TextAbstraction::BidirectionalSupport bidirectionalSupport = TextAbstraction::BidirectionalSupport::Get();
72 const CharacterIndex lastCharacter = startIndex + numberOfCharacters;
74 bool hasRightToLeftScript = false;
76 for(Vector<ScriptRun>::ConstIterator it = scripts.Begin(),
77 endIt = scripts.End();
81 const ScriptRun& scriptRun = *it;
82 const CharacterIndex lastScriptRunIndex = scriptRun.characterRun.characterIndex + scriptRun.characterRun.numberOfCharacters - 1u;
84 if(startIndex > lastScriptRunIndex)
86 // Skip the run as it has already been processed.
90 if(lastCharacter <= scriptRun.characterRun.characterIndex)
92 // Do not get bidirectional info beyond startIndex + numberOfCharacters.
96 if(!hasRightToLeftScript && scriptRun.isRightToLeft)
98 // The script is right to left.
99 hasRightToLeftScript = true;
102 if(TextAbstraction::LINE_MUST_BREAK == *(lineBreakInfoBuffer + lastScriptRunIndex))
104 // A new paragraph has been found.
106 if(hasRightToLeftScript)
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.
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 matchSystemLanguageDirection,
119 bidirectionalRun.direction = bidirectionalSupport.GetParagraphDirection(bidirectionalRun.bidirectionalInfoIndex);
121 bidirectionalInfo.Insert(bidirectionalInfo.Begin() + bidiInfoIndex, bidirectionalRun);
125 // Point to the next paragraph.
126 paragraphCharacterIndex = lastScriptRunIndex + 1u;
128 // Reset whether there is a right to left script.
129 hasRightToLeftScript = false;
133 // Update indices of the bidi runs.
134 for(Vector<BidirectionalParagraphInfoRun>::Iterator it = bidirectionalInfo.Begin() + bidiInfoIndex,
135 endIt = bidirectionalInfo.End();
139 BidirectionalParagraphInfoRun& run = *it;
141 run.characterRun.characterIndex += numberOfCharacters;
145 void ReorderLine(const BidirectionalParagraphInfoRun& bidirectionalParagraphInfo,
146 Vector<BidirectionalLineInfoRun>& lineInfoRuns,
147 BidirectionalLineRunIndex bidiLineIndex,
148 CharacterIndex startIndex,
149 Length numberOfCharacters,
150 CharacterDirection direction)
152 // Handle to the bidirectional info module in text-abstraction.
153 TextAbstraction::BidirectionalSupport bidirectionalSupport = TextAbstraction::BidirectionalSupport::Get();
155 // Creates a bidirectional info for the line run.
156 BidirectionalLineInfoRun lineInfoRun;
157 lineInfoRun.characterRun.characterIndex = startIndex;
158 lineInfoRun.characterRun.numberOfCharacters = numberOfCharacters;
159 lineInfoRun.direction = direction;
160 lineInfoRun.isIdentity = true;
162 // Allocate space for the conversion maps.
163 // The memory is freed after the visual to logical to visual conversion tables are built in the logical model.
164 lineInfoRun.visualToLogicalMap = reinterpret_cast<CharacterIndex*>(malloc(numberOfCharacters * sizeof(CharacterIndex)));
166 if(nullptr != lineInfoRun.visualToLogicalMap)
168 // Reorders the line.
169 bidirectionalSupport.Reorder(bidirectionalParagraphInfo.bidirectionalInfoIndex,
170 lineInfoRun.characterRun.characterIndex - bidirectionalParagraphInfo.characterRun.characterIndex,
171 lineInfoRun.characterRun.numberOfCharacters,
172 lineInfoRun.visualToLogicalMap);
174 // For those LTR lines inside a bidirectional paragraph.
175 // It will save to relayout the line after reordering.
176 for(unsigned int i = 0; i < numberOfCharacters; ++i)
178 if(i != *(lineInfoRun.visualToLogicalMap + i))
180 lineInfoRun.isIdentity = false;
186 // Push the run into the vector.
187 lineInfoRuns.Insert(lineInfoRuns.Begin() + bidiLineIndex, lineInfoRun);
190 bool GetMirroredText(const Vector<Character>& text,
191 const Vector<CharacterDirection>& directions,
192 const Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo,
193 CharacterIndex startIndex,
194 Length numberOfCharacters,
195 Vector<Character>& mirroredText)
197 bool hasTextMirrored = false;
199 // Handle to the bidirectional info module in text-abstraction.
200 TextAbstraction::BidirectionalSupport bidirectionalSupport = TextAbstraction::BidirectionalSupport::Get();
204 Character* mirroredTextBuffer = mirroredText.Begin();
205 CharacterDirection* directionsBuffer = directions.Begin();
207 CharacterIndex index = startIndex;
208 const CharacterIndex lastCharacter = startIndex + numberOfCharacters;
210 // Traverse the paragraphs and mirror the right to left ones.
211 for(Vector<BidirectionalParagraphInfoRun>::ConstIterator it = bidirectionalInfo.Begin(),
212 endIt = bidirectionalInfo.End();
216 const BidirectionalParagraphInfoRun& paragraph = *it;
218 if(index >= paragraph.characterRun.characterIndex + paragraph.characterRun.numberOfCharacters)
220 // Skip the paragraph as it has already been processed.
224 if(lastCharacter <= paragraph.characterRun.characterIndex)
226 // Do not get mirror characters beyond startIndex + numberOfCharacters.
230 index += paragraph.characterRun.numberOfCharacters;
231 const bool tmpMirrored = bidirectionalSupport.GetMirroredText(mirroredTextBuffer + paragraph.characterRun.characterIndex,
232 directionsBuffer + paragraph.characterRun.characterIndex,
233 paragraph.characterRun.numberOfCharacters);
235 hasTextMirrored = hasTextMirrored || tmpMirrored;
238 return hasTextMirrored;
241 void GetCharactersDirection(const Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo,
242 Length totalNumberOfCharacters,
243 CharacterIndex startIndex,
244 Length numberOfCharacters,
245 Vector<CharacterDirection>& directions)
247 // Handle to the bidirectional info module in text-abstraction.
248 TextAbstraction::BidirectionalSupport bidirectionalSupport = TextAbstraction::BidirectionalSupport::Get();
250 // Resize the vector.
251 directions.Resize(totalNumberOfCharacters);
253 // Whether the current buffer is being updated or is set from scratch.
254 const bool updateCurrentBuffer = numberOfCharacters < totalNumberOfCharacters;
256 CharacterDirection* directionsBuffer = NULL;
257 Vector<CharacterDirection> newDirections;
259 if(updateCurrentBuffer)
261 newDirections.Resize(numberOfCharacters);
262 directionsBuffer = newDirections.Begin();
266 directionsBuffer = directions.Begin();
269 const CharacterIndex lastCharacter = startIndex + numberOfCharacters;
270 CharacterIndex index = startIndex;
272 for(Vector<BidirectionalParagraphInfoRun>::ConstIterator it = bidirectionalInfo.Begin(),
273 endIt = bidirectionalInfo.End();
277 const BidirectionalParagraphInfoRun& paragraph = *it;
279 if(index >= paragraph.characterRun.characterIndex + paragraph.characterRun.numberOfCharacters)
281 // Skip the paragraph as it has already been processed.
285 if(lastCharacter <= paragraph.characterRun.characterIndex)
287 // Do not get the character directions beyond startIndex + numberOfCharacters.
291 // Set the directions of any previous left to right characters.
292 const Length numberOfLeftToRightCharacters = paragraph.characterRun.characterIndex - index;
293 if(numberOfLeftToRightCharacters > 0u)
295 memset(directionsBuffer + index - startIndex, false, numberOfLeftToRightCharacters * sizeof(bool));
298 // Set the directions of the bidirectional text.
299 bidirectionalSupport.GetCharactersDirection(paragraph.bidirectionalInfoIndex,
300 directionsBuffer + paragraph.characterRun.characterIndex - startIndex,
301 paragraph.characterRun.numberOfCharacters);
304 index += paragraph.characterRun.numberOfCharacters + numberOfLeftToRightCharacters;
307 // Fills with left to right those paragraphs without right to left characters.
308 memset(directionsBuffer + index - startIndex, false, (lastCharacter - index) * sizeof(bool));
310 // If the direction info is updated, it needs to be inserted in the model.
311 if(updateCurrentBuffer)
313 // Insert the directions in the given buffer.
314 directions.Insert(directions.Begin() + startIndex,
315 newDirections.Begin(),
316 newDirections.End());
317 directions.Resize(totalNumberOfCharacters);
323 } // namespace Toolkit