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.h>
25 #include <dali-toolkit/internal/text/bidirectional-line-info-run.h>
26 #include <dali-toolkit/internal/text/bidirectional-paragraph-info-run.h>
27 #include <dali-toolkit/internal/text/font-run.h>
28 #include <dali-toolkit/internal/text/script-run.h>
39 * @brief caches some temporary values of the GetNumberOfScriptRuns( characterIndex, numberOfCharacters )
40 * operation and the GetNumberOfFontRuns( characterIndex, numberOfCharacters ) as they are going to be
41 * used in the GetScriptRuns() and the GetFontRuns() calls.
45 CharacterIndex characterIndex; ///< The character index.
46 Length numberOfCharacters; ///< The number of characters.
47 Length firstRun; ///< Index to the first run.
48 Length numberOfRuns; ///< The number of runs.
51 struct LogicalModel::Impl
53 Vector<Character> mText;
54 Vector<ScriptRun> mScriptRuns;
55 Vector<FontRun> mFontRuns;
56 Vector<LineBreakInfo> mLineBreakInfo;
57 Vector<WordBreakInfo> mWordBreakInfo;
58 Vector<BidirectionalParagraphInfoRun> mBidirectionalParagraphInfo;
60 Vector<BidirectionalLineInfoRun> mBidirectionalLineInfo;
61 Vector<CharacterIndex> mLogicalToVisualMap; ///< Bidirectional logical to visual conversion table.
62 Vector<CharacterIndex> mVisualToLogicalMap; ///< Bidirectional visual to logical conversion table.
64 GetRunCache mGetScriptCache; ///< Caches the GetNumberOfScriptRuns( characterIndex, numberOfCharacters ) operation.
65 GetRunCache mGetFontCache; ///< Caches the GetNumberOfFontRuns( characterIndex, numberOfCharacters ) operation.
66 GetRunCache mGetBidirectionalCache; ///< Caches the GetNumberOfBidirectionalInfoRuns( characterIndex, numberOfCharacters ) operation.
69 LogicalModelPtr LogicalModel::New()
71 return LogicalModelPtr( new LogicalModel() );
74 void LogicalModel::SetText( const Character* const text,
75 Length numberOfCharacters )
77 Vector<Character>& modelText = mImpl->mText;
79 if( 0u == numberOfCharacters )
85 modelText.Resize( numberOfCharacters );
86 memcpy( modelText.Begin(), text, numberOfCharacters * sizeof( Character ) );
90 Length LogicalModel::GetNumberOfCharacters() const
92 return mImpl->mText.Count();
95 void LogicalModel::GetText( Character* text,
96 CharacterIndex characterIndex,
97 Length numberOfCharacters ) const
99 Vector<Character>& modelText = mImpl->mText;
100 memcpy( text, modelText.Begin() + characterIndex, numberOfCharacters * sizeof( Character ) );
103 Character LogicalModel::GetCharacter( CharacterIndex characterIndex ) const
105 return mImpl->mText[characterIndex];
108 void LogicalModel::ReplaceText( CharacterIndex characterIndex,
109 Length numberOfCharactersToRemove,
110 const Character* const text,
111 Length numberOfCharactersToInsert )
115 void LogicalModel::SetScripts( const ScriptRun* const scripts,
116 Length numberOfRuns )
118 Vector<ScriptRun>& scriptRuns = mImpl->mScriptRuns;
120 if( 0u == numberOfRuns )
126 scriptRuns.Resize( numberOfRuns );
127 memcpy( scriptRuns.Begin(), scripts, numberOfRuns * sizeof( ScriptRun ) );
130 mImpl->mGetScriptCache.characterIndex = 0u;
131 mImpl->mGetScriptCache.numberOfCharacters = 0u;
132 mImpl->mGetScriptCache.firstRun = 0u;
133 mImpl->mGetScriptCache.numberOfRuns = 0u;
136 Length LogicalModel::GetNumberOfScriptRuns( CharacterIndex characterIndex,
137 Length numberOfCharacters ) const
139 GetRunCache& scriptCache = mImpl->mGetScriptCache;
141 // Set the character index and the number of characters into the cache.
142 scriptCache.characterIndex = characterIndex;
143 scriptCache.numberOfCharacters = numberOfCharacters;
145 if( ( 0u == characterIndex ) &&
146 ( mImpl->mText.Count() == numberOfCharacters ) )
148 scriptCache.firstRun = 0u;
149 scriptCache.numberOfRuns = mImpl->mScriptRuns.Count();
150 return scriptCache.numberOfRuns;
153 // Initialize the number of scripts and the index to the first script.
154 scriptCache.firstRun = 0u;
155 scriptCache.numberOfRuns = 0;
156 bool firstScriptFound = false;
158 const Vector<ScriptRun>& modelScripts = mImpl->mScriptRuns;
159 const CharacterIndex lastCharacterIndex = characterIndex + numberOfCharacters;
161 // Traverse the scripts and count those scripts within the range of characters.
162 for( Vector<ScriptRun>::ConstIterator it = modelScripts.Begin(),
163 endIt = modelScripts.End();
167 const ScriptRun& script = *it;
169 if( ( script.characterRun.characterIndex + script.characterRun.numberOfCharacters > characterIndex ) &&
170 ( lastCharacterIndex > script.characterRun.characterIndex ) )
172 firstScriptFound = true;
173 ++scriptCache.numberOfRuns;
175 else if( lastCharacterIndex <= script.characterRun.characterIndex )
177 // nothing else to do.
181 if( !firstScriptFound )
183 ++scriptCache.firstRun;
187 return scriptCache.numberOfRuns;
190 void LogicalModel::GetScriptRuns( ScriptRun* scriptRuns,
191 CharacterIndex characterIndex,
192 Length numberOfCharacters ) const
194 const Vector<ScriptRun>& modelScripts = mImpl->mScriptRuns;
195 GetRunCache& scriptCache = mImpl->mGetScriptCache;
197 if( ( characterIndex != scriptCache.characterIndex ) ||
198 ( numberOfCharacters != scriptCache.numberOfCharacters ) )
200 GetNumberOfScriptRuns( characterIndex,
201 numberOfCharacters );
204 memcpy( scriptRuns, modelScripts.Begin() + scriptCache.firstRun, scriptCache.numberOfRuns * sizeof( ScriptRun ) );
207 Script LogicalModel::GetScript( CharacterIndex characterIndex ) const
209 // If this operation is too slow, consider a binary search.
211 for( Length index = 0u, length = mImpl->mScriptRuns.Count(); index < length; ++index )
213 const ScriptRun* const scriptRun = mImpl->mScriptRuns.Begin() + index;
215 if( ( scriptRun->characterRun.characterIndex <= characterIndex ) &&
216 ( characterIndex < scriptRun->characterRun.characterIndex + scriptRun->characterRun.numberOfCharacters ) )
218 return scriptRun->script;
222 return TextAbstraction::UNKNOWN;
225 void LogicalModel::ReplaceScripts( CharacterIndex characterIndex,
226 Length numberOfCharactersToRemove,
227 const ScriptRun* const scriptRuns,
228 Length numberOfCharactersToInsert )
232 void LogicalModel::SetFonts( const FontRun* const fonts,
233 Length numberOfRuns )
235 Vector<FontRun>& fontRuns = mImpl->mFontRuns;
237 if( 0u == numberOfRuns )
243 fontRuns.Resize( numberOfRuns );
244 memcpy( fontRuns.Begin(), fonts, numberOfRuns * sizeof( FontRun ) );
247 mImpl->mGetFontCache.characterIndex = 0u;
248 mImpl->mGetFontCache.numberOfCharacters = 0u;
249 mImpl->mGetFontCache.firstRun = 0u;
250 mImpl->mGetFontCache.numberOfRuns = 0u;
253 Length LogicalModel::GetNumberOfFontRuns( CharacterIndex characterIndex,
254 Length numberOfCharacters ) const
256 GetRunCache& fontCache = mImpl->mGetFontCache;
258 // Set the character index and the number of characters into the cache.
259 fontCache.characterIndex = characterIndex;
260 fontCache.numberOfCharacters = numberOfCharacters;
262 if( ( 0u == characterIndex ) &&
263 ( mImpl->mText.Count() == numberOfCharacters ) )
265 fontCache.firstRun = 0u;
266 fontCache.numberOfRuns = mImpl->mFontRuns.Count();
267 return fontCache.numberOfRuns;
270 // Initialize the number of fonts and the index to the first font.
271 fontCache.firstRun = 0u;
272 fontCache.numberOfRuns = 0;
273 bool firstFontFound = false;
275 const Vector<FontRun>& modelFonts = mImpl->mFontRuns;
276 const CharacterIndex lastCharacterIndex = characterIndex + numberOfCharacters;
278 // Traverse the fonts and count those fonts within the range of characters.
279 for( Vector<FontRun>::ConstIterator it = modelFonts.Begin(),
280 endIt = modelFonts.End();
284 const FontRun& font = *it;
286 if( ( font.characterRun.characterIndex + font.characterRun.numberOfCharacters > characterIndex ) &&
287 ( characterIndex + numberOfCharacters > font.characterRun.characterIndex ) )
289 firstFontFound = true;
290 ++fontCache.numberOfRuns;
292 else if( lastCharacterIndex <= font.characterRun.characterIndex )
294 // nothing else to do.
298 if( !firstFontFound )
300 ++fontCache.firstRun;
304 return fontCache.numberOfRuns;
307 void LogicalModel::GetFontRuns( FontRun* fontRuns,
308 CharacterIndex characterIndex,
309 Length numberOfCharacters ) const
311 const Vector<FontRun>& modelFonts = mImpl->mFontRuns;
312 GetRunCache& fontCache = mImpl->mGetFontCache;
314 if( ( characterIndex != fontCache.characterIndex ) ||
315 ( numberOfCharacters != fontCache.numberOfCharacters ) )
317 GetNumberOfFontRuns( characterIndex,
318 numberOfCharacters );
321 memcpy( fontRuns, modelFonts.Begin() + fontCache.firstRun, fontCache.numberOfRuns * sizeof( FontRun ) );
324 FontId LogicalModel::GetFont( CharacterIndex characterIndex ) const
326 for( Length index = 0u, length = mImpl->mFontRuns.Count(); index < length; ++index )
328 const FontRun* const fontRun = mImpl->mFontRuns.Begin() + index;
330 if( ( fontRun->characterRun.characterIndex <= characterIndex ) &&
331 ( characterIndex < fontRun->characterRun.characterIndex + fontRun->characterRun.numberOfCharacters ) )
333 return fontRun->fontId;
340 void LogicalModel::ReplaceFonts( CharacterIndex characterIndex,
341 Length numberOfCharactersToRemove,
342 const FontRun* const fontRuns,
343 Length numberOfCharactersToInsert )
347 void LogicalModel::SetLineBreakInfo( const LineBreakInfo* const lineBreakInfo,
350 Vector<LineBreakInfo>& modelLineBreakInfo = mImpl->mLineBreakInfo;
354 modelLineBreakInfo.Clear();
358 modelLineBreakInfo.Resize( length );
359 memcpy( modelLineBreakInfo.Begin(), lineBreakInfo, length * sizeof( LineBreakInfo ) );
363 void LogicalModel::GetLineBreakInfo( LineBreakInfo* lineBreakInfo,
364 CharacterIndex characterIndex,
365 Length numberOfItems ) const
367 memcpy( lineBreakInfo, mImpl->mLineBreakInfo.Begin() + characterIndex, numberOfItems * sizeof( LineBreakInfo ) );
370 LineBreakInfo LogicalModel::GetLineBreakInfo( CharacterIndex characterIndex ) const
372 return *( mImpl->mLineBreakInfo.Begin() + characterIndex );
375 void LogicalModel::ReplaceLineBreakInfo( CharacterIndex characterIndex,
376 Length numberOfItemsToRemove,
377 const LineBreakInfo* const lineBreakInfo,
378 Length numberOfItemsToInsert )
382 void LogicalModel::SetWordBreakInfo( const WordBreakInfo* const wordBreakInfo,
385 Vector<WordBreakInfo>& modelWordBreakInfo = mImpl->mWordBreakInfo;
389 modelWordBreakInfo.Clear();
393 modelWordBreakInfo.Resize( length );
394 memcpy( modelWordBreakInfo.Begin(), wordBreakInfo, length * sizeof( WordBreakInfo ) );
398 void LogicalModel::GetWordBreakInfo( WordBreakInfo* wordBreakInfo,
399 CharacterIndex characterIndex,
400 Length numberOfItems ) const
402 memcpy( wordBreakInfo, mImpl->mWordBreakInfo.Begin() + characterIndex, numberOfItems * sizeof( WordBreakInfo ) );
405 WordBreakInfo LogicalModel::GetWordBreakInfo( CharacterIndex characterIndex ) const
407 return *( mImpl->mWordBreakInfo.Begin() + characterIndex );
410 void LogicalModel::ReplaceWordBreakInfo( CharacterIndex characterIndex,
411 Length numberOfItemsToRemove,
412 const WordBreakInfo* const wordBreakInfo,
413 Length numberOfItemsToInsert )
417 void LogicalModel::SetBidirectionalInfo( const BidirectionalParagraphInfoRun* const bidirectionalInfo,
418 Length numberOfRuns )
420 Vector<BidirectionalParagraphInfoRun>& modelBidirectionalParagraphInfo = mImpl->mBidirectionalParagraphInfo;
422 if( 0u == numberOfRuns )
424 modelBidirectionalParagraphInfo.Clear();
428 modelBidirectionalParagraphInfo.Resize( numberOfRuns );
429 memcpy( modelBidirectionalParagraphInfo.Begin(), bidirectionalInfo, numberOfRuns * sizeof( BidirectionalParagraphInfoRun ) );
432 mImpl->mGetBidirectionalCache.characterIndex = 0u;
433 mImpl->mGetBidirectionalCache.numberOfCharacters = 0u;
434 mImpl->mGetBidirectionalCache.firstRun = 0u;
435 mImpl->mGetBidirectionalCache.numberOfRuns = 0u;
438 Length LogicalModel::GetNumberOfBidirectionalInfoRuns( CharacterIndex characterIndex,
439 Length numberOfCharacters ) const
441 GetRunCache& bidiCache = mImpl->mGetBidirectionalCache;
443 // Set the character index and the number of characters into the cache.
444 bidiCache.characterIndex = characterIndex;
445 bidiCache.numberOfCharacters = numberOfCharacters;
447 if( ( 0u == characterIndex ) &&
448 ( mImpl->mText.Count() == numberOfCharacters ) )
450 bidiCache.firstRun = 0u;
451 bidiCache.numberOfRuns = mImpl->mBidirectionalParagraphInfo.Count();
452 return bidiCache.numberOfRuns;
455 // Initialize the number of bidi paragraphs and the index to the first paragraph.
456 bidiCache.firstRun = 0u;
457 bidiCache.numberOfRuns = 0;
458 bool firstParagraphFound = false;
460 const Vector<BidirectionalParagraphInfoRun>& modelBidirectionalParagraphInfo = mImpl->mBidirectionalParagraphInfo;
462 // Traverse the bidirectional paragraph info and count those bidi paragraphs within the range of characters.
463 for( Vector<BidirectionalParagraphInfoRun>::ConstIterator it = modelBidirectionalParagraphInfo.Begin(),
464 endIt = modelBidirectionalParagraphInfo.End();
468 const BidirectionalParagraphInfoRun& bidi = *it;
470 if( ( bidi.characterRun.characterIndex + bidi.characterRun.numberOfCharacters > characterIndex ) &&
471 ( characterIndex + numberOfCharacters > bidi.characterRun.characterIndex ) )
473 firstParagraphFound = true;
474 ++bidiCache.numberOfRuns;
477 if( !firstParagraphFound )
479 ++bidiCache.firstRun;
483 return bidiCache.numberOfRuns;
486 void LogicalModel::GetBidirectionalInfo( BidirectionalParagraphInfoRun* bidirectionalInfo,
487 CharacterIndex characterIndex,
488 Length numberOfCharacters ) const
490 const Vector<BidirectionalParagraphInfoRun>& modelBidirectionalParagraphInfo = mImpl->mBidirectionalParagraphInfo;
491 GetRunCache& bidiCache = mImpl->mGetBidirectionalCache;
493 if( ( characterIndex != bidiCache.characterIndex ) ||
494 ( numberOfCharacters != bidiCache.numberOfCharacters ) )
496 GetNumberOfBidirectionalInfoRuns( characterIndex,
497 numberOfCharacters );
500 memcpy( bidirectionalInfo, modelBidirectionalParagraphInfo.Begin() + bidiCache.firstRun, bidiCache.numberOfRuns * sizeof( BidirectionalParagraphInfoRun ) );
503 void ReplaceBidirectionalInfo( CharacterIndex characterIndex,
504 Length numberOfCharactersToRemove,
505 const BidirectionalParagraphInfoRun* const bidirectionalInfo,
506 Length numberOfCharactersToInsert )
510 void LogicalModel::GetCharacterDirections( CharacterDirection* directions,
511 CharacterIndex characterIndex,
512 Length numberOfCharacters ) const
516 CharacterDirection LogicalModel::GetCharacterDirection( CharacterIndex characterIndex ) const
521 void LogicalModel::SetVisualToLogicalMap( const BidirectionalLineInfoRun* const bidirectionalInfo,
522 Length numberOfRuns )
524 Vector<CharacterIndex>& modelVisualToLogicalMap = mImpl->mVisualToLogicalMap;
525 Vector<CharacterIndex>& modelLogicalToVisualMap = mImpl->mLogicalToVisualMap;
527 if( 0u == numberOfRuns )
529 modelVisualToLogicalMap.Clear();
530 modelLogicalToVisualMap.Clear();
534 const Length numberOfCharacters = mImpl->mText.Count();
535 modelVisualToLogicalMap.Resize( numberOfCharacters );
536 modelLogicalToVisualMap.Resize( numberOfCharacters );
538 CharacterIndex* modelVisualToLogicalMapBuffer = modelVisualToLogicalMap.Begin();
539 CharacterIndex* modelLogicalToVisualMapBuffer = modelLogicalToVisualMap.Begin();
541 CharacterIndex lastIndex = 0u;
542 for( unsigned int bidiIndex = 0u; bidiIndex < numberOfRuns; ++bidiIndex )
544 const BidirectionalLineInfoRun& bidiLineInfo = *( bidirectionalInfo + bidiIndex );
546 if( lastIndex < bidiLineInfo.characterRun.characterIndex )
548 // Fill with the identity.
549 for( ; lastIndex < bidiLineInfo.characterRun.characterIndex; ++lastIndex )
551 *( modelVisualToLogicalMapBuffer + lastIndex ) = lastIndex;
555 // Fill the conversion table of the run.
556 for( CharacterIndex index = 0u;
557 index < bidiLineInfo.characterRun.numberOfCharacters;
558 ++index, ++lastIndex )
560 *( modelVisualToLogicalMapBuffer + lastIndex ) = bidiLineInfo.characterRun.characterIndex + *( bidiLineInfo.visualToLogicalMap + index );
564 // Complete with the identity if there are some left to right characters after the last right to left.
565 for( ; lastIndex < numberOfCharacters; ++lastIndex )
567 *( modelVisualToLogicalMapBuffer + lastIndex ) = lastIndex;
570 // Sets the logical to visual conversion map.
571 for( CharacterIndex index = 0u; index < numberOfCharacters; ++index )
573 *( modelLogicalToVisualMapBuffer + *( modelVisualToLogicalMapBuffer + index ) ) = index;
578 void LogicalModel::ReplaceVisualToLogicalMap( CharacterIndex characterIndex,
579 Length numberOfCharactersToRemove,
580 const BidirectionalLineInfoRun* const bidirectionalInfo,
581 Length numberOfCharactersToInsert )
585 CharacterIndex LogicalModel::GetVisualCharacterIndex( CharacterIndex logicalCharacterIndex ) const
587 if( 0u == mImpl->mLogicalToVisualMap.Count() )
589 // If there is no logical to visual info is because the whole text is left to right.
590 // Return the identity.
591 return logicalCharacterIndex;
594 return *( mImpl->mLogicalToVisualMap.Begin() + logicalCharacterIndex );
597 CharacterIndex LogicalModel::GetLogicalCharacterIndex( CharacterIndex visualCharacterIndex ) const
599 if( 0u == mImpl->mVisualToLogicalMap.Count() )
601 // If there is no visual to logical info is because the whole text is left to right.
602 // Return the identity.
603 return visualCharacterIndex;
606 return *( mImpl->mVisualToLogicalMap.Begin() + visualCharacterIndex );
609 void LogicalModel::GetLogicalToVisualMap( CharacterIndex* logicalToVisualMap,
610 CharacterIndex characterIndex,
611 Length numberOfCharacters ) const
613 memcpy( logicalToVisualMap, mImpl->mLogicalToVisualMap.Begin() + characterIndex, numberOfCharacters * sizeof( CharacterIndex ) );
616 void LogicalModel::GetVisualToLogicalMap( CharacterIndex* visualToLogicalMap,
617 CharacterIndex characterIndex,
618 Length numberOfCharacters ) const
620 memcpy( visualToLogicalMap, mImpl->mVisualToLogicalMap.Begin() + characterIndex, numberOfCharacters * sizeof( CharacterIndex ) );
623 LogicalModel::~LogicalModel()
628 LogicalModel::LogicalModel()
631 mImpl = new LogicalModel::Impl();
636 } // namespace Toolkit