2 * Copyright (c) 2016 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/logical-model-impl.h>
22 #include <dali-toolkit/internal/text/input-style.h>
23 #include <dali-toolkit/internal/text/text-run-container.h>
34 void FreeFontFamilyNames( Vector<FontDescriptionRun>& fontDescriptionRuns )
36 for( Vector<FontDescriptionRun>::Iterator it = fontDescriptionRuns.Begin(),
37 endIt = fontDescriptionRuns.End();
41 delete (*it).familyName;
44 fontDescriptionRuns.Clear();
47 LogicalModelPtr LogicalModel::New()
49 return LogicalModelPtr( new LogicalModel() );
52 Script LogicalModel::GetScript( CharacterIndex characterIndex ) const
54 // If this operation is too slow, consider a binary search.
56 const ScriptRun* const scriptRunBuffer = mScriptRuns.Begin();
57 for( Length index = 0u, length = mScriptRuns.Count(); index < length; ++index )
59 const ScriptRun* const scriptRun = scriptRunBuffer + index;
61 if( ( scriptRun->characterRun.characterIndex <= characterIndex ) &&
62 ( characterIndex < scriptRun->characterRun.characterIndex + scriptRun->characterRun.numberOfCharacters ) )
64 return scriptRun->script;
68 return TextAbstraction::UNKNOWN;
71 CharacterDirection LogicalModel::GetCharacterDirection( CharacterIndex characterIndex ) const
73 if( characterIndex >= mCharacterDirections.Count() )
75 // The model has no right to left characters, so the vector of directions is void.
79 return *( mCharacterDirections.Begin() + characterIndex );
82 CharacterIndex LogicalModel::GetLogicalCursorIndex( CharacterIndex visualCursorIndex )
84 // The character's directions buffer.
85 const CharacterDirection* const modelCharacterDirections = mCharacterDirections.Begin();
87 // The bidirectional line info.
88 const BidirectionalLineInfoRun* const bidirectionalLineInfo = mBidirectionalLineInfo.Begin() + mBidirectionalLineIndex;
90 // Whether the paragraph starts with a right to left character.
91 const bool isRightToLeftParagraph = bidirectionalLineInfo->direction;
93 // The total number of characters of the line.
94 const Length lastCharacterIndex = bidirectionalLineInfo->characterRun.characterIndex + bidirectionalLineInfo->characterRun.numberOfCharacters;
96 CharacterIndex logicalCursorIndex = 0u;
98 if( bidirectionalLineInfo->characterRun.characterIndex == visualCursorIndex )
100 if( isRightToLeftParagraph )
102 logicalCursorIndex = lastCharacterIndex;
104 else // else logical position is the first of the line.
106 logicalCursorIndex = bidirectionalLineInfo->characterRun.characterIndex;
109 else if( lastCharacterIndex == visualCursorIndex )
111 if( isRightToLeftParagraph )
113 logicalCursorIndex = bidirectionalLineInfo->characterRun.characterIndex;
115 else // else logical position is the number of characters.
117 logicalCursorIndex = lastCharacterIndex;
122 // Get the character indexed by index - 1 and index
123 // and calculate the logical position according the directions of
124 // both characters and the direction of the paragraph.
126 const CharacterIndex previousVisualCursorIndex = visualCursorIndex - 1u;
127 const CharacterIndex previousLogicalCursorIndex = *( bidirectionalLineInfo->visualToLogicalMap + previousVisualCursorIndex - bidirectionalLineInfo->characterRun.characterIndex ) + bidirectionalLineInfo->characterRun.characterIndex;
128 const CharacterIndex currentLogicalCursorIndex = *( bidirectionalLineInfo->visualToLogicalMap + visualCursorIndex - bidirectionalLineInfo->characterRun.characterIndex ) + bidirectionalLineInfo->characterRun.characterIndex;
130 const CharacterDirection previousCharacterDirection = *( modelCharacterDirections + previousLogicalCursorIndex );
131 const CharacterDirection currentCharacterDirection = *( modelCharacterDirections + currentLogicalCursorIndex );
133 if( previousCharacterDirection == currentCharacterDirection )
135 // Both glyphs have the same direction.
136 if( previousCharacterDirection )
138 logicalCursorIndex = previousLogicalCursorIndex;
142 logicalCursorIndex = currentLogicalCursorIndex;
147 if( isRightToLeftParagraph )
149 if( currentCharacterDirection )
151 logicalCursorIndex = currentLogicalCursorIndex + 1u;
155 logicalCursorIndex = previousLogicalCursorIndex;
160 if( previousCharacterDirection )
162 logicalCursorIndex = currentLogicalCursorIndex;
166 logicalCursorIndex = previousLogicalCursorIndex + 1u;
172 return logicalCursorIndex;
175 CharacterIndex LogicalModel::GetLogicalCharacterIndex( CharacterIndex visualCharacterIndex )
177 // The bidirectional line info.
178 const BidirectionalLineInfoRun* const bidirectionalLineInfo = mBidirectionalLineInfo.Begin() + mBidirectionalLineIndex;
180 return *( bidirectionalLineInfo->visualToLogicalMap + visualCharacterIndex - bidirectionalLineInfo->characterRun.characterIndex ) + bidirectionalLineInfo->characterRun.characterIndex;
183 bool LogicalModel::FetchBidirectionalLineInfo( CharacterIndex characterIndex )
185 // The number of bidirectional lines.
186 const Length numberOfBidirectionalLines = mBidirectionalLineInfo.Count();
188 if( 0u == numberOfBidirectionalLines )
190 // If there is no bidirectional info.
194 // Find the bidi line where the character is laid-out.
196 const BidirectionalLineInfoRun* const bidirectionalLineInfoBuffer = mBidirectionalLineInfo.Begin();
198 // Check first if the character is in the previously fetched line.
200 BidirectionalLineRunIndex bidiLineIndex = 0u;
201 CharacterIndex lastCharacterOfRightToLeftRun = 0u;
202 if( mBidirectionalLineIndex < numberOfBidirectionalLines )
204 const BidirectionalLineInfoRun& bidiLineRun = *( bidirectionalLineInfoBuffer + mBidirectionalLineIndex );
206 const CharacterIndex lastCharacterOfRunPlusOne = bidiLineRun.characterRun.characterIndex + bidiLineRun.characterRun.numberOfCharacters;
207 if( ( bidiLineRun.characterRun.characterIndex <= characterIndex ) &&
208 ( characterIndex < lastCharacterOfRunPlusOne ) )
210 // The character is in the previously fetched bidi line.
215 // The character is not in the previously fetched line.
216 // Set the bidi line index from where to start the fetch.
218 if( characterIndex < bidiLineRun.characterRun.characterIndex )
220 // Start the fetch from the beginning.
225 // Start the fetch from the next line.
226 bidiLineIndex = mBidirectionalLineIndex + 1u;
227 lastCharacterOfRightToLeftRun = lastCharacterOfRunPlusOne - 1u;
232 // The character has not been found in the previously fetched bidi line.
233 for( Vector<BidirectionalLineInfoRun>::ConstIterator it = bidirectionalLineInfoBuffer + bidiLineIndex,
234 endIt = mBidirectionalLineInfo.End();
236 ++it, ++bidiLineIndex )
238 const BidirectionalLineInfoRun& bidiLineRun = *it;
240 if( ( lastCharacterOfRightToLeftRun < characterIndex ) &&
241 ( characterIndex < bidiLineRun.characterRun.characterIndex ) )
243 // The character is not inside a bidi line.
247 const CharacterIndex lastCharacterOfRunPlusOne = bidiLineRun.characterRun.characterIndex + bidiLineRun.characterRun.numberOfCharacters;
248 lastCharacterOfRightToLeftRun = lastCharacterOfRunPlusOne - 1u;
249 if( ( bidiLineRun.characterRun.characterIndex <= characterIndex ) &&
250 ( characterIndex < lastCharacterOfRunPlusOne ) )
252 // Bidi line found. Fetch the line.
253 mBidirectionalLineIndex = bidiLineIndex;
261 BidirectionalLineRunIndex LogicalModel::GetBidirectionalLineInfo() const
263 return mBidirectionalLineIndex;
266 void LogicalModel::UpdateTextStyleRuns( CharacterIndex index, int numberOfCharacters )
268 const Length totalNumberOfCharacters = mText.Count();
270 // Process the color runs.
271 Vector<ColorRun> removedColorRuns;
272 UpdateCharacterRuns<ColorRun>( index,
274 totalNumberOfCharacters,
278 // Process the font description runs.
279 Vector<FontDescriptionRun> removedFontDescriptionRuns;
280 UpdateCharacterRuns<FontDescriptionRun>( index,
282 totalNumberOfCharacters,
283 mFontDescriptionRuns,
284 removedFontDescriptionRuns );
286 // Free memory allocated for the font family name.
287 FreeFontFamilyNames( removedFontDescriptionRuns );
290 void LogicalModel::RetrieveStyle( CharacterIndex index, InputStyle& style )
292 unsigned int runIndex = 0u;
294 // Set the text color.
295 bool colorOverriden = false;
296 unsigned int colorIndex = 0u;
297 const ColorRun* const colorRunsBuffer = mColorRuns.Begin();
298 for( Vector<ColorRun>::ConstIterator it = colorRunsBuffer,
299 endIt = mColorRuns.End();
303 const ColorRun& colorRun = *it;
305 if( ( colorRun.characterRun.characterIndex <= index ) &&
306 ( index < colorRun.characterRun.characterIndex + colorRun.characterRun.numberOfCharacters ) )
308 colorIndex = runIndex;
309 colorOverriden = true;
313 // Set the text's color if it's overriden.
316 style.textColor = ( *( colorRunsBuffer + colorIndex ) ).color;
317 style.isDefaultColor = false;
320 // Reset the run index.
323 // Set the font's parameters.
324 bool nameOverriden = false;
325 bool weightOverriden = false;
326 bool widthOverriden = false;
327 bool slantOverriden = false;
328 bool sizeOverriden = false;
329 unsigned int nameIndex = 0u;
330 unsigned int weightIndex = 0u;
331 unsigned int widthIndex = 0u;
332 unsigned int slantIndex = 0u;
333 unsigned int sizeIndex = 0u;
334 const FontDescriptionRun* const fontDescriptionRunsBuffer = mFontDescriptionRuns.Begin();
335 for( Vector<FontDescriptionRun>::ConstIterator it = fontDescriptionRunsBuffer,
336 endIt = mFontDescriptionRuns.End();
340 const FontDescriptionRun& fontDescriptionRun = *it;
342 if( ( fontDescriptionRun.characterRun.characterIndex <= index ) &&
343 ( index < fontDescriptionRun.characterRun.characterIndex + fontDescriptionRun.characterRun.numberOfCharacters ) )
345 if( fontDescriptionRun.familyDefined )
347 nameIndex = runIndex;
348 nameOverriden = true;
351 if( fontDescriptionRun.weightDefined )
353 weightIndex = runIndex;
354 weightOverriden = true;
357 if( fontDescriptionRun.widthDefined )
359 widthIndex = runIndex;
360 widthOverriden = true;
363 if( fontDescriptionRun.slantDefined )
365 slantIndex = runIndex;
366 slantOverriden = true;
369 if( fontDescriptionRun.sizeDefined )
371 sizeIndex = runIndex;
372 sizeOverriden = true;
377 // Set the font's family name if it's overriden.
380 const FontDescriptionRun& fontDescriptionRun = *( fontDescriptionRunsBuffer + nameIndex );
382 style.familyName = std::string( fontDescriptionRun.familyName, fontDescriptionRun.familyLength );
383 style.isFamilyDefined = true;
386 // Set the font's weight if it's overriden.
387 if( weightOverriden )
389 const FontDescriptionRun& fontDescriptionRun = *( fontDescriptionRunsBuffer + weightIndex );
391 style.weight = fontDescriptionRun.weight;
392 style.isWeightDefined = true;
395 // Set the font's width if it's overriden.
398 const FontDescriptionRun& fontDescriptionRun = *( fontDescriptionRunsBuffer + widthIndex );
400 style.width = fontDescriptionRun.width;
401 style.isWidthDefined = true;
404 // Set the font's slant if it's overriden.
407 const FontDescriptionRun& fontDescriptionRun = *( fontDescriptionRunsBuffer + slantIndex );
409 style.slant = fontDescriptionRun.slant;
410 style.isSlantDefined = true;
413 // Set the font's size if it's overriden.
416 const FontDescriptionRun& fontDescriptionRun = *( fontDescriptionRunsBuffer + sizeIndex );
418 style.size = static_cast<float>( fontDescriptionRun.size ) / 64.f;
419 style.isSizeDefined = true;
423 void LogicalModel::ClearFontDescriptionRuns()
425 FreeFontFamilyNames( mFontDescriptionRuns );
428 void LogicalModel::CreateParagraphInfo( CharacterIndex startIndex,
429 Length numberOfCharacters )
431 const Length totalNumberOfCharacters = mLineBreakInfo.Count();
433 // Count the number of LINE_MUST_BREAK to reserve some space for the vector of paragraph's info.
434 Vector<CharacterIndex> paragraphs;
435 paragraphs.Reserve( numberOfCharacters );
436 const TextAbstraction::LineBreakInfo* lineBreakInfoBuffer = mLineBreakInfo.Begin();
437 const CharacterIndex lastCharacterIndexPlusOne = startIndex + numberOfCharacters;
438 for( Length index = startIndex; index < lastCharacterIndexPlusOne; ++index )
440 if( TextAbstraction::LINE_MUST_BREAK == *( lineBreakInfoBuffer + index ) )
442 paragraphs.PushBack( index );
446 // Whether the current paragraphs are updated or set from scratch.
447 const bool updateCurrentParagraphs = numberOfCharacters < totalNumberOfCharacters;
449 // Reserve space for current paragraphs plus new ones.
450 const Length numberOfNewParagraphs = paragraphs.Count();
451 const Length totalNumberOfParagraphs = mParagraphInfo.Count() + numberOfNewParagraphs;
452 mParagraphInfo.Resize( totalNumberOfParagraphs );
454 ParagraphRun* paragraphInfoBuffer = NULL;
455 Vector<ParagraphRun> newParagraphs;
457 if( updateCurrentParagraphs )
459 newParagraphs.Resize( numberOfNewParagraphs );
460 paragraphInfoBuffer = newParagraphs.Begin();
464 paragraphInfoBuffer = mParagraphInfo.Begin();
467 // Find where to insert the new paragraphs.
468 ParagraphRunIndex paragraphIndex = 0u;
469 CharacterIndex firstIndex = startIndex;
471 if( updateCurrentParagraphs )
473 for( Vector<ParagraphRun>::ConstIterator it = mParagraphInfo.Begin(),
474 endIt = mParagraphInfo.Begin() + totalNumberOfParagraphs - numberOfNewParagraphs;
478 const ParagraphRun& paragraph( *it );
480 if( startIndex < paragraph.characterRun.characterIndex + paragraph.characterRun.numberOfCharacters )
482 firstIndex = paragraph.characterRun.characterIndex;
490 // Create the paragraph info.
491 ParagraphRunIndex newParagraphIndex = 0u;
492 for( Vector<CharacterIndex>::ConstIterator it = paragraphs.Begin(),
493 endIt = paragraphs.End();
495 ++it, ++newParagraphIndex )
497 const CharacterIndex index = *it;
499 ParagraphRun& paragraph = *( paragraphInfoBuffer + newParagraphIndex );
500 paragraph.characterRun.characterIndex = firstIndex;
501 paragraph.characterRun.numberOfCharacters = 1u + index - firstIndex;
503 firstIndex += paragraph.characterRun.numberOfCharacters;
507 // Insert the new paragraphs.
508 if( updateCurrentParagraphs )
510 mParagraphInfo.Insert( mParagraphInfo.Begin() + paragraphIndex,
511 newParagraphs.Begin(),
512 newParagraphs.End() );
514 mParagraphInfo.Resize( totalNumberOfParagraphs );
516 // Update the next paragraph indices.
517 for( Vector<ParagraphRun>::Iterator it = mParagraphInfo.Begin() + paragraphIndex + newParagraphs.Count(),
518 endIt = mParagraphInfo.End();
522 ParagraphRun& paragraph( *it );
524 paragraph.characterRun.characterIndex += numberOfCharacters;
529 void LogicalModel::FindParagraphs( CharacterIndex index,
530 Length numberOfCharacters,
531 Vector<ParagraphRunIndex>& paragraphs )
533 // Reserve som space for the paragraph indices.
534 paragraphs.Reserve( mParagraphInfo.Count() );
536 // Traverse the paragraphs to find which ones contain the given characters.
537 ParagraphRunIndex paragraphIndex = 0u;
538 for( Vector<ParagraphRun>::ConstIterator it = mParagraphInfo.Begin(),
539 endIt = mParagraphInfo.End();
541 ++it, ++paragraphIndex )
543 const ParagraphRun& paragraph( *it );
545 if( ( paragraph.characterRun.characterIndex + paragraph.characterRun.numberOfCharacters > index ) &&
546 ( paragraph.characterRun.characterIndex < index + numberOfCharacters ) )
548 paragraphs.PushBack( paragraphIndex );
553 LogicalModel::~LogicalModel()
555 ClearFontDescriptionRuns();
558 LogicalModel::LogicalModel()
559 : mBidirectionalLineIndex( 0u )
565 } // namespace Toolkit