TextModel interface
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / text / logical-model.cpp
1 /*
2  * Copyright (c) 2015 Samsung Electronics Co., Ltd.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  *
16  */
17
18 // CLASS HEADER
19 #include <dali-toolkit/internal/text/logical-model.h>
20
21 // EXTERNAL INCLUDES
22 #include <memory.h>
23
24 // INTERNAL INCLUDES
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>
29
30 namespace Dali
31 {
32
33 namespace Toolkit
34 {
35
36 namespace Text
37 {
38 /**
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.
42  */
43 struct GetRunCache
44 {
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.
49 };
50
51 struct LogicalModel::Impl
52 {
53   Vector<Character>                     mText;
54   Vector<ScriptRun>                     mScriptRuns;
55   Vector<FontRun>                       mFontRuns;
56   Vector<LineBreakInfo>                 mLineBreakInfo;
57   Vector<WordBreakInfo>                 mWordBreakInfo;
58   Vector<BidirectionalParagraphInfoRun> mBidirectionalParagraphInfo;
59
60   Vector<BidirectionalLineInfoRun>      mBidirectionalLineInfo;
61   Vector<CharacterIndex>                mLogicalToVisualMap; ///< Bidirectional logical to visual conversion table.
62   Vector<CharacterIndex>                mVisualToLogicalMap; ///< Bidirectional visual to logical conversion table.
63
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.
67 };
68
69 LogicalModelPtr LogicalModel::New()
70 {
71   return LogicalModelPtr( new LogicalModel() );
72 }
73
74 void LogicalModel::SetText( const Character* const text,
75                             Length numberOfCharacters )
76 {
77   Vector<Character>& modelText = mImpl->mText;
78
79   if( 0u == numberOfCharacters )
80   {
81     modelText.Clear();
82   }
83   else
84   {
85     modelText.Resize( numberOfCharacters );
86     memcpy( modelText.Begin(), text, numberOfCharacters * sizeof( Character ) );
87   }
88 }
89
90 Length LogicalModel::GetNumberOfCharacters() const
91 {
92   return mImpl->mText.Count();
93 }
94
95 void LogicalModel::GetText( Character* text,
96                             CharacterIndex characterIndex,
97                             Length numberOfCharacters ) const
98 {
99   Vector<Character>& modelText = mImpl->mText;
100   memcpy( text, modelText.Begin() + characterIndex, numberOfCharacters * sizeof( Character ) );
101 }
102
103 Character LogicalModel::GetCharacter( CharacterIndex characterIndex ) const
104 {
105   return mImpl->mText[characterIndex];
106 }
107
108 void LogicalModel::ReplaceText( CharacterIndex characterIndex,
109                                 Length numberOfCharactersToRemove,
110                                 const Character* const text,
111                                 Length numberOfCharactersToInsert )
112 {
113 }
114
115 void LogicalModel::SetScripts( const ScriptRun* const scripts,
116                                Length numberOfRuns )
117 {
118   Vector<ScriptRun>& scriptRuns = mImpl->mScriptRuns;
119
120   if( 0u == numberOfRuns )
121   {
122     scriptRuns.Clear();
123   }
124   else
125   {
126     scriptRuns.Resize( numberOfRuns );
127     memcpy( scriptRuns.Begin(), scripts, numberOfRuns * sizeof( ScriptRun ) );
128   }
129
130   mImpl->mGetScriptCache.characterIndex = 0u;
131   mImpl->mGetScriptCache.numberOfCharacters = 0u;
132   mImpl->mGetScriptCache.firstRun = 0u;
133   mImpl->mGetScriptCache.numberOfRuns = 0u;
134 }
135
136 Length LogicalModel::GetNumberOfScriptRuns( CharacterIndex characterIndex,
137                                             Length numberOfCharacters ) const
138 {
139   GetRunCache& scriptCache = mImpl->mGetScriptCache;
140
141   // Set the character index and the number of characters into the cache.
142   scriptCache.characterIndex = characterIndex;
143   scriptCache.numberOfCharacters = numberOfCharacters;
144
145   if( ( 0u == characterIndex ) &&
146       ( mImpl->mText.Count() == numberOfCharacters ) )
147   {
148     scriptCache.firstRun = 0u;
149     scriptCache.numberOfRuns = mImpl->mScriptRuns.Count();
150     return scriptCache.numberOfRuns;
151   }
152
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;
157
158   const Vector<ScriptRun>& modelScripts = mImpl->mScriptRuns;
159   const CharacterIndex lastCharacterIndex = characterIndex + numberOfCharacters;
160
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();
164        it != endIt;
165        ++it )
166   {
167     const ScriptRun& script = *it;
168
169     if( ( script.characterRun.characterIndex + script.characterRun.numberOfCharacters > characterIndex ) &&
170         ( lastCharacterIndex > script.characterRun.characterIndex ) )
171     {
172       firstScriptFound = true;
173       ++scriptCache.numberOfRuns;
174     }
175     else if( lastCharacterIndex <= script.characterRun.characterIndex )
176     {
177       // nothing else to do.
178       break;
179     }
180
181     if( !firstScriptFound )
182     {
183       ++scriptCache.firstRun;
184     }
185   }
186
187   return scriptCache.numberOfRuns;
188 }
189
190 void LogicalModel::GetScriptRuns( ScriptRun* scriptRuns,
191                                   CharacterIndex characterIndex,
192                                   Length numberOfCharacters ) const
193 {
194   const Vector<ScriptRun>& modelScripts = mImpl->mScriptRuns;
195   GetRunCache& scriptCache = mImpl->mGetScriptCache;
196
197   if( ( characterIndex != scriptCache.characterIndex ) ||
198       ( numberOfCharacters != scriptCache.numberOfCharacters ) )
199   {
200     GetNumberOfScriptRuns( characterIndex,
201                            numberOfCharacters );
202   }
203
204   memcpy( scriptRuns, modelScripts.Begin() + scriptCache.firstRun, scriptCache.numberOfRuns * sizeof( ScriptRun ) );
205 }
206
207 Script LogicalModel::GetScript( CharacterIndex characterIndex ) const
208 {
209   // If this operation is too slow, consider a binary search.
210
211   for( Length index = 0u, length = mImpl->mScriptRuns.Count(); index < length; ++index )
212   {
213     const ScriptRun* const scriptRun = mImpl->mScriptRuns.Begin() + index;
214
215     if( ( scriptRun->characterRun.characterIndex <= characterIndex ) &&
216         ( characterIndex < scriptRun->characterRun.characterIndex + scriptRun->characterRun.numberOfCharacters ) )
217     {
218       return scriptRun->script;
219     }
220   }
221
222   return TextAbstraction::UNKNOWN;
223 }
224
225 void LogicalModel::ReplaceScripts( CharacterIndex characterIndex,
226                                    Length numberOfCharactersToRemove,
227                                    const ScriptRun* const scriptRuns,
228                                    Length numberOfCharactersToInsert )
229 {
230 }
231
232 void LogicalModel::SetFonts( const FontRun* const fonts,
233                              Length numberOfRuns )
234 {
235   Vector<FontRun>& fontRuns = mImpl->mFontRuns;
236
237   if( 0u == numberOfRuns )
238   {
239     fontRuns.Clear();
240   }
241   else
242   {
243     fontRuns.Resize( numberOfRuns );
244     memcpy( fontRuns.Begin(), fonts, numberOfRuns * sizeof( FontRun ) );
245   }
246
247   mImpl->mGetFontCache.characterIndex = 0u;
248   mImpl->mGetFontCache.numberOfCharacters = 0u;
249   mImpl->mGetFontCache.firstRun = 0u;
250   mImpl->mGetFontCache.numberOfRuns = 0u;
251 }
252
253 Length LogicalModel::GetNumberOfFontRuns( CharacterIndex characterIndex,
254                                           Length numberOfCharacters ) const
255 {
256   GetRunCache& fontCache = mImpl->mGetFontCache;
257
258   // Set the character index and the number of characters into the cache.
259   fontCache.characterIndex = characterIndex;
260   fontCache.numberOfCharacters = numberOfCharacters;
261
262   if( ( 0u == characterIndex ) &&
263       ( mImpl->mText.Count() == numberOfCharacters ) )
264   {
265     fontCache.firstRun = 0u;
266     fontCache.numberOfRuns = mImpl->mFontRuns.Count();
267     return fontCache.numberOfRuns;
268   }
269
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;
274
275   const Vector<FontRun>& modelFonts = mImpl->mFontRuns;
276   const CharacterIndex lastCharacterIndex = characterIndex + numberOfCharacters;
277
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();
281        it != endIt;
282        ++it )
283   {
284     const FontRun& font = *it;
285
286     if( ( font.characterRun.characterIndex + font.characterRun.numberOfCharacters > characterIndex ) &&
287         ( characterIndex + numberOfCharacters > font.characterRun.characterIndex ) )
288     {
289       firstFontFound = true;
290       ++fontCache.numberOfRuns;
291     }
292     else if( lastCharacterIndex <= font.characterRun.characterIndex )
293     {
294       // nothing else to do.
295       break;
296     }
297
298     if( !firstFontFound )
299     {
300       ++fontCache.firstRun;
301     }
302   }
303
304   return fontCache.numberOfRuns;
305 }
306
307 void LogicalModel::GetFontRuns( FontRun* fontRuns,
308                                 CharacterIndex characterIndex,
309                                 Length numberOfCharacters ) const
310 {
311   const Vector<FontRun>& modelFonts = mImpl->mFontRuns;
312   GetRunCache& fontCache = mImpl->mGetFontCache;
313
314   if( ( characterIndex != fontCache.characterIndex ) ||
315       ( numberOfCharacters != fontCache.numberOfCharacters ) )
316   {
317     GetNumberOfFontRuns( characterIndex,
318                          numberOfCharacters );
319   }
320
321   memcpy( fontRuns, modelFonts.Begin() + fontCache.firstRun, fontCache.numberOfRuns * sizeof( FontRun ) );
322 }
323
324 FontId LogicalModel::GetFont( CharacterIndex characterIndex ) const
325 {
326   for( Length index = 0u, length = mImpl->mFontRuns.Count(); index < length; ++index )
327   {
328     const FontRun* const fontRun = mImpl->mFontRuns.Begin() + index;
329
330     if( ( fontRun->characterRun.characterIndex <= characterIndex ) &&
331         ( characterIndex < fontRun->characterRun.characterIndex + fontRun->characterRun.numberOfCharacters ) )
332     {
333       return fontRun->fontId;
334     }
335   }
336
337   return 0u;
338 }
339
340 void LogicalModel::ReplaceFonts( CharacterIndex characterIndex,
341                                  Length numberOfCharactersToRemove,
342                                  const FontRun* const fontRuns,
343                                  Length numberOfCharactersToInsert )
344 {
345 }
346
347 void LogicalModel::SetLineBreakInfo( const LineBreakInfo* const lineBreakInfo,
348                                      Length length )
349 {
350   Vector<LineBreakInfo>& modelLineBreakInfo = mImpl->mLineBreakInfo;
351
352   if( 0u == length )
353   {
354     modelLineBreakInfo.Clear();
355   }
356   else
357   {
358     modelLineBreakInfo.Resize( length );
359     memcpy( modelLineBreakInfo.Begin(), lineBreakInfo, length * sizeof( LineBreakInfo ) );
360   }
361 }
362
363 void LogicalModel::GetLineBreakInfo( LineBreakInfo* lineBreakInfo,
364                                      CharacterIndex characterIndex,
365                                      Length numberOfItems ) const
366 {
367   memcpy( lineBreakInfo, mImpl->mLineBreakInfo.Begin() + characterIndex, numberOfItems * sizeof( LineBreakInfo ) );
368 }
369
370 LineBreakInfo LogicalModel::GetLineBreakInfo( CharacterIndex characterIndex ) const
371 {
372   return *( mImpl->mLineBreakInfo.Begin() + characterIndex );
373 }
374
375 void LogicalModel::ReplaceLineBreakInfo( CharacterIndex characterIndex,
376                                          Length numberOfItemsToRemove,
377                                          const LineBreakInfo* const lineBreakInfo,
378                                          Length numberOfItemsToInsert )
379 {
380 }
381
382 void LogicalModel::SetWordBreakInfo( const WordBreakInfo* const wordBreakInfo,
383                                      Length length )
384 {
385   Vector<WordBreakInfo>& modelWordBreakInfo = mImpl->mWordBreakInfo;
386
387   if( 0u == length )
388   {
389     modelWordBreakInfo.Clear();
390   }
391   else
392   {
393     modelWordBreakInfo.Resize( length );
394     memcpy( modelWordBreakInfo.Begin(), wordBreakInfo, length * sizeof( WordBreakInfo ) );
395   }
396 }
397
398 void LogicalModel::GetWordBreakInfo( WordBreakInfo* wordBreakInfo,
399                                      CharacterIndex characterIndex,
400                                      Length numberOfItems ) const
401 {
402   memcpy( wordBreakInfo, mImpl->mWordBreakInfo.Begin() + characterIndex, numberOfItems * sizeof( WordBreakInfo ) );
403 }
404
405 WordBreakInfo LogicalModel::GetWordBreakInfo( CharacterIndex characterIndex ) const
406 {
407   return *( mImpl->mWordBreakInfo.Begin() + characterIndex );
408 }
409
410 void LogicalModel::ReplaceWordBreakInfo( CharacterIndex characterIndex,
411                                          Length numberOfItemsToRemove,
412                                          const WordBreakInfo* const wordBreakInfo,
413                                          Length numberOfItemsToInsert )
414 {
415 }
416
417 void LogicalModel::SetBidirectionalInfo( const BidirectionalParagraphInfoRun* const bidirectionalInfo,
418                                          Length numberOfRuns )
419 {
420   Vector<BidirectionalParagraphInfoRun>& modelBidirectionalParagraphInfo = mImpl->mBidirectionalParagraphInfo;
421
422   if( 0u == numberOfRuns )
423   {
424     modelBidirectionalParagraphInfo.Clear();
425   }
426   else
427   {
428     modelBidirectionalParagraphInfo.Resize( numberOfRuns );
429     memcpy( modelBidirectionalParagraphInfo.Begin(), bidirectionalInfo, numberOfRuns * sizeof( BidirectionalParagraphInfoRun ) );
430   }
431
432   mImpl->mGetBidirectionalCache.characterIndex = 0u;
433   mImpl->mGetBidirectionalCache.numberOfCharacters = 0u;
434   mImpl->mGetBidirectionalCache.firstRun = 0u;
435   mImpl->mGetBidirectionalCache.numberOfRuns = 0u;
436 }
437
438 Length LogicalModel::GetNumberOfBidirectionalInfoRuns( CharacterIndex characterIndex,
439                                                        Length numberOfCharacters ) const
440 {
441   GetRunCache& bidiCache = mImpl->mGetBidirectionalCache;
442
443   // Set the character index and the number of characters into the cache.
444   bidiCache.characterIndex = characterIndex;
445   bidiCache.numberOfCharacters = numberOfCharacters;
446
447   if( ( 0u == characterIndex ) &&
448       ( mImpl->mText.Count() == numberOfCharacters ) )
449   {
450     bidiCache.firstRun = 0u;
451     bidiCache.numberOfRuns = mImpl->mBidirectionalParagraphInfo.Count();
452     return bidiCache.numberOfRuns;
453   }
454
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;
459
460   const Vector<BidirectionalParagraphInfoRun>& modelBidirectionalParagraphInfo = mImpl->mBidirectionalParagraphInfo;
461
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();
465        it != endIt;
466        ++it )
467   {
468     const BidirectionalParagraphInfoRun& bidi = *it;
469
470     if( ( bidi.characterRun.characterIndex + bidi.characterRun.numberOfCharacters > characterIndex ) &&
471         ( characterIndex + numberOfCharacters > bidi.characterRun.characterIndex ) )
472     {
473       firstParagraphFound = true;
474       ++bidiCache.numberOfRuns;
475     }
476
477     if( !firstParagraphFound )
478     {
479       ++bidiCache.firstRun;
480     }
481   }
482
483   return bidiCache.numberOfRuns;
484 }
485
486 void LogicalModel::GetBidirectionalInfo( BidirectionalParagraphInfoRun* bidirectionalInfo,
487                                          CharacterIndex characterIndex,
488                                          Length numberOfCharacters ) const
489 {
490   const Vector<BidirectionalParagraphInfoRun>& modelBidirectionalParagraphInfo = mImpl->mBidirectionalParagraphInfo;
491   GetRunCache& bidiCache = mImpl->mGetBidirectionalCache;
492
493   if( ( characterIndex != bidiCache.characterIndex ) ||
494       ( numberOfCharacters != bidiCache.numberOfCharacters ) )
495   {
496     GetNumberOfBidirectionalInfoRuns( characterIndex,
497                                       numberOfCharacters );
498   }
499
500   memcpy( bidirectionalInfo, modelBidirectionalParagraphInfo.Begin() + bidiCache.firstRun, bidiCache.numberOfRuns * sizeof( BidirectionalParagraphInfoRun ) );
501 }
502
503 void ReplaceBidirectionalInfo( CharacterIndex characterIndex,
504                                Length numberOfCharactersToRemove,
505                                const BidirectionalParagraphInfoRun* const bidirectionalInfo,
506                                Length numberOfCharactersToInsert )
507 {
508 }
509
510 void LogicalModel::GetCharacterDirections( CharacterDirection* directions,
511                                            CharacterIndex characterIndex,
512                                            Length numberOfCharacters ) const
513 {
514 }
515
516 CharacterDirection LogicalModel::GetCharacterDirection( CharacterIndex characterIndex ) const
517 {
518   return false;
519 }
520
521 void LogicalModel::SetVisualToLogicalMap( const BidirectionalLineInfoRun* const bidirectionalInfo,
522                                           Length numberOfRuns )
523 {
524   Vector<CharacterIndex>& modelVisualToLogicalMap = mImpl->mVisualToLogicalMap;
525   Vector<CharacterIndex>& modelLogicalToVisualMap = mImpl->mLogicalToVisualMap;
526
527   if( 0u == numberOfRuns )
528   {
529     modelVisualToLogicalMap.Clear();
530     modelLogicalToVisualMap.Clear();
531   }
532   else
533   {
534     const Length numberOfCharacters = mImpl->mText.Count();
535     modelVisualToLogicalMap.Resize( numberOfCharacters );
536     modelLogicalToVisualMap.Resize( numberOfCharacters );
537
538     CharacterIndex* modelVisualToLogicalMapBuffer = modelVisualToLogicalMap.Begin();
539     CharacterIndex* modelLogicalToVisualMapBuffer = modelLogicalToVisualMap.Begin();
540
541     CharacterIndex lastIndex = 0u;
542     for( unsigned int bidiIndex = 0u; bidiIndex < numberOfRuns; ++bidiIndex )
543     {
544       const BidirectionalLineInfoRun& bidiLineInfo = *( bidirectionalInfo + bidiIndex );
545
546       if( lastIndex < bidiLineInfo.characterRun.characterIndex )
547       {
548         // Fill with the identity.
549         for( ; lastIndex < bidiLineInfo.characterRun.characterIndex; ++lastIndex )
550         {
551           *( modelVisualToLogicalMapBuffer + lastIndex ) = lastIndex;
552         }
553       }
554
555       // Fill the conversion table of the run.
556       for( CharacterIndex index = 0u;
557            index < bidiLineInfo.characterRun.numberOfCharacters;
558            ++index, ++lastIndex )
559       {
560         *( modelVisualToLogicalMapBuffer + lastIndex ) = bidiLineInfo.characterRun.characterIndex + *( bidiLineInfo.visualToLogicalMap + index );
561       }
562     }
563
564     // Complete with the identity if there are some left to right characters after the last right to left.
565     for( ; lastIndex < numberOfCharacters; ++lastIndex )
566     {
567       *( modelVisualToLogicalMapBuffer + lastIndex ) = lastIndex;
568     }
569
570     // Sets the logical to visual conversion map.
571     for( CharacterIndex index = 0u; index < numberOfCharacters; ++index )
572     {
573       *( modelLogicalToVisualMapBuffer + *( modelVisualToLogicalMapBuffer + index ) ) = index;
574     }
575   }
576 }
577
578 void LogicalModel::ReplaceVisualToLogicalMap( CharacterIndex characterIndex,
579                                               Length numberOfCharactersToRemove,
580                                               const BidirectionalLineInfoRun* const bidirectionalInfo,
581                                               Length numberOfCharactersToInsert )
582 {
583 }
584
585 CharacterIndex LogicalModel::GetVisualCharacterIndex( CharacterIndex logicalCharacterIndex ) const
586 {
587   if( 0u == mImpl->mLogicalToVisualMap.Count() )
588   {
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;
592   }
593
594   return *( mImpl->mLogicalToVisualMap.Begin() + logicalCharacterIndex );
595 }
596
597 CharacterIndex LogicalModel::GetLogicalCharacterIndex( CharacterIndex visualCharacterIndex ) const
598 {
599   if( 0u == mImpl->mVisualToLogicalMap.Count() )
600   {
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;
604   }
605
606   return *( mImpl->mVisualToLogicalMap.Begin() + visualCharacterIndex );
607 }
608
609 void LogicalModel::GetLogicalToVisualMap( CharacterIndex* logicalToVisualMap,
610                                           CharacterIndex characterIndex,
611                                           Length numberOfCharacters ) const
612 {
613   memcpy( logicalToVisualMap, mImpl->mLogicalToVisualMap.Begin() + characterIndex, numberOfCharacters * sizeof( CharacterIndex ) );
614 }
615
616 void LogicalModel::GetVisualToLogicalMap( CharacterIndex* visualToLogicalMap,
617                                           CharacterIndex characterIndex,
618                                           Length numberOfCharacters ) const
619 {
620   memcpy( visualToLogicalMap, mImpl->mVisualToLogicalMap.Begin() + characterIndex, numberOfCharacters * sizeof( CharacterIndex ) );
621 }
622
623 LogicalModel::~LogicalModel()
624 {
625   delete mImpl;
626 }
627
628 LogicalModel::LogicalModel()
629 : mImpl( NULL )
630 {
631   mImpl = new LogicalModel::Impl();
632 }
633
634 } // namespace Text
635
636 } // namespace Toolkit
637
638 } // namespace Dali