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 matchLayoutDirection,
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 matchLayoutDirection,
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 CharacterIndex startIndexInSecondHalfLine,
151 Length numberOfCharactersInSecondHalfLine,
152 CharacterDirection direction)
154 // Handle to the bidirectional info module in text-abstraction.
155 TextAbstraction::BidirectionalSupport bidirectionalSupport = TextAbstraction::BidirectionalSupport::Get();
157 // Creates a bidirectional info for the line run.
158 BidirectionalLineInfoRun lineInfoRun;
159 lineInfoRun.characterRun.characterIndex = startIndex;
160 lineInfoRun.characterRun.numberOfCharacters = numberOfCharacters;
161 lineInfoRun.direction = direction;
162 lineInfoRun.isIdentity = true;
164 lineInfoRun.characterRunForSecondHalfLine.characterIndex = startIndexInSecondHalfLine;
165 lineInfoRun.characterRunForSecondHalfLine.numberOfCharacters = numberOfCharactersInSecondHalfLine;
167 // Allocate space for the conversion maps.
168 // The memory is freed after the visual to logical to visual conversion tables are built in the logical model.
169 lineInfoRun.visualToLogicalMap = reinterpret_cast<CharacterIndex*>(malloc(numberOfCharacters * sizeof(CharacterIndex)));
171 lineInfoRun.visualToLogicalMapSecondHalf = reinterpret_cast<CharacterIndex*>(malloc(numberOfCharactersInSecondHalfLine * sizeof(CharacterIndex)));
173 if(nullptr != lineInfoRun.visualToLogicalMap && nullptr != lineInfoRun.visualToLogicalMapSecondHalf)
175 // Reorders the line.
176 bidirectionalSupport.Reorder(bidirectionalParagraphInfo.bidirectionalInfoIndex,
177 lineInfoRun.characterRun.characterIndex - bidirectionalParagraphInfo.characterRun.characterIndex,
178 lineInfoRun.characterRun.numberOfCharacters,
179 lineInfoRun.visualToLogicalMap);
181 bidirectionalSupport.Reorder(bidirectionalParagraphInfo.bidirectionalInfoIndex,
182 lineInfoRun.characterRunForSecondHalfLine.characterIndex - bidirectionalParagraphInfo.characterRun.characterIndex,
183 lineInfoRun.characterRunForSecondHalfLine.numberOfCharacters,
184 lineInfoRun.visualToLogicalMapSecondHalf);
186 // For those LTR lines inside a bidirectional paragraph.
187 // It will save to relayout the line after reordering.
188 for(unsigned int i = 0; i < numberOfCharacters; ++i)
190 if(i != *(lineInfoRun.visualToLogicalMap + i))
192 lineInfoRun.isIdentity = false;
197 for(unsigned int i = 0; i < numberOfCharactersInSecondHalfLine; ++i)
199 if(i != *(lineInfoRun.visualToLogicalMapSecondHalf + i))
201 lineInfoRun.isIdentity = false;
207 // Push the run into the vector.
208 lineInfoRuns.Insert(lineInfoRuns.Begin() + bidiLineIndex, lineInfoRun);
211 bool GetMirroredText(const Vector<Character>& text,
212 const Vector<CharacterDirection>& directions,
213 const Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo,
214 CharacterIndex startIndex,
215 Length numberOfCharacters,
216 Vector<Character>& mirroredText)
218 bool hasTextMirrored = false;
220 // Handle to the bidirectional info module in text-abstraction.
221 TextAbstraction::BidirectionalSupport bidirectionalSupport = TextAbstraction::BidirectionalSupport::Get();
225 Character* mirroredTextBuffer = mirroredText.Begin();
226 CharacterDirection* directionsBuffer = directions.Begin();
228 CharacterIndex index = startIndex;
229 const CharacterIndex lastCharacter = startIndex + numberOfCharacters;
231 // Traverse the paragraphs and mirror the right to left ones.
232 for(Vector<BidirectionalParagraphInfoRun>::ConstIterator it = bidirectionalInfo.Begin(),
233 endIt = bidirectionalInfo.End();
237 const BidirectionalParagraphInfoRun& paragraph = *it;
239 if(index >= paragraph.characterRun.characterIndex + paragraph.characterRun.numberOfCharacters)
241 // Skip the paragraph as it has already been processed.
245 if(lastCharacter <= paragraph.characterRun.characterIndex)
247 // Do not get mirror characters beyond startIndex + numberOfCharacters.
251 index += paragraph.characterRun.numberOfCharacters;
252 const bool tmpMirrored = bidirectionalSupport.GetMirroredText(mirroredTextBuffer + paragraph.characterRun.characterIndex,
253 directionsBuffer + paragraph.characterRun.characterIndex,
254 paragraph.characterRun.numberOfCharacters);
256 hasTextMirrored = hasTextMirrored || tmpMirrored;
259 return hasTextMirrored;
262 void GetCharactersDirection(const Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo,
263 Length totalNumberOfCharacters,
264 CharacterIndex startIndex,
265 Length numberOfCharacters,
266 Vector<CharacterDirection>& directions)
268 // Handle to the bidirectional info module in text-abstraction.
269 TextAbstraction::BidirectionalSupport bidirectionalSupport = TextAbstraction::BidirectionalSupport::Get();
271 // Resize the vector.
272 directions.Resize(totalNumberOfCharacters);
274 // Whether the current buffer is being updated or is set from scratch.
275 const bool updateCurrentBuffer = numberOfCharacters < totalNumberOfCharacters;
277 CharacterDirection* directionsBuffer = NULL;
278 Vector<CharacterDirection> newDirections;
280 if(updateCurrentBuffer)
282 newDirections.Resize(numberOfCharacters);
283 directionsBuffer = newDirections.Begin();
287 directionsBuffer = directions.Begin();
290 const CharacterIndex lastCharacter = startIndex + numberOfCharacters;
291 CharacterIndex index = startIndex;
293 for(Vector<BidirectionalParagraphInfoRun>::ConstIterator it = bidirectionalInfo.Begin(),
294 endIt = bidirectionalInfo.End();
298 const BidirectionalParagraphInfoRun& paragraph = *it;
300 if(index >= paragraph.characterRun.characterIndex + paragraph.characterRun.numberOfCharacters)
302 // Skip the paragraph as it has already been processed.
306 if(lastCharacter <= paragraph.characterRun.characterIndex)
308 // Do not get the character directions beyond startIndex + numberOfCharacters.
312 // Set the directions of any previous left to right characters.
313 const Length numberOfLeftToRightCharacters = paragraph.characterRun.characterIndex - index;
314 if(numberOfLeftToRightCharacters > 0u)
316 memset(directionsBuffer + index - startIndex, false, numberOfLeftToRightCharacters * sizeof(bool));
319 // Set the directions of the bidirectional text.
320 bidirectionalSupport.GetCharactersDirection(paragraph.bidirectionalInfoIndex,
321 directionsBuffer + paragraph.characterRun.characterIndex - startIndex,
322 paragraph.characterRun.numberOfCharacters);
325 index += paragraph.characterRun.numberOfCharacters + numberOfLeftToRightCharacters;
328 // Fills with left to right those paragraphs without right to left characters.
329 memset(directionsBuffer + index - startIndex, false, (lastCharacter - index) * sizeof(bool));
331 // If the direction info is updated, it needs to be inserted in the model.
332 if(updateCurrentBuffer)
334 // Insert the directions in the given buffer.
335 directions.Insert(directions.Begin() + startIndex,
336 newDirections.Begin(),
337 newDirections.End());
338 directions.Resize(totalNumberOfCharacters);
344 } // namespace Toolkit