Multi-language support implementation.
[platform/core/uifw/dali-toolkit.git] / base / dali-toolkit / internal / text / multi-language-support-impl.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/multi-language-support-impl.h>
20
21 // INTERNAL INCLUDES
22 #include <dali/public-api/adaptor-framework/singleton-service.h>
23 #include <dali-toolkit/public-api/text/logical-model.h>
24 #include <dali-toolkit/public-api/text/font-run.h>
25 #include <dali-toolkit/public-api/text/script.h>
26 #include <dali-toolkit/public-api/text/script-run.h>
27 #include <dali/integration-api/debug.h>
28
29 namespace Dali
30 {
31
32 namespace Toolkit
33 {
34
35 namespace Text
36 {
37
38 namespace Internal
39 {
40
41 /**
42  * @brief Retrieves the font Id from the font run for a given character's @p index.
43  *
44  * If the character's index exceeds the current font run it increases the iterator to get the next one.
45  *
46  * @param[in] index The character's index.
47  * @param[in,out] fontRunIt Iterator to the current font run.
48  * @param[in] fontRunEndIt Iterator to one after the last font run.
49  *
50  * @return The font id.
51  */
52 FontId GetFontId( Length index,
53                   Vector<FontRun>::ConstIterator& fontRunIt,
54                   const Vector<FontRun>::ConstIterator& fontRunEndIt )
55 {
56   FontId fontId = 0u;
57
58   if( fontRunIt != fontRunEndIt )
59   {
60     const FontRun& fontRun = *fontRunIt;
61
62     if( ( index >= fontRun.characterRun.characterIndex ) &&
63         ( index < fontRun.characterRun.characterIndex + fontRun.characterRun.numberOfCharacters ) )
64     {
65       fontId = fontRun.fontId;
66     }
67
68     if( index == fontRun.characterRun.characterIndex + fontRun.characterRun.numberOfCharacters )
69     {
70       // All the characters of the current run have been traversed. Get the next one for the next iteration.
71       ++fontRunIt;
72     }
73   }
74
75   return fontId;
76 }
77
78 /**
79  * @brief Retrieves the script Id from the script run for a given character's @p index.
80  *
81  * If the character's index exceeds the current script run it increases the iterator to get the next one.
82  *
83  * @param[in] index The character's index.
84  * @param[in,out] scriptRunIt Iterator to the current font run.
85  * @param[in] scriptRunEndIt Iterator to one after the last script run.
86  *
87  * @return The script.
88  */
89 Script GetScript( Length index,
90                   Vector<ScriptRun>::ConstIterator& scriptRunIt,
91                   const Vector<ScriptRun>::ConstIterator& scriptRunEndIt )
92 {
93   Script script = TextAbstraction::UNKNOWN;
94
95   if( scriptRunIt != scriptRunEndIt )
96   {
97     const ScriptRun& scriptRun = *scriptRunIt;
98
99     if( ( index >= scriptRun.characterRun.characterIndex ) &&
100         ( index < scriptRun.characterRun.characterIndex + scriptRun.characterRun.numberOfCharacters ) )
101     {
102       script = scriptRun.script;
103     }
104
105     if( index == scriptRun.characterRun.characterIndex + scriptRun.characterRun.numberOfCharacters )
106     {
107       // All the characters of the current run have been traversed. Get the next one for the next iteration.
108       ++scriptRunIt;
109     }
110   }
111
112   return script;
113 }
114
115 bool ValidateFontsPerScript::FindValidFont( FontId fontId ) const
116 {
117   for( Vector<FontId>::ConstIterator it = mValidFonts.Begin(),
118          endIt = mValidFonts.End();
119        it != endIt;
120        ++it )
121   {
122     if( fontId == *it )
123     {
124       return true;
125     }
126   }
127
128   return false;
129 }
130
131 MultilanguageSupport::MultilanguageSupport()
132 : mDefaultFontPerScriptCache(),
133   mValidFontsPerScriptCache()
134 {
135   // Initializes the default font cache to zero (invalid font).
136   // Reserves space to cache the default fonts and access them with the script as an index.
137   mDefaultFontPerScriptCache.Resize( TextAbstraction::UNKNOWN, 0u );
138
139   // Initializes the valid fonts cache to NULL (no valid fonts).
140   // Reserves space to cache the valid fonts and access them with the script as an index.
141   mValidFontsPerScriptCache.Resize( TextAbstraction::UNKNOWN, NULL );
142 }
143
144 MultilanguageSupport::~MultilanguageSupport()
145 {
146   // Destroy the valid fonts per script cache.
147
148   for( Vector<ValidateFontsPerScript*>::Iterator it = mValidFontsPerScriptCache.Begin(),
149          endIt = mValidFontsPerScriptCache.End();
150        it != endIt;
151        ++it )
152   {
153     delete *it;
154   }
155 }
156
157 Text::MultilanguageSupport MultilanguageSupport::Get()
158 {
159   Text::MultilanguageSupport multilanguageSupportHandle;
160
161   SingletonService service( SingletonService::Get() );
162   if( service )
163   {
164     // Check whether the singleton is already created
165     Dali::BaseHandle handle = service.GetSingleton( typeid( Text::MultilanguageSupport ) );
166     if( handle )
167     {
168       // If so, downcast the handle
169       MultilanguageSupport* impl = dynamic_cast< Internal::MultilanguageSupport* >( handle.GetObjectPtr() );
170       multilanguageSupportHandle = Text::MultilanguageSupport( impl );
171     }
172     else // create and register the object
173     {
174       multilanguageSupportHandle = Text::MultilanguageSupport( new MultilanguageSupport );
175       service.Register( typeid( multilanguageSupportHandle ), multilanguageSupportHandle );
176     }
177   }
178
179   return multilanguageSupportHandle;
180 }
181
182 void MultilanguageSupport::SetScripts( LogicalModel& model )
183 {
184   // 1) Retrieve the text from the model.
185   const Length numberOfCharacters = model.GetNumberOfCharacters();
186
187   if( 0u == numberOfCharacters )
188   {
189     // Nothing to do if there are no characters.
190     return;
191   }
192
193   Vector<Character> text;
194   text.Resize( numberOfCharacters );
195
196   model.GetText( 0u,
197                  text.Begin(),
198                  numberOfCharacters );
199
200   // 2) Traverse all characters and set the scripts.
201
202   // Stores the current script run.
203   ScriptRun currentScriptRun;
204   currentScriptRun.characterRun.characterIndex = 0u;
205   currentScriptRun.characterRun.numberOfCharacters = 0u;
206   currentScriptRun.script = TextAbstraction::UNKNOWN;
207
208   // Temporary stores the script runs.
209   std::vector<ScriptRun> scriptRuns;
210   scriptRuns.reserve( numberOfCharacters << 2u ); // To reduce the number of reallocations.
211
212   for( Vector<Character>::ConstIterator it = text.Begin(),
213          endIt = text.End();
214        it != endIt;
215        ++it )
216   {
217     const Character character = *it;
218
219     Script script = GetCharacterScript( character );
220
221     if( TextAbstraction::UNKNOWN == script )
222     {
223       script = TextAbstraction::LATIN;
224       DALI_ASSERT_DEBUG( !"MultilanguageSupport::SetScripts. Unkown script!" );
225     }
226
227     if( script != currentScriptRun.script )
228     {
229       // Current run needs to be stored and a new one initialized.
230
231       if( 0u != currentScriptRun.characterRun.numberOfCharacters )
232       {
233         // Store the script run.
234         scriptRuns.push_back( currentScriptRun );
235       }
236
237       // Initialize the new one.
238       currentScriptRun.characterRun.characterIndex = currentScriptRun.characterRun.characterIndex + currentScriptRun.characterRun.numberOfCharacters;
239       currentScriptRun.characterRun.numberOfCharacters = 0u;
240       currentScriptRun.script = script;
241     }
242
243     // Add one more character to the run.
244     ++currentScriptRun.characterRun.numberOfCharacters;
245   }
246
247   if( 0u != currentScriptRun.characterRun.numberOfCharacters )
248   {
249     // Store the last run.
250     scriptRuns.push_back( currentScriptRun );
251   }
252
253   // 3) Set the script runs into the model.
254
255   model.SetScripts( &scriptRuns[0u],
256                     scriptRuns.size() );
257 }
258
259 void MultilanguageSupport::ValidateFonts( LogicalModel& model )
260 {
261   // 1) Retrieve the text from the model.
262   const Length numberOfCharacters = model.GetNumberOfCharacters();
263
264   if( 0u == numberOfCharacters )
265   {
266     // Nothing to do if there are no characters.
267     return;
268   }
269
270   Vector<Character> text;
271   text.Resize( numberOfCharacters );
272
273   Character* textBuffer = text.Begin();
274   model.GetText( 0u,
275                  textBuffer,
276                  numberOfCharacters );
277
278   // 2) Retrieve any font previously set.
279
280   const Length numberOfFontRuns = model.GetNumberOfFontRuns( 0u, numberOfCharacters );
281
282   Vector<FontRun> fontRuns;
283   fontRuns.Reserve( numberOfFontRuns );
284
285   FontRun* fontRunsBuffer = fontRuns.Begin();
286   model.GetFontRuns( fontRunsBuffer,
287                      0u,
288                      numberOfCharacters );
289
290   // 3) Retrieve the scripts from the model.
291
292   const Length numberOfScriptRuns = model.GetNumberOfScriptRuns( 0u, numberOfCharacters );
293
294   Vector<ScriptRun> scriptRuns;
295   scriptRuns.Reserve( numberOfScriptRuns );
296
297   ScriptRun* scriptRunsBuffer = scriptRuns.Begin();
298   model.GetScriptRuns( scriptRunsBuffer,
299                        0u,
300                        numberOfCharacters );
301
302   // 4) Traverse the characters and validate/set the fonts.
303
304   // Get the caches.
305   FontId* defaultFontPerScriptCacheBuffer = mDefaultFontPerScriptCache.Begin();
306   ValidateFontsPerScript** validFontsPerScriptCacheBuffer = mValidFontsPerScriptCache.Begin();
307
308   // Stores the validated font runs.
309   Vector<FontRun> validatedFontRuns;
310   validatedFontRuns.Reserve( numberOfFontRuns );
311
312   // Initializes a validated font run.
313   FontRun currentFontRun;
314   currentFontRun.characterRun.characterIndex = 0u;
315   currentFontRun.characterRun.numberOfCharacters = 0u;
316   currentFontRun.fontId = 0u;
317   currentFontRun.isDefault = false;
318
319   // Get the font client.
320   TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
321
322   // Iterators of the font and script runs.
323   Vector<FontRun>::ConstIterator fontRunIt = fontRuns.Begin();
324   Vector<FontRun>::ConstIterator fontRunEndIt = fontRuns.End();
325   Vector<ScriptRun>::ConstIterator scriptRunIt = scriptRuns.Begin();
326   Vector<ScriptRun>::ConstIterator scriptRunEndIt = scriptRuns.End();
327
328   for( Length index = 0u; index < numberOfCharacters; ++index )
329   {
330     // Get the character.
331     const Character character = *( textBuffer + index );
332
333     // Get the font for the character.
334     FontId fontId = GetFontId( index,
335                                fontRunIt,
336                                fontRunEndIt );
337
338     // Get the script for the character.
339     Script script = GetScript( index,
340                                scriptRunIt,
341                                scriptRunEndIt );
342
343     if( TextAbstraction::UNKNOWN == script )
344     {
345       DALI_LOG_WARNING( "MultilanguageSupport::ValidateFonts. Unknown script!" );
346       script = TextAbstraction::LATIN;
347     }
348
349     // Whether the font being validated is a default one not set by the user.
350     const bool isDefault = ( 0u == fontId );
351
352     // The default font point size.
353     PointSize26Dot6 pointSize = TextAbstraction::FontClient::DEFAULT_POINT_SIZE;
354
355     if( !isDefault )
356     {
357       // Validate if the font set by the user supports the character.
358
359       // Check first in the caches.
360
361       // The user may have set the default font. Check it. Otherwise check in the valid fonts cache.
362       if( fontId != *( defaultFontPerScriptCacheBuffer + script ) )
363       {
364         // Check in the valid fonts cache.
365         ValidateFontsPerScript* validateFontsPerScript = *( validFontsPerScriptCacheBuffer + script );
366         if( NULL != validateFontsPerScript )
367         {
368           if( !validateFontsPerScript->FindValidFont( fontId ) )
369           {
370             // Use the font client to validate the font.
371             const GlyphIndex glyphIndex = fontClient.GetGlyphIndex( fontId, character );
372
373             if( 0u == glyphIndex )
374             {
375               // Get the point size of the current font. It will be used to get a default font id.
376               pointSize = fontClient.GetPointSize( fontId );
377
378               // The font is not valid. Set to zero and a default one will be set.
379               fontId = 0u;
380             }
381             else
382             {
383               // Add the font to the valid font cache.
384               validateFontsPerScript->mValidFonts.PushBack( fontId );
385             }
386           }
387         }
388         else
389         {
390           // Use the font client to validate the font.
391           const GlyphIndex glyphIndex = fontClient.GetGlyphIndex( fontId, character );
392
393           if( 0u == glyphIndex )
394           {
395             // Get the point size of the current font. It will be used to get a default font id.
396             pointSize = fontClient.GetPointSize( fontId );
397
398             // The font is not valid. Set to zero and a default one will be set.
399             fontId = 0u;
400           }
401           else
402           {
403             // Add the font to the valid font cache.
404             validateFontsPerScript = new ValidateFontsPerScript();
405             *( validFontsPerScriptCacheBuffer + script ) = validateFontsPerScript;
406
407             validateFontsPerScript->mValidFonts.PushBack( fontId );
408           }
409         }
410       }
411     }
412
413     // The font has not been validated. Find a default one.
414     if( 0u == fontId )
415     {
416       // The character has no font assigned. Get a default one from the cache
417       fontId = *( defaultFontPerScriptCacheBuffer + script );
418
419       // If the cache has not a default font, get one from the font client.
420       if( 0u == fontId )
421       {
422         // Find a default font.
423         fontId = fontClient.FindDefaultFont( character, pointSize );
424
425         // Cache the font.
426         *( defaultFontPerScriptCacheBuffer + script ) = fontId;
427       }
428     }
429
430     // The font is now validated.
431
432     if( ( fontId != currentFontRun.fontId ) ||
433         ( isDefault != currentFontRun.isDefault ) )
434     {
435       // Current run needs to be stored and a new one initialized.
436
437       if( 0u != currentFontRun.characterRun.numberOfCharacters )
438       {
439         // Store the font run.
440         validatedFontRuns.PushBack( currentFontRun );
441       }
442
443       // Initialize the new one.
444       currentFontRun.characterRun.characterIndex = currentFontRun.characterRun.characterIndex + currentFontRun.characterRun.numberOfCharacters;
445       currentFontRun.characterRun.numberOfCharacters = 0u;
446       currentFontRun.fontId = fontId;
447       currentFontRun.isDefault = isDefault;
448     }
449
450     // Add one more character to the run.
451     ++currentFontRun.characterRun.numberOfCharacters;
452   }
453
454   if( 0u != currentFontRun.characterRun.numberOfCharacters )
455   {
456     // Store the last run.
457     validatedFontRuns.PushBack( currentFontRun );
458   }
459
460   // 5) Sets the validated font runs to the model.
461   model.SetFonts( validatedFontRuns.Begin(),
462                   validatedFontRuns.Count() );
463 }
464
465 } // namespace Internal
466
467 } // namespace Text
468
469 } // namespace Toolkit
470
471 } // namespace Dali