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/logical-model-impl.h>
33 LogicalModelPtr LogicalModel::New()
35 return LogicalModelPtr( new LogicalModel() );
38 void LogicalModel::SetText( const Character* const text,
39 Length numberOfCharacters )
41 if( 0u == numberOfCharacters )
47 mText.Resize( numberOfCharacters );
48 memcpy( mText.Begin(), text, numberOfCharacters * sizeof( Character ) );
52 Length LogicalModel::GetNumberOfCharacters() const
57 void LogicalModel::GetText( Character* text,
58 CharacterIndex characterIndex,
59 Length numberOfCharacters ) const
61 memcpy( text, mText.Begin() + characterIndex, numberOfCharacters * sizeof( Character ) );
64 Character LogicalModel::GetCharacter( CharacterIndex characterIndex ) const
66 return mText[characterIndex];
69 void LogicalModel::ReplaceText( CharacterIndex characterIndex,
70 Length numberOfCharactersToRemove,
71 const Character* const text,
72 Length numberOfCharactersToInsert )
76 void LogicalModel::SetScripts( const ScriptRun* const scripts,
79 if( 0u == numberOfRuns )
85 mScriptRuns.Resize( numberOfRuns );
86 memcpy( mScriptRuns.Begin(), scripts, numberOfRuns * sizeof( ScriptRun ) );
90 void LogicalModel::GetNumberOfScriptRuns( CharacterIndex characterIndex,
91 Length numberOfCharacters,
92 ScriptRunIndex& firstScriptRun,
93 Length& numberOfScriptRuns ) const
95 // Initialize the number of scripts and the index to the first script.
97 numberOfScriptRuns = 0;
98 bool firstScriptFound = false;
100 const CharacterIndex lastCharacterIndex = characterIndex + numberOfCharacters;
102 // Traverse the scripts and count those scripts within the range of characters.
103 for( Vector<ScriptRun>::ConstIterator it = mScriptRuns.Begin(),
104 endIt = mScriptRuns.End();
108 const ScriptRun& script = *it;
110 if( ( script.characterRun.characterIndex + script.characterRun.numberOfCharacters > characterIndex ) &&
111 ( lastCharacterIndex > script.characterRun.characterIndex ) )
113 firstScriptFound = true;
114 ++numberOfScriptRuns;
116 else if( lastCharacterIndex <= script.characterRun.characterIndex )
118 // nothing else to do.
122 if( !firstScriptFound )
129 void LogicalModel::GetScriptRuns( ScriptRun* scriptRuns,
130 CharacterIndex characterIndex,
131 Length numberOfCharacters ) const
133 ScriptRunIndex firstScriptRun = 0u;
134 Length numberOfScriptRuns = 0u;
136 GetNumberOfScriptRuns( characterIndex,
139 numberOfScriptRuns );
141 memcpy( scriptRuns, mScriptRuns.Begin() + firstScriptRun, numberOfScriptRuns * sizeof( ScriptRun ) );
144 Script LogicalModel::GetScript( CharacterIndex characterIndex ) const
146 // If this operation is too slow, consider a binary search.
148 for( Length index = 0u, length = mScriptRuns.Count(); index < length; ++index )
150 const ScriptRun* const scriptRun = mScriptRuns.Begin() + index;
152 if( ( scriptRun->characterRun.characterIndex <= characterIndex ) &&
153 ( characterIndex < scriptRun->characterRun.characterIndex + scriptRun->characterRun.numberOfCharacters ) )
155 return scriptRun->script;
159 return TextAbstraction::UNKNOWN;
162 void LogicalModel::ReplaceScripts( CharacterIndex characterIndex,
163 Length numberOfCharactersToRemove,
164 const ScriptRun* const scriptRuns,
165 Length numberOfCharactersToInsert )
169 void LogicalModel::SetFonts( const FontRun* const fonts,
170 Length numberOfRuns )
172 if( 0u == numberOfRuns )
178 mFontRuns.Resize( numberOfRuns );
179 memcpy( mFontRuns.Begin(), fonts, numberOfRuns * sizeof( FontRun ) );
183 void LogicalModel::GetNumberOfFontRuns( CharacterIndex characterIndex,
184 Length numberOfCharacters,
185 FontRunIndex& firstFontRun,
186 Length& numberOfFontRuns ) const
188 // Initialize the number of fonts and the index to the first font.
190 numberOfFontRuns = 0;
191 bool firstFontFound = false;
193 const CharacterIndex lastCharacterIndex = characterIndex + numberOfCharacters;
195 // Traverse the fonts and count those fonts within the range of characters.
196 for( Vector<FontRun>::ConstIterator it = mFontRuns.Begin(),
197 endIt = mFontRuns.End();
201 const FontRun& font = *it;
203 if( ( font.characterRun.characterIndex + font.characterRun.numberOfCharacters > characterIndex ) &&
204 ( characterIndex + numberOfCharacters > font.characterRun.characterIndex ) )
206 firstFontFound = true;
209 else if( lastCharacterIndex <= font.characterRun.characterIndex )
211 // nothing else to do.
215 if( !firstFontFound )
222 void LogicalModel::GetFontRuns( FontRun* fontRuns,
223 CharacterIndex characterIndex,
224 Length numberOfCharacters ) const
226 FontRunIndex firstFontRun = 0u;
227 Length numberOfFontRuns = 0u;
229 GetNumberOfFontRuns( characterIndex,
234 memcpy( fontRuns, mFontRuns.Begin() + firstFontRun, numberOfFontRuns * sizeof( FontRun ) );
237 FontId LogicalModel::GetFont( CharacterIndex characterIndex ) const
239 for( Length index = 0u, length = mFontRuns.Count(); index < length; ++index )
241 const FontRun* const fontRun = mFontRuns.Begin() + index;
243 if( ( fontRun->characterRun.characterIndex <= characterIndex ) &&
244 ( characterIndex < fontRun->characterRun.characterIndex + fontRun->characterRun.numberOfCharacters ) )
246 return fontRun->fontId;
253 void LogicalModel::ReplaceFonts( CharacterIndex characterIndex,
254 Length numberOfCharactersToRemove,
255 const FontRun* const fontRuns,
256 Length numberOfCharactersToInsert )
260 void LogicalModel::SetLineBreakInfo( const LineBreakInfo* const lineBreakInfo,
265 mLineBreakInfo.Clear();
269 mLineBreakInfo.Resize( length );
270 memcpy( mLineBreakInfo.Begin(), lineBreakInfo, length * sizeof( LineBreakInfo ) );
274 void LogicalModel::GetLineBreakInfo( LineBreakInfo* lineBreakInfo,
275 CharacterIndex characterIndex,
276 Length numberOfItems ) const
278 memcpy( lineBreakInfo, mLineBreakInfo.Begin() + characterIndex, numberOfItems * sizeof( LineBreakInfo ) );
281 LineBreakInfo LogicalModel::GetLineBreakInfo( CharacterIndex characterIndex ) const
283 return *( mLineBreakInfo.Begin() + characterIndex );
286 void LogicalModel::ReplaceLineBreakInfo( CharacterIndex characterIndex,
287 Length numberOfItemsToRemove,
288 const LineBreakInfo* const lineBreakInfo,
289 Length numberOfItemsToInsert )
293 void LogicalModel::SetWordBreakInfo( const WordBreakInfo* const wordBreakInfo,
298 mWordBreakInfo.Clear();
302 mWordBreakInfo.Resize( length );
303 memcpy( mWordBreakInfo.Begin(), wordBreakInfo, length * sizeof( WordBreakInfo ) );
307 void LogicalModel::GetWordBreakInfo( WordBreakInfo* wordBreakInfo,
308 CharacterIndex characterIndex,
309 Length numberOfItems ) const
311 memcpy( wordBreakInfo, mWordBreakInfo.Begin() + characterIndex, numberOfItems * sizeof( WordBreakInfo ) );
314 WordBreakInfo LogicalModel::GetWordBreakInfo( CharacterIndex characterIndex ) const
316 return *( mWordBreakInfo.Begin() + characterIndex );
319 void LogicalModel::ReplaceWordBreakInfo( CharacterIndex characterIndex,
320 Length numberOfItemsToRemove,
321 const WordBreakInfo* const wordBreakInfo,
322 Length numberOfItemsToInsert )
326 void LogicalModel::SetBidirectionalInfo( const BidirectionalParagraphInfoRun* const bidirectionalInfo,
327 Length numberOfRuns )
329 if( 0u == numberOfRuns )
331 mBidirectionalParagraphInfo.Clear();
335 mBidirectionalParagraphInfo.Resize( numberOfRuns );
336 memcpy( mBidirectionalParagraphInfo.Begin(), bidirectionalInfo, numberOfRuns * sizeof( BidirectionalParagraphInfoRun ) );
340 void LogicalModel::GetNumberOfBidirectionalInfoRuns( CharacterIndex characterIndex,
341 Length numberOfCharacters,
342 BidirectionalRunIndex& firstBidirectionalRun,
343 Length& numberOfFontRuns ) const
345 // Initialize the number of bidi paragraphs and the index to the first paragraph.
346 firstBidirectionalRun = 0u;
347 numberOfFontRuns = 0;
348 bool firstParagraphFound = false;
350 // Traverse the bidirectional paragraph info and count those bidi paragraphs within the range of characters.
351 for( Vector<BidirectionalParagraphInfoRun>::ConstIterator it = mBidirectionalParagraphInfo.Begin(),
352 endIt = mBidirectionalParagraphInfo.End();
356 const BidirectionalParagraphInfoRun& bidi = *it;
358 if( ( bidi.characterRun.characterIndex + bidi.characterRun.numberOfCharacters > characterIndex ) &&
359 ( characterIndex + numberOfCharacters > bidi.characterRun.characterIndex ) )
361 firstParagraphFound = true;
365 if( !firstParagraphFound )
367 ++firstBidirectionalRun;
372 void LogicalModel::GetBidirectionalInfo( BidirectionalParagraphInfoRun* bidirectionalInfo,
373 CharacterIndex characterIndex,
374 Length numberOfCharacters ) const
376 BidirectionalRunIndex firstBidirectionalRun = 0u;
377 Length numberOfFontRuns = 0u;
379 GetNumberOfBidirectionalInfoRuns( characterIndex,
381 firstBidirectionalRun,
384 memcpy( bidirectionalInfo, mBidirectionalParagraphInfo.Begin() + firstBidirectionalRun, numberOfFontRuns * sizeof( BidirectionalParagraphInfoRun ) );
387 void ReplaceBidirectionalInfo( CharacterIndex characterIndex,
388 Length numberOfCharactersToRemove,
389 const BidirectionalParagraphInfoRun* const bidirectionalInfo,
390 Length numberOfCharactersToInsert )
394 void LogicalModel::SetCharacterDirections( const CharacterDirection* const directions,
395 Length numberOfCharacters )
397 if( 0u == numberOfCharacters )
399 mCharacterDirections.Clear();
403 mCharacterDirections.Resize( numberOfCharacters );
404 memcpy( mCharacterDirections.Begin(), directions, numberOfCharacters * sizeof( CharacterDirection ) );
408 void LogicalModel::GetCharacterDirections( CharacterDirection* directions,
409 CharacterIndex characterIndex,
410 Length numberOfCharacters ) const
412 if( 0u == mCharacterDirections.Count() )
414 // Nothing to retrieve if the model has no right to left characters.
418 memcpy( directions, mCharacterDirections.Begin() + characterIndex, numberOfCharacters * sizeof( CharacterDirection ) );
421 CharacterDirection LogicalModel::GetCharacterDirection( CharacterIndex characterIndex ) const
423 if( characterIndex >= mCharacterDirections.Count() )
425 // The model has no right to left characters, so the vector of directions is void.
429 return *( mCharacterDirections.Begin() + characterIndex );
432 void LogicalModel::SetVisualToLogicalMap( const BidirectionalLineInfoRun* const bidirectionalInfo,
433 Length numberOfRuns )
435 if( 0u == numberOfRuns )
437 mVisualToLogicalMap.Clear();
438 mLogicalToVisualMap.Clear();
439 mVisualToLogicalCursorMap.Clear();
443 const Length numberOfCharacters = mText.Count();
444 mVisualToLogicalMap.Resize( numberOfCharacters );
445 mLogicalToVisualMap.Resize( numberOfCharacters );
447 const Length numberOfCharactersPlus = numberOfCharacters + 1u;
448 mVisualToLogicalCursorMap.Resize( numberOfCharactersPlus );
450 CharacterIndex* modelVisualToLogicalMapBuffer = mVisualToLogicalMap.Begin();
451 CharacterIndex* modelLogicalToVisualMapBuffer = mLogicalToVisualMap.Begin();
453 CharacterIndex* modelVisualToLogicalCursorMap = mVisualToLogicalCursorMap.Begin();
455 CharacterIndex lastIndex = 0u;
456 for( unsigned int bidiIndex = 0u; bidiIndex < numberOfRuns; ++bidiIndex )
458 const BidirectionalLineInfoRun& bidiLineInfo = *( bidirectionalInfo + bidiIndex );
460 if( lastIndex < bidiLineInfo.characterRun.characterIndex )
462 // Fill with the identity.
463 for( ; lastIndex < bidiLineInfo.characterRun.characterIndex; ++lastIndex )
465 *( modelVisualToLogicalMapBuffer + lastIndex ) = lastIndex;
469 // Fill the conversion table of the run.
470 for( CharacterIndex index = 0u;
471 index < bidiLineInfo.characterRun.numberOfCharacters;
472 ++index, ++lastIndex )
474 *( modelVisualToLogicalMapBuffer + lastIndex ) = bidiLineInfo.characterRun.characterIndex + *( bidiLineInfo.visualToLogicalMap + index );
478 // Complete with the identity if there are some left to right characters after the last right to left.
479 for( ; lastIndex < numberOfCharacters; ++lastIndex )
481 *( modelVisualToLogicalMapBuffer + lastIndex ) = lastIndex;
484 // Sets the logical to visual conversion map.
485 for( CharacterIndex index = 0u; index < numberOfCharacters; ++index )
487 *( modelLogicalToVisualMapBuffer + *( modelVisualToLogicalMapBuffer + index ) ) = index;
490 // Sets the visual to logical conversion map for cursor positions.
492 const Length numberOfBidirectionalParagraphs = mBidirectionalParagraphInfo.Count();
493 BidirectionalParagraphInfoRun* bidirectionalParagraphInfoBuffer = mBidirectionalParagraphInfo.Begin();
494 BidirectionalParagraphInfoRun* bidirectionalParagraph = bidirectionalParagraphInfoBuffer;
496 const CharacterDirection* const modelCharacterDirections = mCharacterDirections.Begin();
498 Length bidirectionalParagraphIndex = 0u;
499 bool isRightToLeftParagraph = false;
500 for( CharacterIndex index = 0u; index < numberOfCharactersPlus; ++index )
502 if( bidirectionalParagraph &&
503 ( bidirectionalParagraph->characterRun.characterIndex == index ) )
505 isRightToLeftParagraph = *( modelCharacterDirections + index );
510 if( isRightToLeftParagraph )
512 *( modelVisualToLogicalCursorMap + index ) = numberOfCharacters;
514 else // else logical position is zero.
516 *( modelVisualToLogicalCursorMap + index ) = 0u;
519 else if( numberOfCharacters == index )
521 if( isRightToLeftParagraph )
523 *( modelVisualToLogicalCursorMap + index ) = 0u;
525 else // else logical position is the number of characters.
527 *( modelVisualToLogicalCursorMap + index ) = numberOfCharacters;
532 // Get the character indexed by index - 1 and index
533 // and calculate the logical position according the directions of
534 // both characters and the direction of the paragraph.
536 const CharacterIndex previousIndex = index - 1u;
537 const CharacterIndex logicalPosition0 = *( modelVisualToLogicalMapBuffer + previousIndex );
538 const CharacterIndex logicalPosition1 = *( modelVisualToLogicalMapBuffer + index );
540 const CharacterDirection direction0 = *( modelCharacterDirections + logicalPosition0 );
541 const CharacterDirection direction1 = *( modelCharacterDirections + logicalPosition1 );
543 if( direction0 == direction1 )
545 // Both glyphs have the same direction.
548 *( modelVisualToLogicalCursorMap + index ) = logicalPosition0;
552 *( modelVisualToLogicalCursorMap + index ) = logicalPosition1;
557 if( isRightToLeftParagraph )
561 *( modelVisualToLogicalCursorMap + index ) = logicalPosition1 + 1u;
565 *( modelVisualToLogicalCursorMap + index ) = logicalPosition0;
572 *( modelVisualToLogicalCursorMap + index ) = logicalPosition1;
576 *( modelVisualToLogicalCursorMap + index ) = logicalPosition0 + 1u;
582 if( bidirectionalParagraph &&
583 ( bidirectionalParagraph->characterRun.characterIndex + bidirectionalParagraph->characterRun.numberOfCharacters == index ) )
585 isRightToLeftParagraph = false;
586 ++bidirectionalParagraphIndex;
587 if( bidirectionalParagraphIndex < numberOfBidirectionalParagraphs )
589 bidirectionalParagraph = bidirectionalParagraphInfoBuffer + bidirectionalParagraphIndex;
593 bidirectionalParagraph = NULL;
600 void LogicalModel::ReplaceVisualToLogicalMap( CharacterIndex characterIndex,
601 Length numberOfCharactersToRemove,
602 const BidirectionalLineInfoRun* const bidirectionalInfo,
603 Length numberOfCharactersToInsert )
607 CharacterIndex LogicalModel::GetVisualCharacterIndex( CharacterIndex logicalCharacterIndex ) const
609 if( 0u == mLogicalToVisualMap.Count() )
611 // If there is no logical to visual info is because the whole text is left to right.
612 // Return the identity.
613 return logicalCharacterIndex;
616 return *( mLogicalToVisualMap.Begin() + logicalCharacterIndex );
619 CharacterIndex LogicalModel::GetLogicalCharacterIndex( CharacterIndex visualCharacterIndex ) const
621 if( 0u == mVisualToLogicalMap.Count() )
623 // If there is no visual to logical info is because the whole text is left to right.
624 // Return the identity.
625 return visualCharacterIndex;
628 return *( mVisualToLogicalMap.Begin() + visualCharacterIndex );
631 void LogicalModel::GetLogicalToVisualMap( CharacterIndex* logicalToVisualMap,
632 CharacterIndex characterIndex,
633 Length numberOfCharacters ) const
635 memcpy( logicalToVisualMap, mLogicalToVisualMap.Begin() + characterIndex, numberOfCharacters * sizeof( CharacterIndex ) );
638 void LogicalModel::GetVisualToLogicalMap( CharacterIndex* visualToLogicalMap,
639 CharacterIndex characterIndex,
640 Length numberOfCharacters ) const
642 memcpy( visualToLogicalMap, mVisualToLogicalMap.Begin() + characterIndex, numberOfCharacters * sizeof( CharacterIndex ) );
645 LogicalModel::~LogicalModel()
649 LogicalModel::LogicalModel()
655 } // namespace Toolkit